first commit for chrg

This commit is contained in:
wmano
2025-08-16 22:58:22 +08:00
commit 52a3ed5862
2306 changed files with 1021208 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
menu "Device Drivers"
rsource "core/Kconfig"
rsource "ipc/Kconfig"
rsource "serial/Kconfig"
rsource "can/Kconfig"
rsource "cputime/Kconfig"
rsource "i2c/Kconfig"
rsource "phy/Kconfig"
rsource "misc/Kconfig"
rsource "mtd/Kconfig"
rsource "pm/Kconfig"
rsource "rtc/Kconfig"
rsource "sdio/Kconfig"
rsource "spi/Kconfig"
rsource "watchdog/Kconfig"
rsource "audio/Kconfig"
rsource "sensor/Kconfig"
rsource "touch/Kconfig"
rsource "graphic/Kconfig"
rsource "hwcrypto/Kconfig"
rsource "wlan/Kconfig"
rsource "led/Kconfig"
rsource "mailbox/Kconfig"
rsource "phye/Kconfig"
rsource "ata/Kconfig"
rsource "nvme/Kconfig"
rsource "block/Kconfig"
rsource "scsi/Kconfig"
rsource "regulator/Kconfig"
rsource "reset/Kconfig"
rsource "thermal/Kconfig"
rsource "virtio/Kconfig"
rsource "dma/Kconfig"
rsource "mfd/Kconfig"
rsource "ofw/Kconfig"
rsource "pci/Kconfig"
rsource "pic/Kconfig"
rsource "pin/Kconfig"
rsource "pinctrl/Kconfig"
rsource "ktime/Kconfig"
rsource "clk/Kconfig"
rsource "hwtimer/Kconfig"
rsource "usb/Kconfig"
endmenu
+14
View File
@@ -0,0 +1,14 @@
# for module compiling
import os
from building import *
cwd = GetCurrentDir()
objs = []
list = os.listdir(cwd)
for d in list:
path = os.path.join(cwd, d)
if os.path.isfile(os.path.join(path, 'SConscript')):
objs = objs + SConscript(os.path.join(d, 'SConscript'))
Return('objs')
+22
View File
@@ -0,0 +1,22 @@
menuconfig RT_USING_ATA
bool "Using Advanced Technology Attachment (ATA) device drivers"
depends on RT_USING_DM
depends on RT_USING_BLK
depends on RT_USING_DMA
default n
config RT_ATA_AHCI
bool "Advanced Host Controller Interface (AHCI)"
depends on RT_USING_ATA
depends on RT_USING_SCSI
default y
config RT_ATA_AHCI_PCI
bool "AHCI support on PCI bus"
depends on RT_ATA_AHCI
depends on RT_USING_PCI
default n
if RT_USING_ATA
osource "$(SOC_DM_ATA_DIR)/Kconfig"
endif
@@ -0,0 +1,21 @@
from building import *
group = []
if not GetDepend(['RT_USING_ATA']):
Return('group')
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../include']
src = []
if GetDepend(['RT_ATA_AHCI']):
src += ['ahci.c']
if GetDepend(['RT_ATA_AHCI_PCI']):
src += ['ahci-pci.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
+206
View File
@@ -0,0 +1,206 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#define AHCI_REG_BAR 5
struct pci_ahci_quirk
{
int bar_idx;
rt_bool_t bar_offset;
const struct rt_ahci_ops *ops;
};
struct pci_ahci_host
{
struct rt_ahci_host parent;
const struct pci_ahci_quirk *quirk;
rt_bool_t is_msi;
};
#define raw_to_pci_ahci_host(raw) rt_container_of(raw, struct pci_ahci_host, parent)
static rt_err_t pci_ahci_init(struct rt_ahci_host *host)
{
struct rt_pci_device *pdev;
pdev = rt_container_of(host->parent.dev, struct rt_pci_device, parent);
if (pdev->vendor == PCI_VENDOR_ID_JMICRON)
{
rt_pci_write_config_u8(pdev, 0x41, 0xa1);
}
return RT_EOK;
}
static const struct rt_ahci_ops pci_ahci_ops =
{
.host_init = pci_ahci_init,
};
static rt_err_t pci_ahci_intel_init(struct rt_ahci_host *host)
{
rt_uint16_t val;
struct rt_pci_device *pdev;
pdev = rt_container_of(host->parent.dev, struct rt_pci_device, parent);
rt_pci_read_config_u16(pdev, 0x92, &val);
rt_pci_write_config_u16(pdev, 0x92, val & ~0xf);
rt_thread_mdelay(10);
rt_pci_write_config_u16(pdev, 0x92, val | 0xf);
return RT_EOK;
}
static const struct rt_ahci_ops pci_ahci_intel_ops =
{
.host_init = pci_ahci_intel_init,
};
static rt_err_t pci_ahci_probe(struct rt_pci_device *pdev)
{
rt_err_t err;
int bar_idx;
struct rt_ahci_host *ahci;
struct pci_ahci_host *pci_ahci = rt_calloc(1, sizeof(*pci_ahci));
const struct pci_ahci_quirk *quirk = pdev->id->data;
if (!pci_ahci)
{
return -RT_ENOMEM;
}
pci_ahci->quirk = quirk;
ahci = &pci_ahci->parent;
ahci->parent.dev = &pdev->parent;
bar_idx = quirk && quirk->bar_offset ? quirk->bar_idx : AHCI_REG_BAR;
ahci->regs = rt_pci_iomap(pdev, bar_idx);
if (!ahci->regs)
{
err = -RT_EIO;
goto _fail;
}
ahci->ops = quirk && quirk->ops ? quirk->ops : &pci_ahci_ops;
if (rt_pci_msi_enable(pdev) > 0)
{
pci_ahci->is_msi = RT_TRUE;
}
else
{
rt_pci_irq_unmask(pdev);
}
ahci->irq = pdev->irq;
rt_pci_set_master(pdev);
if ((err = rt_ahci_host_register(ahci)))
{
goto _disable;
}
pdev->parent.user_data = pci_ahci;
return RT_EOK;
_disable:
if (pci_ahci->is_msi)
{
rt_pci_msix_disable(pdev);
}
else
{
rt_pci_irq_mask(pdev);
}
rt_pci_clear_master(pdev);
rt_iounmap(ahci->regs);
_fail:
rt_free(pci_ahci);
return err;
}
static rt_err_t pci_ahci_remove(struct rt_pci_device *pdev)
{
struct rt_ahci_host *ahci;
struct pci_ahci_host *pci_ahci = pdev->parent.user_data;
ahci = &pci_ahci->parent;
rt_ahci_host_unregister(ahci);
if (pci_ahci->is_msi)
{
rt_pci_msi_disable(pdev);
}
else
{
/* INTx is shared, don't mask all */
rt_hw_interrupt_umask(pdev->irq);
rt_pci_irq_mask(pdev);
}
rt_pci_clear_master(pdev);
rt_iounmap(ahci->regs);
rt_free(pci_ahci);
return RT_EOK;
}
static rt_err_t pci_ahci_shutdown(struct rt_pci_device *pdev)
{
return pci_ahci_remove(pdev);
}
static struct pci_ahci_quirk intel_quirk =
{
.ops = &pci_ahci_intel_ops,
};
static struct pci_ahci_quirk cavium_sata_quirk =
{
.bar_idx = 0,
.bar_offset = RT_TRUE,
};
static const struct rt_pci_device_id pci_ahci_ids[] =
{
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_INTEL, 0x2922), .data = &intel_quirk },
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_ASMEDIA, 0x0611) },
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_MARVELL, 0x6121) },
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_MARVELL, 0x6145) },
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_CAVIUM, 0xa01c), .data = &cavium_sata_quirk },
{ RT_PCI_DEVICE_CLASS(PCIS_STORAGE_SATA_AHCI, ~0) },
{ /* sentinel */ }
};
static struct rt_pci_driver pci_ahci_driver =
{
.name = "ahci-pci",
.ids = pci_ahci_ids,
.probe = pci_ahci_probe,
.remove = pci_ahci_remove,
.shutdown = pci_ahci_shutdown,
};
RT_PCI_DRIVER_EXPORT(pci_ahci_driver);
+896
View File
@@ -0,0 +1,896 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "rtdm.ahci"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#define HWREG32_FLUSH(base, value) \
do { \
rt_uint32_t __value = value; \
HWREG32(base) = __value; \
__value = HWREG32(base); \
} while (0)
static void ahci_fill_cmd_slot(struct rt_ahci_port *port, rt_uint32_t opts)
{
rt_ubase_t dma_addr = port->cmd_tbl_dma;
struct rt_ahci_cmd_hdr *cmd_slot = port->cmd_slot;
cmd_slot->opts = rt_cpu_to_le32(opts);
cmd_slot->status = 0;
cmd_slot->tbl_addr_lo = rt_cpu_to_le32(rt_lower_32_bits(dma_addr));
cmd_slot->tbl_addr_hi = rt_cpu_to_le32(rt_upper_32_bits(dma_addr));
}
static int ahci_fill_sg(struct rt_ahci_host *host, int id,
void *buffer, rt_size_t buffer_size)
{
int sg_count;
rt_ubase_t dma_addr;
struct rt_ahci_port *port = &host->ports[id];
struct rt_ahci_sg *ahci_sg = port->cmd_tbl_sg;
sg_count = ((buffer_size - 1) / RT_ACHI_PRDT_BYTES_MAX) + 1;
if (sg_count > RT_AHCI_MAX_SG)
{
return -1;
}
dma_addr = (rt_ubase_t)rt_kmem_v2p(buffer);
for (int i = 0; i < sg_count; ++i, ++ahci_sg)
{
ahci_sg->addr_lo = rt_cpu_to_le32(rt_lower_32_bits(dma_addr));
ahci_sg->addr_hi = rt_cpu_to_le32(rt_upper_32_bits(dma_addr));
if (ahci_sg->addr_hi && !(host->cap & RT_AHCI_CAP_64))
{
return -1;
}
ahci_sg->flags_size = rt_cpu_to_le32(0x3fffff &
(rt_min_t(rt_uint32_t, buffer_size, RT_ACHI_PRDT_BYTES_MAX) - 1));
dma_addr += RT_ACHI_PRDT_BYTES_MAX;
buffer_size -= RT_ACHI_PRDT_BYTES_MAX;
}
return sg_count;
}
static rt_err_t ahci_request_io(struct rt_ahci_host *host, int id,
void *fis, rt_size_t fis_size,
void *buffer, rt_size_t buffer_size, rt_bool_t is_read)
{
int sg_count;
rt_err_t err;
struct rt_ahci_port *port = &host->ports[id];
if ((HWREG32(port->regs + RT_AHCI_PORT_SSTS) & 0xf) != RT_AHCI_PORT_SSTS_DET_PHYRDY)
{
return -RT_EIO;
}
if ((sg_count = ahci_fill_sg(host, id, buffer, buffer_size)) <= 0)
{
return -RT_EINVAL;
}
rt_memcpy(port->cmd_tbl, fis, fis_size);
ahci_fill_cmd_slot(port, (fis_size >> 2) | (sg_count << 16) | (!is_read << 6));
if (!is_read)
{
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, buffer, buffer_size);
}
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CI, 1);
err = rt_completion_wait(&port->done, rt_tick_from_millisecond(10000));
if (!err && is_read)
{
rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, buffer, buffer_size);
}
return err;
}
static rt_err_t ahci_scsi_cmd_rw(struct rt_ahci_host *host, int id,
rt_off_t lba, void *buffer, rt_ssize_t size, rt_bool_t is_read)
{
rt_err_t err;
rt_uint8_t fis[20];
struct rt_ahci_port *port = &host->ports[id];
rt_memset(fis, 0, sizeof(fis));
fis[0] = RT_AHCI_FIS_TYPE_REG_H2D;
fis[1] = 1 << 7; /* Command */
fis[2] = is_read ? RT_AHCI_ATA_CMD_READ_EXT : RT_AHCI_ATA_CMD_WRITE_EXT;
while (size > 0)
{
rt_size_t t_size, t_lba;
t_lba = rt_min_t(rt_size_t, host->max_blocks, size);
t_size = port->block_size * t_lba;
fis[3] = 0xe0; /* Features */
fis[4] = (lba >> 0) & 0xff; /* LBA low register */
fis[5] = (lba >> 8) & 0xff; /* LBA mid register */
fis[6] = (lba >> 16) & 0xff; /* LBA high register */
fis[7] = 1 << 6; /* Device */
fis[8] = ((lba >> 24) & 0xff); /* LBA register, 31:24 */
fis[9] = ((lba >> 32) & 0xff); /* LBA register, 39:32 */
fis[10] = ((lba >> 40) & 0xff); /* LBA register, 47:40 */
fis[12] = (t_lba >> 0) & 0xff; /* Count register, 7:0 */
fis[13] = (t_lba >> 8) & 0xff; /* Count register, 15:8 */
if ((err = ahci_request_io(host, id, fis, sizeof(fis), buffer, t_size, is_read)))
{
return err;
}
size -= t_lba;
lba += t_lba;
buffer += t_size;
}
return RT_EOK;
}
static rt_err_t ahci_scsi_synchronize_cache(struct rt_ahci_host *host, int id,
rt_off_t lba, rt_size_t size)
{
rt_uint8_t fis[20];
rt_uint16_t *ataid;
struct rt_ahci_port *port = &host->ports[id];
ataid = port->ataid;
if (!rt_ahci_ata_id_wcache_enabled(ataid) &&
!rt_ahci_ata_id_has_flush(ataid) &&
!rt_ahci_ata_id_has_flush_ext(ataid))
{
return -RT_ENOSYS;
}
rt_memset(fis, 0, sizeof(fis));
fis[0] = RT_AHCI_FIS_TYPE_REG_H2D;
fis[1] = 1 << 7; /* Command */
if (rt_ahci_ata_id_has_flush_ext(ataid))
{
fis[2] = RT_AHCI_ATA_CMD_FLUSH_EXT;
}
else
{
fis[2] = RT_AHCI_ATA_CMD_FLUSH;
}
rt_memcpy(port->cmd_tbl, fis, 20);
ahci_fill_cmd_slot(port, 5);
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CI, 1);
return rt_completion_wait(&port->done, rt_tick_from_millisecond(5000));
}
static rt_err_t ahci_scsi_cmd_write_same(struct rt_ahci_host *host, int id,
rt_off_t lba, rt_size_t size)
{
rt_uint8_t fis[20];
struct rt_ahci_port *port = &host->ports[id];
rt_memset(fis, 0, sizeof(fis));
fis[0] = RT_AHCI_FIS_TYPE_REG_H2D;
fis[1] = 1 << 7; /* Command */
fis[2] = RT_AHCI_ATA_CMD_DSM;
fis[3] = RT_AHCI_ATA_DSM_TRIM; /* Features */
fis[4] = (lba >> 0) & 0xff; /* LBA low register */
fis[5] = (lba >> 8) & 0xff; /* LBA mid register */
fis[6] = (lba >> 16) & 0xff; /* LBA high register */
fis[7] = 1 << 6; /* Device */
fis[8] = ((lba >> 24) & 0xff); /* LBA register, 31:24 */
fis[9] = ((lba >> 32) & 0xff); /* LBA register, 39:32 */
fis[10] = ((lba >> 40) & 0xff); /* LBA register, 47:40 */
fis[12] = (size >> 0) & 0xff; /* Count register, 7:0 */
fis[13] = (size >> 8) & 0xff; /* Count register, 15:8 */
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CI, 1);
return rt_completion_wait(&port->done, rt_tick_from_millisecond(5000));
}
static rt_err_t ahci_scsi_cmd_read_capacity(struct rt_ahci_host *host, int id,
rt_size_t *out_last_block, rt_size_t *out_block_size)
{
struct rt_ahci_port *port = &host->ports[id];
if (!port->ataid)
{
return -RT_EIO;
}
*out_last_block = rt_ahci_ata_id_n_sectors(port->ataid) - 1;
*out_block_size = port->block_size;
return RT_EOK;
}
static rt_err_t ahci_scsi_cmd_test_unit_ready(struct rt_ahci_host *host, int id)
{
struct rt_ahci_port *port = &host->ports[id];
return port->ataid ? RT_EOK : -RT_EIO;
}
static rt_err_t ahci_scsi_cmd_inquiry(struct rt_ahci_host *host, int id,
char *prodid, rt_size_t prodid_len, char *prodrev, rt_size_t prodrev_len)
{
rt_err_t err;
rt_uint8_t fis[20];
rt_uint16_t *ataid;
struct rt_ahci_port *port = &host->ports[id];
if (!port->link)
{
return -RT_EIO;
}
if (!port->ataid && !(port->ataid = rt_malloc(RT_AHCI_ATA_ID_WORDS * 2)))
{
return -RT_ENOMEM;
}
ataid = port->ataid;
rt_memset(fis, 0, sizeof(fis));
fis[0] = RT_AHCI_FIS_TYPE_REG_H2D;
fis[1] = 1 << 7; /* Command */
fis[2] = RT_AHCI_ATA_CMD_ID_ATA;
if ((err = ahci_request_io(host, id, fis, sizeof(fis),
ataid, RT_AHCI_ATA_ID_WORDS * 2, RT_TRUE)))
{
return err;
}
for (int i = 0; i < RT_AHCI_ATA_ID_WORDS; ++i)
{
ataid[i] = rt_le16_to_cpu(ataid[i]);
}
for (int i = 0; i < prodid_len / 2; ++i)
{
rt_uint16_t src = ataid[RT_AHCI_ATA_ID_PROD + i];
prodid[i] = (src & 0x00ff) << 8 | (src & 0xff00) >> 8;
}
for (int i = 0; i < prodrev_len / 2; ++i)
{
rt_uint16_t src = ataid[RT_AHCI_ATA_ID_FW_REV + i];
prodrev[i] = (src & 0x00ff) << 8 | (src & 0xff00) >> 8;
}
return err;
}
static rt_err_t ahci_scsi_transfer(struct rt_scsi_device *sdev,
struct rt_scsi_cmd *cmd)
{
rt_err_t err;
struct rt_ahci_host *host;
host = rt_container_of(sdev->host, struct rt_ahci_host, parent);
switch (cmd->op.unknow.opcode)
{
case RT_SCSI_CMD_REQUEST_SENSE:
{
struct rt_scsi_request_sense_data *request_sense = &cmd->data.request_sense;
request_sense->error_code = 0x72;
err = RT_EOK;
}
break;
case RT_SCSI_CMD_READ10:
{
struct rt_scsi_read10 *read10 = &cmd->op.read10;
err = ahci_scsi_cmd_rw(host, sdev->id,
rt_be32_to_cpu(read10->lba),
cmd->data.ptr,
rt_be16_to_cpu(read10->size),
RT_TRUE);
}
break;
case RT_SCSI_CMD_READ16:
{
struct rt_scsi_read16 *read16 = &cmd->op.read16;
err = ahci_scsi_cmd_rw(host, sdev->id,
rt_be64_to_cpu(read16->lba),
cmd->data.ptr,
rt_be32_to_cpu(read16->size),
RT_TRUE);
}
break;
case RT_SCSI_CMD_READ12:
{
struct rt_scsi_read12 *read12 = &cmd->op.read12;
err = ahci_scsi_cmd_rw(host, sdev->id,
rt_be32_to_cpu(read12->lba),
cmd->data.ptr,
rt_be32_to_cpu(read12->size),
RT_TRUE);
}
break;
case RT_SCSI_CMD_WRITE10:
{
struct rt_scsi_write10 *write10 = &cmd->op.write10;
err = ahci_scsi_cmd_rw(host, sdev->id,
rt_be32_to_cpu(write10->lba),
cmd->data.ptr,
rt_be16_to_cpu(write10->size),
RT_FALSE);
}
break;
case RT_SCSI_CMD_WRITE16:
{
struct rt_scsi_write16 *write16 = &cmd->op.write16;
err = ahci_scsi_cmd_rw(host, sdev->id,
rt_be64_to_cpu(write16->lba),
cmd->data.ptr,
rt_be32_to_cpu(write16->size),
RT_FALSE);
}
break;
case RT_SCSI_CMD_WRITE12:
{
struct rt_scsi_write12 *write12 = &cmd->op.write12;
err = ahci_scsi_cmd_rw(host, sdev->id,
rt_be32_to_cpu(write12->lba),
cmd->data.ptr,
rt_be32_to_cpu(write12->size),
RT_FALSE);
}
break;
case RT_SCSI_CMD_SYNCHRONIZE_CACHE10:
{
struct rt_scsi_synchronize_cache10 *synchronize_cache10 = &cmd->op.synchronize_cache10;
err = ahci_scsi_synchronize_cache(host, sdev->id,
rt_be32_to_cpu(synchronize_cache10->lba),
rt_be16_to_cpu(synchronize_cache10->size));
}
break;
case RT_SCSI_CMD_SYNCHRONIZE_CACHE16:
{
struct rt_scsi_synchronize_cache16 *synchronize_cache16 = &cmd->op.synchronize_cache16;
err = ahci_scsi_synchronize_cache(host, sdev->id,
rt_be64_to_cpu(synchronize_cache16->lba),
rt_be32_to_cpu(synchronize_cache16->size));
}
break;
case RT_SCSI_CMD_WRITE_SAME10:
{
struct rt_scsi_write_same10 *write_same10 = &cmd->op.write_same10;
err = ahci_scsi_cmd_write_same(host, sdev->id,
rt_be32_to_cpu(write_same10->lba), rt_be16_to_cpu(write_same10->size));
}
break;
case RT_SCSI_CMD_WRITE_SAME16:
{
struct rt_scsi_write_same16 *write_same16 = &cmd->op.write_same16;
err = ahci_scsi_cmd_write_same(host, sdev->id,
rt_be64_to_cpu(write_same16->lba), rt_be32_to_cpu(write_same16->size));
}
break;
case RT_SCSI_CMD_READ_CAPACITY10:
{
rt_size_t last_block, block_size;
struct rt_scsi_read_capacity10_data *data = &cmd->data.read_capacity10;
err = ahci_scsi_cmd_read_capacity(host, sdev->id, &last_block, &block_size);
if (!err)
{
if (last_block > 0x100000000ULL)
{
last_block = 0xffffffff;
}
data->last_block = rt_cpu_to_be32(last_block);
data->block_size = rt_cpu_to_be32(block_size);
}
}
break;
case RT_SCSI_CMD_READ_CAPACITY16:
{
rt_size_t last_block, block_size;
struct rt_scsi_read_capacity16_data *data = &cmd->data.read_capacity16;
err = ahci_scsi_cmd_read_capacity(host, sdev->id, &last_block, &block_size);
if (!err)
{
data->last_block = rt_cpu_to_be64(last_block);
data->block_size = rt_cpu_to_be32(block_size);
}
}
break;
case RT_SCSI_CMD_TEST_UNIT_READY:
err = ahci_scsi_cmd_test_unit_ready(host, sdev->id);
break;
case RT_SCSI_CMD_INQUIRY:
{
struct rt_ahci_port *port = &host->ports[sdev->id];
struct rt_scsi_inquiry_data *inquiry = &cmd->data.inquiry;
err = ahci_scsi_cmd_inquiry(host, sdev->id,
inquiry->prodid, sizeof(inquiry->prodid),
inquiry->prodrev, sizeof(inquiry->prodrev));
if (!err)
{
rt_memcpy(inquiry->vendor, "ATA ", sizeof(inquiry->vendor));
if (HWREG32(port->regs + RT_AHCI_PORT_SIG) != RT_AHCI_PORT_SIG_SATA_CDROM)
{
port->block_size = 512;
inquiry->devtype = SCSI_DEVICE_TYPE_DIRECT;
}
else
{
port->block_size = 2048;
inquiry->devtype = SCSI_DEVICE_TYPE_CDROM;
}
inquiry->rmb = 0;
inquiry->length = 95 - 4;
}
}
break;
case RT_SCSI_CMD_MODE_SENSE:
case RT_SCSI_CMD_MODE_SENSE10:
case RT_SCSI_CMD_MODE_SELECT:
case RT_SCSI_CMD_MODE_SELECT10:
return -RT_ENOSYS;
default:
return -RT_EINVAL;
}
return err;
}
static struct rt_scsi_ops ahci_scsi_ops =
{
.transfer = ahci_scsi_transfer,
};
static void ahci_isr(int irqno, void *param)
{
int id;
rt_uint32_t isr;
rt_bitmap_t int_map;
struct rt_ahci_port *port;
struct rt_ahci_host *host = param;
int_map = HWREG32(host->regs + RT_AHCI_HBA_INTS);
rt_bitmap_for_each_set_bit(&int_map, id, host->ports_nr)
{
port = &host->ports[id];
isr = HWREG32(port->regs + RT_AHCI_PORT_INTS);
if (port->link)
{
if (host->ops->port_isr)
{
host->ops->port_isr(host, port, isr);
}
rt_completion_done(&port->done);
}
HWREG32(port->regs + RT_AHCI_PORT_INTS) = isr;
}
HWREG32(host->regs + RT_AHCI_HBA_INTS) = int_map;
}
rt_err_t rt_ahci_host_register(struct rt_ahci_host *host)
{
rt_err_t err;
rt_uint32_t value;
char dev_name[RT_NAME_MAX];
struct rt_scsi_host *scsi;
if (!host || !host->parent.dev || !host->ops)
{
return -RT_EINVAL;
}
host->max_blocks = host->max_blocks ? : 0x80;
/*
* 1. Reset HBA.
*/
err = -RT_EIO;
value = HWREG32(host->regs + RT_AHCI_HBA_GHC);
if (!(value & RT_AHCI_GHC_RESET))
{
HWREG32_FLUSH(host->regs + RT_AHCI_HBA_GHC, value | RT_AHCI_GHC_RESET);
}
for (int i = 0; i < 5; ++i)
{
rt_thread_mdelay(200);
if (!(HWREG32(host->regs + RT_AHCI_HBA_GHC) & RT_AHCI_GHC_RESET))
{
err = RT_EOK;
break;
}
}
if (err)
{
goto _fail;
}
/*
* 2. Enable AHCI and get the ports' information.
*/
HWREG32_FLUSH(host->regs + RT_AHCI_HBA_GHC, RT_AHCI_GHC_AHCI_EN);
host->cap = HWREG32(host->regs + RT_AHCI_HBA_CAP);
host->cap &= RT_AHCI_CAP_SPM | RT_AHCI_CAP_SSS | RT_AHCI_CAP_SIS;
HWREG32(host->regs + RT_AHCI_HBA_CAP) = host->cap;
host->cap = HWREG32(host->regs + RT_AHCI_HBA_CAP);
HWREG32_FLUSH(host->regs + RT_AHCI_HBA_PI, 0xf);
if (host->ops->host_init && (err = host->ops->host_init(host)))
{
goto _fail;
}
host->ports_nr = (host->cap & RT_AHCI_CAP_NP) + 1;
host->ports_map = HWREG32(host->regs + RT_AHCI_HBA_PI);
/* Check implemented in firmware */
rt_dm_dev_prop_read_u32(host->parent.dev, "ports-implemented", &host->ports_map);
for (int i = 0; i < host->ports_nr; ++i)
{
struct rt_ahci_port *port;
if (!(host->ports_map & RT_BIT(i)))
{
continue;
}
port = &host->ports[i];
/*
* 3. Alloc port io memory.
*/
port->regs = host->regs + 0x100 + (i * 0x80);
/*
* 4. Make port stop.
*/
value = HWREG32(port->regs + RT_AHCI_PORT_CMD);
if (value & (RT_AHCI_PORT_CMD_LIST_ON | RT_AHCI_PORT_CMD_FIS_ON |
RT_AHCI_PORT_CMD_FIS_RX | RT_AHCI_PORT_CMD_START))
{
value &= ~(RT_AHCI_PORT_CMD_LIST_ON | RT_AHCI_PORT_CMD_FIS_ON |
RT_AHCI_PORT_CMD_FIS_RX | RT_AHCI_PORT_CMD_START);
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CMD, value);
rt_thread_mdelay(500);
}
if (host->ops->port_init && (err = host->ops->port_init(host, port)))
{
LOG_E("Init port[%d] error = %s", rt_strerror(err));
continue;
}
value = HWREG32(port->regs + RT_AHCI_PORT_CMD);
value |= RT_AHCI_PORT_CMD_SPIN_UP;
HWREG32(port->regs + RT_AHCI_PORT_CMD) = value;
/*
* 5. Enable port's SATA link.
*/
if (host->ops->port_link_up)
{
err = host->ops->port_link_up(host, port);
}
else
{
err = -RT_ETIMEOUT;
for (int retry = 0; retry < 5; ++retry)
{
value = HWREG32(port->regs + RT_AHCI_PORT_SSTS);
if ((value & RT_AHCI_PORT_SSTS_DET_MASK) == RT_AHCI_PORT_SSTS_DET_PHYRDY)
{
err = RT_EOK;
break;
}
rt_thread_mdelay(2);
}
}
if (err)
{
if (HWREG32(port->regs + RT_AHCI_PORT_SSTS) & RT_AHCI_PORT_SSTS_DET_MASK)
{
LOG_E("SATA[%d] link error = %s", i, rt_strerror(err));
}
else
{
LOG_D("SATA[%d] not device", i);
}
continue;
}
/* Clear error status */
if ((value = HWREG32(port->regs + RT_AHCI_PORT_SERR)))
{
HWREG32(port->regs + RT_AHCI_PORT_SERR) = value;
}
for (int retry = 0; retry < 5; ++retry)
{
value = HWREG32(port->regs + RT_AHCI_PORT_TFD);
if (!(value & (RT_AHCI_PORT_TFDATA_BSY | RT_AHCI_PORT_TFDATA_DRQ)))
{
break;
}
rt_thread_mdelay(2);
value = HWREG32(port->regs + RT_AHCI_PORT_SSTS);
if ((value & RT_AHCI_PORT_SSTS_DET_MASK) == RT_AHCI_PORT_SSTS_DET_PHYRDY)
{
break;
}
}
value = HWREG32(port->regs + RT_AHCI_PORT_SSTS) & RT_AHCI_PORT_SSTS_DET_MASK;
if (value == RT_AHCI_PORT_SSTS_DET_COMINIT)
{
/* Retry to setup */
--i;
continue;
}
/* Clear error */
value = HWREG32(port->regs + RT_AHCI_PORT_SERR);
HWREG32(port->regs + RT_AHCI_PORT_SERR) = value;
/* Clear pending IRQ */
if ((value = HWREG32(port->regs + RT_AHCI_PORT_INTS)))
{
HWREG32(port->regs + RT_AHCI_PORT_INTS) = value;
}
HWREG32(host->regs + RT_AHCI_HBA_INTS) = RT_BIT(i);
value = HWREG32(port->regs + RT_AHCI_PORT_SSTS);
if ((value & RT_AHCI_PORT_SSTS_DET_MASK) == RT_AHCI_PORT_SSTS_DET_PHYRDY)
{
port->link = RT_TRUE;
}
}
HWREG32(host->regs + RT_AHCI_HBA_GHC) |= RT_AHCI_GHC_IRQ_EN;
for (int i = 0; i < host->ports_nr; ++i)
{
void *dma;
rt_ubase_t dma_addr;
rt_tick_t timeout;
struct rt_ahci_port *port = &host->ports[i];
if (!port->link)
{
continue;
}
/*
* 6. Alloc transport memory, Port x Command List and FIS Base Address.
*/
port->dma = rt_dma_alloc_coherent(host->parent.dev,
RT_AHCI_DMA_SIZE, &port->dma_handle);
if (!port->dma)
{
LOG_E("No memory to setup port[%d]", i);
break;
}
dma = port->dma;
rt_memset(dma, 0, RT_AHCI_DMA_SIZE);
port->cmd_slot = dma;
dma += (RT_AHCI_CMD_SLOT_SIZE + 224);
port->rx_fis = dma;
dma += RT_AHCI_RX_FIS_SIZE;
port->cmd_tbl = dma;
port->cmd_tbl_dma = (rt_ubase_t)rt_kmem_v2p(dma);
dma += RT_AHCI_CMD_TBL_HDR;
port->cmd_tbl_sg = dma;
dma_addr = (rt_ubase_t)rt_kmem_v2p(port->cmd_slot);
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CLB, rt_lower_32_bits(dma_addr));
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CLBU, rt_upper_32_bits(dma_addr));
dma_addr = (rt_ubase_t)rt_kmem_v2p(port->rx_fis);
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_FB, rt_lower_32_bits(dma_addr));
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_FBU, rt_upper_32_bits(dma_addr));
if (host->ops->port_dma_init && (err = host->ops->port_dma_init(host, port)))
{
LOG_E("Init port[%d] DMA error = %s", rt_strerror(err));
}
HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CMD, RT_AHCI_PORT_CMD_ACTIVE |
RT_AHCI_PORT_CMD_FIS_RX | RT_AHCI_PORT_CMD_POWER_ON |
RT_AHCI_PORT_CMD_SPIN_UP | RT_AHCI_PORT_CMD_START);
/* Wait spinup */
err = -RT_ETIMEOUT;
timeout = rt_tick_from_millisecond(20000);
timeout += rt_tick_get();
do {
if (!(HWREG32(port->regs + RT_AHCI_PORT_TFD) & RT_AHCI_PORT_TFDATA_BSY))
{
err = RT_EOK;
break;
}
rt_hw_cpu_relax();
} while (rt_tick_get() < timeout);
if (err)
{
rt_dma_free_coherent(host->parent.dev, RT_AHCI_DMA_SIZE, port->dma,
port->dma_handle);
port->dma = RT_NULL;
LOG_E("Start up port[%d] fail", i);
continue;
}
port->int_enabled |= RT_AHCI_PORT_INTE_HBUS_ERR | RT_AHCI_PORT_INTE_IF_ERR |
RT_AHCI_PORT_INTE_CONNECT | RT_AHCI_PORT_INTE_PHYRDY |
RT_AHCI_PORT_INTE_UNK_FIS | RT_AHCI_PORT_INTE_BAD_PMP |
RT_AHCI_PORT_INTE_TF_ERR | RT_AHCI_PORT_INTE_HBUS_DATA_ERR |
RT_AHCI_PORT_INTE_SG_DONE | RT_AHCI_PORT_INTE_SDB_FIS |
RT_AHCI_PORT_INTE_DMAS_FIS | RT_AHCI_PORT_INTE_PIOS_FIS |
RT_AHCI_PORT_INTE_D2H_REG_FIS;
HWREG32(port->regs + RT_AHCI_PORT_INTE) = port->int_enabled;
rt_completion_init(&port->done);
}
rt_snprintf(dev_name, sizeof(dev_name), "ahci-%s",
rt_dm_dev_get_name(host->parent.dev));
rt_hw_interrupt_install(host->irq, ahci_isr, host, dev_name);
rt_hw_interrupt_umask(host->irq);
scsi = &host->parent;
scsi->max_lun = rt_max_t(rt_size_t, scsi->max_lun, 1);
scsi->max_id = host->ports_nr;
scsi->ops = &ahci_scsi_ops;
if ((err = rt_scsi_host_register(scsi)))
{
goto _fail;
}
return RT_EOK;
_fail:
rt_hw_interrupt_mask(host->irq);
rt_pic_detach_irq(host->irq, host);
return err;
}
rt_err_t rt_ahci_host_unregister(struct rt_ahci_host *host)
{
rt_err_t err;
struct rt_scsi_host *scsi;
if (!host)
{
return -RT_EINVAL;
}
scsi = &host->parent;
if ((err = rt_scsi_host_unregister(scsi)))
{
return err;
}
rt_hw_interrupt_mask(host->irq);
rt_pic_detach_irq(host->irq, host);
for (int i = 0; i < host->ports_nr; ++i)
{
struct rt_ahci_port *port = &host->ports[i];
if (port->ataid)
{
rt_free(port->ataid);
}
HWREG32(port->regs) &= ~(RT_AHCI_PORT_CMD_ACTIVE | RT_AHCI_PORT_CMD_POWER_ON |
RT_AHCI_PORT_CMD_SPIN_UP | RT_AHCI_PORT_CMD_START);
if (port->dma)
{
rt_dma_free_coherent(host->parent.dev, RT_AHCI_DMA_SIZE, port->dma,
port->dma_handle);
}
}
HWREG32(host->regs + RT_AHCI_HBA_GHC) &= ~(RT_AHCI_GHC_AHCI_EN | RT_AHCI_GHC_IRQ_EN);
return RT_EOK;
}
@@ -0,0 +1,21 @@
config RT_USING_AUDIO
bool "Using Audio device drivers"
default n
if RT_USING_AUDIO
config RT_AUDIO_REPLAY_MP_BLOCK_SIZE
int "Replay memory pool block size"
default 4096
config RT_AUDIO_REPLAY_MP_BLOCK_COUNT
int "Replay memory pool block count"
default 2
config RT_AUDIO_RECORD_PIPE_SIZE
int "Record pipe size"
default 2048
config RT_UTEST_USING_AUDIO_DRIVER
bool "Enable rt_audio_api testcase"
default n
endif
@@ -0,0 +1,14 @@
from building import *
cwd = GetCurrentDir()
src = Glob('*.c')
CPPPATH = [cwd]
group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_AUDIO'], CPPPATH = CPPPATH)
list = os.listdir(cwd)
for item in list:
if os.path.isfile(os.path.join(cwd, item, 'SConscript')):
group = group + SConscript(os.path.join(item, 'SConscript'))
Return('group')
@@ -0,0 +1,784 @@
/*
* Copyright (c) 2006-2025 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2017-05-09 Urey first version
* 2019-07-09 Zero-Free improve device ops interface and data flows
* 2025-03-04 wumingzi add doxygen comments.
*/
#include <stdio.h>
#include <string.h>
#include <rthw.h>
#include <rtdevice.h>
#define DBG_TAG "audio"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
/**
* @addtogroup group_drivers_audio
*/
/** @{ */
enum
{
REPLAY_EVT_NONE = 0x00,
REPLAY_EVT_START = 0x01,
REPLAY_EVT_STOP = 0x02,
};
/**
* @brief Send a replay frame to the audio hardware device
*
* This function handles sending audio data from the memory queue to the hardware buffer for playback.
* If there is no data available in the queue, it sends zero frames. Otherwise, it copies data from the memory pool
* to the hardware device FIFO and manages the read index and position accordingly.
*
* @param[in] audio pointer to the audio device structure
*
* @return error code, RT_EOK is successful otherwise means failure
*
* @note This function may temporarily disable interrupts or perform time-consuming operations like memcpy,
* which could affect system responsiveness
*/
static rt_err_t _audio_send_replay_frame(struct rt_audio_device *audio)
{
rt_err_t result = RT_EOK;
rt_uint8_t *data;
rt_size_t dst_size, src_size;
rt_uint16_t position, remain_bytes = 0, index = 0;
struct rt_audio_buf_info *buf_info;
RT_ASSERT(audio != RT_NULL);
buf_info = &audio->replay->buf_info;
/* save current pos */
position = audio->replay->pos;
dst_size = buf_info->block_size;
/* check replay queue is empty */
if (rt_data_queue_peek(&audio->replay->queue, (const void **)&data, &src_size) != RT_EOK)
{
/* ack stop event */
if (audio->replay->event & REPLAY_EVT_STOP)
rt_completion_done(&audio->replay->cmp);
/* send zero frames */
rt_memset(&buf_info->buffer[audio->replay->pos], 0, dst_size);
audio->replay->pos += dst_size;
audio->replay->pos %= buf_info->total_size;
}
else
{
rt_memset(&buf_info->buffer[audio->replay->pos], 0, dst_size);
/* copy data from memory pool to hardware device fifo */
while (index < dst_size)
{
result = rt_data_queue_peek(&audio->replay->queue, (const void **)&data, &src_size);
if (result != RT_EOK)
{
LOG_D("under run %d, remain %d", audio->replay->pos, remain_bytes);
audio->replay->pos -= remain_bytes;
audio->replay->pos += dst_size;
audio->replay->pos %= buf_info->total_size;
audio->replay->read_index = 0;
result = -RT_EEMPTY;
break;
}
remain_bytes = MIN((dst_size - index), (src_size - audio->replay->read_index));
rt_memcpy(&buf_info->buffer[audio->replay->pos],
&data[audio->replay->read_index], remain_bytes);
index += remain_bytes;
audio->replay->read_index += remain_bytes;
audio->replay->pos += remain_bytes;
audio->replay->pos %= buf_info->total_size;
if (audio->replay->read_index == src_size)
{
/* free memory */
audio->replay->read_index = 0;
rt_data_queue_pop(&audio->replay->queue, (const void **)&data, &src_size, RT_WAITING_NO);
rt_mp_free(data);
/* notify transmitted complete. */
if (audio->parent.tx_complete != RT_NULL)
audio->parent.tx_complete(&audio->parent, (void *)data);
}
}
}
if (audio->ops->transmit != RT_NULL)
{
if (audio->ops->transmit(audio, &buf_info->buffer[position], RT_NULL, dst_size) != dst_size)
result = -RT_ERROR;
}
return result;
}
/**
* @brief Write replay frame into audio device replay queue
*
* @param[in] audio pointer to audio device
*
* @return error code, RT_EOK is successful otherwise means failure
*/
static rt_err_t _audio_flush_replay_frame(struct rt_audio_device *audio)
{
rt_err_t result = RT_EOK;
if (audio->replay->write_index)
{
result = rt_data_queue_push(&audio->replay->queue,
(const void **)audio->replay->write_data,
audio->replay->write_index,
RT_WAITING_FOREVER);
audio->replay->write_index = 0;
}
return result;
}
/**
* @brief Replay audio
*
* @param[in] audio pointer to audio device
*
* @return error code, RT_EOK is successful otherwise means failure
*/
static rt_err_t _aduio_replay_start(struct rt_audio_device *audio)
{
rt_err_t result = RT_EOK;
if (audio->replay->activated != RT_TRUE)
{
/* start playback hardware device */
if (audio->ops->start)
result = audio->ops->start(audio, AUDIO_STREAM_REPLAY);
audio->replay->activated = RT_TRUE;
LOG_D("start audio replay device");
}
return result;
}
/**
* @brief Stop replaying audio
*
* When audio->replay->queue is empty and the audio->replay->event was set REPLAY_EVT_STOP,
* _audio_send_replay_frame will send completion to stop replaying audio.
*
* @param[in] audio pointer to audio device
*
* @return error code, RT_EOK is successful otherwise means failure
*/
static rt_err_t _aduio_replay_stop(struct rt_audio_device *audio)
{
rt_err_t result = RT_EOK;
if (audio->replay->activated == RT_TRUE)
{
/* flush replay remian frames */
_audio_flush_replay_frame(audio);
/* notify irq(or thread) to stop the data transmission */
audio->replay->event |= REPLAY_EVT_STOP;
/* waiting for the remaining data transfer to complete */
rt_completion_init(&audio->replay->cmp);
rt_completion_wait(&audio->replay->cmp, RT_WAITING_FOREVER);
audio->replay->event &= ~REPLAY_EVT_STOP;
/* stop playback hardware device */
if (audio->ops->stop)
result = audio->ops->stop(audio, AUDIO_STREAM_REPLAY);
audio->replay->activated = RT_FALSE;
LOG_D("stop audio replay device");
}
return result;
}
/**
* @brief Open audio pipe and start to record audio
*
* @param[in] audio pointer to audio device
*
* @return error code, RT_EOK is successful otherwise means failure
*/
static rt_err_t _audio_record_start(struct rt_audio_device *audio)
{
rt_err_t result = RT_EOK;
if (audio->record->activated != RT_TRUE)
{
/* open audio record pipe */
rt_device_open(RT_DEVICE(&audio->record->pipe), RT_DEVICE_OFLAG_RDONLY);
/* start record hardware device */
if (audio->ops->start)
result = audio->ops->start(audio, AUDIO_STREAM_RECORD);
audio->record->activated = RT_TRUE;
LOG_D("start audio record device");
}
return result;
}
/**
* @brief stop recording audio and closeaudio pipe
*
* @param[in] audio pointer to audio device
*
* @return error code, RT_EOK is successful otherwise means failure
*/
static rt_err_t _audio_record_stop(struct rt_audio_device *audio)
{
rt_err_t result = RT_EOK;
if (audio->record->activated == RT_TRUE)
{
/* stop record hardware device */
if (audio->ops->stop)
result = audio->ops->stop(audio, AUDIO_STREAM_RECORD);
/* close audio record pipe */
rt_device_close(RT_DEVICE(&audio->record->pipe));
audio->record->activated = RT_FALSE;
LOG_D("stop audio record device");
}
return result;
}
/**
* @brief Init audio pipe
*
* In kernel, this function will set replay or record function depending on device
* flag. For replaying, it will malloc for managing audio replay struct meanwhile
* creating mempool and dataqueue.For recording, it will creat audio pipe and
* it's ringbuffer.
* In driver, this function will only execute hardware driver initialization code
* and get hardware buffer infomation.
*
* @param[in] dev pointer to audio device
*
* @return error code, RT_EOK is successful otherwise means failure
*/
static rt_err_t _audio_dev_init(struct rt_device *dev)
{
rt_err_t result = RT_EOK;
struct rt_audio_device *audio;
RT_ASSERT(dev != RT_NULL);
audio = (struct rt_audio_device *) dev;
/* initialize replay & record */
audio->replay = RT_NULL;
audio->record = RT_NULL;
/* initialize replay */
if (dev->flag & RT_DEVICE_FLAG_WRONLY)
{
struct rt_audio_replay *replay = (struct rt_audio_replay *) rt_malloc(sizeof(struct rt_audio_replay));
if (replay == RT_NULL)
return -RT_ENOMEM;
rt_memset(replay, 0, sizeof(struct rt_audio_replay));
/* init memory pool for replay */
replay->mp = rt_mp_create("adu_mp", RT_AUDIO_REPLAY_MP_BLOCK_COUNT, RT_AUDIO_REPLAY_MP_BLOCK_SIZE);
if (replay->mp == RT_NULL)
{
rt_free(replay);
LOG_E("create memory pool for replay failed");
return -RT_ENOMEM;
}
/* init queue for audio replay */
rt_data_queue_init(&replay->queue, CFG_AUDIO_REPLAY_QUEUE_COUNT, 0, RT_NULL);
/* init mutex lock for audio replay */
rt_mutex_init(&replay->lock, "replay", RT_IPC_FLAG_PRIO);
replay->activated = RT_FALSE;
audio->replay = replay;
}
/* initialize record */
if (dev->flag & RT_DEVICE_FLAG_RDONLY)
{
struct rt_audio_record *record = (struct rt_audio_record *) rt_malloc(sizeof(struct rt_audio_record));
rt_uint8_t *buffer;
if (record == RT_NULL)
return -RT_ENOMEM;
rt_memset(record, 0, sizeof(struct rt_audio_record));
/* init pipe for record*/
buffer = rt_malloc(RT_AUDIO_RECORD_PIPE_SIZE);
if (buffer == RT_NULL)
{
rt_free(record);
LOG_E("malloc memory for for record pipe failed");
return -RT_ENOMEM;
}
rt_audio_pipe_init(&record->pipe, "record",
(rt_int32_t)(RT_PIPE_FLAG_FORCE_WR | RT_PIPE_FLAG_BLOCK_RD),
buffer,
RT_AUDIO_RECORD_PIPE_SIZE);
record->activated = RT_FALSE;
audio->record = record;
}
/* initialize hardware configuration */
if (audio->ops->init)
audio->ops->init(audio);
/* get replay buffer information */
if (audio->ops->buffer_info)
audio->ops->buffer_info(audio, &audio->replay->buf_info);
return result;
}
/**
* @brief Start record audio
*
* @param[in] dev pointer to audio device
*
* @param[in] oflag device flag
*
* @return error code, RT_EOK is successful otherwise means failure
*/
static rt_err_t _audio_dev_open(struct rt_device *dev, rt_uint16_t oflag)
{
struct rt_audio_device *audio;
RT_ASSERT(dev != RT_NULL);
audio = (struct rt_audio_device *) dev;
/* check device flag with the open flag */
if ((oflag & RT_DEVICE_OFLAG_RDONLY) && !(dev->flag & RT_DEVICE_FLAG_RDONLY))
return -RT_EIO;
if ((oflag & RT_DEVICE_OFLAG_WRONLY) && !(dev->flag & RT_DEVICE_FLAG_WRONLY))
return -RT_EIO;
/* get open flags */
dev->open_flag = oflag & 0xff;
/* initialize the Rx/Tx structure according to open flag */
if (oflag & RT_DEVICE_OFLAG_WRONLY)
{
if (audio->replay->activated != RT_TRUE)
{
LOG_D("open audio replay device, oflag = %x\n", oflag);
audio->replay->write_index = 0;
audio->replay->read_index = 0;
audio->replay->pos = 0;
audio->replay->event = REPLAY_EVT_NONE;
}
dev->open_flag |= RT_DEVICE_OFLAG_WRONLY;
}
if (oflag & RT_DEVICE_OFLAG_RDONLY)
{
/* open record pipe */
if (audio->record->activated != RT_TRUE)
{
LOG_D("open audio record device ,oflag = %x\n", oflag);
_audio_record_start(audio);
audio->record->activated = RT_TRUE;
}
dev->open_flag |= RT_DEVICE_OFLAG_RDONLY;
}
return RT_EOK;
}
/**
* @brief Stop record, replay or both.
*
* @param[in] dev pointer to audio device
*
* @return useless param
*/
static rt_err_t _audio_dev_close(struct rt_device *dev)
{
struct rt_audio_device *audio;
RT_ASSERT(dev != RT_NULL);
audio = (struct rt_audio_device *) dev;
if (dev->open_flag & RT_DEVICE_OFLAG_WRONLY)
{
/* stop replay stream */
_aduio_replay_stop(audio);
dev->open_flag &= ~RT_DEVICE_OFLAG_WRONLY;
}
if (dev->open_flag & RT_DEVICE_OFLAG_RDONLY)
{
/* stop record stream */
_audio_record_stop(audio);
dev->open_flag &= ~RT_DEVICE_OFLAG_RDONLY;
}
return RT_EOK;
}
/**
* @brief Read audio device
*
* @param[in] dev pointer to device
*
* @param[in] pos position when reading
*
* @param[out] buffer a data buffer to save the read data
*
* @param[in] size buffer size
*
* @return the actually read size on successfully, otherwise 0 will be returned.
*
* @note
*/
static rt_ssize_t _audio_dev_read(struct rt_device *dev, rt_off_t pos, void *buffer, rt_size_t size)
{
struct rt_audio_device *audio;
RT_ASSERT(dev != RT_NULL);
audio = (struct rt_audio_device *) dev;
if (!(dev->open_flag & RT_DEVICE_OFLAG_RDONLY) || (audio->record == RT_NULL))
return 0;
return rt_device_read(RT_DEVICE(&audio->record->pipe), pos, buffer, size);
}
/**
* @brief Write data into replay data queue and replay it
*
* @param[in] dev pointer to device
*
* @param[in] pos useless param
*
* @param[in] buffer a data buffer to be written into data queue
*
* @param[in] size buffer size
*
* @return the actually read size on successfully, otherwise 0 will be returned.
*
* @note This function will take mutex.
*/
static rt_ssize_t _audio_dev_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
struct rt_audio_device *audio;
rt_uint8_t *ptr;
rt_uint16_t block_size, remain_bytes, index = 0;
RT_ASSERT(dev != RT_NULL);
audio = (struct rt_audio_device *) dev;
if (!(dev->open_flag & RT_DEVICE_OFLAG_WRONLY) || (audio->replay == RT_NULL))
return 0;
/* push a new frame to replay data queue */
ptr = (rt_uint8_t *)buffer;
block_size = RT_AUDIO_REPLAY_MP_BLOCK_SIZE;
rt_mutex_take(&audio->replay->lock, RT_WAITING_FOREVER);
while (index < size)
{
/* request buffer from replay memory pool */
if (audio->replay->write_index % block_size == 0)
{
audio->replay->write_data = rt_mp_alloc(audio->replay->mp, RT_WAITING_FOREVER);
rt_memset(audio->replay->write_data, 0, block_size);
}
/* copy data to replay memory pool */
remain_bytes = MIN((block_size - audio->replay->write_index), (size - index));
rt_memcpy(&audio->replay->write_data[audio->replay->write_index], &ptr[index], remain_bytes);
index += remain_bytes;
audio->replay->write_index += remain_bytes;
audio->replay->write_index %= block_size;
if (audio->replay->write_index == 0)
{
rt_data_queue_push(&audio->replay->queue,
audio->replay->write_data,
block_size,
RT_WAITING_FOREVER);
}
}
rt_mutex_release(&audio->replay->lock);
/* check replay state */
if (audio->replay->activated != RT_TRUE)
{
_aduio_replay_start(audio);
audio->replay->activated = RT_TRUE;
}
return index;
}
/**
* @brief Control audio device
*
* @param[in] dev pointer to device
*
* @param[in] cmd audio cmd, it can be one of value in @ref group_audio_control
*
* @param[in] args command argument
*
* @return error code, RT_EOK is successful otherwise means failure
*/
static rt_err_t _audio_dev_control(struct rt_device *dev, int cmd, void *args)
{
rt_err_t result = RT_EOK;
struct rt_audio_device *audio;
RT_ASSERT(dev != RT_NULL);
audio = (struct rt_audio_device *) dev;
/* dev stat...*/
switch (cmd)
{
case AUDIO_CTL_GETCAPS:
{
struct rt_audio_caps *caps = (struct rt_audio_caps *) args;
LOG_D("AUDIO_CTL_GETCAPS: main_type = %d,sub_type = %d", caps->main_type, caps->sub_type);
if (audio->ops->getcaps != RT_NULL)
{
result = audio->ops->getcaps(audio, caps);
}
break;
}
case AUDIO_CTL_CONFIGURE:
{
struct rt_audio_caps *caps = (struct rt_audio_caps *) args;
LOG_D("AUDIO_CTL_CONFIGURE: main_type = %d,sub_type = %d", caps->main_type, caps->sub_type);
if (audio->ops->configure != RT_NULL)
{
result = audio->ops->configure(audio, caps);
}
break;
}
case AUDIO_CTL_START:
{
int stream = *(int *) args;
LOG_D("AUDIO_CTL_START: stream = %d", stream);
if (stream == AUDIO_STREAM_REPLAY)
{
result = _aduio_replay_start(audio);
}
else
{
result = _audio_record_start(audio);
}
break;
}
case AUDIO_CTL_STOP:
{
int stream = *(int *) args;
LOG_D("AUDIO_CTL_STOP: stream = %d", stream);
if (stream == AUDIO_STREAM_REPLAY)
{
result = _aduio_replay_stop(audio);
}
else
{
result = _audio_record_stop(audio);
}
break;
}
default:
break;
}
return result;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops audio_ops =
{
_audio_dev_init,
_audio_dev_open,
_audio_dev_close,
_audio_dev_read,
_audio_dev_write,
_audio_dev_control
};
#endif
/**
* @brief Register and initialize audio device
*
* @param[in] audio pointer to audio deive
*
* @param[in] name device name
*
* @param[in] flag device flags
*
* @param[in] data user data
*
* @return error code, RT_EOK is successful otherwise means failure
*/
rt_err_t rt_audio_register(struct rt_audio_device *audio, const char *name, rt_uint32_t flag, void *data)
{
rt_err_t result = RT_EOK;
struct rt_device *device;
RT_ASSERT(audio != RT_NULL);
device = &(audio->parent);
device->type = RT_Device_Class_Sound;
device->rx_indicate = RT_NULL;
device->tx_complete = RT_NULL;
#ifdef RT_USING_DEVICE_OPS
device->ops = &audio_ops;
#else
device->init = _audio_dev_init;
device->open = _audio_dev_open;
device->close = _audio_dev_close;
device->read = _audio_dev_read;
device->write = _audio_dev_write;
device->control = _audio_dev_control;
#endif
device->user_data = data;
/* register a character device */
result = rt_device_register(device, name, flag | RT_DEVICE_FLAG_REMOVABLE);
/* initialize audio device */
if (result == RT_EOK)
result = rt_device_init(device);
return result;
}
/**
* @brief Set audio sample rate
*
* @param[in] bitValue audio sample rate, it can be one of value in @ref group_audio_samp_rates
*
* @return speed has been set
*/
int rt_audio_samplerate_to_speed(rt_uint32_t bitValue)
{
int speed = 0;
switch (bitValue)
{
case AUDIO_SAMP_RATE_8K:
speed = 8000;
break;
case AUDIO_SAMP_RATE_11K:
speed = 11052;
break;
case AUDIO_SAMP_RATE_16K:
speed = 16000;
break;
case AUDIO_SAMP_RATE_22K:
speed = 22050;
break;
case AUDIO_SAMP_RATE_32K:
speed = 32000;
break;
case AUDIO_SAMP_RATE_44K:
speed = 44100;
break;
case AUDIO_SAMP_RATE_48K:
speed = 48000;
break;
case AUDIO_SAMP_RATE_96K:
speed = 96000;
break;
case AUDIO_SAMP_RATE_128K:
speed = 128000;
break;
case AUDIO_SAMP_RATE_160K:
speed = 160000;
break;
case AUDIO_SAMP_RATE_172K:
speed = 176400;
break;
case AUDIO_SAMP_RATE_192K:
speed = 192000;
break;
default:
break;
}
return speed;
}
/**
* @brief Send a replay frame to the audio hardware device
*
* See _audio_send_replay_frame for details
*
* @param[in] audio pointer to audio device
*
* @return void
*/
void rt_audio_tx_complete(struct rt_audio_device *audio)
{
/* try to send next frame */
_audio_send_replay_frame(audio);
}
/**
* @brief Receive recording from audio device
*
* @param[in] audio pointer to audio device
*
* @param[in] pbuf pointer ro data to be received
*
* @param[in] len buffer size
*
* @return void
*/
void rt_audio_rx_done(struct rt_audio_device *audio, rt_uint8_t *pbuf, rt_size_t len)
{
/* save data to record pipe */
rt_device_write(RT_DEVICE(&audio->record->pipe), 0, pbuf, len);
/* invoke callback */
if (audio->parent.rx_indicate != RT_NULL)
audio->parent.rx_indicate(&audio->parent, len);
}
/** @} group_drivers_audio */
@@ -0,0 +1,370 @@
/*
* Copyright (c) 2006-2025 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2012-09-30 Bernard first version.
* 2025-03-04 wumingzi add doxygen comments.
*/
#include <rthw.h>
#include <rtdevice.h>
#include "dev_audio_pipe.h"
static void _rt_audio_pipe_resume_writer(struct rt_audio_pipe *pipe)
{
if (!rt_list_isempty(&pipe->suspended_write_list))
{
rt_thread_t thread;
RT_ASSERT(pipe->flag & RT_PIPE_FLAG_BLOCK_WR);
/* get suspended thread */
thread = RT_THREAD_LIST_NODE_ENTRY(pipe->suspended_write_list.next);
/* resume the write thread */
rt_thread_resume(thread);
rt_schedule();
}
}
/**
* @brief Read audio pipe
*
* @param[in] dev pointer to audio device will be read
*
* @param[in] pos useless param
*
* @param[in] buffer pointer to ringbuffer of audio pipe to be read
*
* @param[in] size number of bytes will be read
*
* @return number of read bytes
*
* @note This function will execute time-consuming or affecting the
* system operations like memcpy and disable interrupt.
*/
static rt_ssize_t rt_audio_pipe_read(rt_device_t dev,
rt_off_t pos,
void *buffer,
rt_size_t size)
{
rt_base_t level;
rt_thread_t thread;
struct rt_audio_pipe *pipe;
rt_size_t read_nbytes;
pipe = (struct rt_audio_pipe *)dev;
RT_ASSERT(pipe != RT_NULL);
if (!(pipe->flag & RT_PIPE_FLAG_BLOCK_RD))
{
level = rt_hw_interrupt_disable();
read_nbytes = rt_ringbuffer_get(&(pipe->ringbuffer), (rt_uint8_t *)buffer, size);
/* if the ringbuffer is empty, there won't be any writer waiting */
if (read_nbytes)
_rt_audio_pipe_resume_writer(pipe);
rt_hw_interrupt_enable(level);
return read_nbytes;
}
thread = rt_thread_self();
/* current context checking */
RT_DEBUG_NOT_IN_INTERRUPT;
do
{
level = rt_hw_interrupt_disable();
read_nbytes = rt_ringbuffer_get(&(pipe->ringbuffer), (rt_uint8_t *)buffer, size);
if (read_nbytes == 0)
{
rt_thread_suspend(thread);
/* waiting on suspended read list */
rt_list_insert_before(&(pipe->suspended_read_list),
&RT_THREAD_LIST_NODE(thread));
rt_hw_interrupt_enable(level);
rt_schedule();
}
else
{
_rt_audio_pipe_resume_writer(pipe);
rt_hw_interrupt_enable(level);
break;
}
}
while (read_nbytes == 0);
return read_nbytes;
}
/**
* @brief Resume audio pipe reader thread
*
* @param[in] pipe pointer to suspended audio pipe thread
*/
static void _rt_audio_pipe_resume_reader(struct rt_audio_pipe *pipe)
{
if (pipe->parent.rx_indicate)
pipe->parent.rx_indicate(&pipe->parent,
rt_ringbuffer_data_len(&pipe->ringbuffer));
if (!rt_list_isempty(&pipe->suspended_read_list))
{
rt_thread_t thread;
RT_ASSERT(pipe->flag & RT_PIPE_FLAG_BLOCK_RD);
/* get suspended thread */
thread = RT_THREAD_LIST_NODE_ENTRY(pipe->suspended_read_list.next);
/* resume the read thread */
rt_thread_resume(thread);
rt_schedule();
}
}
/**
* @brief Write data into audio pipe
*
* @param[in] dev pointer to audio pipe that has been configured
*
* @param[in] pos useless param
*
* @param[in] buffer pointer to buffer of ringbuffer
*
* @param[in] size size of data will be written
*
* @return number of written bytes
*
* @note The function will disable interrupt and may suspend current thread
*/
static rt_ssize_t rt_audio_pipe_write(rt_device_t dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
rt_base_t level;
rt_thread_t thread;
struct rt_audio_pipe *pipe;
rt_size_t write_nbytes;
pipe = (struct rt_audio_pipe *)dev;
RT_ASSERT(pipe != RT_NULL);
if ((pipe->flag & RT_PIPE_FLAG_FORCE_WR) ||
!(pipe->flag & RT_PIPE_FLAG_BLOCK_WR))
{
level = rt_hw_interrupt_disable();
if (pipe->flag & RT_PIPE_FLAG_FORCE_WR)
write_nbytes = rt_ringbuffer_put_force(&(pipe->ringbuffer),
(const rt_uint8_t *)buffer, size);
else
write_nbytes = rt_ringbuffer_put(&(pipe->ringbuffer),
(const rt_uint8_t *)buffer, size);
_rt_audio_pipe_resume_reader(pipe);
rt_hw_interrupt_enable(level);
return write_nbytes;
}
thread = rt_thread_self();
/* current context checking */
RT_DEBUG_NOT_IN_INTERRUPT;
do
{
level = rt_hw_interrupt_disable();
write_nbytes = rt_ringbuffer_put(&(pipe->ringbuffer), (const rt_uint8_t *)buffer, size);
if (write_nbytes == 0)
{
/* pipe full, waiting on suspended write list */
rt_thread_suspend(thread);
/* waiting on suspended read list */
rt_list_insert_before(&(pipe->suspended_write_list),
&RT_THREAD_LIST_NODE(thread));
rt_hw_interrupt_enable(level);
rt_schedule();
}
else
{
_rt_audio_pipe_resume_reader(pipe);
rt_hw_interrupt_enable(level);
break;
}
}
while (write_nbytes == 0);
return write_nbytes;
}
/**
* @brief Control audio pipe
*
* @param[in] dev pointer to pipe
*
* @param[in] cmd control command
*
* @param[in] args control argument
*
* @return error code, RT_EOK is successful otherwise means failure
*/
static rt_err_t rt_audio_pipe_control(rt_device_t dev, int cmd, void *args)
{
struct rt_audio_pipe *pipe;
pipe = (struct rt_audio_pipe *)dev;
if (cmd == PIPE_CTRL_GET_SPACE && args)
*(rt_size_t *)args = rt_ringbuffer_space_len(&pipe->ringbuffer);
return RT_EOK;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops audio_pipe_ops =
{
RT_NULL,
RT_NULL,
RT_NULL,
rt_audio_pipe_read,
rt_audio_pipe_write,
rt_audio_pipe_control
};
#endif
/**
* @brief Init audio pipe
*
* This function will initialize a pipe device and put it under control of
* resource management.
*
* @param pipe the pipe device
*
* @param name the name of pipe device
*
* @param flag the attribute of the pipe device
*
* @param buf the buffer of pipe device
*
* @param size the size of pipe device buffer
*
* @return the operation status, RT_EOK on successful
*/
rt_err_t rt_audio_pipe_init(struct rt_audio_pipe *pipe,
const char *name,
rt_int32_t flag,
rt_uint8_t *buf,
rt_size_t size)
{
RT_ASSERT(pipe);
RT_ASSERT(buf);
/* initialize suspended list */
rt_list_init(&pipe->suspended_read_list);
rt_list_init(&pipe->suspended_write_list);
/* initialize ring buffer */
rt_ringbuffer_init(&pipe->ringbuffer, buf, size);
pipe->flag = flag;
/* create pipe */
pipe->parent.type = RT_Device_Class_Pipe;
#ifdef RT_USING_DEVICE_OPS
pipe->parent.ops = &audio_pipe_ops;
#else
pipe->parent.init = RT_NULL;
pipe->parent.open = RT_NULL;
pipe->parent.close = RT_NULL;
pipe->parent.read = rt_audio_pipe_read;
pipe->parent.write = rt_audio_pipe_write;
pipe->parent.control = rt_audio_pipe_control;
#endif
return rt_device_register(&(pipe->parent), name, RT_DEVICE_FLAG_RDWR);
}
/**
* @brief This function will detach a pipe device from resource management
*
* @param pipe the pipe device
*
* @return the operation status, RT_EOK on successful
*/
rt_err_t rt_audio_pipe_detach(struct rt_audio_pipe *pipe)
{
return rt_device_unregister(&pipe->parent);
}
/**
* @brief Creat audio pipe
*
* @param[in] name pipe name
*
* @param[in] flag pipe flags, it can be one of enum rt_audio_pipe_flag items
*
* @param[in] size ringbuffer size
*
* @return error code, RT_EOK on initialization successfully
*
* @note depend on RT_USING_HEAP
*/
#ifdef RT_USING_HEAP
rt_err_t rt_audio_pipe_create(const char *name, rt_int32_t flag, rt_size_t size)
{
rt_uint8_t *rb_memptr = RT_NULL;
struct rt_audio_pipe *pipe = RT_NULL;
/* get aligned size */
size = RT_ALIGN(size, RT_ALIGN_SIZE);
pipe = (struct rt_audio_pipe *)rt_calloc(1, sizeof(struct rt_audio_pipe));
if (pipe == RT_NULL)
return -RT_ENOMEM;
/* create ring buffer of pipe */
rb_memptr = (rt_uint8_t *)rt_malloc(size);
if (rb_memptr == RT_NULL)
{
rt_free(pipe);
return -RT_ENOMEM;
}
return rt_audio_pipe_init(pipe, name, flag, rb_memptr, size);
}
/**
* @brief Detachaudio pipe and free its ringbuffer
*
* @param[in] pipe pointer to the pipe will be destory
*
* @note depend on RT_USING_HEAP
*/
void rt_audio_pipe_destroy(struct rt_audio_pipe *pipe)
{
if (pipe == RT_NULL)
return;
/* un-register pipe device */
rt_audio_pipe_detach(pipe);
/* release memory */
rt_free(pipe->ringbuffer.buffer_ptr);
rt_free(pipe);
return;
}
#endif /* RT_USING_HEAP */
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2006-2025 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-03-04 wumingzi add doxygen comments.
*/
#ifndef __DEV_AUDIO_PIPE_H__
#define __DEV_AUDIO_PIPE_H__
/**
* Pipe Device
*/
#include <rtdevice.h>
#ifndef RT_PIPE_BUFSZ
#define PIPE_BUFSZ 512
#else
#define PIPE_BUFSZ RT_PIPE_BUFSZ
#endif
/**
* @brief Portal device
*/
struct rt_audio_portal_device
{
struct rt_device parent;
struct rt_device *write_dev;
struct rt_device *read_dev;
};
/**
* @brief Aduio pipe flags
*/
enum rt_audio_pipe_flag
{
RT_PIPE_FLAG_NONBLOCK_RDWR = 0x00, /**< both read and write won't block */
RT_PIPE_FLAG_BLOCK_RD = 0x01, /**< read would block */
RT_PIPE_FLAG_BLOCK_WR = 0x02, /**< write would block */
RT_PIPE_FLAG_FORCE_WR = 0x04, /**< write to this pipe will discard some data when the pipe is full.
* When this flag is set, RT_PIPE_FLAG_BLOCK_WR will be ignored since write
* operation will always be success. */
};
/**
* @brief Audio buffer info
*
* The preferred number and size of audio pipeline buffer for the audio device, it
* will be used in rt_audio_replay struct.
*
*/
struct rt_audio_pipe
{
struct rt_device parent;
struct rt_ringbuffer ringbuffer; /**< ring buffer in pipe device */
rt_int32_t flag;
rt_list_t suspended_read_list; /**< suspended thread list for reading */
rt_list_t suspended_write_list; /**< suspended thread list for writing */
struct rt_audio_portal_device *write_portal;
struct rt_audio_portal_device *read_portal;
};
#define PIPE_CTRL_GET_SPACE 0x14 /**< get the remaining size of a pipe device */
rt_err_t rt_audio_pipe_init(struct rt_audio_pipe *pipe,
const char *name,
rt_int32_t flag,
rt_uint8_t *buf,
rt_size_t size);
rt_err_t rt_audio_pipe_detach(struct rt_audio_pipe *pipe);
#ifdef RT_USING_HEAP
rt_err_t rt_audio_pipe_create(const char *name, rt_int32_t flag, rt_size_t size);
void rt_audio_pipe_destroy(struct rt_audio_pipe *pipe);
#endif /* RT_USING_HEAP */
#endif /* __DEV_AUDIO_PIPE_H__ */
@@ -0,0 +1,13 @@
Import('rtconfig')
from building import *
cwd = GetCurrentDir()
src = []
CPPPATH = [cwd]
if GetDepend('RT_UTEST_USING_ALL_CASES') or GetDepend('RT_UTEST_USING_AUDIO_DRIVER'):
src += Glob('tc_*.c')
group = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES', 'RT_USING_AUDIO'], CPPPATH = CPPPATH)
Return('group')
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2006-2025 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-05-02 wumingzi first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <rttypes.h>
#include "utest.h"
/* DMA buffer of audio player device refresh is triggered only when the amount of transmitted data is
* greater than the size of a single block in the data queue */
#define TX_DMA_BLOCK_SIZE RT_AUDIO_REPLAY_MP_BLOCK_SIZE
#define TX_DMA_FIFO_SIZE (RT_AUDIO_REPLAY_MP_BLOCK_SIZE * 2)
#define RX_DMA_BLOCK_SIZE RT_AUDIO_RECORD_PIPE_SIZE
#define RX_DMA_FIFO_SIZE (RT_AUDIO_RECORD_PIPE_SIZE * 2)
#define SOUND_PLAYER_DEVICE_NAME "sound0"
#define SOUND_MIC_DEVICE_NAME "mic0"
#define PLAYER_SAMPLEBITS 16
#define PLAYER_SAMPLERATE 16000
#define PLAYER_CHANNEL 2
#define PLAYER_VOLUME 30
#define MIC_SAMPLEBITS 16
#define MIC_SAMPLERATE 16000
#define MIC_CHANNEL 2
#define MIC_TIME_MS 5000
extern rt_uint8_t audio_fsm_step ;
struct mic_device
{
struct rt_audio_device audio;
struct rt_audio_configure config;
rt_uint8_t *rx_fifo;
};
struct sound_device
{
struct rt_audio_device audio;
struct rt_audio_configure config;
rt_uint8_t volume;
rt_uint8_t *tx_fifo;
};
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2006-2025 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-05-02 wumingzi First version
*/
#include "tc_audio_common.h"
#define THREAD_PRIORITY 9
#define THREAD_TIMESLICE 5
#define thread_simulate_intr_create_stacksize 1024
static rt_thread_t thread_simulate_intr_handle;
static struct mic_device mic_dev;
/* Simulate callback function */
static void thread_simulate_intr(void *parameter)
{
/* Send the data(0xAA) from DMA buffer to kernel */
rt_memset((void*)&mic_dev.rx_fifo[0], 0xAA, RX_DMA_BLOCK_SIZE);
rt_audio_rx_done((struct rt_audio_device *)&(mic_dev.audio), mic_dev.rx_fifo, RX_DMA_BLOCK_SIZE);
audio_fsm_step = 1;
while (1)
{
if(audio_fsm_step == 2)
{
/* Send the the data(0x55) from DMA buffer to kernel */
rt_memset((void*)&mic_dev.rx_fifo[RX_DMA_BLOCK_SIZE], 0x55, RX_DMA_BLOCK_SIZE);
rt_audio_rx_done(&mic_dev.audio, &mic_dev.rx_fifo[RX_DMA_BLOCK_SIZE], RX_DMA_BLOCK_SIZE);
audio_fsm_step = 3;
break;
}
if(audio_fsm_step == 4)
{
rt_thread_mdelay(10);
rt_audio_rx_done(&mic_dev.audio, &mic_dev.rx_fifo[RX_DMA_BLOCK_SIZE], RX_DMA_BLOCK_SIZE);
break;
}
rt_thread_mdelay(10);
}
while(1)
{
rt_thread_mdelay(10);
}
}
static void thread_simulate_intr_create(void)
{
thread_simulate_intr_handle = rt_thread_create(
"thread_simulate_intr",
thread_simulate_intr,
RT_NULL,
thread_simulate_intr_create_stacksize,
THREAD_PRIORITY - 1, THREAD_TIMESLICE);
if (thread_simulate_intr_handle == RT_NULL)
{
rt_kprintf("Error: Failed to create thread!\n");
return;
}
if (rt_thread_startup(thread_simulate_intr_handle) != RT_EOK)
{
rt_kprintf("Error: Failed to start thread!\n");
thread_simulate_intr_handle = RT_NULL;
}
}
static rt_err_t mic_device_init(struct rt_audio_device *audio)
{
return RT_EOK;
}
/* Simulate DMA interrupt */
static rt_err_t mic_device_start(struct rt_audio_device *audio, int stream)
{
thread_simulate_intr_create();
return RT_EOK;
}
static rt_err_t mic_device_stop(struct rt_audio_device *audio, int stream)
{
if (thread_simulate_intr_handle != RT_NULL)
{
rt_thread_delete(thread_simulate_intr_handle);
thread_simulate_intr_handle = RT_NULL;
}
return RT_EOK;
}
static rt_err_t mic_device_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
return RT_EOK;
}
static rt_err_t mic_device_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
return RT_EOK;
}
static struct rt_audio_ops _mic_audio_ops =
{
.getcaps = mic_device_getcaps,
.configure = mic_device_configure,
.init = mic_device_init,
.start = mic_device_start,
.stop = mic_device_stop,
.transmit = RT_NULL,
.buffer_info = RT_NULL,
};
static int rt_hw_mic_init(void)
{
struct rt_audio_device *audio = &mic_dev.audio;
/* mic default */
mic_dev.rx_fifo = rt_malloc(RX_DMA_FIFO_SIZE);
if (mic_dev.rx_fifo == RT_NULL)
{
return -RT_ENOMEM;
}
mic_dev.config.channels = MIC_CHANNEL;
mic_dev.config.samplerate = MIC_SAMPLERATE;
mic_dev.config.samplebits = MIC_SAMPLEBITS;
/* register mic device */
audio->ops = &_mic_audio_ops;
rt_audio_register(audio, SOUND_MIC_DEVICE_NAME, RT_DEVICE_FLAG_RDONLY, (void *)&mic_dev);
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_mic_init);
@@ -0,0 +1,150 @@
/*
* Copyright (c) 2006-2025 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-05-02 wumingzi First version
*/
#include "tc_audio_common.h"
#define THREAD_PRIORITY 9
#define THREAD_TIMESLICE 5
#define thread_simulate_intr_create_stacksize 1024
static rt_thread_t thread_simulate_intr_handle;
static struct sound_device snd_dev;
static void thread_simulate_intr(void *parameter)
{
rt_flag_t exec_once = 0;
while(1)
{
if(audio_fsm_step == 1 && exec_once == 0)
{
/* Move the data(0xAA) from kernel to DMA buffer */
rt_audio_tx_complete(&snd_dev.audio);
audio_fsm_step = 2;
exec_once = 1;
rt_thread_mdelay(10);
}
else if(audio_fsm_step == 2 && exec_once == 1)
{
/* Move the data(0x55) from kernel to DMA buffer */
rt_audio_tx_complete(&snd_dev.audio);
audio_fsm_step = 3;
rt_thread_mdelay(10);
}
else if(audio_fsm_step == 4)
{
/* rt_device_close will call rt_completion_wait(FOREVER), so we need delay to
* let system run the point */
rt_thread_mdelay(10);
rt_audio_tx_complete(&snd_dev.audio);
break;
}
rt_thread_mdelay(10);
}
while (1)
{
rt_thread_mdelay(10);
}
}
static void thread_simulate_intr_create(void)
{
thread_simulate_intr_handle = rt_thread_create(
"thread_simulate_intr",
thread_simulate_intr,
RT_NULL,
thread_simulate_intr_create_stacksize,
THREAD_PRIORITY - 1, THREAD_TIMESLICE);
rt_thread_startup(thread_simulate_intr_handle);
}
static rt_err_t player_device_init(struct rt_audio_device *audio)
{
return RT_EOK;
}
/* Simulate DMA interrupt */
static rt_err_t player_device_start(struct rt_audio_device *audio, int stream)
{
thread_simulate_intr_create();
return RT_EOK;
}
static rt_err_t player_device_stop(struct rt_audio_device *audio, int stream)
{
rt_thread_delete(thread_simulate_intr_handle);
return RT_EOK;
}
static rt_err_t player_device_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
return RT_EOK;
}
static rt_err_t player_device_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
return RT_EOK;
}
static rt_ssize_t player_device_transmit(struct rt_audio_device *audio, const void *writeBuf, void *readBuf, rt_size_t size)
{
return size;
}
static void player_device_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)
{
RT_ASSERT(audio != RT_NULL);
/**
* TX_FIFO
* +----------------+----------------+
* | block1 | block2 |
* +----------------+----------------+
* \ block_size /
*/
info->buffer = snd_dev.tx_fifo;
info->total_size = TX_DMA_FIFO_SIZE;
info->block_size = TX_DMA_BLOCK_SIZE;
info->block_count = RT_AUDIO_REPLAY_MP_BLOCK_COUNT;
}
static struct rt_audio_ops audio_ops =
{
.getcaps = player_device_getcaps,
.configure = player_device_configure,
.init = player_device_init,
.start = player_device_start,
.stop = player_device_stop,
.transmit = player_device_transmit,
.buffer_info = player_device_buffer_info,
};
static int rt_hw_sound_init(void)
{
rt_uint8_t *tx_fifo = RT_NULL;
tx_fifo = rt_malloc(TX_DMA_FIFO_SIZE);
if (tx_fifo == NULL)
{
return -RT_ENOMEM;
}
snd_dev.tx_fifo = tx_fifo;
/* Init default configuration */
{
snd_dev.config.samplerate = PLAYER_SAMPLERATE;
snd_dev.config.channels = PLAYER_CHANNEL;
snd_dev.config.samplebits = PLAYER_SAMPLEBITS;
snd_dev.volume = PLAYER_VOLUME;
}
snd_dev.audio.ops = &audio_ops;
rt_audio_register(&snd_dev.audio, SOUND_PLAYER_DEVICE_NAME, RT_DEVICE_FLAG_WRONLY, &snd_dev);
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_sound_init);
@@ -0,0 +1,309 @@
/*
* Copyright (c) 2006-2025 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-05-01 wumingzi first version
*/
/* The file can test the rt-thread audio driver framework including following api via memory
* simulation.
*
* rt_audio_register
* rt_audio_rx_done
* rt_audio_tx_complete
*
* When audio devices generate or receive new data, the corresponding buffer in device will
* receive date from kernel or surroundings. The same phenomenon will also happen at the
* application level. Thus we can fill memory to simulate the generation of data then track
* and check memory to ensure kernel processing audio data correctly. And this depends on
* implementations of audio drivers.
*
* Therefore, if the player_test testcase failed, it could mean rt_audio_register or
* rt_audio_tx_complete existing bugs. Similarly, if mic_test testcase failed, it could mean
* rt_audio_register or rt_audio_rx_done existing bugs.
*/
#include "tc_audio_common.h"
rt_uint8_t audio_fsm_step = 0;
/* Allocate and initialize memory filled by fill_byte */
static void *alloc_filled_mem(rt_uint8_t fill_byte, rt_size_t size)
{
void *ptr = rt_malloc(size);
if (ptr != NULL)
{
rt_memset(ptr, fill_byte, size);
}
return ptr;
}
/* Check if the memory is filled with fill_byte */
static rt_err_t check_filled_mem(rt_uint8_t fill_byte, rt_uint8_t *mem, size_t size)
{
rt_uint8_t *p = mem;
for (size_t i = 0; i < size; ++i)
{
if (*(p+i) != fill_byte)
{
return -RT_ERROR;
}
}
return RT_EOK;
}
static void player_test(void)
{
int res = 0;
void* player_buffer = RT_NULL;
rt_device_t dev_obj;
dev_obj = rt_device_find(SOUND_PLAYER_DEVICE_NAME);
if (dev_obj == RT_NULL)
{
uassert_not_null(dev_obj);
goto __exit;
}
if (dev_obj->type != RT_Device_Class_Sound)
{
LOG_E("Not an audio player device\n");
goto __exit;
}
res = rt_device_open(dev_obj, RT_DEVICE_OFLAG_WRONLY);
if (res != RT_EOK)
{
LOG_E("Audio player device failed\n");
uassert_true(0);
goto __exit;
}
/* The sampling rate is set by the driver default, so there isn't configuration step */
struct rt_audio_device *audio_dev = rt_container_of(dev_obj, struct rt_audio_device, parent);
struct rt_audio_buf_info buf_info = audio_dev->replay->buf_info;
struct sound_device *snd_dev = rt_container_of(audio_dev, struct sound_device, audio);
player_buffer = alloc_filled_mem(0xAA, TX_DMA_BLOCK_SIZE);
if (player_buffer == RT_NULL)
{
rt_kprintf("Allocate test memory failed\n");
uassert_true(0);
goto __exit;
}
if(snd_dev->tx_fifo == RT_NULL)
{
rt_kprintf("snd_dev->tx_fifo == RT_NULL ");
uassert_true(0);
goto __exit;
}
res = rt_device_write(dev_obj, 0, player_buffer, TX_DMA_BLOCK_SIZE);
if (res != RT_EOK && res != TX_DMA_BLOCK_SIZE)
{
rt_kprintf("Failed to write data to the player device, res = %d\n",res);
uassert_true(0);
goto __exit;
}
audio_fsm_step = 1;
while (1)
{
if(audio_fsm_step == 2)
{
break;
}
rt_thread_mdelay(10);
}
res = check_filled_mem(0xAA, &buf_info.buffer[0], TX_DMA_BLOCK_SIZE);
if (res != RT_EOK)
{
rt_kprintf("The first memory check failed! Buffer dump\n");
for (rt_size_t i = 0; i < TX_DMA_FIFO_SIZE; i++)
{
rt_kprintf("%02X ", buf_info.buffer[i]);
if (i % 16 == 15) rt_kprintf("\n");
}
rt_kprintf("\n");
uassert_true(0);
goto __exit;
}
rt_free(player_buffer);
player_buffer = RT_NULL;
player_buffer = alloc_filled_mem(0x55, TX_DMA_BLOCK_SIZE);
if (player_buffer == RT_NULL)
{
rt_kprintf("Allocate test memory failed\n");
uassert_true(0);
goto __exit;
}
res = rt_device_write(dev_obj, TX_DMA_BLOCK_SIZE, player_buffer, TX_DMA_BLOCK_SIZE);
if (res != RT_EOK && res != TX_DMA_BLOCK_SIZE)
{
rt_kprintf("Failed to write data to the player device, res = %d\n",res);
uassert_true(0);
goto __exit;
}
audio_fsm_step = 2;
while (res != RT_EOK)
{
if(audio_fsm_step == 3)
{
break;
}
rt_thread_mdelay(10);
}
res = check_filled_mem(0x55,&buf_info.buffer[TX_DMA_BLOCK_SIZE], TX_DMA_BLOCK_SIZE);
if (res != RT_EOK)
{
rt_kprintf("The second memory check failed! Buffer dump\n");
for (rt_size_t i = 0; i < TX_DMA_FIFO_SIZE; i++)
{
rt_kprintf("%02X ", buf_info.buffer[i]);
if (i % 16 == 15) rt_kprintf("\n");
}
rt_kprintf("\n");
uassert_true(0);
goto __exit;
}
__exit:
if (player_buffer)
{
rt_free(player_buffer);
player_buffer = RT_NULL;
}
if (dev_obj != RT_NULL)
{
audio_fsm_step = 4;
rt_device_close(dev_obj);
}
}
static void mic_test(void)
{
rt_device_t dev_obj;
rt_uint8_t *mic_buffer = RT_NULL;
rt_ssize_t res = 0;
rt_ssize_t length = 0;
mic_buffer = (rt_uint8_t *)rt_malloc(RX_DMA_BLOCK_SIZE);
if (mic_buffer == RT_NULL)
{
rt_kprintf("The mic_buffer memory allocate failed\n");
uassert_true(0);
goto __exit;
}
dev_obj = rt_device_find(SOUND_MIC_DEVICE_NAME);
if (dev_obj == RT_NULL)
{
LOG_E("Not a mic device\n");
uassert_true(0);
goto __exit;
}
res = rt_device_open(dev_obj, RT_DEVICE_OFLAG_RDONLY);
if (res != RT_EOK)
{
LOG_E("Audio player device failed\n");
uassert_true(0);
goto __exit;
}
length = rt_device_read(dev_obj, 0, mic_buffer,RX_DMA_BLOCK_SIZE);
if(length < 0)
{
LOG_E("Mic device read err\n");
}
if(audio_fsm_step == 1)
{
res = check_filled_mem(0xAA, (rt_uint8_t*)(mic_buffer), length);
}
if (res != RT_EOK)
{
LOG_E("The first memory check failed! Buffer dump\n");
for (rt_size_t i = 0; i < RX_DMA_FIFO_SIZE; i++)
{
rt_kprintf("%02X ",mic_buffer[i]);
if (i % 16 == 15) rt_kprintf("\n");
}
rt_kprintf("\n");
uassert_true(0);
goto __exit;
}
audio_fsm_step = 2;
while (1)
{
if(audio_fsm_step == 3)
{
length = rt_device_read(dev_obj, 0, mic_buffer, RX_DMA_FIFO_SIZE);
if(length < 0)
{
LOG_E("Mic device read err\n");
}
res = check_filled_mem(0x55, (rt_uint8_t*)(&mic_buffer[0]), length);
if(res != RT_EOK)
{
LOG_E("The second memory check failed! Buffer dump\n");
for (rt_size_t i = 0; i < RX_DMA_FIFO_SIZE; i++)
{
rt_kprintf("%02X ",mic_buffer[i]);
if (i % 16 == 15) rt_kprintf("\n");
}
rt_kprintf("\n");
uassert_true(0);
goto __exit;
}
break;
}
rt_thread_mdelay(100);
}
__exit:
if (mic_buffer)
{
rt_free(mic_buffer);
}
if (dev_obj != RT_NULL)
{
audio_fsm_step = 4;
rt_device_close(dev_obj);
}
}
static void testcase(void)
{
UTEST_UNIT_RUN(player_test);
UTEST_UNIT_RUN(mic_test);
}
static rt_err_t utest_tc_init(void)
{
return RT_EOK;
}
static rt_err_t utest_tc_cleanup(void)
{
return RT_EOK;
}
UTEST_TC_EXPORT(testcase, "audio.tc_audio_main", utest_tc_init, utest_tc_cleanup, 10);
@@ -0,0 +1,7 @@
menuconfig RT_USING_BLK
bool "Using Block device drivers"
default n
if RT_USING_BLK
rsource "partitions/Kconfig"
endif
@@ -0,0 +1,23 @@
from building import *
group = []
objs = []
if not GetDepend(['RT_USING_BLK']):
Return('group')
cwd = GetCurrentDir()
list = os.listdir(cwd)
CPPPATH = [cwd + '/../include']
src = ['blk.c', 'blk_dev.c', 'blk_dfs.c', 'blk_partition.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
for d in list:
path = os.path.join(cwd, d)
if os.path.isfile(os.path.join(path, 'SConscript')):
objs = objs + SConscript(os.path.join(d, 'SConscript'))
objs = objs + group
Return('objs')
+573
View File
@@ -0,0 +1,573 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#define DBG_TAG "rtdm.blk"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include "blk_dev.h"
#include "blk_dfs.h"
static void blk_remove_all(struct rt_blk_disk *disk)
{
struct rt_blk_device *blk, *blk_next;
/* Remove all partitions */
rt_list_for_each_entry_safe(blk, blk_next, &disk->part_nodes, list)
{
disk_remove_blk_dev(blk, RT_TRUE);
}
}
static rt_err_t blk_open(rt_device_t dev, rt_uint16_t oflag)
{
struct rt_blk_disk *disk = to_blk_disk(dev);
if (disk->read_only && (oflag & RT_DEVICE_OFLAG_WRONLY))
{
return -RT_EINVAL;
}
return RT_EOK;
}
static rt_err_t blk_close(rt_device_t dev)
{
return RT_EOK;
}
static rt_ssize_t blk_read(rt_device_t dev, rt_off_t sector,
void *buffer, rt_size_t sector_count)
{
rt_ssize_t res;
struct rt_blk_disk *disk = to_blk_disk(dev);
rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER);
res = disk->ops->read(disk, sector, buffer, sector_count);
rt_sem_release(&disk->usr_lock);
return res;
}
static rt_ssize_t blk_write(rt_device_t dev, rt_off_t sector,
const void *buffer, rt_size_t sector_count)
{
rt_ssize_t res;
struct rt_blk_disk *disk = to_blk_disk(dev);
if (!disk->read_only)
{
rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER);
res = disk->ops->write(disk, sector, buffer, sector_count);
rt_sem_release(&disk->usr_lock);
return res;
}
return -RT_ENOSYS;
}
static rt_ssize_t blk_parallel_read(rt_device_t dev, rt_off_t sector,
void *buffer, rt_size_t sector_count)
{
struct rt_blk_disk *disk = to_blk_disk(dev);
return disk->ops->read(disk, sector, buffer, sector_count);
}
static rt_ssize_t blk_parallel_write(rt_device_t dev, rt_off_t sector,
const void *buffer, rt_size_t sector_count)
{
struct rt_blk_disk *disk = to_blk_disk(dev);
if (!disk->read_only)
{
return disk->ops->write(disk, sector, buffer, sector_count);
}
return -RT_ENOSYS;
}
static rt_err_t blk_control(rt_device_t dev, int cmd, void *args)
{
rt_err_t err;
struct rt_blk_disk *disk = to_blk_disk(dev);
switch (cmd)
{
case RT_DEVICE_CTRL_BLK_GETGEOME:
if (args)
{
err = disk->ops->getgeome(disk, args);
}
else
{
err = -RT_EINVAL;
}
break;
case RT_DEVICE_CTRL_BLK_SYNC:
if (disk->ops->sync)
{
rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER);
spin_lock(&disk->lock);
err = disk->ops->sync(disk);
spin_unlock(&disk->lock);
rt_sem_release(&disk->usr_lock);
}
else
{
err = -RT_ENOSYS;
}
break;
case RT_DEVICE_CTRL_BLK_ERASE:
if (disk->ops->erase)
{
rt_sem_take(&disk->usr_lock, RT_WAITING_FOREVER);
spin_lock(&disk->lock);
if (disk->parent.ref_count != 1)
{
err = -RT_EBUSY;
goto _unlock;
}
blk_remove_all(disk);
err = disk->ops->erase(disk);
_unlock:
spin_unlock(&disk->lock);
rt_sem_release(&disk->usr_lock);
}
else
{
err = -RT_ENOSYS;
}
break;
case RT_DEVICE_CTRL_BLK_AUTOREFRESH:
if (disk->ops->autorefresh)
{
err = disk->ops->autorefresh(disk, !!args);
}
else
{
err = -RT_ENOSYS;
}
break;
case RT_DEVICE_CTRL_BLK_PARTITION:
err = -RT_EINVAL;
break;
case RT_DEVICE_CTRL_BLK_SSIZEGET:
device_get_blk_ssize(dev, args);
err = RT_EOK;
break;
case RT_DEVICE_CTRL_ALL_BLK_SSIZEGET:
device_get_all_blk_ssize(dev, args);
err = RT_EOK;
break;
default:
if (disk->ops->control)
{
err = disk->ops->control(disk, RT_NULL, cmd, args);
}
else
{
err = -RT_ENOSYS;
}
break;
}
return err;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops blk_ops =
{
.open = blk_open,
.close = blk_close,
.read = blk_read,
.write = blk_write,
.control = blk_control,
};
const static struct rt_device_ops blk_parallel_ops =
{
.open = blk_open,
.close = blk_close,
.read = blk_parallel_read,
.write = blk_parallel_write,
.control = blk_control,
};
#endif /* RT_USING_DEVICE_OPS */
rt_err_t rt_hw_blk_disk_register(struct rt_blk_disk *disk)
{
rt_err_t err;
#ifdef RT_USING_DM
int device_id;
#endif
const char *disk_name;
rt_uint16_t flags = RT_DEVICE_FLAG_RDONLY;
if (!disk || !disk->ops)
{
return -RT_EINVAL;
}
#ifdef RT_USING_DM
if (!disk->ida)
{
return -RT_EINVAL;
}
#endif
#if RT_NAME_MAX > 0
if (disk->parent.parent.name[0] == '\0')
#else
if (disk->parent.parent.name)
#endif
{
return -RT_EINVAL;
}
#ifdef RT_USING_DM
if ((device_id = rt_dm_ida_alloc(disk->ida)) < 0)
{
return -RT_EFULL;
}
#endif
disk->__magic = RT_BLK_DISK_MAGIC;
disk_name = to_disk_name(disk);
err = rt_sem_init(&disk->usr_lock, disk_name, 1, RT_IPC_FLAG_PRIO);
if (err)
{
#ifdef RT_USING_DM
rt_dm_ida_free(disk->ida, device_id);
#endif
LOG_E("%s: Init user mutex error = %s", rt_strerror(err));
return err;
}
rt_list_init(&disk->part_nodes);
rt_spin_lock_init(&disk->lock);
disk->parent.type = RT_Device_Class_Block;
#ifdef RT_USING_DEVICE_OPS
if (disk->parallel_io)
{
disk->parent.ops = &blk_parallel_ops;
}
else
{
disk->parent.ops = &blk_ops;
}
#else
disk->parent.open = blk_open;
disk->parent.close = blk_close;
if (disk->parallel_io)
{
disk->parent.read = blk_parallel_read;
disk->parent.write = blk_parallel_write;
}
else
{
disk->parent.read = blk_read;
disk->parent.write = blk_write;
}
disk->parent.control = blk_control;
#endif
if (!disk->ops->write)
{
disk->read_only = RT_TRUE;
}
if (!disk->read_only)
{
flags |= RT_DEVICE_FLAG_WRONLY;
}
#ifdef RT_USING_DM
disk->parent.master_id = disk->ida->master_id;
disk->parent.device_id = device_id;
#endif
device_set_blk_fops(&disk->parent);
err = rt_device_register(&disk->parent, disk_name, flags);
if (err)
{
rt_sem_detach(&disk->usr_lock);
}
/* Ignore partition scanning errors */
rt_blk_disk_probe_partition(disk);
return err;
}
rt_err_t rt_hw_blk_disk_unregister(struct rt_blk_disk *disk)
{
rt_err_t err;
if (!disk)
{
return -RT_EINVAL;
}
spin_lock(&disk->lock);
if (disk->parent.ref_count > 0)
{
err = -RT_EBUSY;
goto _unlock;
}
/* Flush all data */
if (disk->ops->sync)
{
err = disk->ops->sync(disk);
if (err)
{
LOG_E("%s: Sync error = %s", to_disk_name(disk), rt_strerror(err));
goto _unlock;
}
}
rt_sem_detach(&disk->usr_lock);
blk_remove_all(disk);
#ifdef RT_USING_DM
rt_dm_ida_free(disk->ida, disk->parent.device_id);
#endif
err = rt_device_unregister(&disk->parent);
_unlock:
spin_unlock(&disk->lock);
return err;
}
rt_ssize_t rt_blk_disk_get_capacity(struct rt_blk_disk *disk)
{
rt_ssize_t res;
struct rt_device_blk_geometry geometry;
if (!disk)
{
return -RT_EINVAL;
}
res = disk->ops->getgeome(disk, &geometry);
if (!res)
{
return geometry.sector_count;
}
return res;
}
rt_ssize_t rt_blk_disk_get_logical_block_size(struct rt_blk_disk *disk)
{
rt_ssize_t res;
struct rt_device_blk_geometry geometry;
if (!disk)
{
return -RT_EINVAL;
}
res = disk->ops->getgeome(disk, &geometry);
if (!res)
{
return geometry.bytes_per_sector;
}
return res;
}
#ifdef RT_USING_DFS_MNTTABLE
static int blk_dfs_mnt_table(void)
{
rt_ubase_t level;
struct rt_object *obj;
struct rt_device *dev;
struct rt_blk_disk *disk;
struct rt_blk_device *blk_dev;
struct rt_object_information *info = rt_object_get_information(RT_Object_Class_Device);
level = rt_hw_interrupt_disable();
rt_list_for_each_entry(obj, &info->object_list, list)
{
dev = rt_container_of(obj, struct rt_device, parent);
if (dev->type != RT_Device_Class_Block)
{
continue;
}
disk = to_blk_disk(dev);
if (disk->__magic != RT_BLK_DISK_MAGIC)
{
continue;
}
if (disk->max_partitions == RT_BLK_PARTITION_NONE)
{
dfs_mount_device(&disk->parent);
continue;
}
rt_list_for_each_entry(blk_dev, &disk->part_nodes, list)
{
dfs_mount_device(&blk_dev->parent);
}
}
rt_hw_interrupt_enable(level);
return 0;
}
INIT_ENV_EXPORT(blk_dfs_mnt_table);
#endif /* RT_USING_DFS_MNTTABLE */
#if defined(RT_USING_CONSOLE) && defined(RT_USING_MSH)
const char *convert_size(struct rt_device_blk_geometry *geome,
rt_size_t sector_count, rt_size_t *out_cap, rt_size_t *out_minor)
{
rt_size_t cap, minor = 0;
int size_index = 0;
const char *size_name[] = { "B", "K", "M", "G", "T", "P", "E" };
cap = geome->bytes_per_sector * sector_count;
for (size_index = 0; size_index < RT_ARRAY_SIZE(size_name) - 1; ++size_index)
{
if (cap < 1024)
{
break;
}
/* Only one decimal point */
minor = (cap % 1024) * 10 / 1024;
cap = cap / 1024;
}
*out_cap = cap;
*out_minor = minor;
return size_name[size_index];
}
static int list_blk(int argc, char**argv)
{
rt_ubase_t level;
rt_size_t cap, minor;
const char *size_name;
struct rt_object *obj;
struct rt_device *dev;
struct rt_blk_disk *disk;
struct rt_blk_device *blk_dev;
struct rt_device_blk_geometry geome;
struct rt_object_information *info = rt_object_get_information(RT_Object_Class_Device);
level = rt_hw_interrupt_disable();
rt_kprintf("%-*.s MAJ:MIN RM SIZE\tRO TYPE MOUNTPOINT\n", RT_NAME_MAX, "NAME");
rt_list_for_each_entry(obj, &info->object_list, list)
{
dev = rt_container_of(obj, struct rt_device, parent);
if (dev->type != RT_Device_Class_Block)
{
continue;
}
disk = to_blk_disk(dev);
if (disk->__magic != RT_BLK_DISK_MAGIC)
{
continue;
}
if (disk->ops->getgeome(disk, &geome))
{
continue;
}
size_name = convert_size(&geome, geome.sector_count, &cap, &minor);
rt_kprintf("%-*.s %3u.%-3u %u %u.%u%s\t%u disk %s\n",
RT_NAME_MAX, to_disk_name(disk),
#ifdef RT_USING_DM
disk->parent.master_id, disk->parent.device_id,
#else
0, 0,
#endif
disk->removable, cap, minor, size_name, disk->read_only,
disk->max_partitions != RT_BLK_PARTITION_NONE ? "\b" :
(dfs_filesystem_get_mounted_path(&disk->parent) ? : "\b"));
rt_list_for_each_entry(blk_dev, &disk->part_nodes, list)
{
size_name = convert_size(&geome, blk_dev->sector_count, &cap, &minor);
rt_kprintf("%c--%-*.s %3u.%-3u %u %u.%u%s\t%u part %s\n",
blk_dev->list.next != &disk->part_nodes ? '|' : '`',
RT_NAME_MAX - 3, to_blk_name(blk_dev),
#ifdef RT_USING_DM
blk_dev->parent.master_id, blk_dev->parent.device_id,
#else
0, 0,
#endif
disk->removable, cap, minor, size_name, disk->read_only,
dfs_filesystem_get_mounted_path(&blk_dev->parent) ? : "");
}
}
rt_hw_interrupt_enable(level);
return 0;
}
MSH_CMD_EXPORT(list_blk, dump all of blks information);
#endif /* RT_USING_CONSOLE && RT_USING_MSH */
@@ -0,0 +1,297 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI first version
*/
#include "blk_dev.h"
#include "blk_dfs.h"
#define DBG_TAG "blk.dm"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#ifdef RT_USING_DFS
#include <dfs_fs.h>
#endif
static rt_err_t blk_dev_open(rt_device_t dev, rt_uint16_t oflag)
{
struct rt_blk_device *blk = to_blk(dev);
return rt_device_open(&blk->disk->parent, oflag);
}
static rt_err_t blk_dev_close(rt_device_t dev)
{
struct rt_blk_device *blk = to_blk(dev);
return rt_device_close(&blk->disk->parent);
}
static rt_ssize_t blk_dev_read(rt_device_t dev, rt_off_t sector,
void *buffer, rt_size_t sector_count)
{
struct rt_blk_device *blk = to_blk(dev);
if (sector <= blk->sector_start + blk->sector_count &&
sector_count <= blk->sector_count)
{
return rt_device_read(&blk->disk->parent,
blk->sector_start + sector, buffer, sector_count);
}
return -RT_EINVAL;
}
static rt_ssize_t blk_dev_write(rt_device_t dev, rt_off_t sector,
const void *buffer, rt_size_t sector_count)
{
struct rt_blk_device *blk = to_blk(dev);
if (sector <= blk->sector_start + blk->sector_count &&
sector_count <= blk->sector_count)
{
return rt_device_write(&blk->disk->parent,
blk->sector_start + sector, buffer, sector_count);
}
return -RT_EINVAL;
}
static rt_err_t blk_dev_control(rt_device_t dev, int cmd, void *args)
{
rt_err_t err = -RT_EINVAL;
struct rt_blk_device *blk = to_blk(dev);
struct rt_blk_disk *disk = blk->disk;
struct rt_device_blk_geometry disk_geometry, *geometry;
switch (cmd)
{
case RT_DEVICE_CTRL_BLK_GETGEOME:
if ((geometry = args))
{
if (!(err = disk->ops->getgeome(disk, &disk_geometry)))
{
geometry->bytes_per_sector = disk_geometry.bytes_per_sector;
geometry->block_size = disk_geometry.block_size;
geometry->sector_count = blk->sector_count;
}
}
else
{
err = -RT_EINVAL;
}
break;
case RT_DEVICE_CTRL_BLK_SYNC:
rt_device_control(&disk->parent, cmd, args);
break;
case RT_DEVICE_CTRL_BLK_ERASE:
case RT_DEVICE_CTRL_BLK_AUTOREFRESH:
if (disk->partitions <= 1)
{
rt_device_control(&disk->parent, cmd, args);
}
else
{
err = -RT_EIO;
}
break;
case RT_DEVICE_CTRL_BLK_PARTITION:
if (args)
{
rt_memcpy(args, &blk->partition, sizeof(blk->partition));
}
else
{
err = -RT_EINVAL;
}
break;
case RT_DEVICE_CTRL_BLK_SSIZEGET:
device_get_blk_ssize(dev, args);
err = RT_EOK;
break;
case RT_DEVICE_CTRL_ALL_BLK_SSIZEGET:
device_get_all_blk_ssize(dev, args);
err = RT_EOK;
break;
default:
if (disk->ops->control)
{
err = disk->ops->control(disk, blk, cmd, args);
}
break;
}
return err;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops blk_dev_ops =
{
.open = blk_dev_open,
.close = blk_dev_close,
.read = blk_dev_read,
.write = blk_dev_write,
.control = blk_dev_control,
};
#endif
rt_err_t blk_dev_initialize(struct rt_blk_device *blk)
{
struct rt_device *dev;
if (!blk)
{
return -RT_EINVAL;
}
dev = &blk->parent;
dev->type = RT_Device_Class_Block;
#ifdef RT_USING_DEVICE_OPS
dev->ops = &blk_dev_ops;
#else
dev->open = blk_dev_open;
dev->close = blk_dev_close;
dev->read = blk_dev_read;
dev->write = blk_dev_write;
dev->control = blk_dev_control;
#endif
return RT_EOK;
}
rt_err_t disk_add_blk_dev(struct rt_blk_disk *disk, struct rt_blk_device *blk)
{
rt_err_t err;
#ifdef RT_USING_DM
int device_id;
#endif
const char *disk_name, *name_fmt;
if (!disk || !blk)
{
return -RT_EINVAL;
}
#ifdef RT_USING_DM
if ((device_id = rt_dm_ida_alloc(disk->ida)) < 0)
{
return -RT_EFULL;
}
#endif
blk->disk = disk;
rt_list_init(&blk->list);
disk_name = to_disk_name(disk);
/* End is [a-zA-Z] or [0-9] */
if (disk_name[rt_strlen(disk_name) - 1] < 'a')
{
name_fmt = "%sp%d";
}
else
{
name_fmt = "%s%d";
}
#ifdef RT_USING_DM
rt_dm_dev_set_name(&blk->parent, name_fmt, disk_name, blk->partno);
blk->parent.master_id = disk->ida->master_id;
blk->parent.device_id = device_id;
#else
rt_snprintf(blk->parent.parent.name, RT_NAME_MAX, name_fmt, disk_name, blk->partno);
#endif
device_set_blk_fops(&blk->parent);
err = rt_device_register(&blk->parent, to_blk_name(blk),
disk->parent.flag & RT_DEVICE_FLAG_RDWR);
if (err)
{
#ifdef RT_USING_DM
rt_dm_ida_free(disk->ida, device_id);
#endif
return err;
}
spin_lock(&disk->lock);
rt_list_insert_before(&disk->part_nodes, &blk->list);
spin_unlock(&disk->lock);
return RT_EOK;
}
rt_err_t disk_remove_blk_dev(struct rt_blk_device *blk, rt_bool_t lockless)
{
struct rt_blk_disk *disk;
if (!blk)
{
return -RT_EINVAL;
}
disk = blk->disk;
if (!disk)
{
return -RT_EINVAL;
}
else
{
#ifdef RT_USING_DFS
const char *mountpath;
if ((mountpath = dfs_filesystem_get_mounted_path(&blk->parent)))
{
dfs_unmount(mountpath);
LOG_D("%s: Unmount file system on %s",
to_blk_name(blk), mountpath);
}
#endif
}
#ifdef RT_USING_DM
rt_dm_ida_free(disk->ida, blk->parent.device_id);
#endif
rt_device_unregister(&blk->parent);
if (!lockless)
{
spin_lock(&disk->lock);
}
rt_list_remove(&blk->list);
if (!lockless)
{
spin_unlock(&disk->lock);
}
--disk->partitions;
return RT_EOK;
}
rt_uint32_t blk_request_ioprio(void)
{
struct rt_thread *task = rt_thread_self();
return task ? RT_SCHED_PRIV(task).current_priority : 0;
}
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI first version
*/
#ifndef __BLK_DEV_H__
#define __BLK_DEV_H__
#include <rthw.h>
#include <rtthread.h>
#include <drivers/blk.h>
#include <drivers/misc.h>
#define to_blk_disk(dev) rt_container_of(dev, struct rt_blk_disk, parent)
#define to_blk(dev) rt_container_of(dev, struct rt_blk_device, parent)
#ifdef RT_USING_DM
#define to_disk_name(disk) rt_dm_dev_get_name(&(disk)->parent)
#define to_blk_name(blk) rt_dm_dev_get_name(&(blk)->parent)
#else
#define to_disk_name(disk) (disk)->parent.parent.name
#define to_blk_name(blk) (blk)->parent.parent.name
#endif
/* %c%c name */
#define letter_name(n) ('a' + (n) / ((n) >= 26 ? (26 * 2) : 1)), ((n) >= 26 ? 'a' + (n) % 26 : '\0')
rt_inline void spin_lock(struct rt_spinlock *spinlock)
{
rt_hw_spin_lock(&spinlock->lock);
}
rt_inline void spin_unlock(struct rt_spinlock *spinlock)
{
rt_hw_spin_unlock(&spinlock->lock);
}
rt_err_t blk_dev_initialize(struct rt_blk_device *blk);
rt_err_t disk_add_blk_dev(struct rt_blk_disk *disk, struct rt_blk_device *blk);
rt_err_t disk_remove_blk_dev(struct rt_blk_device *blk, rt_bool_t lockless);
rt_uint32_t blk_request_ioprio(void);
#endif /* __BLK_DEV_H__ */
@@ -0,0 +1,274 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-08-08 GuEe-GUI first version
*/
#include "blk_dfs.h"
#include <dfs_file.h>
#include <drivers/classes/block.h>
#if defined(RT_USING_POSIX_DEVIO) && defined(RT_USING_DFS_V2)
struct blk_fops_data
{
struct rt_device_blk_geometry geometry;
};
static int blk_fops_open(struct dfs_file *file)
{
struct rt_device *dev = file->vnode->data;
struct blk_fops_data *data = rt_malloc(sizeof(*data));
if (!data)
{
return (int)-RT_ENOMEM;
}
dev->user_data = data;
rt_device_control(dev, RT_DEVICE_CTRL_BLK_GETGEOME, &data->geometry);
rt_device_control(dev, RT_DEVICE_CTRL_ALL_BLK_SSIZEGET, &file->vnode->size);
return 0;
}
static int blk_fops_close(struct dfs_file *file)
{
struct rt_device *dev = file->vnode->data;
rt_free(dev->user_data);
dev->user_data = RT_NULL;
return 0;
}
static int blk_fops_ioctl(struct dfs_file *file, int cmd, void *arg)
{
struct rt_device *dev = file->vnode->data;
return (int)rt_device_control(dev, cmd, arg);
}
static ssize_t blk_fops_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
{
void *rbuf;
rt_ssize_t res = 0;
int bytes_per_sector, blk_pos, first_offs, rsize = 0;
struct rt_device *dev = file->vnode->data;
struct blk_fops_data *data = dev->user_data;
bytes_per_sector = data->geometry.bytes_per_sector;
blk_pos = *pos / bytes_per_sector;
first_offs = *pos % bytes_per_sector;
if ((rbuf = rt_malloc(bytes_per_sector)))
{
/*
** #1: read first unalign block size.
*/
res = rt_device_read(dev, blk_pos, rbuf, 1);
if (res == 1)
{
if (count > bytes_per_sector - first_offs)
{
rsize = bytes_per_sector - first_offs;
}
else
{
rsize = count;
}
rt_memcpy(buf, rbuf + first_offs, rsize);
++blk_pos;
/*
** #2: read continuous block size.
*/
while (rsize < count)
{
res = rt_device_read(dev, blk_pos++, rbuf, 1);
if (res != 1)
{
break;
}
if (count - rsize >= bytes_per_sector)
{
rt_memcpy(buf + rsize, rbuf, bytes_per_sector);
rsize += bytes_per_sector;
}
else
{
rt_memcpy(buf + rsize, rbuf, count - rsize);
rsize = count;
}
}
*pos += rsize;
}
rt_free(rbuf);
}
return rsize;
}
static ssize_t blk_fops_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos)
{
void *rbuf;
rt_ssize_t res = 0;
int bytes_per_sector, blk_pos, first_offs, wsize = 0;
struct rt_device *dev = file->vnode->data;
struct blk_fops_data *data = dev->user_data;
bytes_per_sector = data->geometry.bytes_per_sector;
blk_pos = *pos / bytes_per_sector;
first_offs = *pos % bytes_per_sector;
/*
** #1: write first unalign block size.
*/
if (first_offs != 0)
{
if (count > bytes_per_sector - first_offs)
{
wsize = bytes_per_sector - first_offs;
}
else
{
wsize = count;
}
if ((rbuf = rt_malloc(bytes_per_sector)))
{
res = rt_device_read(dev, blk_pos, rbuf, 1);
if (res == 1)
{
rt_memcpy(rbuf + first_offs, buf, wsize);
res = rt_device_write(dev, blk_pos, (const void *)rbuf, 1);
if (res == 1)
{
blk_pos += 1;
rt_free(rbuf);
goto _goon;
}
}
rt_free(rbuf);
}
return 0;
}
_goon:
/*
** #2: write continuous block size.
*/
if ((count - wsize) / bytes_per_sector != 0)
{
res = rt_device_write(dev, blk_pos, buf + wsize, (count - wsize) / bytes_per_sector);
wsize += res * bytes_per_sector;
blk_pos += res;
if (res != (count - wsize) / bytes_per_sector)
{
*pos += wsize;
return wsize;
}
}
/*
** # 3: write last unalign block size.
*/
if ((count - wsize) != 0)
{
if ((rbuf = rt_malloc(bytes_per_sector)))
{
res = rt_device_read(dev, blk_pos, rbuf, 1);
if (res == 1)
{
rt_memcpy(rbuf, buf + wsize, count - wsize);
res = rt_device_write(dev, blk_pos, (const void *)rbuf, 1);
if (res == 1)
{
wsize += count - wsize;
}
}
rt_free(rbuf);
}
}
*pos += wsize;
return wsize;
}
static int blk_fops_flush(struct dfs_file *file)
{
struct rt_device *dev = file->vnode->data;
return (int)rt_device_control(dev, RT_DEVICE_CTRL_BLK_SYNC, RT_NULL);
}
static int blk_fops_poll(struct dfs_file *file, struct rt_pollreq *req)
{
int mask = 0;
return mask;
}
const static struct dfs_file_ops blk_fops =
{
.open = blk_fops_open,
.close = blk_fops_close,
.ioctl = blk_fops_ioctl,
.read = blk_fops_read,
.write = blk_fops_write,
.flush = blk_fops_flush,
.lseek = generic_dfs_lseek,
.poll = blk_fops_poll
};
void device_set_blk_fops(struct rt_device *dev)
{
dev->fops = &blk_fops;
}
#else
void device_set_blk_fops(struct rt_device *dev)
{
}
#endif /* RT_USING_POSIX_DEVIO && RT_USING_DFS_V2 */
void device_get_blk_ssize(struct rt_device *dev, void *args)
{
rt_uint32_t bytes_per_sector;
struct rt_device_blk_geometry geometry;
rt_device_control(dev, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry);
bytes_per_sector = geometry.bytes_per_sector;
RT_ASSERT(sizeof(bytes_per_sector) == sizeof(geometry.bytes_per_sector));
rt_memcpy(args, &bytes_per_sector, sizeof(bytes_per_sector));
}
void device_get_all_blk_ssize(struct rt_device *dev, void *args)
{
rt_uint64_t count_mul_per;
struct rt_device_blk_geometry geometry;
rt_device_control(dev, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry);
count_mul_per = geometry.bytes_per_sector * geometry.sector_count;
rt_memcpy(args, &count_mul_per, sizeof(count_mul_per));
}
@@ -0,0 +1,23 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-08-08 GuEe-GUI first version
*/
#ifndef __BLK_DFS_H__
#define __BLK_DFS_H__
#include <rtdef.h>
#define RT_DEVICE_CTRL_BLK_SSIZEGET 0x00001268 /**< get number of bytes per sector */
#define RT_DEVICE_CTRL_ALL_BLK_SSIZEGET 0x80081272 /**< get number of bytes per sector * sector counts */
void device_set_blk_fops(struct rt_device *dev);
void device_get_blk_ssize(struct rt_device *dev, void *args);
void device_get_all_blk_ssize(struct rt_device *dev, void *args);
#endif /* __BLK_DFS_H__ */
@@ -0,0 +1,154 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#define DBG_TAG "blk.part"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include "blk_partition.h"
static rt_err_t (*partition_list[])(struct rt_blk_disk *) =
{
#ifdef RT_BLK_PARTITION_EFI
efi_partition,
#endif
#ifdef RT_BLK_PARTITION_DFS
dfs_partition,
#endif
};
rt_err_t blk_put_partition(struct rt_blk_disk *disk, const char *type,
rt_size_t start, rt_size_t count, int partno)
{
rt_err_t err;
struct rt_blk_device *blk = rt_calloc(1, sizeof(*blk));
if (type && rt_strcmp(type, "dfs"))
{
rt_uint32_t ssz = rt_blk_disk_get_logical_block_size(disk);
rt_kprintf("found part[%u], begin: %lu, size: ", partno, start * ssz);
if ((count >> 11) == 0)
{
rt_kprintf("%u%cB\n", count >> 1, 'K'); /* KB */
}
else
{
rt_uint32_t size_mb = count >> 11; /* MB */
if ((size_mb >> 10) == 0)
{
rt_kprintf("%u.%u%cB\n", size_mb, (count >> 1) & 0x3ff, 'M');
}
else
{
rt_kprintf("%u.%u%cB\n", size_mb >> 10, size_mb & 0x3ff, 'G');
}
}
}
if (!blk)
{
err = -RT_ENOMEM;
goto _fail;
}
err = blk_dev_initialize(blk);
if (err)
{
goto _fail;
}
blk->partno = partno;
blk->sector_start = start;
blk->sector_count = count;
blk->partition.offset = start;
blk->partition.size = count;
blk->partition.lock = &disk->usr_lock;
err = disk_add_blk_dev(disk, blk);
if (err)
{
goto _fail;
}
++disk->partitions;
return RT_EOK;
_fail:
LOG_E("%s: Put partition.%s[%u] start = %lu count = %lu error = %s",
to_disk_name(disk), type, partno, start, count, rt_strerror(err));
if (blk)
{
rt_free(blk);
}
return err;
}
rt_err_t rt_blk_disk_probe_partition(struct rt_blk_disk *disk)
{
rt_err_t err = RT_EOK;
if (!disk)
{
return -RT_EINVAL;
}
LOG_D("%s: Probing disk partitions", to_disk_name(disk));
if (disk->partitions)
{
return err;
}
err = -RT_EEMPTY;
if (disk->max_partitions == RT_BLK_PARTITION_NONE)
{
LOG_D("%s: Unsupported partitions", to_disk_name(disk));
return err;
}
for (int i = 0; i < RT_ARRAY_SIZE(partition_list); ++i)
{
rt_err_t part_err = partition_list[i](disk);
if (part_err == -RT_ENOMEM)
{
err = part_err;
break;
}
if (!part_err)
{
err = RT_EOK;
break;
}
}
if ((err && err != -RT_ENOMEM) || disk->partitions == 0)
{
/* No partition found */
rt_size_t total_sectors = rt_blk_disk_get_capacity(disk);
err = blk_put_partition(disk, RT_NULL, 0, total_sectors, 0);
}
return err;
}
@@ -0,0 +1,22 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI first version
*/
#ifndef __BLK_PARTITION_H__
#define __BLK_PARTITION_H__
#include "blk_dev.h"
rt_err_t blk_put_partition(struct rt_blk_disk *disk, const char *type,
rt_size_t start, rt_size_t count, int partno);
rt_err_t dfs_partition(struct rt_blk_disk *disk);
rt_err_t efi_partition(struct rt_blk_disk *disk);
#endif /* __BLK_PARTITION_H__ */
@@ -0,0 +1,12 @@
menu "Partition Types"
config RT_BLK_PARTITION_DFS
bool "DFS Partition support"
depends on RT_USING_DFS
default y
config RT_BLK_PARTITION_EFI
bool "EFI Globally Unique Identifier (GUID) Partition support"
default y
endmenu
@@ -0,0 +1,18 @@
from building import *
group = []
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../../include']
src = []
if GetDepend(['RT_BLK_PARTITION_DFS']):
src += ['dfs.c']
if GetDepend(['RT_BLK_PARTITION_EFI']):
src += ['efi.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2011-07-25 weety first version
* 2023-02-25 GuEe-GUI make blk interface
*/
#include "efi.h"
#define DBG_TAG "blk.part.dfs"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
rt_err_t dfs_partition(struct rt_blk_disk *disk)
{
rt_ssize_t res;
struct dfs_partition part;
rt_uint8_t *sector = rt_malloc(rt_blk_disk_get_logical_block_size(disk));
if (!sector)
{
return -RT_ENOMEM;
}
res = disk->ops->read(disk, 0, sector, 1);
if (res < 0)
{
rt_free(sector);
return res;
}
for (rt_size_t i = 0; i < disk->max_partitions; ++i)
{
res = dfs_filesystem_get_partition(&part, sector, i);
if (res)
{
break;
}
if (blk_put_partition(disk, "dfs", part.offset, part.size, i) == -RT_ENOMEM)
{
break;
}
}
rt_free(sector);
return RT_EOK;
}
@@ -0,0 +1,738 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-05-05 linzhenxing first version
* 2023-02-25 GuEe-GUI make blk interface
*/
#include "efi.h"
#define DBG_TAG "blk.part.efi"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
static rt_bool_t force_gpt = 0;
static int force_gpt_setup(void)
{
#ifdef RT_USING_OFW
force_gpt = !!rt_ofw_bootargs_select("gpt", 0);
#endif
return 0;
}
INIT_CORE_EXPORT(force_gpt_setup);
/**
* @brief This function is EFI version of crc32 function.
*
* @param buf the buffer to calculate crc32 of.
* @param len the length of buf.
* @return EFI-style CRC32 value for @buf.
*/
rt_inline rt_uint32_t efi_crc32(const rt_uint8_t *buf, rt_size_t len)
{
rt_ubase_t crc = 0xffffffffUL;
for (rt_size_t i = 0; i < len; ++i)
{
crc ^= buf[i];
for (int j = 0; j < 8; ++j)
{
crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320L : 0);
}
}
return ~crc;
}
/**
* @brief This function get number of last logical block of device.
*
* @param disk the blk of disk.
* @return last LBA value on success, 0 on error.
* This is stored (by sd and ide-geometry) in
* the part[0] entry for this disk, and is the number of
* physical sectors available on the disk.
*/
static rt_size_t last_lba(struct rt_blk_disk *disk)
{
return rt_blk_disk_get_capacity(disk) - 1ULL;
}
rt_inline int pmbr_part_valid(gpt_mbr_record *part)
{
if (part->os_type != EFI_PMBR_OSTYPE_EFI_GPT)
{
return 0;
}
/* set to 0x00000001 (i.e., the LBA of the GPT Partition Header) */
if (rt_le32_to_cpu(part->starting_lba) != GPT_PRIMARY_PARTITION_TABLE_LBA)
{
return 0;
}
return GPT_MBR_PROTECTIVE;
}
/**
* @brief This function test Protective MBR for validity.
*
* @param mbr the pointer to a legacy mbr structure.
* @param total_sectors the amount of sectors in the device
* @return
* 0 -> Invalid MBR
* 1 -> GPT_MBR_PROTECTIVE
* 2 -> GPT_MBR_HYBRID
*/
static int is_pmbr_valid(legacy_mbr *mbr, rt_size_t total_sectors)
{
rt_uint32_t sz = 0;
int part = 0, ret = 0; /* invalid by default */
if (!mbr || rt_le16_to_cpu(mbr->signature) != MSDOS_MBR_SIGNATURE)
{
goto _done;
}
for (int i = 0; i < 4; ++i)
{
ret = pmbr_part_valid(&mbr->partition_record[i]);
if (ret == GPT_MBR_PROTECTIVE)
{
part = i;
/*
* Ok, we at least know that there's a protective MBR,
* now check if there are other partition types for
* hybrid MBR.
*/
goto _check_hybrid;
}
}
if (ret != GPT_MBR_PROTECTIVE)
{
goto _done;
}
_check_hybrid:
for (int i = 0; i < 4; i++)
{
if (mbr->partition_record[i].os_type != EFI_PMBR_OSTYPE_EFI_GPT &&
mbr->partition_record[i].os_type != 0x00)
{
ret = GPT_MBR_HYBRID;
}
}
/*
* Protective MBRs take up the lesser of the whole disk
* or 2 TiB (32bit LBA), ignoring the rest of the disk.
* Some partitioning programs, nonetheless, choose to set
* the size to the maximum 32-bit limitation, disregarding
* the disk size.
*
* Hybrid MBRs do not necessarily comply with this.
*
* Consider a bad value here to be a warning to support dd'ing
* an image from a smaller disk to a larger disk.
*/
if (ret == GPT_MBR_PROTECTIVE)
{
sz = rt_le32_to_cpu(mbr->partition_record[part].size_in_lba);
if (sz != (rt_uint32_t)total_sectors - 1 && sz != 0xffffffff)
{
LOG_W("GPT: mbr size in lba (%u) different than whole disk (%u)",
sz, rt_min_t(rt_uint32_t, total_sectors - 1, 0xffffffff));
}
}
_done:
return ret;
}
/**
* @brief This function read bytes from disk, starting at given LBA.
*
* @param disk the blk of disk.
* @param lba the Logical Block Address of the partition table.
* @param buffer the destination buffer.
* @param count the bytes to read.
* @return number of bytes read on success, 0 on error.
*/
static rt_size_t read_lba(struct rt_blk_disk *disk,
rt_uint64_t lba, rt_uint8_t *buffer, rt_size_t count)
{
rt_size_t totalreadcount = 0;
if (!buffer || lba > last_lba(disk))
{
return 0;
}
for (rt_uint64_t n = lba; count; ++n)
{
int copied = 512;
disk->ops->read(disk, n, buffer, 1);
if (copied > count)
{
copied = count;
}
buffer += copied;
totalreadcount += copied;
count -= copied;
}
return totalreadcount;
}
/**
* @brief This function reads partition entries from disk.
*
* @param disk the blk of disk.
* @param gpt the GPT header
* @return ptes on success, null on error.
*/
static gpt_entry *alloc_read_gpt_entries(struct rt_blk_disk *disk,
gpt_header *gpt)
{
rt_size_t count;
gpt_entry *pte;
rt_uint64_t entry_lba;
if (!gpt)
{
return RT_NULL;
}
count = (rt_size_t)rt_le32_to_cpu(gpt->num_partition_entries) *
rt_le32_to_cpu(gpt->sizeof_partition_entry);
if (!count)
{
return RT_NULL;
}
pte = rt_malloc(count);
if (!pte)
{
return RT_NULL;
}
entry_lba = rt_le64_to_cpu(gpt->partition_entry_lba);
if (read_lba(disk, entry_lba, (rt_uint8_t *)pte, count) < count)
{
rt_free(pte);
pte = RT_NULL;
return RT_NULL;
}
/* Remember to free pte when done */
return pte;
}
/**
* @brief This function allocates GPT header, reads into it from disk.
*
* @param disk the blk of disk.
* @param lba the Logical Block Address of the partition table
* @return GPT header on success, null on error.
*/
static gpt_header *alloc_read_gpt_header(struct rt_blk_disk *disk, rt_uint64_t lba)
{
gpt_header *gpt;
rt_uint32_t ssz = rt_blk_disk_get_logical_block_size(disk);
gpt = rt_malloc(ssz);
if (!gpt)
{
return RT_NULL;
}
if (read_lba(disk, lba, (rt_uint8_t *)gpt, ssz) < ssz)
{
rt_free(gpt);
gpt = RT_NULL;
return RT_NULL;
}
/* Remember to free gpt when finished with it */
return gpt;
}
/**
* @brief This function tests one GPT header and PTEs for validity.
*
* @param disk the blk of disk.
* @param lba the Logical Block Address of the GPT header to test.
* @param gpt the GPT header ptr, filled on return.
* @param ptes the PTEs ptr, filled on return.
* @returns true if valid, false on error.
* If valid, returns pointers to newly allocated GPT header and PTEs.
*/
static rt_bool_t is_gpt_valid(struct rt_blk_disk *disk,
rt_uint64_t lba, gpt_header **gpt, gpt_entry **ptes)
{
rt_uint32_t crc, origcrc;
rt_uint64_t lastlba, pt_size;
rt_ssize_t logical_block_size;
if (!ptes)
{
return RT_FALSE;
}
if (!(*gpt = alloc_read_gpt_header(disk, lba)))
{
return RT_FALSE;
}
/* Check the GUID Partition Table signature */
if (rt_le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE)
{
LOG_D("%s: GUID Partition Table Header signature is wrong: %lld != %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu((*gpt)->signature),
(rt_uint64_t)GPT_HEADER_SIGNATURE);
goto _fail;
}
/* Check the GUID Partition Table header size is too big */
logical_block_size = rt_blk_disk_get_logical_block_size(disk);
if (rt_le32_to_cpu((*gpt)->header_size) > logical_block_size)
{
LOG_D("%s: GUID Partition Table Header size is too large: %u > %u",
to_disk_name(disk),
rt_le32_to_cpu((*gpt)->header_size),
logical_block_size);
goto _fail;
}
/* Check the GUID Partition Table header size is too small */
if (rt_le32_to_cpu((*gpt)->header_size) < sizeof(gpt_header))
{
LOG_D("%s: GUID Partition Table Header size is too small: %u < %u",
to_disk_name(disk),
rt_le32_to_cpu((*gpt)->header_size),
sizeof(gpt_header));
goto _fail;
}
/* Check the GUID Partition Table CRC */
origcrc = rt_le32_to_cpu((*gpt)->header_crc32);
(*gpt)->header_crc32 = 0;
crc = efi_crc32((const rt_uint8_t *)(*gpt), rt_le32_to_cpu((*gpt)->header_size));
if (crc != origcrc)
{
LOG_D("%s: GUID Partition Table Header CRC is wrong: %x != %x",
to_disk_name(disk), crc, origcrc);
goto _fail;
}
(*gpt)->header_crc32 = rt_cpu_to_le32(origcrc);
/*
* Check that the start_lba entry points to the LBA that contains
* the GUID Partition Table
*/
if (rt_le64_to_cpu((*gpt)->start_lba) != lba)
{
LOG_D("%s: GPT start_lba incorrect: %lld != %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu((*gpt)->start_lba),
(rt_uint64_t)lba);
goto _fail;
}
/* Check the first_usable_lba and last_usable_lba are within the disk */
lastlba = last_lba(disk);
if (rt_le64_to_cpu((*gpt)->first_usable_lba) > lastlba)
{
LOG_D("%s: GPT: first_usable_lba incorrect: %lld > %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu((*gpt)->first_usable_lba),
(rt_uint64_t)lastlba);
goto _fail;
}
if (rt_le64_to_cpu((*gpt)->last_usable_lba) > lastlba)
{
LOG_D("%s: GPT: last_usable_lba incorrect: %lld > %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu((*gpt)->last_usable_lba),
(rt_uint64_t)lastlba);
goto _fail;
}
if (rt_le64_to_cpu((*gpt)->last_usable_lba) < rt_le64_to_cpu((*gpt)->first_usable_lba))
{
LOG_D("%s: GPT: last_usable_lba incorrect: %lld > %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu((*gpt)->last_usable_lba),
(rt_uint64_t)rt_le64_to_cpu((*gpt)->first_usable_lba));
goto _fail;
}
/* Check that sizeof_partition_entry has the correct value */
if (rt_le32_to_cpu((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry))
{
LOG_D("%s: GUID Partition Entry Size check failed", to_disk_name(disk));
goto _fail;
}
/* Sanity check partition table size */
pt_size = (rt_uint64_t)rt_le32_to_cpu((*gpt)->num_partition_entries) *
rt_le32_to_cpu((*gpt)->sizeof_partition_entry);
if (!(*ptes = alloc_read_gpt_entries(disk, *gpt)))
{
goto _fail;
}
/* Check the GUID Partition Entry Array CRC */
crc = efi_crc32((const rt_uint8_t *)(*ptes), pt_size);
if (crc != rt_le32_to_cpu((*gpt)->partition_entry_array_crc32))
{
LOG_D("%s: GUID Partition Entry Array CRC check failed", to_disk_name(disk));
goto _fail_ptes;
}
/* We're done, all's well */
return RT_TRUE;
_fail_ptes:
rt_free(*ptes);
*ptes = RT_NULL;
_fail:
rt_free(*gpt);
*gpt = RT_NULL;
return RT_FALSE;
}
/**
* @brief This function tests one PTE for validity.
*
* @param pte the pte to check.
* @param lastlba the last lba of the disk.
* @return valid boolean of pte.
*/
rt_inline rt_bool_t is_pte_valid(const gpt_entry *pte, const rt_size_t lastlba)
{
if ((!efi_guidcmp(pte->partition_type_guid, NULL_GUID)) ||
rt_le64_to_cpu(pte->starting_lba) > lastlba ||
rt_le64_to_cpu(pte->ending_lba) > lastlba)
{
return RT_FALSE;
}
return RT_TRUE;
}
/**
* @brief This function search disk for valid GPT headers and PTEs.
*
* @param disk the blk of disk.
* @param pgpt the primary GPT header.
* @param agpt the alternate GPT header.
* @param lastlba the last LBA number.
*/
static void compare_gpts(struct rt_blk_disk *disk,
gpt_header *pgpt, gpt_header *agpt, rt_uint64_t lastlba)
{
int error_found = 0;
if (!pgpt || !agpt)
{
return;
}
if (rt_le64_to_cpu(pgpt->start_lba) != rt_le64_to_cpu(agpt->alternate_lba))
{
LOG_W("%s: GPT:Primary header LBA(%lld) != Alt(%lld), header alternate_lba",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu(pgpt->start_lba),
(rt_uint64_t)rt_le64_to_cpu(agpt->alternate_lba));
++error_found;
}
if (rt_le64_to_cpu(pgpt->alternate_lba) != rt_le64_to_cpu(agpt->start_lba))
{
LOG_W("%s: GPT:Primary header alternate_lba(%lld) != Alt(%lld), header start_lba",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu(pgpt->alternate_lba),
(rt_uint64_t)rt_le64_to_cpu(agpt->start_lba));
++error_found;
}
if (rt_le64_to_cpu(pgpt->first_usable_lba) != rt_le64_to_cpu(agpt->first_usable_lba))
{
LOG_W("%s: GPT:first_usable_lbas don't match %lld != %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu(pgpt->first_usable_lba),
(rt_uint64_t)rt_le64_to_cpu(agpt->first_usable_lba));
++error_found;
}
if (rt_le64_to_cpu(pgpt->last_usable_lba) != rt_le64_to_cpu(agpt->last_usable_lba))
{
LOG_W("%s: GPT:last_usable_lbas don't match %lld != %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu(pgpt->last_usable_lba),
(rt_uint64_t)rt_le64_to_cpu(agpt->last_usable_lba));
++error_found;
}
if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid))
{
LOG_W("%s: GPT:disk_guids don't match", to_disk_name(disk));
++error_found;
}
if (rt_le32_to_cpu(pgpt->num_partition_entries) !=
rt_le32_to_cpu(agpt->num_partition_entries))
{
LOG_W("%s: GPT:num_partition_entries don't match: 0x%x != 0x%x",
to_disk_name(disk),
rt_le32_to_cpu(pgpt->num_partition_entries),
rt_le32_to_cpu(agpt->num_partition_entries));
++error_found;
}
if (rt_le32_to_cpu(pgpt->sizeof_partition_entry) !=
rt_le32_to_cpu(agpt->sizeof_partition_entry))
{
LOG_W("%s: GPT:sizeof_partition_entry values don't match: 0x%x != 0x%x",
to_disk_name(disk),
rt_le32_to_cpu(pgpt->sizeof_partition_entry),
rt_le32_to_cpu(agpt->sizeof_partition_entry));
++error_found;
}
if (rt_le32_to_cpu(pgpt->partition_entry_array_crc32) !=
rt_le32_to_cpu(agpt->partition_entry_array_crc32))
{
LOG_W("%s: GPT:partition_entry_array_crc32 values don't match: 0x%x != 0x%x",
to_disk_name(disk),
rt_le32_to_cpu(pgpt->partition_entry_array_crc32),
rt_le32_to_cpu(agpt->partition_entry_array_crc32));
++error_found;
}
if (rt_le64_to_cpu(pgpt->alternate_lba) != lastlba)
{
LOG_W("%s: GPT:Primary header thinks Alt. header is not at the end of the disk: %lld != %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu(pgpt->alternate_lba),
(rt_uint64_t)lastlba);
++error_found;
}
if (rt_le64_to_cpu(agpt->start_lba) != lastlba)
{
LOG_W("%s: GPT:Alternate GPT header not at the end of the disk: %lld != %lld",
to_disk_name(disk),
(rt_uint64_t)rt_le64_to_cpu(agpt->start_lba),
(rt_uint64_t)lastlba);
++error_found;
}
if (error_found)
{
LOG_W("GPT: Use GNU Parted to correct GPT errors");
}
}
/**
* @brief This function search disk for valid GPT headers and PTEs.
*
* @param disk the disk parsed partitions.
* @param gpt the GPT header ptr, filled on return.
* @param ptes the PTEs ptr, filled on return.
* @return 1 if valid, 0 on error.
* If valid, returns pointers to newly allocated GPT header and PTEs.
* Validity depends on PMBR being valid (or being overridden by the
* 'gpt' kernel command line option) and finding either the Primary
* GPT header and PTEs valid, or the Alternate GPT header and PTEs
* valid. If the Primary GPT header is not valid, the Alternate GPT header
* is not checked unless the 'gpt' kernel command line option is passed.
* This protects against devices which misreport their size, and forces
* the user to decide to use the Alternate GPT.
*/
static rt_bool_t find_valid_gpt(struct rt_blk_disk *disk,
gpt_header **gpt, gpt_entry **ptes)
{
int good_pgpt = 0, good_agpt = 0, good_pmbr = 0;
gpt_header *pgpt = RT_NULL, *agpt = RT_NULL;
gpt_entry *pptes = RT_NULL, *aptes = RT_NULL;
legacy_mbr *legacymbr;
rt_size_t total_sectors = rt_blk_disk_get_capacity(disk);
rt_size_t lastlba;
if (!ptes)
{
return RT_FALSE;
}
lastlba = last_lba(disk);
if (!force_gpt)
{
/* This will be added to the EFI Spec. per Intel after v1.02. */
legacymbr = rt_malloc(sizeof(*legacymbr));
if (!legacymbr)
{
return RT_FALSE;
}
read_lba(disk, 0, (rt_uint8_t *)legacymbr, sizeof(*legacymbr));
good_pmbr = is_pmbr_valid(legacymbr, total_sectors);
rt_free(legacymbr);
if (!good_pmbr)
{
return RT_FALSE;
}
LOG_D("%s: Device has a %s MBR", to_disk_name(disk),
good_pmbr == GPT_MBR_PROTECTIVE ? "protective" : "hybrid");
}
good_pgpt = is_gpt_valid(disk, GPT_PRIMARY_PARTITION_TABLE_LBA, &pgpt, &pptes);
if (good_pgpt)
{
good_agpt = is_gpt_valid(disk, rt_le64_to_cpu(pgpt->alternate_lba), &agpt, &aptes);
}
if (!good_agpt && force_gpt)
{
good_agpt = is_gpt_valid(disk, lastlba, &agpt, &aptes);
}
/* The obviously unsuccessful case */
if (!good_pgpt && !good_agpt)
{
goto _fail;
}
compare_gpts(disk, pgpt, agpt, lastlba);
/* The good cases */
if (good_pgpt)
{
*gpt = pgpt;
*ptes = pptes;
rt_free(agpt);
rt_free(aptes);
if (!good_agpt)
{
LOG_D("%s: Alternate GPT is invalid, using primary GPT", to_disk_name(disk));
}
return RT_TRUE;
}
else if (good_agpt)
{
*gpt = agpt;
*ptes = aptes;
rt_free(pgpt);
rt_free(pptes);
LOG_D("%s: Primary GPT is invalid, using alternate GPT", to_disk_name(disk));
return RT_TRUE;
}
_fail:
rt_free(pgpt);
rt_free(agpt);
rt_free(pptes);
rt_free(aptes);
*gpt = RT_NULL;
*ptes = RT_NULL;
return RT_FALSE;
}
rt_err_t efi_partition(struct rt_blk_disk *disk)
{
rt_uint32_t entries_nr;
gpt_header *gpt = RT_NULL;
gpt_entry *ptes = RT_NULL;
if (!find_valid_gpt(disk, &gpt, &ptes) || !gpt || !ptes)
{
rt_free(gpt);
rt_free(ptes);
return -RT_EINVAL;
}
entries_nr = rt_le32_to_cpu(gpt->num_partition_entries);
for (int i = 0; i < entries_nr && i < disk->max_partitions; ++i)
{
rt_uint64_t start = rt_le64_to_cpu(ptes[i].starting_lba);
rt_uint64_t size = rt_le64_to_cpu(ptes[i].ending_lba) -
rt_le64_to_cpu(ptes[i].starting_lba) + 1ULL;
if (!is_pte_valid(&ptes[i], last_lba(disk)))
{
continue;
}
if (blk_put_partition(disk, "gpt", start, size, i) == -RT_ENOMEM)
{
break;
}
}
rt_free(gpt);
rt_free(ptes);
return RT_EOK;
}
@@ -0,0 +1,142 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-05-05 linzhenxing first version
* 2023-02-25 GuEe-GUI make blk interface
*/
#ifndef __PARTITIONS_EFI_H__
#define __PARTITIONS_EFI_H__
#include "../blk_partition.h"
#include <drivers/misc.h>
#include <drivers/byteorder.h>
#define MSDOS_MBR_SIGNATURE 0xaa55
#define EFI_PMBR_OSTYPE_EFI 0xef
#define EFI_PMBR_OSTYPE_EFI_GPT 0xee
#define GPT_MBR_PROTECTIVE 1
#define GPT_MBR_HYBRID 2
#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL
#define GPT_HEADER_REVISION_V1 0x00010000
#define GPT_PRIMARY_PARTITION_TABLE_LBA 1
#ifndef __UUID_H__
#define UUID_SIZE 16
rt_packed(struct _guid_t
{
rt_uint8_t b[UUID_SIZE];
});
typedef struct _guid_t guid_t;
#endif /* __UUID_H__ */
#ifndef __EFI_H__
typedef guid_t efi_guid_t rt_align(4);
#define EFI_GUID(a, b, c, d...) (efi_guid_t) \
{{ \
(a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
(b) & 0xff, ((b) >> 8) & 0xff, \
(c) & 0xff, ((c) >> 8) & 0xff, \
d \
}}
#define NULL_GUID \
EFI_GUID(0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
rt_inline int efi_guidcmp(efi_guid_t left, efi_guid_t right)
{
return rt_memcmp(&left, &right, sizeof (efi_guid_t));
}
#endif /* __EFI_H__ */
#define PARTITION_SYSTEM_GUID \
EFI_GUID(0xc12a7328, 0xf81f, 0x11d2, 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b)
#define LEGACY_MBR_PARTITION_GUID \
EFI_GUID(0x024dee41, 0x33e7, 0x11d3, 0x9d, 0x69, 0x00, 0x08, 0xc7, 0x81, 0xf3, 0x9f)
#define PARTITION_MSFT_RESERVED_GUID \
EFI_GUID(0xe3c9e316, 0x0b5c, 0x4db8, 0x81, 0x7d, 0xf9, 0x2d, 0xf0, 0x02, 0x15, 0xae)
#define PARTITION_BASIC_DATA_GUID \
EFI_GUID(0xebd0a0a2, 0xb9e5, 0x4433, 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7)
rt_packed(struct _gpt_header
{
rt_le64_t signature;
rt_le32_t revision;
rt_le32_t header_size;
rt_le32_t header_crc32;
rt_le32_t reserved1;
rt_le64_t start_lba;
rt_le64_t alternate_lba;
rt_le64_t first_usable_lba;
rt_le64_t last_usable_lba;
efi_guid_t disk_guid;
rt_le64_t partition_entry_lba;
rt_le32_t num_partition_entries;
rt_le32_t sizeof_partition_entry;
rt_le32_t partition_entry_array_crc32;
/*
* The rest of the logical block is reserved by UEFI and must be zero.
* EFI standard handles this by:
*
* uint8_t reserved2[BlockSize - 92];
*/
});
typedef struct _gpt_header gpt_header;
rt_packed(struct _gpt_entry_attributes
{
rt_uint64_t required_to_function:1;
rt_uint64_t reserved:47;
rt_uint64_t type_guid_specific:16;
});
typedef struct _gpt_entry_attributes gpt_entry_attributes;
rt_packed(struct _gpt_entry
{
efi_guid_t partition_type_guid;
efi_guid_t unique_partition_guid;
rt_le64_t starting_lba;
rt_le64_t ending_lba;
gpt_entry_attributes attributes;
rt_le16_t partition_name[72/sizeof(rt_le16_t)];
});
typedef struct _gpt_entry gpt_entry;
rt_packed(struct _gpt_mbr_record
{
rt_uint8_t boot_indicator; /* unused by EFI, set to 0x80 for bootable */
rt_uint8_t start_head; /* unused by EFI, pt start in CHS */
rt_uint8_t start_sector; /* unused by EFI, pt start in CHS */
rt_uint8_t start_track;
rt_uint8_t os_type; /* EFI and legacy non-EFI OS types */
rt_uint8_t end_head; /* unused by EFI, pt end in CHS */
rt_uint8_t end_sector; /* unused by EFI, pt end in CHS */
rt_uint8_t end_track; /* unused by EFI, pt end in CHS */
rt_le32_t starting_lba; /* used by EFI - start addr of the on disk pt */
rt_le32_t size_in_lba; /* used by EFI - size of pt in LBA */
});
typedef struct _gpt_mbr_record gpt_mbr_record;
rt_packed(struct _legacy_mbr
{
rt_uint8_t boot_code[440];
rt_le32_t unique_mbr_signature;
rt_le16_t unknown;
gpt_mbr_record partition_record[4];
rt_le16_t signature;
});
typedef struct _legacy_mbr legacy_mbr;
#endif /* __PARTITIONS_EFI_H__ */
+31
View File
@@ -0,0 +1,31 @@
config RT_USING_CAN
bool "Using CAN device drivers"
default n
if RT_USING_CAN
config RT_CAN_USING_HDR
bool "Enable CAN hardware filter"
default n
config RT_CAN_USING_CANFD
bool "Enable CANFD support"
default n
config RT_CANMSG_BOX_SZ
int "CAN message box size"
default 16
help
Set the size of the CAN message box.
config RT_CANSND_BOX_NUM
int "Number of CAN send queues"
default 1
help
Set the number of CAN send queues.
config RT_CANSND_MSG_TIMEOUT
int "CAN send message timeout"
default 100
help
Set the timeout for CAN send messages.
endif
@@ -0,0 +1,8 @@
from building import *
cwd = GetCurrentDir()
src = Glob('*.c')
CPPPATH = [cwd + '/../include']
group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_CAN'], CPPPATH = CPPPATH)
Return('group')
+989
View File
@@ -0,0 +1,989 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2015-05-14 aubrcool@qq.com first version
* 2015-07-06 Bernard code cleanup and remove RT_CAN_USING_LED;
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#define CAN_LOCK(can) rt_mutex_take(&(can->lock), RT_WAITING_FOREVER)
#define CAN_UNLOCK(can) rt_mutex_release(&(can->lock))
static rt_err_t rt_can_init(struct rt_device *dev)
{
rt_err_t result = RT_EOK;
struct rt_can_device *can;
RT_ASSERT(dev != RT_NULL);
can = (struct rt_can_device *)dev;
/* initialize rx/tx */
can->can_rx = RT_NULL;
can->can_tx = RT_NULL;
#ifdef RT_CAN_USING_HDR
can->hdr = RT_NULL;
#endif
/* apply configuration */
if (can->ops->configure)
result = can->ops->configure(can, &can->config);
else
result = -RT_ENOSYS;
return result;
}
/*
* can interrupt routines
*/
rt_inline rt_ssize_t _can_int_rx(struct rt_can_device *can, struct rt_can_msg *data, rt_ssize_t msgs)
{
rt_ssize_t size;
struct rt_can_rx_fifo *rx_fifo;
RT_ASSERT(can != RT_NULL);
size = msgs;
rx_fifo = (struct rt_can_rx_fifo *) can->can_rx;
RT_ASSERT(rx_fifo != RT_NULL);
/* read from software FIFO */
while (msgs / sizeof(struct rt_can_msg) > 0)
{
rt_base_t level;
#ifdef RT_CAN_USING_HDR
rt_int8_t hdr;
#endif /*RT_CAN_USING_HDR*/
struct rt_can_msg_list *listmsg = RT_NULL;
/* disable interrupt */
level = rt_hw_interrupt_disable();
#ifdef RT_CAN_USING_HDR
hdr = data->hdr_index;
if (hdr >= 0 && can->hdr && hdr < can->config.maxhdr && !rt_list_isempty(&can->hdr[hdr].list))
{
listmsg = rt_list_entry(can->hdr[hdr].list.next, struct rt_can_msg_list, hdrlist);
rt_list_remove(&listmsg->list);
rt_list_remove(&listmsg->hdrlist);
if (can->hdr[hdr].msgs)
{
can->hdr[hdr].msgs--;
}
listmsg->owner = RT_NULL;
}
else if (hdr == -1)
#endif /*RT_CAN_USING_HDR*/
{
if (!rt_list_isempty(&rx_fifo->uselist))
{
listmsg = rt_list_entry(rx_fifo->uselist.next, struct rt_can_msg_list, list);
rt_list_remove(&listmsg->list);
#ifdef RT_CAN_USING_HDR
rt_list_remove(&listmsg->hdrlist);
if (listmsg->owner != RT_NULL && listmsg->owner->msgs)
{
listmsg->owner->msgs--;
}
listmsg->owner = RT_NULL;
#endif /*RT_CAN_USING_HDR*/
}
else
{
/* no data, enable interrupt and break out */
rt_hw_interrupt_enable(level);
break;
}
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
if (listmsg != RT_NULL)
{
rt_memcpy(data, &listmsg->data, sizeof(struct rt_can_msg));
level = rt_hw_interrupt_disable();
rt_list_insert_before(&rx_fifo->freelist, &listmsg->list);
rx_fifo->freenumbers++;
RT_ASSERT(rx_fifo->freenumbers <= can->config.msgboxsz);
rt_hw_interrupt_enable(level);
listmsg = RT_NULL;
}
else
{
break;
}
data ++;
msgs -= sizeof(struct rt_can_msg);
}
return (size - msgs);
}
rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *data, int msgs)
{
int size;
struct rt_can_tx_fifo *tx_fifo;
RT_ASSERT(can != RT_NULL);
size = msgs;
tx_fifo = (struct rt_can_tx_fifo *) can->can_tx;
RT_ASSERT(tx_fifo != RT_NULL);
while (msgs)
{
rt_base_t level;
rt_uint32_t no;
rt_uint32_t result;
struct rt_can_sndbxinx_list *tx_tosnd = RT_NULL;
rt_sem_take(&(tx_fifo->sem), RT_WAITING_FOREVER);
level = rt_hw_interrupt_disable();
tx_tosnd = rt_list_entry(tx_fifo->freelist.next, struct rt_can_sndbxinx_list, list);
RT_ASSERT(tx_tosnd != RT_NULL);
rt_list_remove(&tx_tosnd->list);
rt_hw_interrupt_enable(level);
no = ((rt_ubase_t)tx_tosnd - (rt_ubase_t)tx_fifo->buffer) / sizeof(struct rt_can_sndbxinx_list);
tx_tosnd->result = RT_CAN_SND_RESULT_WAIT;
rt_completion_init(&tx_tosnd->completion);
if (can->ops->sendmsg(can, data, no) != RT_EOK)
{
/* send failed. */
level = rt_hw_interrupt_disable();
rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list);
rt_hw_interrupt_enable(level);
rt_sem_release(&(tx_fifo->sem));
goto err_ret;
}
can->status.sndchange |= 1<<no;
if (rt_completion_wait(&(tx_tosnd->completion), RT_CANSND_MSG_TIMEOUT) != RT_EOK)
{
level = rt_hw_interrupt_disable();
rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list);
can->status.sndchange &= ~ (1<<no);
rt_hw_interrupt_enable(level);
rt_sem_release(&(tx_fifo->sem));
goto err_ret;
}
level = rt_hw_interrupt_disable();
result = tx_tosnd->result;
if (!rt_list_isempty(&tx_tosnd->list))
{
rt_list_remove(&tx_tosnd->list);
}
rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list);
rt_hw_interrupt_enable(level);
rt_sem_release(&(tx_fifo->sem));
if (result == RT_CAN_SND_RESULT_OK)
{
level = rt_hw_interrupt_disable();
can->status.sndpkg++;
rt_hw_interrupt_enable(level);
data ++;
msgs -= sizeof(struct rt_can_msg);
if (!msgs) break;
}
else
{
err_ret:
level = rt_hw_interrupt_disable();
can->status.dropedsndpkg++;
rt_hw_interrupt_enable(level);
break;
}
}
return (size - msgs);
}
rt_inline int _can_int_tx_priv(struct rt_can_device *can, const struct rt_can_msg *data, int msgs)
{
int size;
rt_base_t level;
rt_uint32_t no, result;
struct rt_can_tx_fifo *tx_fifo;
RT_ASSERT(can != RT_NULL);
size = msgs;
tx_fifo = (struct rt_can_tx_fifo *) can->can_tx;
RT_ASSERT(tx_fifo != RT_NULL);
while (msgs)
{
no = data->priv;
if (no >= can->config.sndboxnumber)
{
break;
}
level = rt_hw_interrupt_disable();
if ((tx_fifo->buffer[no].result != RT_CAN_SND_RESULT_OK))
{
rt_hw_interrupt_enable(level);
rt_completion_wait(&(tx_fifo->buffer[no].completion), RT_WAITING_FOREVER);
continue;
}
tx_fifo->buffer[no].result = RT_CAN_SND_RESULT_WAIT;
rt_hw_interrupt_enable(level);
if (can->ops->sendmsg(can, data, no) != RT_EOK)
{
continue;
}
can->status.sndchange |= 1<<no;
if (rt_completion_wait(&(tx_fifo->buffer[no].completion), RT_CANSND_MSG_TIMEOUT) != RT_EOK)
{
can->status.sndchange &= ~ (1<<no);
continue;
}
result = tx_fifo->buffer[no].result;
if (result == RT_CAN_SND_RESULT_OK)
{
level = rt_hw_interrupt_disable();
can->status.sndpkg++;
rt_hw_interrupt_enable(level);
data ++;
msgs -= sizeof(struct rt_can_msg);
if (!msgs) break;
}
else
{
level = rt_hw_interrupt_disable();
can->status.dropedsndpkg++;
rt_hw_interrupt_enable(level);
break;
}
}
return (size - msgs);
}
static rt_err_t rt_can_open(struct rt_device *dev, rt_uint16_t oflag)
{
struct rt_can_device *can;
char tmpname[16];
RT_ASSERT(dev != RT_NULL);
can = (struct rt_can_device *)dev;
CAN_LOCK(can);
/* get open flags */
dev->open_flag = oflag & 0xff;
if (can->can_rx == RT_NULL)
{
if (oflag & RT_DEVICE_FLAG_INT_RX)
{
int i = 0;
struct rt_can_rx_fifo *rx_fifo;
rx_fifo = (struct rt_can_rx_fifo *) rt_malloc(sizeof(struct rt_can_rx_fifo) +
can->config.msgboxsz * sizeof(struct rt_can_msg_list));
RT_ASSERT(rx_fifo != RT_NULL);
rx_fifo->buffer = (struct rt_can_msg_list *)(rx_fifo + 1);
rt_memset(rx_fifo->buffer, 0, can->config.msgboxsz * sizeof(struct rt_can_msg_list));
rt_list_init(&rx_fifo->freelist);
rt_list_init(&rx_fifo->uselist);
rx_fifo->freenumbers = can->config.msgboxsz;
for (i = 0; i < can->config.msgboxsz; i++)
{
rt_list_insert_before(&rx_fifo->freelist, &rx_fifo->buffer[i].list);
#ifdef RT_CAN_USING_HDR
rt_list_init(&rx_fifo->buffer[i].hdrlist);
rx_fifo->buffer[i].owner = RT_NULL;
#endif
}
can->can_rx = rx_fifo;
dev->open_flag |= RT_DEVICE_FLAG_INT_RX;
/* open can rx interrupt */
can->ops->control(can, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_RX);
}
}
if (can->can_tx == RT_NULL)
{
if (oflag & RT_DEVICE_FLAG_INT_TX)
{
int i = 0;
struct rt_can_tx_fifo *tx_fifo;
tx_fifo = (struct rt_can_tx_fifo *) rt_malloc(sizeof(struct rt_can_tx_fifo) +
can->config.sndboxnumber * sizeof(struct rt_can_sndbxinx_list));
RT_ASSERT(tx_fifo != RT_NULL);
tx_fifo->buffer = (struct rt_can_sndbxinx_list *)(tx_fifo + 1);
rt_memset(tx_fifo->buffer, 0,
can->config.sndboxnumber * sizeof(struct rt_can_sndbxinx_list));
rt_list_init(&tx_fifo->freelist);
for (i = 0; i < can->config.sndboxnumber; i++)
{
rt_list_insert_before(&tx_fifo->freelist, &tx_fifo->buffer[i].list);
rt_completion_init(&(tx_fifo->buffer[i].completion));
tx_fifo->buffer[i].result = RT_CAN_SND_RESULT_OK;
}
rt_sprintf(tmpname, "%stl", dev->parent.name);
rt_sem_init(&(tx_fifo->sem), tmpname, can->config.sndboxnumber, RT_IPC_FLAG_FIFO);
can->can_tx = tx_fifo;
dev->open_flag |= RT_DEVICE_FLAG_INT_TX;
/* open can tx interrupt */
can->ops->control(can, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_TX);
}
}
can->ops->control(can, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_CAN_INT_ERR);
#ifdef RT_CAN_USING_HDR
if (can->hdr == RT_NULL)
{
int i = 0;
struct rt_can_hdr *phdr;
phdr = (struct rt_can_hdr *) rt_malloc(can->config.maxhdr * sizeof(struct rt_can_hdr));
RT_ASSERT(phdr != RT_NULL);
rt_memset(phdr, 0, can->config.maxhdr * sizeof(struct rt_can_hdr));
for (i = 0; i < can->config.maxhdr; i++)
{
rt_list_init(&phdr[i].list);
}
can->hdr = phdr;
}
#endif
if (!can->timerinitflag)
{
can->timerinitflag = 1;
rt_timer_start(&can->timer);
}
CAN_UNLOCK(can);
return RT_EOK;
}
static rt_err_t rt_can_close(struct rt_device *dev)
{
struct rt_can_device *can;
RT_ASSERT(dev != RT_NULL);
can = (struct rt_can_device *)dev;
CAN_LOCK(can);
/* this device has more reference count */
if (dev->ref_count > 1)
{
CAN_UNLOCK(can);
return RT_EOK;
}
if (can->timerinitflag)
{
can->timerinitflag = 0;
rt_timer_stop(&can->timer);
}
can->status_indicate.ind = RT_NULL;
can->status_indicate.args = RT_NULL;
#ifdef RT_CAN_USING_HDR
if (can->hdr != RT_NULL)
{
rt_free(can->hdr);
can->hdr = RT_NULL;
}
#endif
if (dev->open_flag & RT_DEVICE_FLAG_INT_RX)
{
struct rt_can_rx_fifo *rx_fifo;
/* clear can rx interrupt */
can->ops->control(can, RT_DEVICE_CTRL_CLR_INT, (void *)RT_DEVICE_FLAG_INT_RX);
rx_fifo = (struct rt_can_rx_fifo *)can->can_rx;
RT_ASSERT(rx_fifo != RT_NULL);
rt_free(rx_fifo);
dev->open_flag &= ~RT_DEVICE_FLAG_INT_RX;
can->can_rx = RT_NULL;
}
if (dev->open_flag & RT_DEVICE_FLAG_INT_TX)
{
struct rt_can_tx_fifo *tx_fifo;
/* clear can tx interrupt */
can->ops->control(can, RT_DEVICE_CTRL_CLR_INT, (void *)RT_DEVICE_FLAG_INT_TX);
tx_fifo = (struct rt_can_tx_fifo *)can->can_tx;
RT_ASSERT(tx_fifo != RT_NULL);
rt_sem_detach(&(tx_fifo->sem));
rt_free(tx_fifo);
dev->open_flag &= ~RT_DEVICE_FLAG_INT_TX;
can->can_tx = RT_NULL;
}
can->ops->control(can, RT_DEVICE_CTRL_CLR_INT, (void *)RT_DEVICE_CAN_INT_ERR);
can->ops->control(can, RT_CAN_CMD_START, RT_FALSE);
CAN_UNLOCK(can);
return RT_EOK;
}
static rt_ssize_t rt_can_read(struct rt_device *dev,
rt_off_t pos,
void *buffer,
rt_size_t size)
{
struct rt_can_device *can;
RT_ASSERT(dev != RT_NULL);
if (size == 0) return 0;
can = (struct rt_can_device *)dev;
if ((dev->open_flag & RT_DEVICE_FLAG_INT_RX) && (dev->ref_count > 0))
{
return _can_int_rx(can, buffer, size);
}
return 0;
}
static rt_ssize_t rt_can_write(struct rt_device *dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
struct rt_can_device *can;
RT_ASSERT(dev != RT_NULL);
if (size == 0) return 0;
can = (struct rt_can_device *)dev;
if ((dev->open_flag & RT_DEVICE_FLAG_INT_TX) && (dev->ref_count > 0))
{
if (can->config.privmode)
{
return _can_int_tx_priv(can, buffer, size);
}
else
{
return _can_int_tx(can, buffer, size);
}
}
return 0;
}
static rt_err_t rt_can_control(struct rt_device *dev,
int cmd,
void *args)
{
struct rt_can_device *can;
rt_err_t res;
res = RT_EOK;
RT_ASSERT(dev != RT_NULL);
can = (struct rt_can_device *)dev;
switch (cmd)
{
case RT_DEVICE_CTRL_SUSPEND:
/* suspend device */
dev->flag |= RT_DEVICE_FLAG_SUSPENDED;
break;
case RT_DEVICE_CTRL_RESUME:
/* resume device */
dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED;
break;
case RT_DEVICE_CTRL_CONFIG:
/* configure device */
res = can->ops->configure(can, (struct can_configure *)args);
break;
case RT_CAN_CMD_SET_PRIV:
/* configure device */
if ((rt_uint32_t)(rt_ubase_t)args != can->config.privmode)
{
int i;
rt_base_t level;
struct rt_can_tx_fifo *tx_fifo;
res = can->ops->control(can, cmd, args);
if (res != RT_EOK) return res;
tx_fifo = (struct rt_can_tx_fifo *) can->can_tx;
if (can->config.privmode)
{
for (i = 0; i < can->config.sndboxnumber; i++)
{
level = rt_hw_interrupt_disable();
if(rt_list_isempty(&tx_fifo->buffer[i].list))
{
rt_sem_release(&(tx_fifo->sem));
}
else
{
rt_list_remove(&tx_fifo->buffer[i].list);
}
rt_hw_interrupt_enable(level);
}
}
else
{
for (i = 0; i < can->config.sndboxnumber; i++)
{
level = rt_hw_interrupt_disable();
if (tx_fifo->buffer[i].result == RT_CAN_SND_RESULT_OK)
{
rt_list_insert_before(&tx_fifo->freelist, &tx_fifo->buffer[i].list);
}
rt_hw_interrupt_enable(level);
}
}
}
break;
case RT_CAN_CMD_SET_STATUS_IND:
can->status_indicate.ind = ((rt_can_status_ind_type_t)args)->ind;
can->status_indicate.args = ((rt_can_status_ind_type_t)args)->args;
break;
#ifdef RT_CAN_USING_HDR
case RT_CAN_CMD_SET_FILTER:
res = can->ops->control(can, cmd, args);
if (res != RT_EOK || can->hdr == RT_NULL)
{
return res;
}
struct rt_can_filter_config *pfilter;
struct rt_can_filter_item *pitem;
rt_uint32_t count;
rt_base_t level;
pfilter = (struct rt_can_filter_config *)args;
RT_ASSERT(pfilter);
count = pfilter->count;
pitem = pfilter->items;
if (pfilter->actived)
{
while (count)
{
if (pitem->hdr_bank >= can->config.maxhdr || pitem->hdr_bank < 0)
{
count--;
pitem++;
continue;
}
level = rt_hw_interrupt_disable();
if (!can->hdr[pitem->hdr_bank].connected)
{
rt_hw_interrupt_enable(level);
rt_memcpy(&can->hdr[pitem->hdr_bank].filter, pitem,
sizeof(struct rt_can_filter_item));
level = rt_hw_interrupt_disable();
can->hdr[pitem->hdr_bank].connected = 1;
can->hdr[pitem->hdr_bank].msgs = 0;
rt_list_init(&can->hdr[pitem->hdr_bank].list);
}
rt_hw_interrupt_enable(level);
count--;
pitem++;
}
}
else
{
while (count)
{
if (pitem->hdr_bank >= can->config.maxhdr || pitem->hdr_bank < 0)
{
count--;
pitem++;
continue;
}
level = rt_hw_interrupt_disable();
if (can->hdr[pitem->hdr_bank].connected)
{
can->hdr[pitem->hdr_bank].connected = 0;
can->hdr[pitem->hdr_bank].msgs = 0;
if (!rt_list_isempty(&can->hdr[pitem->hdr_bank].list))
{
rt_list_remove(can->hdr[pitem->hdr_bank].list.next);
}
rt_hw_interrupt_enable(level);
rt_memset(&can->hdr[pitem->hdr_bank].filter, 0,
sizeof(struct rt_can_filter_item));
}
else
{
rt_hw_interrupt_enable(level);
}
count--;
pitem++;
}
}
break;
#endif /*RT_CAN_USING_HDR*/
#ifdef RT_CAN_USING_BUS_HOOK
case RT_CAN_CMD_SET_BUS_HOOK:
can->bus_hook = (rt_can_bus_hook) args;
break;
#endif /*RT_CAN_USING_BUS_HOOK*/
default :
/* control device */
if (can->ops->control != RT_NULL)
{
res = can->ops->control(can, cmd, args);
}
else
{
res = -RT_ENOSYS;
}
break;
}
return res;
}
/*
* can timer
*/
static void cantimeout(void *arg)
{
rt_can_t can;
can = (rt_can_t)arg;
RT_ASSERT(can);
rt_device_control((rt_device_t)can, RT_CAN_CMD_GET_STATUS, (void *)&can->status);
if (can->status_indicate.ind != RT_NULL)
{
can->status_indicate.ind(can, can->status_indicate.args);
}
#ifdef RT_CAN_USING_BUS_HOOK
if(can->bus_hook)
{
can->bus_hook(can);
}
#endif /*RT_CAN_USING_BUS_HOOK*/
if (can->timerinitflag == 1)
{
can->timerinitflag = 0xFF;
}
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops can_device_ops =
{
rt_can_init,
rt_can_open,
rt_can_close,
rt_can_read,
rt_can_write,
rt_can_control
};
#endif
/*
* can register
*/
rt_err_t rt_hw_can_register(struct rt_can_device *can,
const char *name,
const struct rt_can_ops *ops,
void *data)
{
struct rt_device *device;
RT_ASSERT(can != RT_NULL);
device = &(can->parent);
device->type = RT_Device_Class_CAN;
device->rx_indicate = RT_NULL;
device->tx_complete = RT_NULL;
#ifdef RT_CAN_USING_HDR
can->hdr = RT_NULL;
#endif
can->can_rx = RT_NULL;
can->can_tx = RT_NULL;
rt_mutex_init(&(can->lock), "can", RT_IPC_FLAG_PRIO);
#ifdef RT_CAN_USING_BUS_HOOK
can->bus_hook = RT_NULL;
#endif /*RT_CAN_USING_BUS_HOOK*/
#ifdef RT_USING_DEVICE_OPS
device->ops = &can_device_ops;
#else
device->init = rt_can_init;
device->open = rt_can_open;
device->close = rt_can_close;
device->read = rt_can_read;
device->write = rt_can_write;
device->control = rt_can_control;
#endif
can->ops = ops;
can->status_indicate.ind = RT_NULL;
can->status_indicate.args = RT_NULL;
rt_memset(&can->status, 0, sizeof(can->status));
device->user_data = data;
can->timerinitflag = 0;
rt_timer_init(&can->timer,
name,
cantimeout,
(void *)can,
can->config.ticks,
RT_TIMER_FLAG_PERIODIC);
/* register a character device */
return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
}
/* ISR for can interrupt */
void rt_hw_can_isr(struct rt_can_device *can, int event)
{
switch (event & 0xff)
{
case RT_CAN_EVENT_RXOF_IND:
{
rt_base_t level;
level = rt_hw_interrupt_disable();
can->status.dropedrcvpkg++;
rt_hw_interrupt_enable(level);
}
case RT_CAN_EVENT_RX_IND:
{
struct rt_can_msg tmpmsg;
struct rt_can_rx_fifo *rx_fifo;
struct rt_can_msg_list *listmsg = RT_NULL;
#ifdef RT_CAN_USING_HDR
rt_int8_t hdr;
#endif
int ch = -1;
rt_base_t level;
rt_uint32_t no;
rx_fifo = (struct rt_can_rx_fifo *)can->can_rx;
RT_ASSERT(rx_fifo != RT_NULL);
/* interrupt mode receive */
RT_ASSERT(can->parent.open_flag & RT_DEVICE_FLAG_INT_RX);
no = event >> 8;
ch = can->ops->recvmsg(can, &tmpmsg, no);
if (ch == -1) break;
/* disable interrupt */
level = rt_hw_interrupt_disable();
can->status.rcvpkg++;
can->status.rcvchange = 1;
if (!rt_list_isempty(&rx_fifo->freelist))
{
listmsg = rt_list_entry(rx_fifo->freelist.next, struct rt_can_msg_list, list);
rt_list_remove(&listmsg->list);
#ifdef RT_CAN_USING_HDR
rt_list_remove(&listmsg->hdrlist);
if (listmsg->owner != RT_NULL && listmsg->owner->msgs)
{
listmsg->owner->msgs--;
}
listmsg->owner = RT_NULL;
#endif /*RT_CAN_USING_HDR*/
RT_ASSERT(rx_fifo->freenumbers > 0);
rx_fifo->freenumbers--;
}
else if (!rt_list_isempty(&rx_fifo->uselist))
{
listmsg = rt_list_entry(rx_fifo->uselist.next, struct rt_can_msg_list, list);
can->status.dropedrcvpkg++;
rt_list_remove(&listmsg->list);
#ifdef RT_CAN_USING_HDR
rt_list_remove(&listmsg->hdrlist);
if (listmsg->owner != RT_NULL && listmsg->owner->msgs)
{
listmsg->owner->msgs--;
}
listmsg->owner = RT_NULL;
#endif
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
if (listmsg != RT_NULL)
{
rt_memcpy(&listmsg->data, &tmpmsg, sizeof(struct rt_can_msg));
level = rt_hw_interrupt_disable();
rt_list_insert_before(&rx_fifo->uselist, &listmsg->list);
#ifdef RT_CAN_USING_HDR
hdr = tmpmsg.hdr_index;
if (can->hdr != RT_NULL)
{
RT_ASSERT(hdr < can->config.maxhdr && hdr >= 0);
if (can->hdr[hdr].connected)
{
rt_list_insert_before(&can->hdr[hdr].list, &listmsg->hdrlist);
listmsg->owner = &can->hdr[hdr];
can->hdr[hdr].msgs++;
}
}
#endif
rt_hw_interrupt_enable(level);
}
/* invoke callback */
#ifdef RT_CAN_USING_HDR
if (can->hdr != RT_NULL && can->hdr[hdr].connected && can->hdr[hdr].filter.ind)
{
rt_size_t rx_length;
RT_ASSERT(hdr < can->config.maxhdr && hdr >= 0);
level = rt_hw_interrupt_disable();
rx_length = can->hdr[hdr].msgs * sizeof(struct rt_can_msg);
rt_hw_interrupt_enable(level);
if (rx_length)
{
can->hdr[hdr].filter.ind(&can->parent, can->hdr[hdr].filter.args, hdr, rx_length);
}
}
else
#endif
{
if (can->parent.rx_indicate != RT_NULL)
{
rt_size_t rx_length;
level = rt_hw_interrupt_disable();
/* get rx length */
rx_length = rt_list_len(&rx_fifo->uselist)* sizeof(struct rt_can_msg);
rt_hw_interrupt_enable(level);
if (rx_length)
{
can->parent.rx_indicate(&can->parent, rx_length);
}
}
}
break;
}
case RT_CAN_EVENT_TX_DONE:
case RT_CAN_EVENT_TX_FAIL:
{
struct rt_can_tx_fifo *tx_fifo;
rt_uint32_t no;
no = event >> 8;
tx_fifo = (struct rt_can_tx_fifo *) can->can_tx;
RT_ASSERT(tx_fifo != RT_NULL);
if (can->status.sndchange&(1<<no))
{
if ((event & 0xff) == RT_CAN_EVENT_TX_DONE)
{
tx_fifo->buffer[no].result = RT_CAN_SND_RESULT_OK;
}
else
{
tx_fifo->buffer[no].result = RT_CAN_SND_RESULT_ERR;
}
rt_completion_done(&(tx_fifo->buffer[no].completion));
}
break;
}
}
}
#ifdef RT_USING_FINSH
#include <finsh.h>
int cmd_canstat(int argc, void **argv)
{
static const char *ErrCode[] =
{
"No Error!",
"Warning !",
"Passive !",
"Bus Off !"
};
if (argc >= 2)
{
struct rt_can_status status;
rt_device_t candev = rt_device_find(argv[1]);
if (!candev)
{
rt_kprintf(" Can't find can device %s\n", argv[1]);
return -1;
}
rt_kprintf(" Found can device: %s...", argv[1]);
rt_device_control(candev, RT_CAN_CMD_GET_STATUS, &status);
rt_kprintf("\n Receive...error..count: %010ld. Send.....error....count: %010ld.",
status.rcverrcnt, status.snderrcnt);
rt_kprintf("\n Bit..pad..error..count: %010ld. Format...error....count: %010ld",
status.bitpaderrcnt, status.formaterrcnt);
rt_kprintf("\n Ack.......error..count: %010ld. Bit......error....count: %010ld.",
status.ackerrcnt, status.biterrcnt);
rt_kprintf("\n CRC.......error..count: %010ld. Error.code.[%010ld]: ",
status.crcerrcnt, status.errcode);
switch (status.errcode)
{
case 0:
rt_kprintf("%s.", ErrCode[0]);
break;
case 1:
rt_kprintf("%s.", ErrCode[1]);
break;
case 2:
case 3:
rt_kprintf("%s.", ErrCode[2]);
break;
case 4:
case 5:
case 6:
case 7:
rt_kprintf("%s.", ErrCode[3]);
break;
}
rt_kprintf("\n Total.receive.packages: %010ld. Dropped.receive.packages: %010ld.",
status.rcvpkg, status.dropedrcvpkg);
rt_kprintf("\n Total..send...packages: %010ld. Dropped...send..packages: %010ld.\n",
status.sndpkg + status.dropedsndpkg, status.dropedsndpkg);
}
else
{
rt_kprintf(" Invalid Call %s\n", argv[0]);
rt_kprintf(" Please using %s cannamex .Here canname is driver name and x is candrive number.\n", argv[0]);
}
return 0;
}
MSH_CMD_EXPORT_ALIAS(cmd_canstat, canstat, stat can device status);
#endif
@@ -0,0 +1,132 @@
说明:
本驱动完成了can控制器硬件抽象
一 CAN Driver 注册
Can driver注册需要填充以下几个数据结构:
1、struct can_configure
{
rt_uint32_t baud_rate;
rt_uint32_t msgboxsz;
rt_uint32_t sndboxnumber;
rt_uint32_t mode :8;
rt_uint32_t privmode :8;
rt_uint32_t reserved :16;
#ifdef RT_CAN_USING_LED
const struct rt_can_led* rcvled;
const struct rt_can_led* sndled;
const struct rt_can_led* errled;
#endif /*RT_CAN_USING_LED*/
rt_uint32_t ticks;
#ifdef RT_CAN_USING_HDR
rt_uint32_t maxhdr;
#endif
};
struct can_configure 为can驱动的基本配置信息:
baud_rate :
enum CANBAUD
{
CAN1MBaud=0, // 1 MBit/sec
CAN800kBaud, // 800 kBit/sec
CAN500kBaud, // 500 kBit/sec
CAN250kBaud, // 250 kBit/sec
CAN125kBaud, // 125 kBit/sec
CAN100kBaud, // 100 kBit/sec
CAN50kBaud, // 50 kBit/sec
CAN20kBaud, // 20 kBit/sec
CAN10kBaud // 10 kBit/sec
};
配置Can的波特率。
msgboxsz : Can接收邮箱缓冲数量,本驱动在软件层开辟msgboxsz个接收邮箱。
sndboxnumber : can 发送通道数量,该配置为Can控制器实际的发送通道数量。
mode
#define RT_CAN_MODE_NORMAL 0 正常模式
#define RT_CAN_MODE_LISEN 1 只听模式
#define RT_CAN_MODE_LOOPBACK 2 自发自收模式
#define RT_CAN_MODE_LOOPBACKANLISEN 3 自发自收只听模式
配置Can 的工作状态。
privmode :
#define RT_CAN_MODE_PRIV 0x01 处于优先级模式,高优先级的消息优先发送。
#define RT_CAN_MODE_NOPRIV 0x00
配置Can driver的优先级模式。
#ifdef RT_CAN_USING_LED
const struct rt_can_led* rcvled;
const struct rt_can_led* sndled;
const struct rt_can_led* errled;
#endif /*RT_CAN_USING_LED*/
配置can led信息, 当前can驱动的led使用了 pin驱动,
开启RT_CAN_USING_LED时要确保当前系统已实现pin驱动。
rt_uint32_t ticks : 配置Can driver timer周期。
#ifdef RT_CAN_USING_HDR
rt_uint32_t maxhdr;
#endif
如果使用硬件过滤,则开启RT_CAN_USING_HDR, maxhdr 为Can控制器过滤表的数量。
2、struct rt_can_ops
{
rt_err_t (*configure)(struct rt_can_device *can, struct can_configure *cfg);
rt_err_t (*control)(struct rt_can_device *can, int cmd, void *arg);
int (*sendmsg)(struct rt_can_device *can, const void* buf, rt_uint32_t boxno);
int (*recvmsg)(struct rt_can_device *can,void* buf, rt_uint32_t boxno);
};
struct rt_can_ops 为要实现的特定的can控制器操作。
rt_err_t (*configure)(struct rt_can_device *can, struct can_configure *cfg);
configure根据配置信息初始化Can控制器工作模式。
rt_err_t (*control)(struct rt_can_device *can, int cmd, void *arg);
control 当前接受以下cmd参数:
#define RT_CAN_CMD_SET_FILTER 0x13
#define RT_CAN_CMD_SET_BAUD 0x14
#define RT_CAN_CMD_SET_MODE 0x15
#define RT_CAN_CMD_SET_PRIV 0x16
#define RT_CAN_CMD_GET_STATUS 0x17
#define RT_CAN_CMD_SET_STATUS_IND 0x18
int (*sendmsg)(struct rt_can_device *can, const void* buf, rt_uint32_t boxno);
sendmsg向Can控制器发送数,boxno为发送通道号。
int (*recvmsg)(struct rt_can_device *can,void* buf, rt_uint32_t boxno);
recvmsg从Can控制器接收数据,boxno为接收通道号。
struct rt_can_device
{
struct rt_device parent;
const struct rt_can_ops *ops;
struct can_configure config;
struct rt_can_status status;
rt_uint32_t timerinitflag;
struct rt_timer timer;
struct rt_can_status_ind_type status_indicate;
#ifdef RT_CAN_USING_HDR
struct rt_can_hdr* hdr;
#endif
void *can_rx;
void *can_tx;
};
填充完成后,便可调用rt_hw_can_register完成can驱动的注册。
二、 CAN Driver 的添加:
要添加一个新的Can驱动,至少要完成以下接口。
1、struct rt_can_ops
{
rt_err_t (*configure)(struct rt_can_device *can, struct can_configure *cfg);
rt_err_t (*control)(struct rt_can_device *can, int cmd, void *arg);
int (*sendmsg)(struct rt_can_device *can, const void* buf, rt_uint32_t boxno);
int (*recvmsg)(struct rt_can_device *can,void* buf, rt_uint32_t boxno);
};
2、 rt_err_t (*control)(struct rt_can_device *can, int cmd, void *arg);
接口的
#define RT_CAN_CMD_SET_FILTER 0x13
#define RT_CAN_CMD_SET_BAUD 0x14
#define RT_CAN_CMD_SET_MODE 0x15
#define RT_CAN_CMD_SET_PRIV 0x16
#define RT_CAN_CMD_GET_STATUS 0x17
#define RT_CAN_CMD_SET_STATUS_IND 0x18
若干命令。
3、can口中断,要完接收,发送结束,以及错误中断。
#define RT_CAN_EVENT_RX_IND 0x01 /* Rx indication */
#define RT_CAN_EVENT_TX_DONE 0x02 /* Tx complete */
#define RT_CAN_EVENT_TX_FAIL 0x03 /* Tx complete */
#define RT_CAN_EVENT_RX_TIMEOUT 0x05 /* Rx timeout */
#define RT_CAN_EVENT_RXOF_IND 0x06 /* Rx overflow */
中断产生后,调用rt_hw_can_isr(struct rt_can_device *can, int event)
进入相应的操作,其中接收发送中断的event,最低8位为上面的事件,16到24位为通信通道号。
一个作为一个例子,参见bsp/stm32f10x/driver下的bxcan.c 。
三、CAN Driver的使用:
一个使用的例子,参数bsp/stm32f10x/applications下的canapp.c
四、当前Can驱动,没有实现轮模式,采用中断模式,bxcan驱动工作在loopback模式下的时候不能读数据。
五、当前Can驱动,在stm32f105上测试,暂无问题。
+9
View File
@@ -0,0 +1,9 @@
menuconfig RT_USING_CLK
bool "Using Common Clock Framework (CLK)"
depends on RT_USING_DM
select RT_USING_ADT_REF
default y
if RT_USING_CLK
osource "$(SOC_DM_CLK_DIR)/Kconfig"
endif
@@ -0,0 +1,26 @@
from building import *
group = []
objs = []
if not GetDepend(['RT_USING_CLK']):
Return('group')
cwd = GetCurrentDir()
list = os.listdir(cwd)
CPPPATH = [cwd + '/../include']
src = ['clk.c']
if GetDepend(['RT_USING_OFW']):
src += ['clk-fixed-rate.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
for d in list:
path = os.path.join(cwd, d)
if os.path.isfile(os.path.join(path, 'SConscript')):
objs = objs + SConscript(os.path.join(d, 'SConscript'))
objs = objs + group
Return('objs')
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-11-26 GuEe-GUI first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <drivers/platform.h>
static rt_err_t fixed_clk_ofw_init(struct rt_platform_device *pdev, struct rt_clk_fixed_rate *clk_fixed)
{
rt_err_t err = RT_EOK;
rt_uint32_t rate, accuracy;
struct rt_ofw_node *np = pdev->parent.ofw_node;
const char *clk_name = np->name;
if (!rt_ofw_prop_read_u32(np, "clock-frequency", &rate))
{
rt_ofw_prop_read_u32(np, "clock-accuracy", &accuracy);
rt_ofw_prop_read_string(np, "clock-output-names", &clk_name);
clk_fixed->clk.name = clk_name;
clk_fixed->clk.rate = rate;
clk_fixed->clk.min_rate = rate;
clk_fixed->clk.max_rate = rate;
clk_fixed->fixed_rate = rate;
clk_fixed->fixed_accuracy = accuracy;
rt_ofw_data(np) = &clk_fixed->clk;
}
else
{
err = -RT_EIO;
}
return err;
}
static rt_err_t fixed_clk_probe(struct rt_platform_device *pdev)
{
rt_err_t err = RT_EOK;
struct rt_clk_fixed_rate *clk_fixed = rt_calloc(1, sizeof(*clk_fixed));
if (clk_fixed)
{
err = fixed_clk_ofw_init(pdev, clk_fixed);
}
else
{
err = -RT_ENOMEM;
}
if (!err)
{
err = rt_clk_register(&clk_fixed->clk, RT_NULL);
}
if (err && clk_fixed)
{
rt_free(clk_fixed);
}
return err;
}
static const struct rt_ofw_node_id fixed_clk_ofw_ids[] =
{
{ .compatible = "fixed-clock" },
{ /* sentinel */ }
};
static struct rt_platform_driver fixed_clk_driver =
{
.name = "clk-fixed-rate",
.ids = fixed_clk_ofw_ids,
.probe = fixed_clk_probe,
};
static int fixed_clk_drv_register(void)
{
rt_platform_driver_register(&fixed_clk_driver);
return 0;
}
INIT_SUBSYS_EXPORT(fixed_clk_drv_register);
File diff suppressed because it is too large Load Diff
+11
View File
@@ -0,0 +1,11 @@
config RT_USING_DM
bool "Enable device driver model with device tree"
default n
help
Enable device driver model with device tree (FDT). It will use more memory
to parse and support device tree feature.
config RT_USING_DEV_BUS
bool "Using Device Bus device drivers"
default y if RT_USING_SMART
default n
@@ -0,0 +1,21 @@
from building import *
cwd = GetCurrentDir()
src = ['device.c']
CPPPATH = [cwd + '/../include']
if GetDepend(['RT_USING_DEV_BUS']) or GetDepend(['RT_USING_DM']):
src = src + ['bus.c']
if GetDepend(['RT_USING_DM']):
src = src + ['dm.c', 'driver.c', 'numa.c', 'platform.c', 'power_domain.c']
if GetDepend(['RT_USING_DFS']):
src += ['mnt.c'];
if GetDepend(['RT_USING_OFW']):
src += ['platform_ofw.c']
group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_DEVICE'], CPPPATH = CPPPATH)
Return('group')
+520
View File
@@ -0,0 +1,520 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-10-13 flybreak the first version
* 2023-04-12 ErikChan support rt_bus
*/
#include <rtthread.h>
#include <string.h>
#include <stdlib.h>
#define DBG_TAG "dev_bus"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#ifdef RT_USING_DEV_BUS
#if defined(RT_USING_POSIX_DEVIO)
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <dfs_file.h>
static int bus_fops_open(struct dfs_file *fd)
{
LOG_D("bus fops open");
return 0;
}
static int bus_fops_close(struct dfs_file *fd)
{
LOG_D("bus fops close");
return 0;
}
static const struct dfs_file_ops bus_fops =
{
bus_fops_open,
bus_fops_close,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
};
#endif
rt_device_t rt_device_bus_create(char *name, int attach_size)
{
rt_err_t result = RT_EOK;
rt_device_t dev = rt_device_create(RT_Device_Class_Bus, 0);
result = rt_device_register(dev, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE);
if (result < 0)
{
rt_kprintf("dev bus [%s] register failed!, ret=%d\n", name, result);
return RT_NULL;
}
#if defined(RT_USING_POSIX_DEVIO)
dev->fops = &bus_fops;
#endif
LOG_D("bus create");
return dev;
}
rt_err_t rt_device_bus_destroy(rt_device_t dev)
{
rt_device_unregister(dev);
dev->parent.type = RT_Object_Class_Device;
rt_device_destroy(dev);
LOG_D("bus destroy");
return RT_EOK;
}
#endif
#ifdef RT_USING_DM
#include <drivers/core/bus.h>
static RT_DEFINE_SPINLOCK(bus_lock);
static rt_list_t bus_nodes = RT_LIST_OBJECT_INIT(bus_nodes);
static void _dm_bus_lock(struct rt_spinlock *spinlock)
{
rt_hw_spin_lock(&spinlock->lock);
}
static void _dm_bus_unlock(struct rt_spinlock *spinlock)
{
rt_hw_spin_unlock(&spinlock->lock);
}
/**
* @brief This function loop the dev_list of the bus, and call fn in each loop
*
* @param bus the target bus
*
* @param data the data push when call fn
*
* @param fn the function callback in each loop
*
* @return the error code, RT_EOK on added successfully.
*/
rt_err_t rt_bus_for_each_dev(rt_bus_t bus, void *data, int (*fn)(rt_device_t dev, void *))
{
rt_device_t dev;
rt_err_t err = -RT_EEMPTY;
rt_list_t *dev_list;
struct rt_spinlock *dev_lock;
RT_ASSERT(bus != RT_NULL);
dev_list = &bus->dev_list;
dev_lock = &bus->dev_lock;
_dm_bus_lock(dev_lock);
dev = rt_list_entry(dev_list->next, struct rt_device, node);
_dm_bus_unlock(dev_lock);
while (&dev->node != dev_list)
{
if (!fn(dev, data))
{
err = RT_EOK;
break;
}
_dm_bus_lock(dev_lock);
dev = rt_list_entry(dev->node.next, struct rt_device, node);
_dm_bus_unlock(dev_lock);
}
return err;
}
/**
* @brief This function loop the drv_list of the bus, and call fn in each loop
*
* @param bus the target bus
*
* @param data the data push when call fn
*
* @param fn the function callback in each loop
*
* @return the error code, RT_EOK on added successfully.
*/
rt_err_t rt_bus_for_each_drv(rt_bus_t bus, void *data, int (*fn)(rt_driver_t drv, void *))
{
rt_driver_t drv;
rt_err_t err = -RT_EEMPTY;
rt_list_t *drv_list;
struct rt_spinlock *drv_lock;
RT_ASSERT(bus != RT_NULL);
drv_list = &bus->drv_list;
drv_lock = &bus->drv_lock;
_dm_bus_lock(drv_lock);
drv = rt_list_entry(drv_list->next, struct rt_driver, node);
_dm_bus_unlock(drv_lock);
while (&drv->node != drv_list)
{
if (!fn(drv, data))
{
err = RT_EOK;
break;
}
_dm_bus_lock(drv_lock);
drv = rt_list_entry(drv->node.next, struct rt_driver, node);
_dm_bus_unlock(drv_lock);
}
return err;
}
static rt_err_t bus_probe(rt_driver_t drv, rt_device_t dev)
{
rt_bus_t bus = drv->bus;
rt_err_t err = -RT_EEMPTY;
if (!bus)
{
bus = dev->bus;
}
if (!dev->drv && bus->match(drv, dev))
{
dev->drv = drv;
err = bus->probe(dev);
if (err)
{
dev->drv = RT_NULL;
}
}
return err;
}
static int bus_probe_driver(rt_device_t dev, void *drv_ptr)
{
bus_probe(drv_ptr, dev);
/*
* The driver is shared by multiple devices,
* so we always return the '1' to enumerate all devices.
*/
return 1;
}
static int bus_probe_device(rt_driver_t drv, void *dev_ptr)
{
rt_err_t err;
err = bus_probe(drv, dev_ptr);
if (!err)
{
rt_bus_t bus = drv->bus;
_dm_bus_lock(&bus->drv_lock);
++drv->ref_count;
_dm_bus_unlock(&bus->drv_lock);
}
return err;
}
/**
* @brief This function add a driver to the drv_list of a specific bus
*
* @param bus the bus to add
*
* @param drv the driver to be added
*
* @return the error code, RT_EOK on added successfully.
*/
rt_err_t rt_bus_add_driver(rt_bus_t bus, rt_driver_t drv)
{
RT_ASSERT(bus != RT_NULL);
RT_ASSERT(drv != RT_NULL);
drv->bus = bus;
rt_list_init(&drv->node);
_dm_bus_lock(&bus->drv_lock);
rt_list_insert_before(&bus->drv_list, &drv->node);
_dm_bus_unlock(&bus->drv_lock);
rt_bus_for_each_dev(bus, drv, bus_probe_driver);
return RT_EOK;
}
/**
* @brief This function add a device to the dev_list of a specific bus
*
* @param bus the bus to add
*
* @param dev the device to be added
*
* @return the error code, RT_EOK on added successfully.
*/
rt_err_t rt_bus_add_device(rt_bus_t bus, rt_device_t dev)
{
RT_ASSERT(bus != RT_NULL);
RT_ASSERT(dev != RT_NULL);
dev->bus = bus;
rt_list_init(&dev->node);
_dm_bus_lock(&bus->dev_lock);
rt_list_insert_before(&bus->dev_list, &dev->node);
_dm_bus_unlock(&bus->dev_lock);
rt_bus_for_each_drv(bus, dev, bus_probe_device);
return RT_EOK;
}
/**
* @brief This function remove a driver from bus
*
* @param drv the driver to be removed
*
* @return the error code, RT_EOK on added successfully.
*/
rt_err_t rt_bus_remove_driver(rt_driver_t drv)
{
rt_err_t err;
rt_bus_t bus;
RT_ASSERT(drv != RT_NULL);
RT_ASSERT(drv->bus != RT_NULL);
bus = drv->bus;
LOG_D("Bus(%s) remove driver %s", bus->name, drv->parent.name);
_dm_bus_lock(&bus->drv_lock);
if (drv->ref_count)
{
err = -RT_EBUSY;
}
else
{
rt_list_remove(&drv->node);
err = RT_EOK;
}
_dm_bus_unlock(&bus->drv_lock);
return err;
}
/**
* @brief This function remove a device from bus
*
* @param dev the device to be removed
*
* @return the error code, RT_EOK on added successfully.
*/
rt_err_t rt_bus_remove_device(rt_device_t dev)
{
rt_bus_t bus;
rt_driver_t drv;
rt_err_t err = RT_EOK;
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(dev->bus != RT_NULL);
bus = dev->bus;
drv = dev->drv;
LOG_D("Bus(%s) remove device %s", bus->name, dev->parent.name);
_dm_bus_lock(&bus->dev_lock);
rt_list_remove(&dev->node);
_dm_bus_unlock(&bus->dev_lock);
if (dev->bus->remove)
{
err = dev->bus->remove(dev);
}
else if (drv)
{
if (drv->remove)
{
err = drv->remove(dev);
}
/* device and driver are in the same bus */
_dm_bus_lock(&bus->drv_lock);
--drv->ref_count;
_dm_bus_unlock(&bus->drv_lock);
}
return err;
}
struct bus_shutdown_info
{
rt_bus_t bus;
rt_err_t err;
};
static int device_shutdown(rt_device_t dev, void *info_ptr)
{
rt_bus_t bus;
rt_err_t err = RT_EOK;
struct bus_shutdown_info *info = info_ptr;
bus = info->bus;
if (bus->shutdown)
{
LOG_D("Device(%s) shutdown", dev->parent.name);
err = bus->shutdown(dev);
LOG_D(" Result: %s", rt_strerror(err));
}
else if (dev->drv && dev->drv->shutdown)
{
LOG_D("Device(%s) shutdown", dev->parent.name);
err = dev->drv->shutdown(dev);
LOG_D(" Result: %s", rt_strerror(err));
}
if (err)
{
/* Only get the last one while system not crash */
info->err = err;
}
/* Go on, we want to ask all devices to shutdown */
return 1;
}
/**
* @brief This function call all buses' shutdown
*
* @return the error code, RT_EOK on shutdown successfully.
*/
rt_err_t rt_bus_shutdown(void)
{
rt_bus_t bus = RT_NULL;
struct bus_shutdown_info info =
{
.err = RT_EOK,
};
_dm_bus_lock(&bus_lock);
rt_list_for_each_entry(bus, &bus_nodes, list)
{
info.bus = bus;
rt_bus_for_each_dev(bus, &info, device_shutdown);
}
_dm_bus_unlock(&bus_lock);
return info.err;
}
/**
* @brief This function find a bus by name
* @param bus the name to be finded
*
* @return the bus finded by name.
*/
rt_bus_t rt_bus_find_by_name(const char *name)
{
rt_bus_t bus = RT_NULL;
RT_ASSERT(name != RT_NULL);
_dm_bus_lock(&bus_lock);
rt_list_for_each_entry(bus, &bus_nodes, list)
{
if (!rt_strncmp(bus->name, name, RT_NAME_MAX))
{
break;
}
}
_dm_bus_unlock(&bus_lock);
return bus;
}
/**
* @brief This function transfer dev_list and drv_list to the other bus
*
* @param new_bus the bus to transfer
*
* @param dev the target device
*
* @return the error code, RT_EOK on added successfully.
*/
rt_err_t rt_bus_reload_driver_device(rt_bus_t new_bus, rt_device_t dev)
{
rt_bus_t old_bus;
RT_ASSERT(new_bus != RT_NULL);
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(dev->bus != RT_NULL);
RT_ASSERT(dev->bus != new_bus);
old_bus = dev->bus;
_dm_bus_lock(&old_bus->dev_lock);
rt_list_remove(&dev->node);
_dm_bus_unlock(&old_bus->dev_lock);
return rt_bus_add_device(new_bus, dev);
}
/**
* @brief This function register a bus
* @param bus the bus to be registered
*
* @return the error code, RT_EOK on registeration successfully.
*/
rt_err_t rt_bus_register(rt_bus_t bus)
{
RT_ASSERT(bus != RT_NULL);
rt_list_init(&bus->list);
rt_list_init(&bus->dev_list);
rt_list_init(&bus->drv_list);
rt_spin_lock_init(&bus->dev_lock);
rt_spin_lock_init(&bus->drv_lock);
_dm_bus_lock(&bus_lock);
rt_list_insert_before(&bus_nodes, &bus->list);
_dm_bus_unlock(&bus_lock);
return RT_EOK;
}
#endif
+483
View File
@@ -0,0 +1,483 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2007-01-21 Bernard the first version
* 2010-05-04 Bernard add rt_device_init implementation
* 2012-10-20 Bernard add device check in register function,
* provided by Rob <rdent@iinet.net.au>
* 2012-12-25 Bernard return RT_EOK if the device interface not exist.
* 2013-07-09 Grissiom add ref_count support
* 2016-04-02 Bernard fix the open_flag initialization issue.
* 2021-03-19 Meco Man remove rt_device_init_all()
* 2024-09-15 milo fix log format issue
* fix reopen with a different oflag issue
*/
#include <rtthread.h>
#define DBG_TAG "kernel.device"
#ifdef RT_DEBUG_DEVICE
#define DBG_LVL DBG_LOG
#else
#define DBG_LVL DBG_WARNING
#endif /* defined (RT_DEBUG_DEVICE) */
#include <rtdbg.h>
#ifdef RT_USING_POSIX_DEVIO
#include <rtdevice.h> /* for wqueue_init */
#endif /* RT_USING_POSIX_DEVIO */
#if defined (RT_USING_DFS_V2) && defined (RT_USING_DFS_DEVFS)
#include <devfs.h>
#endif /* RT_USING_DFS_V2 RT_USING_DFS_DEVFS */
#ifdef RT_USING_DEVICE
#ifdef RT_USING_DEVICE_OPS
#define device_init (dev->ops ? dev->ops->init : RT_NULL)
#define device_open (dev->ops ? dev->ops->open : RT_NULL)
#define device_close (dev->ops ? dev->ops->close : RT_NULL)
#define device_read (dev->ops ? dev->ops->read : RT_NULL)
#define device_write (dev->ops ? dev->ops->write : RT_NULL)
#define device_control (dev->ops ? dev->ops->control : RT_NULL)
#else
#define device_init (dev->init)
#define device_open (dev->open)
#define device_close (dev->close)
#define device_read (dev->read)
#define device_write (dev->write)
#define device_control (dev->control)
#endif /* RT_USING_DEVICE_OPS */
/**
* @brief This function registers a device driver with a specified name.
*
* @param dev is the pointer of device driver structure.
*
* @param name is the device driver's name.
*
* @param flags is the capabilities flag of device.
*
* @return the error code, RT_EOK on initialization successfully.
*/
rt_err_t rt_device_register(rt_device_t dev,
const char *name,
rt_uint16_t flags)
{
if (dev == RT_NULL)
return -RT_ERROR;
if (rt_device_find(name) != RT_NULL)
return -RT_ERROR;
rt_object_init(&(dev->parent), RT_Object_Class_Device, name);
dev->flag = flags;
dev->ref_count = 0;
dev->open_flag = 0;
#ifdef RT_USING_POSIX_DEVIO
dev->fops = RT_NULL;
rt_wqueue_init(&(dev->wait_queue));
#endif /* RT_USING_POSIX_DEVIO */
#if defined (RT_USING_DFS_V2) && defined (RT_USING_DFS_DEVFS)
dfs_devfs_device_add(dev);
#endif /* RT_USING_DFS_V2 */
return RT_EOK;
}
RTM_EXPORT(rt_device_register);
/**
* @brief This function removes a previously registered device driver.
*
* @param dev is the pointer of device driver structure.
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_device_unregister(rt_device_t dev)
{
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
RT_ASSERT(rt_object_is_systemobject(&dev->parent));
rt_object_detach(&(dev->parent));
return RT_EOK;
}
RTM_EXPORT(rt_device_unregister);
/**
* @brief This function finds a device driver by specified name.
*
* @param name is the device driver's name.
*
* @return the registered device driver on successful, or RT_NULL on failure.
*/
rt_device_t rt_device_find(const char *name)
{
return (rt_device_t)rt_object_find(name, RT_Object_Class_Device);
}
RTM_EXPORT(rt_device_find);
#ifdef RT_USING_HEAP
/**
* @brief This function creates a device object with user data size.
*
* @param type is the type of the device object.
*
* @param attach_size is the size of user data.
*
* @return the allocated device object, or RT_NULL when failed.
*/
rt_device_t rt_device_create(int type, int attach_size)
{
int size;
rt_device_t device;
size = RT_ALIGN(sizeof(struct rt_device), RT_ALIGN_SIZE);
attach_size = RT_ALIGN(attach_size, RT_ALIGN_SIZE);
/* use the total size */
size += attach_size;
device = (rt_device_t)rt_malloc(size);
if (device)
{
rt_memset(device, 0x0, sizeof(struct rt_device));
device->type = (enum rt_device_class_type)type;
}
return device;
}
RTM_EXPORT(rt_device_create);
/**
* @brief This function destroy the specific device object.
*
* @param dev is a specific device object.
*/
void rt_device_destroy(rt_device_t dev)
{
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Null);
RT_ASSERT(rt_object_is_systemobject(&dev->parent) == RT_FALSE);
rt_object_detach(&(dev->parent));
/* release this device object */
rt_free(dev);
}
RTM_EXPORT(rt_device_destroy);
#endif /* RT_USING_HEAP */
/**
* @brief This function will initialize the specified device.
*
* @param dev is the pointer of device driver structure.
*
* @return the result, RT_EOK on successfully.
*/
rt_err_t rt_device_init(rt_device_t dev)
{
rt_err_t result = RT_EOK;
RT_ASSERT(dev != RT_NULL);
/* get device_init handler */
if (device_init != RT_NULL)
{
if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED))
{
result = device_init(dev);
if (result != RT_EOK)
{
LOG_E("To initialize device:%.*s failed. The error code is %d",
RT_NAME_MAX, dev->parent.name, result);
}
else
{
dev->flag |= RT_DEVICE_FLAG_ACTIVATED;
}
}
}
return result;
}
/**
* @brief This function will open a device.
*
* @param dev is the pointer of device driver structure.
*
* @param oflag is the flags for device open.
*
* @return the result, RT_EOK on successfully.
*/
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)
{
rt_err_t result = RT_EOK;
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
/* if device is not initialized, initialize it. */
if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED))
{
if (device_init != RT_NULL)
{
result = device_init(dev);
if (result != RT_EOK)
{
LOG_E("To initialize device:%.*s failed. The error code is %d",
RT_NAME_MAX, dev->parent.name, result);
return result;
}
}
dev->flag |= RT_DEVICE_FLAG_ACTIVATED;
}
/* device is a stand alone device and opened */
if ((dev->flag & RT_DEVICE_FLAG_STANDALONE) &&
(dev->open_flag & RT_DEVICE_OFLAG_OPEN))
{
return -RT_EBUSY;
}
/* device is not opened or opened by other oflag, call device_open interface */
if (!(dev->open_flag & RT_DEVICE_OFLAG_OPEN) ||
((dev->open_flag & RT_DEVICE_OFLAG_MASK) != ((oflag & RT_DEVICE_OFLAG_MASK) | RT_DEVICE_OFLAG_OPEN)))
{
if (device_open != RT_NULL)
{
result = device_open(dev, oflag);
}
else
{
/* set open flag */
dev->open_flag = (oflag & RT_DEVICE_OFLAG_MASK);
}
}
/* set open flag */
if (result == RT_EOK || result == -RT_ENOSYS)
{
dev->open_flag |= RT_DEVICE_OFLAG_OPEN;
dev->ref_count++;
/* don't let bad things happen silently. If you are bitten by this assert,
* please set the ref_count to a bigger type. */
RT_ASSERT(dev->ref_count != 0);
}
return result;
}
RTM_EXPORT(rt_device_open);
/**
* @brief This function will close a device.
*
* @param dev is the pointer of device driver structure.
*
* @return the result, RT_EOK on successfully.
*/
rt_err_t rt_device_close(rt_device_t dev)
{
rt_err_t result = RT_EOK;
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
if (dev->ref_count == 0)
return -RT_ERROR;
dev->ref_count--;
if (dev->ref_count != 0)
return RT_EOK;
/* call device_close interface */
if (device_close != RT_NULL)
{
result = device_close(dev);
}
/* set open flag */
if (result == RT_EOK || result == -RT_ENOSYS)
dev->open_flag = RT_DEVICE_OFLAG_CLOSE;
return result;
}
RTM_EXPORT(rt_device_close);
/**
* @brief This function will read some data from a device.
*
* @param dev is the pointer of device driver structure.
*
* @param pos is the position when reading.
*
* @param buffer is a data buffer to save the read data.
*
* @param size is the size of buffer.
*
* @return the actually read size on successful, otherwise 0 will be returned.
*
* @note the unit of size/pos is a block for block device.
*/
rt_ssize_t rt_device_read(rt_device_t dev,
rt_off_t pos,
void *buffer,
rt_size_t size)
{
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
if (dev->ref_count == 0)
{
rt_set_errno(-RT_ERROR);
return 0;
}
/* call device_read interface */
if (device_read != RT_NULL)
{
return device_read(dev, pos, buffer, size);
}
/* set error code */
rt_set_errno(-RT_ENOSYS);
return 0;
}
RTM_EXPORT(rt_device_read);
/**
* @brief This function will write some data to a device.
*
* @param dev is the pointer of device driver structure.
*
* @param pos is the position when writing.
*
* @param buffer is the data buffer to be written to device.
*
* @param size is the size of buffer.
*
* @return the actually written size on successful, otherwise 0 will be returned.
*
* @note the unit of size/pos is a block for block device.
*/
rt_ssize_t rt_device_write(rt_device_t dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
if (dev->ref_count == 0)
{
rt_set_errno(-RT_ERROR);
return 0;
}
/* call device_write interface */
if (device_write != RT_NULL)
{
return device_write(dev, pos, buffer, size);
}
/* set error code */
rt_set_errno(-RT_ENOSYS);
return 0;
}
RTM_EXPORT(rt_device_write);
/**
* @brief This function will perform a variety of control functions on devices.
*
* @param dev is the pointer of device driver structure.
*
* @param cmd is the command sent to device.
*
* @param arg is the argument of command.
*
* @return the result, -RT_ENOSYS for failed.
*/
rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg)
{
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
/* call device_write interface */
if (device_control != RT_NULL)
{
return device_control(dev, cmd, arg);
}
return -RT_ENOSYS;
}
RTM_EXPORT(rt_device_control);
/**
* @brief This function will set the reception indication callback function. This callback function
* is invoked when this device receives data.
*
* @param dev is the pointer of device driver structure.
*
* @param rx_ind is the indication callback function.
*
* @return RT_EOK
*/
rt_err_t rt_device_set_rx_indicate(rt_device_t dev,
rt_err_t (*rx_ind)(rt_device_t dev,
rt_size_t size))
{
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
dev->rx_indicate = rx_ind;
return RT_EOK;
}
RTM_EXPORT(rt_device_set_rx_indicate);
/**
* @brief This function will set a callback function. The callback function
* will be called when device has written data to physical hardware.
*
* @param dev is the pointer of device driver structure.
*
* @param tx_done is the indication callback function.
*
* @return RT_EOK
*/
rt_err_t rt_device_set_tx_complete(rt_device_t dev,
rt_err_t (*tx_done)(rt_device_t dev,
void *buffer))
{
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
dev->tx_complete = tx_done;
return RT_EOK;
}
RTM_EXPORT(rt_device_set_tx_complete);
#endif /* RT_USING_DEVICE */
+600
View File
@@ -0,0 +1,600 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-04-20 ErikChan the first version
*/
#include <rtthread.h>
#ifdef RT_USING_OFW
#include <drivers/ofw_io.h>
#include <drivers/ofw_irq.h>
#endif
#include <drivers/core/dm.h>
#ifdef RT_USING_SMP
static int rti_secondary_cpu_start(void)
{
return 0;
}
INIT_EXPORT(rti_secondary_cpu_start, "6.end");
static int rti_secondary_cpu_end(void)
{
return 0;
}
INIT_EXPORT(rti_secondary_cpu_end, "7.end");
void rt_dm_secondary_cpu_init(void)
{
#ifdef RT_DEBUGING_AUTO_INIT
int result;
const struct rt_init_desc *desc;
rt_kprintf("do secondary cpu initialization.\n");
for (desc = &__rt_init_desc_rti_secondary_cpu_start; desc < &__rt_init_desc_rti_secondary_cpu_end; ++desc)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_secondary_cpu_start; fn_ptr < &__rt_init_rti_secondary_cpu_end; ++fn_ptr)
{
(*fn_ptr)();
}
#endif /* RT_DEBUGING_AUTO_INIT */
}
#endif /* RT_USING_SMP */
/**
* @brief This function will alloc an id in an IDA object
*
* @param ida is the IDA object
*
* @return the id or -RT_EEMPTY
*/
int rt_dm_ida_alloc(struct rt_dm_ida *ida)
{
int id;
RT_ASSERT(ida != RT_NULL);
rt_spin_lock(&ida->lock);
id = rt_bitmap_next_clear_bit(ida->map, 0, RT_DM_IDA_NUM);
if (id != RT_DM_IDA_NUM)
{
rt_bitmap_set_bit(ida->map, id);
}
rt_spin_unlock(&ida->lock);
if (id != RT_DM_IDA_NUM)
{
return id;
}
return -RT_EEMPTY;
}
/**
* @brief This function will take (force) an id in an IDA object
*
* @param ida is the IDA object
*
* @param id is the id that want to take
*
* @return the result of taking
*/
rt_bool_t rt_dm_ida_take(struct rt_dm_ida *ida, int id)
{
RT_ASSERT(ida != RT_NULL);
RT_ASSERT(id >= 0);
rt_spin_lock(&ida->lock);
if (!rt_bitmap_test_bit(ida->map, id))
{
rt_bitmap_set_bit(ida->map, id);
}
else
{
id = RT_DM_IDA_NUM;
}
rt_spin_unlock(&ida->lock);
return id != RT_DM_IDA_NUM;
}
/**
* @brief This function will release an id in an IDA object
*
* @param ida is the IDA object
*
* @param id is the id of IDA object
*/
void rt_dm_ida_free(struct rt_dm_ida *ida, int id)
{
RT_ASSERT(ida != RT_NULL);
RT_ASSERT(id >= 0);
rt_spin_lock(&ida->lock);
rt_bitmap_clear_bit(ida->map, id);
rt_spin_unlock(&ida->lock);
}
/**
* @brief This function will return the specified master id and device id of device.
*
* @param master_id is the master id (0, 255] of device
*
* @param device_id is the device id [-1, 255] of device, when device_id is -1,
* the function will end when find the first device.
*
* @return the device object or RT_NULL
*/
rt_device_t rt_dm_device_find(int master_id, int device_id)
{
struct rt_device *dev, *ret_dev = RT_NULL;
struct rt_object_information *information = RT_NULL;
if (master_id <= 0 || device_id > 255)
{
return RT_NULL;
}
information = rt_object_get_information(RT_Object_Class_Device);
/* parameter check */
if (!information)
{
return RT_NULL;
}
/* which is invoke in interrupt status */
RT_DEBUG_NOT_IN_INTERRUPT;
/* enter critical */
rt_enter_critical();
/* try to find object */
rt_list_for_each_entry(dev, &information->object_list, parent.list)
{
if (master_id == dev->master_id &&
(device_id == -1 || device_id == dev->device_id))
{
ret_dev = dev;
break;
}
}
/* leave critical */
rt_exit_critical();
return ret_dev;
}
struct prefix_track
{
rt_list_t list;
int uid;
const char *prefix;
};
static RT_DEFINE_SPINLOCK(_prefix_nodes_lock);
static rt_list_t _prefix_nodes = RT_LIST_OBJECT_INIT(_prefix_nodes);
int rt_dm_dev_set_name_auto(rt_device_t dev, const char *prefix)
{
int uid = -1;
struct prefix_track *pt = RT_NULL;
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(prefix != RT_NULL);
RT_DEBUG_NOT_IN_INTERRUPT;
rt_spin_lock(&_prefix_nodes_lock);
rt_list_for_each_entry(pt, &_prefix_nodes, list)
{
/* caller always input constants string, check ptr is faster */
if (pt->prefix == prefix || !rt_strcmp(pt->prefix, prefix))
{
uid = ++pt->uid;
break;
}
}
rt_spin_unlock(&_prefix_nodes_lock);
if (uid < 0)
{
pt = rt_malloc(sizeof(*pt));
if (!pt)
{
return -RT_ENOMEM;
}
rt_list_init(&pt->list);
pt->uid = uid = 0;
pt->prefix = prefix;
rt_spin_lock(&_prefix_nodes_lock);
rt_list_insert_before(&_prefix_nodes, &pt->list);
rt_spin_unlock(&_prefix_nodes_lock);
}
return rt_dm_dev_set_name(dev, "%s%u", prefix, uid);
}
int rt_dm_dev_get_name_id(rt_device_t dev)
{
int id = 0, len;
const char *name;
RT_ASSERT(dev != RT_NULL);
name = rt_dm_dev_get_name(dev);
len = rt_strlen(name) - 1;
name += len;
while (len --> 0)
{
if (*name < '0' || *name > '9')
{
while (*(++name))
{
id *= 10;
id += *name - '0';
}
break;
}
--name;
}
return id;
}
int rt_dm_dev_set_name(rt_device_t dev, const char *format, ...)
{
int n;
va_list arg_ptr;
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(format != RT_NULL);
va_start(arg_ptr, format);
n = rt_vsnprintf(dev->parent.name, RT_NAME_MAX, format, arg_ptr);
va_end(arg_ptr);
return n;
}
const char *rt_dm_dev_get_name(rt_device_t dev)
{
RT_ASSERT(dev != RT_NULL);
return dev->parent.name;
}
#ifdef RT_USING_OFW
#define ofw_api_call(name, ...) rt_ofw_##name(__VA_ARGS__)
#define ofw_api_call_ptr(name, ...) ofw_api_call(name, __VA_ARGS__)
#else
#define ofw_api_call(name, ...) (-RT_ENOSYS)
#define ofw_api_call_ptr(name, ...) RT_NULL
#endif
int rt_dm_dev_get_address_count(rt_device_t dev)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(get_address_count, dev->ofw_node);
}
#endif
return -RT_ENOSYS;
}
rt_err_t rt_dm_dev_get_address(rt_device_t dev, int index,
rt_uint64_t *out_address, rt_uint64_t *out_size)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(get_address, dev->ofw_node, index,
out_address, out_size);
}
#endif
return -RT_ENOSYS;
}
rt_err_t rt_dm_dev_get_address_by_name(rt_device_t dev, const char *name,
rt_uint64_t *out_address, rt_uint64_t *out_size)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(get_address_by_name, dev->ofw_node, name,
out_address, out_size);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_get_address_array(rt_device_t dev, int nr, rt_uint64_t *out_regs)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(get_address_array, dev->ofw_node, nr, out_regs);
}
#endif
return -RT_ENOSYS;
}
void *rt_dm_dev_iomap(rt_device_t dev, int index)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call_ptr(iomap, dev->ofw_node, index);
}
#endif
return RT_NULL;
}
void *rt_dm_dev_iomap_by_name(rt_device_t dev, const char *name)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call_ptr(iomap_by_name, dev->ofw_node, name);
}
#endif
return RT_NULL;
}
int rt_dm_dev_get_irq_count(rt_device_t dev)
{
RT_ASSERT(dev != RT_NULL);
#if defined(RT_USING_OFW) && defined(RT_USING_PIC)
if (dev->ofw_node)
{
return ofw_api_call(get_irq_count, dev->ofw_node);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_get_irq(rt_device_t dev, int index)
{
RT_ASSERT(dev != RT_NULL);
#if defined(RT_USING_OFW) && defined(RT_USING_PIC)
if (dev->ofw_node)
{
return ofw_api_call(get_irq, dev->ofw_node, index);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_get_irq_by_name(rt_device_t dev, const char *name)
{
RT_ASSERT(dev != RT_NULL);
#if defined(RT_USING_OFW) && defined(RT_USING_PIC)
if (dev->ofw_node)
{
return ofw_api_call(get_irq_by_name, dev->ofw_node, name);
}
#endif
return -RT_ENOSYS;
}
void rt_dm_dev_bind_fwdata(rt_device_t dev, void *fw_np, void *data)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (!dev->ofw_node && fw_np)
{
dev->ofw_node = fw_np;
rt_ofw_data(fw_np) = data;
}
if (dev->ofw_node == RT_NULL)
{
rt_kprintf("[%s:%s] line=%d ofw_node is NULL\r\n", __FILE__, __func__, __LINE__);
return;
}
rt_ofw_data(dev->ofw_node) = data;
#endif
}
void rt_dm_dev_unbind_fwdata(rt_device_t dev, void *fw_np)
{
RT_ASSERT(dev!= RT_NULL);
#ifdef RT_USING_OFW
void *dev_fw_np = RT_NULL;
if (!dev->ofw_node && fw_np)
{
dev_fw_np = fw_np;
rt_ofw_data(fw_np) = RT_NULL;
}
if (dev_fw_np == RT_NULL)
{
rt_kprintf("[%s:%s] line=%d dev_fw_np is NULL\r\n", __FILE__, __func__, __LINE__);
return;
}
rt_ofw_data(dev_fw_np) = RT_NULL;
#endif
}
int rt_dm_dev_prop_read_u8_array_index(rt_device_t dev, const char *propname,
int index, int nr, rt_uint8_t *out_values)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_UISNG_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_read_u8_array_index, dev->ofw_node, propname,
index, nr, out_value);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_prop_read_u16_array_index(rt_device_t dev, const char *propname,
int index, int nr, rt_uint16_t *out_values)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_read_u16_array_index, dev->ofw_node, propname,
index, nr, out_values);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_prop_read_u32_array_index(rt_device_t dev, const char *propname,
int index, int nr, rt_uint32_t *out_values)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_read_u32_array_index, dev->ofw_node, propname,
index, nr, out_values);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_prop_read_u64_array_index(rt_device_t dev, const char *propname,
int index, int nr, rt_uint64_t *out_values)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_read_u64_array_index, dev->ofw_node, propname,
index, nr, out_values);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_prop_read_string_array_index(rt_device_t dev, const char *propname,
int index, int nr, const char **out_strings)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_read_string_array_index, dev->ofw_node, propname,
index, nr, out_strings);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_prop_count_of_size(rt_device_t dev, const char *propname, int size)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_count_of_size, dev->ofw_node, propname, size);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_prop_index_of_string(rt_device_t dev, const char *propname, const char *string)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_index_of_string, dev->ofw_node, propname, string);
}
#endif
return -RT_ENOSYS;
}
rt_bool_t rt_dm_dev_prop_read_bool(rt_device_t dev, const char *propname)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_read_bool, dev->ofw_node, propname);
}
#endif
return RT_FALSE;
}
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <rtthread.h>
#include <drivers/core/bus.h>
#if defined(RT_USING_POSIX_DEVIO)
#include <rtdevice.h> /* for wqueue_init */
#endif
/**
* This function attach a driver to bus
*
* @param drv the driver to be attached
*/
rt_err_t rt_driver_register(rt_driver_t drv)
{
rt_err_t ret;
struct rt_bus *bus = RT_NULL;
RT_ASSERT(drv != RT_NULL);
if (drv->bus)
{
bus = drv->bus;
ret = rt_bus_add_driver(bus, drv);
}
else
{
ret = -RT_EINVAL;
}
return ret;
}
RTM_EXPORT(rt_driver_register);
/**
* This function remove driver from system.
*
* @param drv the driver to be removed
*/
rt_err_t rt_driver_unregister(rt_driver_t drv)
{
rt_err_t ret;
ret = rt_bus_remove_driver(drv);
return ret;
}
RTM_EXPORT(rt_driver_unregister);
+170
View File
@@ -0,0 +1,170 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-21 GuEe-GUI first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "rtdm.mnt"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <stdlib.h>
#include <dfs_fs.h>
#ifdef RT_USING_FINSH
#include <msh.h>
#endif
#include <ioremap.h>
#include <mm_memblock.h>
#ifdef RT_USING_OFW
#define bootargs_select rt_ofw_bootargs_select
#else
#error Platform have not kernel parameters select interfaces!
#endif
static int rootfs_mnt_init(void)
{
rt_err_t err = -RT_ERROR;
void *fsdata = RT_NULL;
const char *cromfs_type = "crom";
const char *dev = bootargs_select("root=", 0);
const char *fstype = bootargs_select("rootfstype=", 0);
const char *rw = bootargs_select("rw", 0);
if (!dev || !fstype)
{
const char *name = "initrd";
rt_uint64_t initrd_start = 0, initrd_end = 0;
struct rt_mmblk_reg *iter = RT_NULL;
rt_slist_for_each_entry(iter, &(rt_memblock_get_reserved()->reg_list), node)
{
if (rt_strcmp(iter->memreg.name, name) == 0)
{
initrd_start = iter->memreg.start;
initrd_end = iter->memreg.end;
break;
}
}
if (initrd_start && initrd_end)
{
size_t initrd_size = initrd_end - initrd_start;
if ((fsdata = rt_ioremap_cached((void *)initrd_start, initrd_size)))
{
fstype = cromfs_type;
}
}
}
if (fstype != cromfs_type && dev)
{
rt_tick_t timeout = 0;
const char *rootwait, *rootdelay = RT_NULL;
rootwait = bootargs_select("rootwait", 0);
/* Maybe it is undefined or 'rootwaitABC' */
if (!rootwait || *rootwait)
{
rootdelay = bootargs_select("rootdelay=", 0);
if (rootdelay)
{
timeout = rt_tick_from_millisecond(atoi(rootdelay));
}
rootwait = RT_NULL;
}
/*
* Delays in boot flow is a terrible behavior in RTOS, but the RT-Thread
* SDIO framework init the devices in a task that we need to wait for
* SDIO devices to init complete...
*
* WHAT THE F*CK PROBLEMS WILL HAPPENED?
*
* Your main PE, applications, services that depend on the root FS and
* the multi cores setup, init will delay, too...
*
* So, you can try to link this function to `INIT_APP_EXPORT` even later
* and remove the delays if you want to optimize the boot time and mount
* the FS auto.
*/
for (; rootdelay || rootwait; --timeout)
{
if (!rootwait && timeout == 0)
{
LOG_E("Wait for /dev/%s init time out", dev);
/*
* We don't return at once because the device driver may init OK
* when we break from this point, might as well give it another
* try.
*/
break;
}
if (rt_device_find(dev))
{
break;
}
rt_thread_mdelay(1);
}
}
if (fstype)
{
if (!(err = dfs_mount(dev, "/", fstype, rw ? 0 : ~0, fsdata)))
{
LOG_I("Mount root %s%s type=%s %s",
(dev && *dev) ? "on /dev/" : "",
(dev && *dev) ? dev : "\b",
fstype, "done");
}
else
{
LOG_W("Mount root %s%s type=%s %s",
(dev && *dev) ? "on /dev/" : "",
(dev && *dev) ? dev : "\b",
fstype, "fail");
if (fstype == cromfs_type)
{
rt_iounmap(fsdata);
}
}
}
return 0;
}
INIT_ENV_EXPORT(rootfs_mnt_init);
static int fstab_mnt_init(void)
{
mkdir("/mnt", 0755);
#ifdef RT_USING_FINSH
/* Try mount by table */
msh_exec_script("fstab.sh", 16);
#endif
#ifdef RT_USING_DFS_PROCFS
mkdir("/proc", 0755);
dfs_mount(RT_NULL, "/proc", "procfs", 0, RT_NULL);
#endif
LOG_I("File system initialization done");
return 0;
}
INIT_FS_EXPORT(fstab_mnt_init);
+171
View File
@@ -0,0 +1,171 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-09-24 GuEe-GUI the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "rtdm.numa"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <drivers/pic.h>
struct numa_memory
{
rt_list_t list;
int nid;
rt_uint64_t start;
rt_uint64_t end;
union
{
void *ofw_node;
};
};
static rt_bool_t numa_enabled = RT_FALSE;
static int cpu_numa_map[RT_CPUS_NR] rt_section(".bss.noclean.numa");
static rt_list_t numa_memory_nodes rt_section(".bss.noclean.numa");
int rt_numa_cpu_id(int cpuid)
{
if (!numa_enabled)
{
return -RT_ENOSYS;
}
return cpuid < RT_ARRAY_SIZE(cpu_numa_map) ? cpu_numa_map[cpuid] : -RT_EINVAL;
}
int rt_numa_device_id(struct rt_device *dev)
{
rt_uint32_t nid = (rt_uint32_t)-RT_ENOSYS;
if (!numa_enabled)
{
return nid;
}
return rt_dm_dev_prop_read_u32(dev, "numa-node-id", &nid) ? : (int)nid;
}
rt_err_t rt_numa_memory_affinity(rt_uint64_t phy_addr, rt_bitmap_t *out_affinity)
{
struct numa_memory *nm;
if (!out_affinity)
{
return -RT_EINVAL;
}
if (!numa_enabled)
{
/* Default to CPU#0 */
RT_IRQ_AFFINITY_SET(out_affinity, 0);
return RT_EOK;
}
rt_memset(out_affinity, 0, sizeof(*out_affinity) * RT_BITMAP_LEN(RT_CPUS_NR));
rt_list_for_each_entry(nm, &numa_memory_nodes, list)
{
if (phy_addr >= nm->start && phy_addr < nm->end)
{
for (int i = 0; i < RT_ARRAY_SIZE(cpu_numa_map); ++i)
{
if (cpu_numa_map[i] == nm->nid)
{
RT_IRQ_AFFINITY_SET(out_affinity, i);
}
}
return RT_EOK;
}
}
return -RT_EEMPTY;
}
#ifdef RT_USING_OFW
static int numa_ofw_init(void)
{
int i = 0;
rt_uint32_t nid;
const char *numa_conf;
struct rt_ofw_node *np = RT_NULL;
numa_conf = rt_ofw_bootargs_select("numa=", 0);
if (!numa_conf || rt_strcmp(numa_conf, "on"))
{
return (int)RT_EOK;
}
numa_enabled = RT_TRUE;
for (int i = 0; i < RT_ARRAY_SIZE(cpu_numa_map); ++i)
{
cpu_numa_map[i] = -RT_ENOSYS;
}
rt_list_init(&numa_memory_nodes);
rt_ofw_foreach_cpu_node(np)
{
rt_ofw_prop_read_u32(np, "numa-node-id", (rt_uint32_t *)&cpu_numa_map[i]);
if (++i >= RT_CPUS_NR)
{
break;
}
}
rt_ofw_foreach_node_by_type(np, "memory")
{
if (!rt_ofw_prop_read_u32(np, "numa-node-id", &nid))
{
int mem_nr = rt_ofw_get_address_count(np);
for (i = 0; i < mem_nr; ++i)
{
rt_uint64_t addr, size;
struct numa_memory *nm;
if (rt_ofw_get_address(np, i, &addr, &size))
{
continue;
}
nm = rt_malloc(sizeof(*nm));
if (!nm)
{
LOG_E("No memory to record NUMA[%d] memory[%p, %p] info",
nid, addr, addr + size);
return (int)-RT_ENOMEM;
}
nm->start = addr;
nm->end = addr + size;
nm->ofw_node = np;
rt_list_init(&nm->list);
rt_list_insert_before(&numa_memory_nodes, &nm->list);
}
}
}
return 0;
}
INIT_CORE_EXPORT(numa_ofw_init);
#endif /* RT_USING_OFW */
@@ -0,0 +1,208 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-04-12 ErikChan the first version
* 2023-10-13 zmshahaha distinguish ofw and none-ofw situation
*/
#include <rtthread.h>
#define DBG_TAG "rtdm.pltaform"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <drivers/platform.h>
#include <drivers/core/bus.h>
#include <drivers/core/dm.h>
#include <drivers/core/power_domain.h>
static struct rt_bus platform_bus;
/**
* @brief This function create a platform device.
*
* @param name is name of the platform device.
*
* @return a new platform device.
*/
struct rt_platform_device *rt_platform_device_alloc(const char *name)
{
struct rt_platform_device *pdev = rt_calloc(1, sizeof(*pdev));
if (!pdev)
{
return RT_NULL;
}
pdev->parent.bus = &platform_bus;
pdev->name = name;
return pdev;
}
/**
* @brief This function register a rt_driver to platform bus.
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_platform_driver_register(struct rt_platform_driver *pdrv)
{
RT_ASSERT(pdrv != RT_NULL);
pdrv->parent.bus = &platform_bus;
#if RT_NAME_MAX > 0
rt_strcpy(pdrv->parent.parent.name, pdrv->name);
#else
pdrv->parent.parent.name = pdrv->name;
#endif
return rt_driver_register(&pdrv->parent);
}
/**
* @brief This function register a rt_device to platform bus.
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_platform_device_register(struct rt_platform_device *pdev)
{
RT_ASSERT(pdev != RT_NULL);
return rt_bus_add_device(&platform_bus, &pdev->parent);
}
static rt_bool_t platform_match(rt_driver_t drv, rt_device_t dev)
{
struct rt_platform_driver *pdrv = rt_container_of(drv, struct rt_platform_driver, parent);
struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent);
struct rt_ofw_node *np = dev->ofw_node;
/* 1、match with ofw node */
if (np)
{
#ifdef RT_USING_OFW
pdev->id = rt_ofw_node_match(np, pdrv->ids);
#else
pdev->id = RT_NULL;
#endif
if (pdev->id)
{
return RT_TRUE;
}
}
/* 2、match with name */
if (pdev->name && pdrv->name)
{
if (pdev->name == pdrv->name)
{
return RT_TRUE;
}
else
{
return !rt_strcmp(pdrv->name, pdev->name);
}
}
return RT_FALSE;
}
static rt_err_t platform_probe(rt_device_t dev)
{
rt_err_t err;
struct rt_platform_driver *pdrv = rt_container_of(dev->drv, struct rt_platform_driver, parent);
struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent);
#ifdef RT_USING_OFW
struct rt_ofw_node *np = dev->ofw_node;
#endif
err = rt_dm_power_domain_attach(dev, RT_TRUE);
if (err && err != -RT_EEMPTY)
{
LOG_E("Attach power domain error = %s in device %s", rt_strerror(err),
#ifdef RT_USING_OFW
(pdev->name && pdev->name[0]) ? pdev->name : rt_ofw_node_full_name(np)
#else
pdev->name
#endif
);
return err;
}
err = pdrv->probe(pdev);
if (!err)
{
#ifdef RT_USING_OFW
if (np)
{
rt_ofw_node_set_flag(np, RT_OFW_F_READLY);
}
#endif
}
else
{
if (err == -RT_ENOMEM)
{
LOG_W("System not memory in driver %s", pdrv->name);
}
rt_dm_power_domain_detach(dev, RT_TRUE);
}
return err;
}
static rt_err_t platform_remove(rt_device_t dev)
{
struct rt_platform_driver *pdrv = rt_container_of(dev->drv, struct rt_platform_driver, parent);
struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent);
if (pdrv && pdrv->remove)
{
pdrv->remove(pdev);
}
rt_dm_power_domain_detach(dev, RT_TRUE);
rt_platform_ofw_free(pdev);
return RT_EOK;
}
static rt_err_t platform_shutdown(rt_device_t dev)
{
struct rt_platform_driver *pdrv = rt_container_of(dev->drv, struct rt_platform_driver, parent);
struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent);
if (pdrv && pdrv->shutdown)
{
pdrv->shutdown(pdev);
}
rt_dm_power_domain_detach(dev, RT_TRUE);
rt_platform_ofw_free(pdev);
return RT_EOK;
}
static struct rt_bus platform_bus =
{
.name = "platform",
.match = platform_match,
.probe = platform_probe,
.remove = platform_remove,
.shutdown = platform_shutdown,
};
static int platform_bus_init(void)
{
rt_bus_register(&platform_bus);
return 0;
}
INIT_CORE_EXPORT(platform_bus_init);
@@ -0,0 +1,327 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-06-04 GuEe-GUI the first version
*/
#include <rtthread.h>
#define DBG_TAG "drv.platform"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <drivers/ofw_io.h>
#include <drivers/ofw_fdt.h>
#include <drivers/platform.h>
#include <drivers/core/bus.h>
#include <drivers/core/dm.h>
#include "../ofw/ofw_internal.h"
static const struct rt_ofw_node_id platform_ofw_ids[] =
{
{ .compatible = "simple-bus", },
#ifdef RT_USING_MFD
{ .compatible = "simple-mfd", },
#endif
#ifdef RT_USING_ISA
{ .compatible = "isa", },
#endif
#ifdef RT_USING_AMBA_BUS
/*
* Maybe ARM has replaced it with compatible: "arm,primecell" and will not
* used anymore in the future.
*/
{ .compatible = "arm,amba-bus", },
#endif
{ /* sentinel */ }
};
static void ofw_device_rename(struct rt_device *dev)
{
rt_uint32_t mask;
rt_uint64_t addr;
const char *dev_name = dev->parent.name;
struct rt_ofw_node *np = dev->ofw_node;
#if RT_NAME_MAX > 0
if (dev_name[0] == '\0')
{
dev_name = RT_NULL;
}
#endif
while (np->parent)
{
if (!rt_ofw_get_address(np, 0, &addr, RT_NULL))
{
const char *node_name = rt_fdt_node_name(np->full_name);
rt_size_t tag_len = strchrnul(node_name, '@') - node_name;
if (!rt_ofw_prop_read_u32(np, "mask", &mask))
{
rt_dm_dev_set_name(dev, dev_name ? "%lx.%x.%.*s:%s" : "%lx.%x.%.*s",
addr, __rt_ffs(mask) - 1, tag_len, node_name, dev_name);
}
else
{
rt_dm_dev_set_name(dev, dev_name ? "%lx.%.*s:%s" : "%lx.%.*s",
addr, tag_len, node_name, dev_name);
}
return;
}
rt_dm_dev_set_name(dev, dev_name ? "%s:%s" : "%s",
rt_fdt_node_name(np->full_name), dev_name);
np = np->parent;
}
}
static struct rt_platform_device *alloc_ofw_platform_device(struct rt_ofw_node *np)
{
struct rt_platform_device *pdev = rt_platform_device_alloc("");
if (pdev)
{
/* inc reference of dt-node */
rt_ofw_node_get(np);
rt_ofw_node_set_flag(np, RT_OFW_F_PLATFORM);
pdev->parent.ofw_node = np;
ofw_device_rename(&pdev->parent);
}
else
{
LOG_E("Alloc device fail for %s", rt_ofw_node_full_name(np));
}
return pdev;
}
static rt_err_t platform_ofw_device_probe_once(struct rt_ofw_node *parent_np)
{
rt_err_t err = RT_EOK;
struct rt_ofw_node *np;
struct rt_platform_device *pdev;
rt_ofw_foreach_available_child_node(parent_np, np)
{
const char *name;
struct rt_ofw_node_id *id;
struct rt_ofw_prop *compat_prop = RT_NULL;
if (np->dev)
{
/* Check first */
continue;
}
LOG_D("%s found in %s", np->full_name, parent_np->full_name);
/* Is system node or have driver */
if (rt_ofw_node_test_flag(np, RT_OFW_F_SYSTEM) ||
rt_ofw_node_test_flag(np, RT_OFW_F_READLY))
{
continue;
}
compat_prop = rt_ofw_get_prop(np, "compatible", RT_NULL);
name = rt_ofw_node_name(np);
/* Not have name and compatible */
if (!compat_prop && (name == (const char *)"<NULL>" || !rt_strcmp(name, "<NULL>")))
{
continue;
}
id = rt_ofw_prop_match(compat_prop, platform_ofw_ids);
if (id && np->child)
{
/* scan next level */
err = platform_ofw_device_probe_once(np);
if (err)
{
rt_ofw_node_put(np);
LOG_E("%s bus probe fail", np->full_name);
break;
}
}
if (np->dev)
{
/* Maybe the childs have requested this node */
continue;
}
pdev = alloc_ofw_platform_device(np);
if (!pdev)
{
rt_ofw_node_put(np);
err = -RT_ENOMEM;
break;
}
pdev->dev_id = ofw_alias_node_id(np);
np->dev = &pdev->parent;
LOG_D("%s register to bus", np->full_name);
rt_platform_device_register(pdev);
}
return err;
}
rt_err_t rt_platform_ofw_device_probe_child(struct rt_ofw_node *np)
{
rt_err_t err;
struct rt_ofw_node *parent = rt_ofw_get_parent(np);
if (parent && rt_strcmp(parent->name, "/") &&
rt_ofw_get_prop(np, "compatible", RT_NULL) &&
!rt_ofw_node_test_flag(np, RT_OFW_F_PLATFORM))
{
struct rt_platform_device *pdev = alloc_ofw_platform_device(np);
if (pdev)
{
err = rt_platform_device_register(pdev);
}
else
{
err = -RT_ENOMEM;
}
}
else
{
err = -RT_EINVAL;
}
rt_ofw_node_put(parent);
return err;
}
rt_err_t rt_platform_ofw_request(struct rt_ofw_node *np)
{
rt_err_t err;
if (np)
{
struct rt_device *dev = np->dev;
if (dev)
{
/* Was create */
if (dev->drv)
{
/* Was probe OK */
err = RT_EOK;
}
else
{
err = rt_bus_reload_driver_device(dev->bus, dev);
}
}
else
{
struct rt_platform_device *pdev = alloc_ofw_platform_device(np);
if (pdev)
{
pdev->dev_id = ofw_alias_node_id(np);
np->dev = &pdev->parent;
LOG_D("%s register to bus", np->full_name);
err = rt_platform_device_register(pdev);
}
else
{
err = -RT_ENOMEM;
}
}
}
else
{
err = -RT_EINVAL;
}
return err;
}
static int platform_ofw_device_probe(void)
{
rt_err_t err = RT_EOK;
struct rt_ofw_node *node;
if (ofw_node_root)
{
rt_ofw_node_get(ofw_node_root);
err = platform_ofw_device_probe_once(ofw_node_root);
rt_ofw_node_put(ofw_node_root);
if ((node = rt_ofw_find_node_by_path("/firmware")))
{
platform_ofw_device_probe_once(node);
rt_ofw_node_put(node);
}
if ((node = rt_ofw_find_node_by_path("/clocks")))
{
platform_ofw_device_probe_once(node);
rt_ofw_node_put(node);
}
rt_ofw_node_get(ofw_node_chosen);
if ((node = rt_ofw_get_child_by_compatible(ofw_node_chosen, "simple-framebuffer")))
{
platform_ofw_device_probe_once(node);
rt_ofw_node_put(node);
}
rt_ofw_node_get(ofw_node_chosen);
}
else
{
err = -RT_ENOSYS;
}
return (int)err;
}
INIT_PLATFORM_EXPORT(platform_ofw_device_probe);
rt_err_t rt_platform_ofw_free(struct rt_platform_device *pdev)
{
rt_err_t err = RT_EOK;
if (pdev)
{
struct rt_ofw_node *np = pdev->parent.ofw_node;
if (np)
{
rt_ofw_node_clear_flag(np, RT_OFW_F_PLATFORM);
rt_ofw_node_put(np);
rt_free(pdev);
}
}
else
{
err = -RT_EINVAL;
}
return err;
}
@@ -0,0 +1,477 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-09-24 GuEe-GUI the first version
*/
#include <rtdevice.h>
#define DBG_TAG "rtdm.power_domain"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <drivers/ofw.h>
void rt_dm_power_domain_proxy_default_name(struct rt_dm_power_domain_proxy *proxy)
{
#if RT_NAME_MAX > 0
rt_strncpy(proxy->parent.name, RT_POWER_DOMAIN_OBJ_NAME, RT_NAME_MAX);
#else
proxy->parent.name = RT_POWER_DOMAIN_OBJ_NAME;
#endif
}
void rt_dm_power_domain_proxy_ofw_bind(struct rt_dm_power_domain_proxy *proxy,
struct rt_ofw_node *np)
{
if (!proxy || !proxy->ofw_parse || !np)
{
return;
}
rt_dm_power_domain_proxy_default_name(proxy);
rt_ofw_data(np) = proxy;
}
static void dm_power_domain_init(struct rt_dm_power_domain *domain)
{
#if RT_NAME_MAX > 0
rt_strncpy(domain->parent.name, RT_POWER_DOMAIN_OBJ_NAME, RT_NAME_MAX);
#else
domain->parent.name = RT_POWER_DOMAIN_OBJ_NAME;
#endif
domain->parent_domain = RT_NULL;
rt_list_init(&domain->list);
rt_list_init(&domain->child_nodes);
rt_list_init(&domain->unit_nodes);
rt_ref_init(&domain->ref);
rt_spin_lock_init(&domain->lock);
}
static rt_bool_t dm_power_domain_is_free(struct rt_dm_power_domain *domain)
{
return rt_ref_read(&domain->ref) == 1 && !rt_list_isempty(&domain->child_nodes);
}
rt_err_t rt_dm_power_domain_register(struct rt_dm_power_domain *domain)
{
if (!domain)
{
return -RT_EINVAL;
}
dm_power_domain_init(domain);
return RT_EOK;
}
rt_err_t rt_dm_power_domain_unregister(struct rt_dm_power_domain *domain)
{
rt_err_t err = RT_EOK;
if (!domain)
{
return -RT_EINVAL;
}
if (!dm_power_domain_is_free(domain))
{
return -RT_EBUSY;
}
if (domain->parent_domain)
{
err = rt_dm_power_domain_unregister_child(domain->parent_domain, domain);
}
return err;
}
rt_err_t rt_dm_power_domain_register_child(struct rt_dm_power_domain *domain,
struct rt_dm_power_domain *child_domain)
{
if (!domain || !child_domain)
{
return -RT_EINVAL;
}
dm_power_domain_init(child_domain);
child_domain->parent_domain = domain;
return RT_EOK;
}
rt_err_t rt_dm_power_domain_unregister_child(struct rt_dm_power_domain *domain,
struct rt_dm_power_domain *child_domain)
{
rt_err_t err = RT_EOK;
if (!domain || !child_domain)
{
return -RT_EINVAL;
}
rt_hw_spin_lock(&domain->lock.lock);
if (dm_power_domain_is_free(domain))
{
rt_list_remove(&child_domain->list);
}
else
{
err = -RT_EBUSY;
}
rt_hw_spin_unlock(&domain->lock.lock);
return err;
}
rt_err_t rt_dm_power_domain_power_on(struct rt_dm_power_domain *domain)
{
rt_err_t err = RT_EOK;
struct rt_dm_power_domain *child_domain;
if (!domain)
{
return -RT_EINVAL;
}
rt_hw_spin_lock(&domain->lock.lock);
if (rt_ref_read(&domain->ref) == 1)
{
err = domain->power_on(domain);
}
if (!err)
{
struct rt_dm_power_domain *fail_domain = RT_NULL;
rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
{
err = rt_dm_power_domain_power_on(child_domain);
if (err)
{
fail_domain = child_domain;
break;
}
}
if (fail_domain)
{
rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
{
if (child_domain == fail_domain)
{
break;
}
rt_dm_power_domain_power_off(child_domain);
}
}
}
rt_hw_spin_unlock(&domain->lock.lock);
if (!err)
{
rt_ref_get(&domain->ref);
}
return err;
}
static void dm_power_domain_release(struct rt_ref *r)
{
struct rt_dm_power_domain *domain = rt_container_of(r, struct rt_dm_power_domain, ref);
if (domain->dev)
{
LOG_E("%s power domain is release", rt_dm_dev_get_name(domain->dev));
}
RT_ASSERT(0);
}
rt_err_t rt_dm_power_domain_power_off(struct rt_dm_power_domain *domain)
{
rt_err_t err;
struct rt_dm_power_domain *child_domain;
if (!domain)
{
return -RT_EINVAL;
}
rt_ref_put(&domain->ref, dm_power_domain_release);
rt_hw_spin_lock(&domain->lock.lock);
if (rt_ref_read(&domain->ref) == 1)
{
err = domain->power_off(domain);
}
else
{
err = -RT_EBUSY;
}
if (!err)
{
struct rt_dm_power_domain *fail_domain = RT_NULL;
rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
{
err = rt_dm_power_domain_power_off(child_domain);
if (err)
{
fail_domain = child_domain;
break;
}
}
if (fail_domain)
{
rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
{
if (child_domain == fail_domain)
{
break;
}
rt_dm_power_domain_power_on(child_domain);
}
}
}
rt_hw_spin_unlock(&domain->lock.lock);
if (err)
{
rt_ref_get(&domain->ref);
}
return err;
}
#ifdef RT_USING_OFW
static struct rt_dm_power_domain *ofw_find_power_domain(struct rt_device *dev,
int index, struct rt_ofw_cell_args *args)
{
struct rt_object *obj;
struct rt_dm_power_domain_proxy *proxy;
struct rt_dm_power_domain *domain = RT_NULL;
struct rt_ofw_node *np = dev->ofw_node, *power_domain_np;
if (!rt_ofw_parse_phandle_cells(np, "power-domains", "#power-domain-cells",
index, args))
{
power_domain_np = args->data;
if (power_domain_np && (obj = rt_ofw_data(power_domain_np)))
{
if (!rt_strcmp(obj->name, RT_POWER_DOMAIN_OBJ_NAME))
{
proxy = rt_container_of(obj, struct rt_dm_power_domain_proxy, parent);
domain = proxy->ofw_parse(proxy, args);
}
else if (!rt_strcmp(obj->name, RT_POWER_DOMAIN_OBJ_NAME))
{
domain = rt_container_of(obj, struct rt_dm_power_domain, parent);
}
else if ((obj = rt_ofw_parse_object(power_domain_np,
RT_POWER_DOMAIN_PROXY_OBJ_NAME, "#power-domain-cells")))
{
proxy = rt_container_of(obj, struct rt_dm_power_domain_proxy, parent);
domain = proxy->ofw_parse(proxy, args);
}
else if ((obj = rt_ofw_parse_object(power_domain_np,
RT_POWER_DOMAIN_OBJ_NAME, "#power-domain-cells")))
{
domain = rt_container_of(obj, struct rt_dm_power_domain, parent);
}
rt_ofw_node_put(power_domain_np);
}
}
return domain;
}
#else
rt_inline struct rt_dm_power_domain *ofw_find_power_domain(struct rt_device *dev,
int index, struct rt_ofw_cell_args *args)
{
return RT_NULL;
}
#endif /* RT_USING_OFW */
struct rt_dm_power_domain *rt_dm_power_domain_get_by_index(struct rt_device *dev,
int index)
{
struct rt_ofw_cell_args args;
struct rt_dm_power_domain *domain;
if (!dev || index < 0)
{
return RT_NULL;
}
if ((domain = ofw_find_power_domain(dev, index, &args)))
{
goto _end;
}
_end:
return domain;
}
struct rt_dm_power_domain *rt_dm_power_domain_get_by_name(struct rt_device *dev,
const char *name)
{
int index;
if (!dev || !name)
{
return RT_NULL;
}
if ((index = rt_dm_dev_prop_index_of_string(dev, "power-domain-names", name)) < 0)
{
LOG_E("%s find power domain %s not found", rt_dm_dev_get_name(dev));
return RT_NULL;
}
return rt_dm_power_domain_get_by_index(dev, index);
}
rt_err_t rt_dm_power_domain_put(struct rt_dm_power_domain *domain)
{
if (!domain)
{
return -RT_EINVAL;
}
return RT_EOK;
}
rt_err_t rt_dm_power_domain_attach(struct rt_device *dev, rt_bool_t on)
{
int id = -1;
rt_err_t err = RT_EOK;
struct rt_ofw_cell_args args;
struct rt_dm_power_domain *domain;
struct rt_dm_power_domain_unit *unit;
if (!dev)
{
return -RT_EINVAL;
}
/* We only attach the first one, get domains self if there are multiple domains */
if ((domain = ofw_find_power_domain(dev, 0, &args)))
{
id = args.args[0];
}
if (!domain)
{
return -RT_EEMPTY;
}
unit = rt_malloc(sizeof(*unit));
if (!unit)
{
return -RT_ENOMEM;
}
rt_list_init(&unit->list);
unit->id = id;
unit->domain = domain;
dev->power_domain_unit = unit;
rt_hw_spin_lock(&domain->lock.lock);
if (domain->attach_dev)
{
err = domain->attach_dev(domain, dev);
}
if (!err)
{
rt_list_insert_before(&domain->unit_nodes, &unit->list);
}
rt_hw_spin_unlock(&domain->lock.lock);
if (err)
{
dev->power_domain_unit = RT_NULL;
rt_free(unit);
return err;
}
if (on)
{
err = rt_dm_power_domain_power_on(domain);
}
return err;
}
rt_err_t rt_dm_power_domain_detach(struct rt_device *dev, rt_bool_t off)
{
rt_err_t err = RT_EOK;
struct rt_dm_power_domain *domain;
struct rt_dm_power_domain_unit *unit;
if (!dev || !dev->power_domain_unit)
{
return -RT_EINVAL;
}
unit = dev->power_domain_unit;
domain = unit->domain;
rt_hw_spin_lock(&domain->lock.lock);
if (domain->detach_dev)
{
err = domain->detach_dev(domain, dev);
}
if (!err)
{
rt_list_remove(&unit->list);
}
rt_hw_spin_unlock(&domain->lock.lock);
if (err)
{
return err;
}
rt_free(unit);
dev->power_domain_unit = RT_NULL;
if (off)
{
err = rt_dm_power_domain_power_off(domain);
}
return err;
}
@@ -0,0 +1,34 @@
config RT_USING_CPUTIME
bool "Enable CPU time for high resolution clock counter"
default n
help
When enable this option, the BSP should provide a rt_clock_cputime_ops
for CPU time by:
const static struct rt_clock_cputime_ops _ops = {...};
clock_cpu_setops(&_ops);
Then user can use high resolution clock counter with:
ts1 = clock_cpu_gettime();
ts2 = clock_cpu_gettime();
/* and get the ms of delta tick with API: */
ms_tick = clock_cpu_millisecond(t2 - t1);
us_tick = clock_cpu_microsecond(t2 - t1);
if RT_USING_CPUTIME
config RT_USING_CPUTIME_CORTEXM
bool "Support Cortex-M CPU"
default y
depends on ARCH_ARM_CORTEX_M0 || ARCH_ARM_CORTEX_M3 || ARCH_ARM_CORTEX_M4 || ARCH_ARM_CORTEX_M7
select PKG_USING_PERF_COUNTER
config RT_USING_CPUTIME_RISCV
bool "Use rdtime instructions for CPU time"
default y
depends on ARCH_RISCV64
help
Some RISCV64 MCU Use rdtime instructions read CPU time.
config CPUTIME_TIMER_FREQ
int "CPUTIME timer freq"
default 0
endif
@@ -0,0 +1,18 @@
from building import *
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../include']
src = Split('''
cputime.c
cputimer.c
''')
if GetDepend('RT_USING_CPUTIME_CORTEXM'):
src += ['cputime_cortexm.c']
if GetDepend('RT_USING_CPUTIME_RISCV'):
src += ['cputime_riscv.c']
group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_CPUTIME'], CPPPATH = CPPPATH)
Return('group')
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2017-12-23 Bernard first version
*/
#include <rtdevice.h>
#include <rtthread.h>
#include <sys/errno.h>
static const struct rt_clock_cputime_ops *_cputime_ops = RT_NULL;
/**
* The clock_cpu_getres() function shall return the resolution of CPU time, the
* number of nanosecond per tick.
*
* @return the number of nanosecond per tick(x (1000UL * 1000))
*/
uint64_t clock_cpu_getres(void)
{
if (_cputime_ops)
return _cputime_ops->cputime_getres();
rt_set_errno(ENOSYS);
return 0;
}
/**
* The clock_cpu_gettime() function shall return the current value of cpu time tick.
*
* @return the cpu tick
*/
uint64_t clock_cpu_gettime(void)
{
if (_cputime_ops)
return _cputime_ops->cputime_gettime();
rt_set_errno(ENOSYS);
return 0;
}
/**
* The clock_cpu_settimeout() fucntion set timeout time and timeout callback function
* The timeout callback function will be called when the timeout time is reached
*
* @param tick the Timeout tick
* @param timeout the Timeout function
* @param parameter the Parameters of timeout function
*
*/
int clock_cpu_settimeout(uint64_t tick, void (*timeout)(void *param), void *param)
{
if (_cputime_ops)
return _cputime_ops->cputime_settimeout(tick, timeout, param);
rt_set_errno(ENOSYS);
return 0;
}
int clock_cpu_issettimeout(void)
{
if (_cputime_ops)
return _cputime_ops->cputime_settimeout != RT_NULL;
return RT_FALSE;
}
/**
* The clock_cpu_microsecond() fucntion shall return the microsecond according to
* cpu_tick parameter.
*
* @param cpu_tick the cpu tick
*
* @return the microsecond
*/
uint64_t clock_cpu_microsecond(uint64_t cpu_tick)
{
uint64_t unit = clock_cpu_getres();
return (uint64_t)(((cpu_tick * unit) / (1000UL * 1000)) / 1000);
}
/**
* The clock_cpu_microsecond() fucntion shall return the millisecond according to
* cpu_tick parameter.
*
* @param cpu_tick the cpu tick
*
* @return the millisecond
*/
uint64_t clock_cpu_millisecond(uint64_t cpu_tick)
{
uint64_t unit = clock_cpu_getres();
return (uint64_t)(((cpu_tick * unit) / (1000UL * 1000)) / (1000UL * 1000));
}
/**
* The clock_cpu_seops() function shall set the ops of cpu time.
*
* @return always return 0.
*/
int clock_cpu_setops(const struct rt_clock_cputime_ops *ops)
{
_cputime_ops = ops;
if (ops)
{
RT_ASSERT(ops->cputime_getres != RT_NULL);
RT_ASSERT(ops->cputime_gettime != RT_NULL);
}
return 0;
}
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2017-12-23 Bernard first version
* 2022-06-14 Meco Man suuport pref_counter
*/
#include <rthw.h>
#include <rtdevice.h>
#include <rtthread.h>
#include <board.h>
#ifdef PKG_USING_PERF_COUNTER
#include <perf_counter.h>
#endif
/* Use Cycle counter of Data Watchpoint and Trace Register for CPU time */
static uint64_t cortexm_cputime_getres(void)
{
uint64_t ret = 1000UL * 1000 * 1000;
ret = (ret * (1000UL * 1000)) / SystemCoreClock;
return ret;
}
static uint64_t cortexm_cputime_gettime(void)
{
#ifdef PKG_USING_PERF_COUNTER
return get_system_ticks();
#else
return DWT->CYCCNT;
#endif
}
const static struct rt_clock_cputime_ops _cortexm_ops =
{
cortexm_cputime_getres,
cortexm_cputime_gettime
};
int cortexm_cputime_init(void)
{
#ifdef PKG_USING_PERF_COUNTER
clock_cpu_setops(&_cortexm_ops);
#else
/* check support bit */
if ((DWT->CTRL & (1UL << DWT_CTRL_NOCYCCNT_Pos)) == 0)
{
/* enable trace*/
CoreDebug->DEMCR |= (1UL << CoreDebug_DEMCR_TRCENA_Pos);
/* whether cycle counter not enabled */
if ((DWT->CTRL & (1UL << DWT_CTRL_CYCCNTENA_Pos)) == 0)
{
/* enable cycle counter */
DWT->CTRL |= (1UL << DWT_CTRL_CYCCNTENA_Pos);
}
clock_cpu_setops(&_cortexm_ops);
}
#endif /* PKG_USING_PERF_COUNTER */
return 0;
}
INIT_BOARD_EXPORT(cortexm_cputime_init);
@@ -0,0 +1,37 @@
#include <rthw.h>
#include <rtdevice.h>
#include <rtthread.h>
#include <board.h>
/* Use Cycle counter of Data Watchpoint and Trace Register for CPU time */
static uint64_t riscv_cputime_getres(void)
{
uint64_t ret = 1000UL * 1000 * 1000;
ret = (ret * (1000UL * 1000)) / CPUTIME_TIMER_FREQ;
return ret;
}
static uint64_t riscv_cputime_gettime(void)
{
uint64_t time_elapsed;
__asm__ __volatile__(
"rdtime %0"
: "=r"(time_elapsed));
return time_elapsed;
}
const static struct rt_clock_cputime_ops _riscv_ops =
{
riscv_cputime_getres,
riscv_cputime_gettime
};
int riscv_cputime_init(void)
{
clock_cpu_setops(&_riscv_ops);
return 0;
}
INIT_BOARD_EXPORT(riscv_cputime_init);
@@ -0,0 +1,339 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-13 zhkag first version
* 2023-04-03 xqyjlj fix cputimer in multithreading
*/
#include <rtdevice.h>
#include <rthw.h>
#include <rtthread.h>
static rt_list_t _cputimer_list = RT_LIST_OBJECT_INIT(_cputimer_list);
static struct rt_cputimer *_cputimer_nowtimer = RT_NULL;
static void _cputime_sleep_timeout(void *parameter)
{
struct rt_semaphore *sem;
sem = (struct rt_semaphore *)parameter;
rt_sem_release(sem);
}
static void _cputime_timeout_callback(void *parameter)
{
struct rt_cputimer *timer;
timer = (struct rt_cputimer *)parameter;
rt_base_t level;
level = rt_hw_interrupt_disable();
_cputimer_nowtimer = RT_NULL;
rt_list_remove(&(timer->row));
rt_hw_interrupt_enable(level);
timer->timeout_func(timer->parameter);
if (&_cputimer_list != _cputimer_list.prev)
{
struct rt_cputimer *t;
t = rt_list_entry(_cputimer_list.next, struct rt_cputimer, row);
clock_cpu_settimeout(t->timeout_tick, _cputime_timeout_callback, t);
}
else
{
clock_cpu_settimeout(RT_NULL, RT_NULL, RT_NULL);
}
}
static void _set_next_timeout()
{
struct rt_cputimer *t;
if (&_cputimer_list != _cputimer_list.prev)
{
t = rt_list_entry((&_cputimer_list)->next, struct rt_cputimer, row);
if (_cputimer_nowtimer != RT_NULL)
{
if (t != _cputimer_nowtimer && t->timeout_tick < _cputimer_nowtimer->timeout_tick)
{
_cputimer_nowtimer = t;
clock_cpu_settimeout(t->timeout_tick, _cputime_timeout_callback, t);
}
}
else
{
_cputimer_nowtimer = t;
clock_cpu_settimeout(t->timeout_tick, _cputime_timeout_callback, t);
}
}
else
{
_cputimer_nowtimer = RT_NULL;
clock_cpu_settimeout(RT_NULL, RT_NULL, RT_NULL);
}
}
void rt_cputimer_init(rt_cputimer_t timer,
const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_uint64_t tick,
rt_uint8_t flag)
{
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(timeout != RT_NULL);
RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);
/* set flag */
timer->parent.flag = flag;
/* set deactivated */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
timer->timeout_func = timeout;
timer->parameter = parameter;
timer->timeout_tick = tick + clock_cpu_gettime();
timer->init_tick = tick;
rt_list_init(&(timer->row));
rt_sem_init(&(timer->sem), "cputime", 0, RT_IPC_FLAG_PRIO);
}
rt_err_t rt_cputimer_delete(rt_cputimer_t timer)
{
rt_base_t level;
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);
/* disable interrupt */
level = rt_hw_interrupt_disable();
rt_list_remove(&timer->row);
/* stop timer */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
/* enable interrupt */
rt_hw_interrupt_enable(level);
_set_next_timeout();
return RT_EOK;
}
rt_err_t rt_cputimer_start(rt_cputimer_t timer)
{
rt_list_t *timer_list;
rt_base_t level;
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);
/* stop timer firstly */
level = rt_hw_interrupt_disable();
/* remove timer from list */
rt_list_remove(&timer->row);
/* change status of timer */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
timer_list = &_cputimer_list;
for (; timer_list != _cputimer_list.prev;
timer_list = timer_list->next)
{
struct rt_cputimer *t;
rt_list_t *p = timer_list->next;
t = rt_list_entry(p, struct rt_cputimer, row);
if ((t->timeout_tick - timer->timeout_tick) == 0)
{
continue;
}
else if ((t->timeout_tick - timer->timeout_tick) < 0x7fffffffffffffff)
{
break;
}
}
rt_list_insert_after(timer_list, &(timer->row));
timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;
_set_next_timeout();
/* enable interrupt */
rt_hw_interrupt_enable(level);
return RT_EOK;
}
rt_err_t rt_cputimer_stop(rt_cputimer_t timer)
{
rt_base_t level;
/* disable interrupt */
level = rt_hw_interrupt_disable();
/* timer check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);
if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
rt_hw_interrupt_enable(level);
return -RT_ERROR;
}
rt_list_remove(&timer->row);
/* change status */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
_set_next_timeout();
/* enable interrupt */
rt_hw_interrupt_enable(level);
return RT_EOK;
}
rt_err_t rt_cputimer_control(rt_cputimer_t timer, int cmd, void *arg)
{
rt_base_t level;
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);
level = rt_hw_interrupt_disable();
switch (cmd)
{
case RT_TIMER_CTRL_GET_TIME:
*(rt_uint64_t *)arg = timer->init_tick;
break;
case RT_TIMER_CTRL_SET_TIME:
RT_ASSERT((*(rt_uint64_t *)arg) < 0x7fffffffffffffff);
timer->init_tick = *(rt_uint64_t *)arg;
timer->timeout_tick = *(rt_uint64_t *)arg + clock_cpu_gettime();
break;
case RT_TIMER_CTRL_SET_ONESHOT:
timer->parent.flag &= ~RT_TIMER_FLAG_PERIODIC;
break;
case RT_TIMER_CTRL_SET_PERIODIC:
timer->parent.flag |= RT_TIMER_FLAG_PERIODIC;
break;
case RT_TIMER_CTRL_GET_STATE:
if (timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)
{
/*timer is start and run*/
*(rt_uint32_t *)arg = RT_TIMER_FLAG_ACTIVATED;
}
else
{
/*timer is stop*/
*(rt_uint32_t *)arg = RT_TIMER_FLAG_DEACTIVATED;
}
break;
case RT_TIMER_CTRL_GET_REMAIN_TIME:
*(rt_uint64_t *)arg = timer->timeout_tick;
break;
case RT_TIMER_CTRL_GET_FUNC:
arg = (void *)timer->timeout_func;
break;
case RT_TIMER_CTRL_SET_FUNC:
timer->timeout_func = (void (*)(void *))arg;
break;
case RT_TIMER_CTRL_GET_PARM:
*(void **)arg = timer->parameter;
break;
case RT_TIMER_CTRL_SET_PARM:
timer->parameter = arg;
break;
default:
break;
}
rt_hw_interrupt_enable(level);
return RT_EOK;
}
rt_err_t rt_cputimer_detach(rt_cputimer_t timer)
{
rt_base_t level;
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);
/* disable interrupt */
level = rt_hw_interrupt_disable();
rt_list_remove(&timer->row);
/* stop timer */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
_set_next_timeout();
/* enable interrupt */
rt_hw_interrupt_enable(level);
rt_sem_detach(&(timer->sem));
return RT_EOK;
}
rt_err_t rt_cputime_sleep(rt_uint64_t tick)
{
rt_base_t level;
struct rt_cputimer cputimer;
if (!clock_cpu_issettimeout())
{
rt_int32_t ms = clock_cpu_millisecond(tick);
return rt_thread_delay(rt_tick_from_millisecond(ms));
}
if (tick == 0)
{
return -RT_EINVAL;
}
rt_cputimer_init(&cputimer, "cputime_sleep", _cputime_sleep_timeout, &(cputimer.sem), tick,
RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER);
/* disable interrupt */
level = rt_hw_interrupt_disable();
rt_cputimer_start(&cputimer); /* reset the timeout of thread timer and start it */
rt_hw_interrupt_enable(level);
rt_sem_take_interruptible(&(cputimer.sem), RT_WAITING_FOREVER);
rt_cputimer_detach(&cputimer);
return RT_EOK;
}
rt_err_t rt_cputime_ndelay(rt_uint64_t ns)
{
uint64_t unit = clock_cpu_getres();
return rt_cputime_sleep(ns * (1000UL * 1000) / unit);
}
rt_err_t rt_cputime_udelay(rt_uint64_t us)
{
return rt_cputime_ndelay(us * 1000);
}
rt_err_t rt_cputime_mdelay(rt_uint64_t ms)
{
return rt_cputime_ndelay(ms * 1000000);
}
+10
View File
@@ -0,0 +1,10 @@
menuconfig RT_USING_DMA
bool "Using Direct Memory Access (DMA)"
depends on RT_USING_DM
select RT_USING_ADT
select RT_USING_ADT_BITMAP
default n
if RT_USING_DMA
osource "$(SOC_DM_DMA_DIR)/Kconfig"
endif
@@ -0,0 +1,15 @@
from building import *
group = []
if not GetDepend(['RT_USING_DMA']):
Return('group')
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../include']
src = ['dma.c', 'dma_pool.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
+589
View File
@@ -0,0 +1,589 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "rtdm.dma"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
static rt_list_t dmac_nodes = RT_LIST_OBJECT_INIT(dmac_nodes);
static RT_DEFINE_SPINLOCK(dmac_nodes_lock);
rt_err_t rt_dma_controller_register(struct rt_dma_controller *ctrl)
{
const char *dev_name;
char dma_name[RT_NAME_MAX];
if (!ctrl || !ctrl->dev || !ctrl->ops)
{
return -RT_EINVAL;
}
dev_name = rt_dm_dev_get_name(ctrl->dev);
if (rt_bitmap_next_set_bit(ctrl->dir_cap, 0, RT_DMA_DIR_MAX) == RT_DMA_DIR_MAX)
{
LOG_E("%s: Not direction capability", dev_name);
return -RT_EINVAL;
}
rt_snprintf(dma_name, sizeof(dma_name), "%s-dmac", dev_name);
rt_list_init(&ctrl->list);
rt_spin_lock(&dmac_nodes_lock);
rt_list_insert_before(&dmac_nodes, &ctrl->list);
rt_spin_unlock(&dmac_nodes_lock);
rt_list_init(&ctrl->channels_nodes);
rt_mutex_init(&ctrl->mutex, dma_name, RT_IPC_FLAG_PRIO);
if (ctrl->dev->ofw_node)
{
rt_dm_dev_bind_fwdata(ctrl->dev, RT_NULL, ctrl);
}
return RT_EOK;
}
rt_err_t rt_dma_controller_unregister(struct rt_dma_controller *ctrl)
{
if (!ctrl)
{
return -RT_EINVAL;
}
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
if (!rt_list_isempty(&ctrl->channels_nodes))
{
rt_mutex_release(&ctrl->mutex);
return -RT_EBUSY;
}
if (ctrl->dev->ofw_node)
{
rt_dm_dev_unbind_fwdata(ctrl->dev, RT_NULL);
}
rt_mutex_release(&ctrl->mutex);
rt_mutex_detach(&ctrl->mutex);
rt_spin_lock(&dmac_nodes_lock);
rt_list_remove(&ctrl->list);
rt_spin_unlock(&dmac_nodes_lock);
return RT_EOK;
}
rt_err_t rt_dma_chan_start(struct rt_dma_chan *chan)
{
rt_err_t err;
struct rt_dma_controller *ctrl;
if (!chan)
{
return -RT_EINVAL;
}
if (chan->prep_err)
{
LOG_D("%s: Not config done", rt_dm_dev_get_name(chan->slave));
return chan->prep_err;
}
ctrl = chan->ctrl;
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
err = ctrl->ops->start(chan);
rt_mutex_release(&ctrl->mutex);
return err;
}
rt_err_t rt_dma_chan_stop(struct rt_dma_chan *chan)
{
rt_err_t err;
struct rt_dma_controller *ctrl;
if (!chan)
{
return -RT_EINVAL;
}
if (chan->prep_err)
{
LOG_D("%s: Not prepare done", rt_dm_dev_get_name(chan->slave));
return chan->prep_err;
}
ctrl = chan->ctrl;
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
err = ctrl->ops->stop(chan);
rt_mutex_release(&ctrl->mutex);
return err;
}
rt_err_t rt_dma_chan_config(struct rt_dma_chan *chan,
struct rt_dma_slave_config *conf)
{
rt_err_t err;
struct rt_dma_controller *ctrl;
enum rt_dma_transfer_direction dir;
if (!chan || !conf)
{
err = -RT_EINVAL;
goto _end;
}
dir = conf->direction;
if (dir >= RT_DMA_DIR_MAX)
{
err = -RT_EINVAL;
goto _end;
}
if (conf->src_addr_width >= RT_DMA_SLAVE_BUSWIDTH_BYTES_MAX ||
conf->dst_addr_width >= RT_DMA_SLAVE_BUSWIDTH_BYTES_MAX)
{
err = -RT_EINVAL;
goto _end;
}
ctrl = chan->ctrl;
if (!rt_bitmap_test_bit(ctrl->dir_cap, dir))
{
err = -RT_ENOSYS;
goto _end;
}
if (!chan->name && dir != RT_DMA_MEM_TO_MEM)
{
LOG_E("%s: illegal config for uname channels",
rt_dm_dev_get_name(ctrl->dev));
err = -RT_EINVAL;
goto _end;
}
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
err = ctrl->ops->config(chan, conf);
rt_mutex_release(&ctrl->mutex);
if (!err)
{
rt_memcpy(&chan->conf, conf, sizeof(*conf));
}
_end:
chan->conf_err = err;
return err;
}
rt_err_t rt_dma_chan_done(struct rt_dma_chan *chan, rt_size_t size)
{
if (!chan)
{
return -RT_EINVAL;
}
if (chan->callback)
{
chan->callback(chan, size);
}
return RT_EOK;
}
static rt_bool_t range_is_illegal(const char *name, const char *desc,
rt_ubase_t addr0, rt_ubase_t addr1)
{
rt_bool_t illegal = addr0 < addr1;
if (illegal)
{
LOG_E("%s: %s %p is out of config %p", name, desc, addr0, addr1);
}
return illegal;
}
rt_err_t rt_dma_prep_memcpy(struct rt_dma_chan *chan,
struct rt_dma_slave_transfer *transfer)
{
rt_err_t err;
rt_size_t len;
rt_ubase_t dma_addr_src, dma_addr_dst;
struct rt_dma_controller *ctrl;
struct rt_dma_slave_config *conf;
if (!chan || !transfer)
{
return -RT_EINVAL;
}
ctrl = chan->ctrl;
conf = &chan->conf;
if (chan->conf_err)
{
LOG_D("%s: Not config done", rt_dm_dev_get_name(chan->slave));
return chan->conf_err;
}
RT_ASSERT(chan->conf.direction == RT_DMA_MEM_TO_MEM);
dma_addr_src = transfer->src_addr;
dma_addr_dst = transfer->dst_addr;
len = transfer->buffer_len;
if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "source",
dma_addr_src, conf->src_addr))
{
return -RT_EINVAL;
}
if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "dest",
dma_addr_dst, conf->dst_addr))
{
return -RT_EINVAL;
}
if (ctrl->ops->prep_memcpy)
{
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
err = ctrl->ops->prep_memcpy(chan, dma_addr_src, dma_addr_dst, len);
rt_mutex_release(&ctrl->mutex);
}
else
{
err = -RT_ENOSYS;
}
if (!err)
{
rt_memcpy(&chan->transfer, transfer, sizeof(*transfer));
}
chan->prep_err = err;
return err;
}
rt_err_t rt_dma_prep_cyclic(struct rt_dma_chan *chan,
struct rt_dma_slave_transfer *transfer)
{
rt_err_t err;
rt_ubase_t dma_buf_addr;
struct rt_dma_controller *ctrl;
struct rt_dma_slave_config *conf;
enum rt_dma_transfer_direction dir;
if (!chan || !transfer)
{
return -RT_EINVAL;
}
ctrl = chan->ctrl;
conf = &chan->conf;
if (chan->conf_err)
{
LOG_D("%s: Not config done", rt_dm_dev_get_name(chan->slave));
return chan->conf_err;
}
dir = chan->conf.direction;
if (dir == RT_DMA_MEM_TO_DEV || dir == RT_DMA_MEM_TO_MEM)
{
dma_buf_addr = transfer->src_addr;
if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "source",
dma_buf_addr, conf->src_addr))
{
return -RT_EINVAL;
}
}
else if (dir == RT_DMA_DEV_TO_MEM)
{
dma_buf_addr = transfer->dst_addr;
if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "dest",
dma_buf_addr, conf->dst_addr))
{
return -RT_EINVAL;
}
}
else
{
dma_buf_addr = ~0UL;
}
if (ctrl->ops->prep_cyclic)
{
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
err = ctrl->ops->prep_cyclic(chan, dma_buf_addr,
transfer->buffer_len, transfer->period_len, dir);
rt_mutex_release(&ctrl->mutex);
}
else
{
err = -RT_ENOSYS;
}
if (!err)
{
rt_memcpy(&chan->transfer, transfer, sizeof(*transfer));
}
chan->prep_err = err;
return err;
}
rt_err_t rt_dma_prep_single(struct rt_dma_chan *chan,
struct rt_dma_slave_transfer *transfer)
{
rt_err_t err;
rt_ubase_t dma_buf_addr;
struct rt_dma_controller *ctrl;
struct rt_dma_slave_config *conf;
enum rt_dma_transfer_direction dir;
if (!chan || !transfer)
{
return -RT_EINVAL;
}
ctrl = chan->ctrl;
conf = &chan->conf;
if (chan->conf_err)
{
LOG_D("%s: Not config done", rt_dm_dev_get_name(chan->slave));
return chan->conf_err;
}
dir = chan->conf.direction;
if (dir == RT_DMA_MEM_TO_DEV || dir == RT_DMA_MEM_TO_MEM)
{
dma_buf_addr = transfer->src_addr;
if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "source",
dma_buf_addr, conf->src_addr))
{
return -RT_EINVAL;
}
}
else if (dir == RT_DMA_DEV_TO_MEM)
{
dma_buf_addr = transfer->dst_addr;
if (range_is_illegal(rt_dm_dev_get_name(ctrl->dev), "dest",
dma_buf_addr, conf->dst_addr))
{
return -RT_EINVAL;
}
}
else
{
dma_buf_addr = ~0UL;
}
if (ctrl->ops->prep_single)
{
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
err = ctrl->ops->prep_single(chan, dma_buf_addr,
transfer->buffer_len, dir);
rt_mutex_release(&ctrl->mutex);
}
else
{
err = -RT_ENOSYS;
}
if (!err)
{
rt_memcpy(&chan->transfer, transfer, sizeof(*transfer));
}
chan->prep_err = err;
return err;
}
static struct rt_dma_controller *ofw_find_dma_controller(struct rt_device *dev,
const char *name, struct rt_ofw_cell_args *args)
{
struct rt_dma_controller *ctrl = RT_NULL;
#ifdef RT_USING_OFW
int index;
struct rt_ofw_node *np = dev->ofw_node, *ctrl_np;
if (!np)
{
return RT_NULL;
}
index = rt_ofw_prop_index_of_string(np, "dma-names", name);
if (index < 0)
{
return RT_NULL;
}
if (!rt_ofw_parse_phandle_cells(np, "dmas", "#dma-cells", index, args))
{
ctrl_np = args->data;
if (!rt_ofw_data(ctrl_np))
{
rt_platform_ofw_request(ctrl_np);
}
ctrl = rt_ofw_data(ctrl_np);
rt_ofw_node_put(ctrl_np);
}
#endif /* RT_USING_OFW */
return ctrl;
}
struct rt_dma_chan *rt_dma_chan_request(struct rt_device *dev, const char *name)
{
void *fw_data = RT_NULL;
struct rt_dma_chan *chan;
struct rt_ofw_cell_args dma_args;
struct rt_dma_controller *ctrl = RT_NULL;
if (!dev)
{
return rt_err_ptr(-RT_EINVAL);
}
if (name)
{
fw_data = &dma_args;
ctrl = ofw_find_dma_controller(dev, name, &dma_args);
}
else
{
struct rt_dma_controller *ctrl_tmp;
rt_spin_lock(&dmac_nodes_lock);
rt_list_for_each_entry(ctrl_tmp, &dmac_nodes, list)
{
/* Only memory to memory for uname request */
if (rt_bitmap_test_bit(ctrl_tmp->dir_cap, RT_DMA_MEM_TO_MEM))
{
ctrl = ctrl_tmp;
break;
}
}
rt_spin_unlock(&dmac_nodes_lock);
}
if (rt_is_err_or_null(ctrl))
{
return ctrl ? ctrl : rt_err_ptr(-RT_ENOSYS);
}
if (ctrl->ops->request_chan)
{
chan = ctrl->ops->request_chan(ctrl, dev, fw_data);
}
else
{
chan = rt_calloc(1, sizeof(*chan));
if (!chan)
{
chan = rt_err_ptr(-RT_ENOMEM);
}
}
if (rt_is_err(chan))
{
return chan;
}
if (!chan)
{
LOG_E("%s: unset request channels error", rt_dm_dev_get_name(ctrl->dev));
return rt_err_ptr(-RT_ERROR);
}
chan->name = name;
chan->ctrl = ctrl;
chan->slave = dev;
rt_list_init(&chan->list);
chan->conf_err = -RT_ERROR;
chan->prep_err = -RT_ERROR;
rt_mutex_take(&ctrl->mutex, RT_WAITING_FOREVER);
rt_list_insert_before(&ctrl->channels_nodes, &chan->list);
rt_mutex_release(&ctrl->mutex);
return chan;
}
rt_err_t rt_dma_chan_release(struct rt_dma_chan *chan)
{
rt_err_t err = RT_EOK;
if (!chan)
{
return -RT_EINVAL;
}
rt_mutex_take(&chan->ctrl->mutex, RT_WAITING_FOREVER);
rt_list_remove(&chan->list);
rt_mutex_release(&chan->ctrl->mutex);
if (chan->ctrl->ops->release_chan)
{
err = chan->ctrl->ops->release_chan(chan);
}
else
{
rt_free(chan);
}
return err;
}
+691
View File
@@ -0,0 +1,691 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "dma.pool"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <mm_aspace.h>
#include <dt-bindings/size.h>
static RT_DEFINE_SPINLOCK(dma_pools_lock);
static rt_list_t dma_pool_nodes = RT_LIST_OBJECT_INIT(dma_pool_nodes);
static struct rt_dma_pool *dma_pool_install(rt_region_t *region);
static void *dma_alloc(struct rt_device *dev, rt_size_t size,
rt_ubase_t *dma_handle, rt_ubase_t flags);
static void dma_free(struct rt_device *dev, rt_size_t size,
void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags);
rt_inline void region_pool_lock(void)
{
rt_hw_spin_lock(&dma_pools_lock.lock);
}
rt_inline void region_pool_unlock(void)
{
rt_hw_spin_unlock(&dma_pools_lock.lock);
}
static rt_err_t dma_map_coherent_sync_out_data(struct rt_device *dev,
void *data, rt_size_t size, rt_ubase_t *dma_handle, rt_ubase_t flags)
{
if (dma_handle)
{
*dma_handle = (rt_ubase_t)rt_kmem_v2p(data);
}
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data, size);
return RT_EOK;
}
static rt_err_t dma_map_coherent_sync_in_data(struct rt_device *dev,
void *out_data, rt_size_t size, rt_ubase_t dma_handle, rt_ubase_t flags)
{
rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, out_data, size);
return RT_EOK;
}
static const struct rt_dma_map_ops dma_map_coherent_ops =
{
.sync_out_data = dma_map_coherent_sync_out_data,
.sync_in_data = dma_map_coherent_sync_in_data,
};
static rt_err_t dma_map_nocoherent_sync_out_data(struct rt_device *dev,
void *data, rt_size_t size, rt_ubase_t *dma_handle, rt_ubase_t flags)
{
if (dma_handle)
{
*dma_handle = (rt_ubase_t)rt_kmem_v2p(data);
}
return RT_EOK;
}
static rt_err_t dma_map_nocoherent_sync_in_data(struct rt_device *dev,
void *out_data, rt_size_t size, rt_ubase_t dma_handle, rt_ubase_t flags)
{
return RT_EOK;
}
static const struct rt_dma_map_ops dma_map_nocoherent_ops =
{
.sync_out_data = dma_map_nocoherent_sync_out_data,
.sync_in_data = dma_map_nocoherent_sync_in_data,
};
#ifdef RT_USING_OFW
rt_inline rt_ubase_t ofw_addr_cpu2dma(struct rt_device *dev, rt_ubase_t addr)
{
return (rt_ubase_t)rt_ofw_translate_cpu2dma(dev->ofw_node, addr);
}
rt_inline rt_ubase_t ofw_addr_dma2cpu(struct rt_device *dev, rt_ubase_t addr)
{
return (rt_ubase_t)rt_ofw_translate_dma2cpu(dev->ofw_node, addr);
}
static void *ofw_dma_map_alloc(struct rt_device *dev, rt_size_t size,
rt_ubase_t *dma_handle, rt_ubase_t flags)
{
void *cpu_addr = dma_alloc(dev, size, dma_handle, flags);
if (cpu_addr && dma_handle)
{
*dma_handle = ofw_addr_cpu2dma(dev, *dma_handle);
}
return cpu_addr;
}
static void ofw_dma_map_free(struct rt_device *dev, rt_size_t size,
void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags)
{
dma_handle = ofw_addr_dma2cpu(dev, dma_handle);
dma_free(dev, size, cpu_addr, dma_handle, flags);
}
static rt_err_t ofw_dma_map_sync_out_data(struct rt_device *dev,
void *data, rt_size_t size,
rt_ubase_t *dma_handle, rt_ubase_t flags)
{
rt_err_t err;
if (flags & RT_DMA_F_NOCACHE)
{
err = dma_map_nocoherent_sync_out_data(dev, data, size, dma_handle, flags);
}
else
{
err = dma_map_coherent_sync_out_data(dev, data, size, dma_handle, flags);
}
if (!err && dma_handle)
{
*dma_handle = ofw_addr_cpu2dma(dev, *dma_handle);
}
return err;
}
static rt_err_t ofw_dma_map_sync_in_data(struct rt_device *dev,
void *out_data, rt_size_t size,
rt_ubase_t dma_handle, rt_ubase_t flags)
{
dma_handle = ofw_addr_dma2cpu(dev, dma_handle);
if (flags & RT_DMA_F_NOCACHE)
{
return dma_map_nocoherent_sync_in_data(dev, out_data, size, dma_handle, flags);
}
return dma_map_coherent_sync_in_data(dev, out_data, size, dma_handle, flags);
}
static const struct rt_dma_map_ops ofw_dma_map_ops =
{
.alloc = ofw_dma_map_alloc,
.free = ofw_dma_map_free,
.sync_out_data = ofw_dma_map_sync_out_data,
.sync_in_data = ofw_dma_map_sync_in_data,
};
static const struct rt_dma_map_ops *ofw_device_dma_ops(struct rt_device *dev)
{
rt_err_t err;
int region_nr = 0;
const fdt32_t *cell;
rt_phandle phandle;
rt_region_t region;
struct rt_ofw_prop *prop;
struct rt_dma_pool *dma_pool;
const struct rt_dma_map_ops *ops = RT_NULL;
struct rt_ofw_node *mem_np, *np = dev->ofw_node;
rt_ofw_foreach_prop_u32(np, "memory-region", prop, cell, phandle)
{
rt_uint64_t addr, size;
if (!(mem_np = rt_ofw_find_node_by_phandle(phandle)))
{
if (region_nr == 0)
{
return RT_NULL;
}
break;
}
if ((err = rt_ofw_get_address(mem_np, 0, &addr, &size)))
{
LOG_E("%s: Read '%s' error = %s", rt_ofw_node_full_name(mem_np),
"memory-region", rt_strerror(err));
rt_ofw_node_put(mem_np);
continue;
}
region.start = addr;
region.end = addr + size;
region.name = rt_dm_dev_get_name(dev);
rt_ofw_node_put(mem_np);
if (!(dma_pool = dma_pool_install(&region)))
{
return RT_NULL;
}
if (rt_ofw_prop_read_bool(mem_np, "no-map"))
{
dma_pool->flags |= RT_DMA_F_NOMAP;
}
if (!rt_dma_device_is_coherent(dev))
{
dma_pool->flags |= RT_DMA_F_NOCACHE;
}
dma_pool->dev = dev;
++region_nr;
}
if (region_nr)
{
ops = &ofw_dma_map_ops;
}
return ops;
}
#endif /* RT_USING_OFW */
static const struct rt_dma_map_ops *device_dma_ops(struct rt_device *dev)
{
const struct rt_dma_map_ops *ops = dev->dma_ops;
if (ops)
{
return ops;
}
#ifdef RT_USING_OFW
if (dev->ofw_node && (ops = ofw_device_dma_ops(dev)))
{
return ops;
}
#endif
if (rt_dma_device_is_coherent(dev))
{
ops = &dma_map_coherent_ops;
}
else
{
ops = &dma_map_nocoherent_ops;
}
dev->dma_ops = ops;
return ops;
}
static rt_ubase_t dma_pool_alloc(struct rt_dma_pool *pool, rt_size_t size)
{
rt_size_t bit, next_bit, end_bit, max_bits;
size = RT_DIV_ROUND_UP(size, ARCH_PAGE_SIZE);
max_bits = pool->bits - size;
rt_bitmap_for_each_clear_bit(pool->map, bit, max_bits)
{
end_bit = bit + size;
for (next_bit = bit + 1; next_bit < end_bit; ++next_bit)
{
if (rt_bitmap_test_bit(pool->map, next_bit))
{
bit = next_bit;
goto _next;
}
}
if (next_bit == end_bit)
{
while (next_bit --> bit)
{
rt_bitmap_set_bit(pool->map, next_bit);
}
return pool->start + bit * ARCH_PAGE_SIZE;
}
_next:
}
return RT_NULL;
}
static void dma_pool_free(struct rt_dma_pool *pool, rt_ubase_t offset, rt_size_t size)
{
rt_size_t bit = (offset - pool->start) / ARCH_PAGE_SIZE, end_bit;
size = RT_DIV_ROUND_UP(size, ARCH_PAGE_SIZE);
end_bit = bit + size;
for (; bit < end_bit; ++bit)
{
rt_bitmap_clear_bit(pool->map, bit);
}
}
static void *dma_alloc(struct rt_device *dev, rt_size_t size,
rt_ubase_t *dma_handle, rt_ubase_t flags)
{
void *dma_buffer = RT_NULL;
struct rt_dma_pool *pool;
region_pool_lock();
rt_list_for_each_entry(pool, &dma_pool_nodes, list)
{
if (pool->flags & RT_DMA_F_DEVICE)
{
if (!(flags & RT_DMA_F_DEVICE) || pool->dev != dev)
{
continue;
}
}
else if ((flags & RT_DMA_F_DEVICE))
{
continue;
}
if ((flags & RT_DMA_F_NOMAP) && !((pool->flags & RT_DMA_F_NOMAP)))
{
continue;
}
if ((flags & RT_DMA_F_32BITS) && !((pool->flags & RT_DMA_F_32BITS)))
{
continue;
}
if ((flags & RT_DMA_F_LINEAR) && !((pool->flags & RT_DMA_F_LINEAR)))
{
continue;
}
*dma_handle = dma_pool_alloc(pool, size);
if (*dma_handle && !(flags & RT_DMA_F_NOMAP))
{
if (flags & RT_DMA_F_NOCACHE)
{
dma_buffer = rt_ioremap_nocache((void *)*dma_handle, size);
}
else
{
dma_buffer = rt_ioremap_cached((void *)*dma_handle, size);
}
if (!dma_buffer)
{
dma_pool_free(pool, *dma_handle, size);
continue;
}
break;
}
else if (*dma_handle)
{
dma_buffer = (void *)*dma_handle;
break;
}
}
region_pool_unlock();
return dma_buffer;
}
static void dma_free(struct rt_device *dev, rt_size_t size,
void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags)
{
struct rt_dma_pool *pool;
region_pool_lock();
rt_list_for_each_entry(pool, &dma_pool_nodes, list)
{
if (dma_handle >= pool->region.start &&
dma_handle <= pool->region.end)
{
rt_iounmap(cpu_addr);
dma_pool_free(pool, dma_handle, size);
break;
}
}
region_pool_unlock();
}
void *rt_dma_alloc(struct rt_device *dev, rt_size_t size,
rt_ubase_t *dma_handle, rt_ubase_t flags)
{
void *dma_buffer = RT_NULL;
rt_ubase_t dma_handle_s = 0;
const struct rt_dma_map_ops *ops;
if (!dev || !size)
{
return RT_NULL;
}
ops = device_dma_ops(dev);
if (ops->alloc)
{
dma_buffer = ops->alloc(dev, size, &dma_handle_s, flags);
}
else
{
dma_buffer = dma_alloc(dev, size, &dma_handle_s, flags);
}
if (!dma_buffer)
{
return dma_buffer;
}
if (dma_handle)
{
*dma_handle = dma_handle_s;
}
return dma_buffer;
}
void rt_dma_free(struct rt_device *dev, rt_size_t size,
void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags)
{
const struct rt_dma_map_ops *ops;
if (!dev || !size || !cpu_addr)
{
return;
}
ops = device_dma_ops(dev);
if (ops->free)
{
ops->free(dev, size, cpu_addr, dma_handle, flags);
}
else
{
dma_free(dev, size, cpu_addr, dma_handle, flags);
}
}
rt_err_t rt_dma_sync_out_data(struct rt_device *dev, void *data, rt_size_t size,
rt_ubase_t *dma_handle, rt_ubase_t flags)
{
rt_err_t err;
rt_ubase_t dma_handle_s = 0;
const struct rt_dma_map_ops *ops;
if (!data || !size)
{
return -RT_EINVAL;
}
ops = device_dma_ops(dev);
err = ops->sync_out_data(dev, data, size, &dma_handle_s, flags);
if (dma_handle)
{
*dma_handle = dma_handle_s;
}
return err;
}
rt_err_t rt_dma_sync_in_data(struct rt_device *dev, void *out_data, rt_size_t size,
rt_ubase_t dma_handle, rt_ubase_t flags)
{
rt_err_t err;
const struct rt_dma_map_ops *ops;
if (!out_data || !size)
{
return -RT_EINVAL;
}
ops = device_dma_ops(dev);
err = ops->sync_in_data(dev, out_data, size, dma_handle, flags);
return err;
}
static struct rt_dma_pool *dma_pool_install(rt_region_t *region)
{
rt_err_t err;
struct rt_dma_pool *pool;
if (!(pool = rt_calloc(1, sizeof(*pool))))
{
LOG_E("Install pool[%p, %p] error = %s",
region->start, region->end, rt_strerror(-RT_ENOMEM));
return RT_NULL;
}
rt_memcpy(&pool->region, region, sizeof(*region));
pool->flags |= RT_DMA_F_LINEAR;
if (region->end < 4UL * SIZE_GB)
{
pool->flags |= RT_DMA_F_32BITS;
}
pool->start = RT_ALIGN(pool->region.start, ARCH_PAGE_SIZE);
pool->bits = (pool->region.end - pool->start) / ARCH_PAGE_SIZE;
if (!pool->bits)
{
err = -RT_EINVAL;
goto _fail;
}
pool->map = rt_calloc(RT_BITMAP_LEN(pool->bits), sizeof(*pool->map));
if (!pool->map)
{
err = -RT_ENOMEM;
goto _fail;
}
rt_list_init(&pool->list);
region_pool_lock();
rt_list_insert_before(&dma_pool_nodes, &pool->list);
region_pool_unlock();
return pool;
_fail:
rt_free(pool);
LOG_E("Install pool[%p, %p] error = %s",
region->start, region->end, rt_strerror(err));
return RT_NULL;
}
struct rt_dma_pool *rt_dma_pool_install(rt_region_t *region)
{
struct rt_dma_pool *pool;
if (!region)
{
return RT_NULL;
}
if ((pool = dma_pool_install(region)))
{
region = &pool->region;
LOG_I("%s: Reserved %u.%u MiB at %p",
region->name,
(region->end - region->start) / SIZE_MB,
(region->end - region->start) / SIZE_KB & (SIZE_KB - 1),
region->start);
}
return pool;
}
rt_err_t rt_dma_pool_extract(rt_region_t *region_list, rt_size_t list_len,
rt_size_t cma_size, rt_size_t coherent_pool_size)
{
struct rt_dma_pool *pool;
rt_region_t *region = region_list, *region_high = RT_NULL, cma, coherent_pool;
if (!region_list || !list_len || cma_size < coherent_pool_size)
{
return -RT_EINVAL;
}
for (rt_size_t i = 0; i < list_len; ++i, ++region)
{
if (!region->name)
{
continue;
}
/* Always use low address in 4G */
if (region->end - region->start >= cma_size)
{
if ((rt_ssize_t)((4UL * SIZE_GB) - region->start) < cma_size)
{
region_high = region;
continue;
}
goto _found;
}
}
if (region_high)
{
region = region_high;
LOG_W("No available DMA zone in 4G");
goto _found;
}
return -RT_EEMPTY;
_found:
if (region->end - region->start != cma_size)
{
cma.start = region->start;
cma.end = cma.start + cma_size;
/* Update input region */
region->start += cma_size;
}
else
{
rt_memcpy(&cma, region, sizeof(cma));
}
coherent_pool.name = "coherent-pool";
coherent_pool.start = cma.start;
coherent_pool.end = coherent_pool.start + coherent_pool_size;
cma.name = "cma";
cma.start += coherent_pool_size;
if (!(pool = rt_dma_pool_install(&coherent_pool)))
{
return -RT_ENOMEM;
}
/* Use: CMA > coherent-pool */
if (!(pool = rt_dma_pool_install(&cma)))
{
return -RT_ENOMEM;
}
return RT_EOK;
}
#if defined(RT_USING_CONSOLE) && defined(RT_USING_MSH)
static int list_dma_pool(int argc, char**argv)
{
int count = 0;
rt_region_t *region;
struct rt_dma_pool *pool;
rt_kprintf("%-*.s Region\n", RT_NAME_MAX, "Name");
region_pool_lock();
rt_list_for_each_entry(pool, &dma_pool_nodes, list)
{
region = &pool->region;
rt_kprintf("%-*.s [%p, %p]\n", RT_NAME_MAX, region->name,
region->start, region->end);
++count;
}
rt_kprintf("%d DMA memory found\n", count);
region_pool_unlock();
return 0;
}
MSH_CMD_EXPORT(list_dma_pool, dump all dma memory pool);
#endif /* RT_USING_CONSOLE && RT_USING_MSH */
@@ -0,0 +1,3 @@
config RT_USING_LCD
bool "Using LCD graphic drivers"
default n
@@ -0,0 +1,165 @@
menuconfig RT_USING_HWCRYPTO
bool "Using Hardware Crypto drivers"
default n
if RT_USING_HWCRYPTO
config RT_HWCRYPTO_DEFAULT_NAME
string "Hardware crypto device name"
default "hwcryto"
config RT_HWCRYPTO_IV_MAX_SIZE
int "IV max size"
default "16"
config RT_HWCRYPTO_KEYBIT_MAX_SIZE
int "Key max bit length"
default 256
config RT_HWCRYPTO_USING_GCM
bool "Using Hardware GCM"
default n
config RT_HWCRYPTO_USING_AES
bool "Using Hardware AES"
default n
if RT_HWCRYPTO_USING_AES
config RT_HWCRYPTO_USING_AES_ECB
bool "Using Hardware AES ECB mode"
default y
config RT_HWCRYPTO_USING_AES_CBC
bool "Using Hardware AES CBC mode"
default n
config RT_HWCRYPTO_USING_AES_CFB
bool "Using Hardware AES CFB mode"
default n
config RT_HWCRYPTO_USING_AES_CTR
bool "Using Hardware AES CTR mode"
default n
config RT_HWCRYPTO_USING_AES_OFB
bool "Using Hardware AES OFB mode"
default n
endif
config RT_HWCRYPTO_USING_DES
bool "Using Hardware DES"
default n
if RT_HWCRYPTO_USING_DES
config RT_HWCRYPTO_USING_DES_ECB
bool "Using Hardware DES ECB mode"
default y
config RT_HWCRYPTO_USING_DES_CBC
bool "Using Hardware DES CBC mode"
default n
endif
config RT_HWCRYPTO_USING_3DES
bool "Using Hardware 3DES"
default n
if RT_HWCRYPTO_USING_3DES
config RT_HWCRYPTO_USING_3DES_ECB
bool "Using Hardware 3DES ECB mode"
default y
config RT_HWCRYPTO_USING_3DES_CBC
bool "Using Hardware 3DES CBC mode"
default n
endif
config RT_HWCRYPTO_USING_RC4
bool "Using Hardware RC4"
default n
config RT_HWCRYPTO_USING_MD5
bool "Using Hardware MD5"
default n
config RT_HWCRYPTO_USING_SHA1
bool "Using Hardware SHA1"
default n
config RT_HWCRYPTO_USING_SHA2
bool "Using Hardware SHA2"
default n
if RT_HWCRYPTO_USING_SHA2
config RT_HWCRYPTO_USING_SHA2_224
bool "Using Hardware SHA2_224 mode"
default n
config RT_HWCRYPTO_USING_SHA2_256
bool "Using Hardware SHA2_256 mode"
default y
config RT_HWCRYPTO_USING_SHA2_384
bool "Using Hardware SHA2_384 mode"
default n
config RT_HWCRYPTO_USING_SHA2_512
bool "Using Hardware SHA2_512 mode"
default n
endif
config RT_HWCRYPTO_USING_RNG
bool "Using Hardware RNG"
default n
config RT_HWCRYPTO_USING_CRC
bool "Using Hardware CRC"
default n
if RT_HWCRYPTO_USING_CRC
config RT_HWCRYPTO_USING_CRC_07
bool "Using Hardware CRC-8 0x07 polynomial"
default n
config RT_HWCRYPTO_USING_CRC_8005
bool "Using Hardware CRC-16 0x8005 polynomial"
default n
config RT_HWCRYPTO_USING_CRC_1021
bool "Using Hardware CRC-16 0x1021 polynomial"
default n
config RT_HWCRYPTO_USING_CRC_3D65
bool "Using Hardware CRC-16 0x3D65 polynomial"
default n
config RT_HWCRYPTO_USING_CRC_04C11DB7
bool "Using Hardware CRC-32 0x04C11DB7 polynomial"
default n
endif
config RT_HWCRYPTO_USING_BIGNUM
bool "Using Hardware bignum"
default n
if RT_HWCRYPTO_USING_BIGNUM
config RT_HWCRYPTO_USING_BIGNUM_EXPTMOD
bool "Using Hardware bignum expt_mod operation"
default y
config RT_HWCRYPTO_USING_BIGNUM_MULMOD
bool "Using Hardware bignum mul_mod operation"
default y
config RT_HWCRYPTO_USING_BIGNUM_MUL
bool "Using Hardware bignum mul operation"
default n
config RT_HWCRYPTO_USING_BIGNUM_ADD
bool "Using Hardware bignum add operation"
default n
config RT_HWCRYPTO_USING_BIGNUM_SUB
bool "Using Hardware bignum sub operation"
default n
endif
endif
@@ -0,0 +1,34 @@
Import('RTT_ROOT')
Import('rtconfig')
from building import *
cwd = GetCurrentDir()
CPPPATH = [cwd, str(Dir('#'))]
src = ['hwcrypto.c']
if (GetDepend(['RT_HWCRYPTO_USING_AES']) or
GetDepend(['RT_HWCRYPTO_USING_DES']) or
GetDepend(['RT_HWCRYPTO_USING_3DES']) or
GetDepend(['RT_HWCRYPTO_USING_RC4'])):
src += ['hw_symmetric.c']
if GetDepend(['RT_HWCRYPTO_USING_GCM']):
src += ['hw_gcm.c']
if (GetDepend(['RT_HWCRYPTO_USING_MD5']) or
GetDepend(['RT_HWCRYPTO_USING_SHA1']) or
GetDepend(['RT_HWCRYPTO_USING_SHA2'])):
src += ['hw_hash.c']
if GetDepend(['RT_HWCRYPTO_USING_RNG']):
src += ['hw_rng.c']
if GetDepend(['RT_HWCRYPTO_USING_CRC']):
src += ['hw_crc.c']
if GetDepend(['RT_HWCRYPTO_USING_BIGNUM']):
src += ['hw_bignum.c']
group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_HWCRYPTO'], CPPPATH = CPPPATH)
Return('group')
@@ -0,0 +1,318 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-04-25 tyx the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <hw_bignum.h>
static struct rt_hwcrypto_ctx *bignum_default;
rt_inline rt_err_t hwcrypto_bignum_dev_is_init(void)
{
struct rt_hwcrypto_device *dev;
if (bignum_default)
{
return RT_EOK;
}
dev = rt_hwcrypto_dev_default();
if (dev == RT_NULL)
{
return -RT_ERROR;
}
return rt_hwcrypto_bignum_default(dev);
}
/**
* @brief Setting bignum default devices
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_bignum_default(struct rt_hwcrypto_device *device)
{
if (bignum_default)
{
rt_hwcrypto_ctx_destroy(bignum_default);
bignum_default = RT_NULL;
}
if (device == RT_NULL)
{
return RT_EOK;
}
bignum_default = rt_hwcrypto_ctx_create(device, HWCRYPTO_TYPE_BIGNUM, sizeof(struct hwcrypto_bignum));
if (bignum_default == RT_NULL)
{
return -RT_ERROR;
}
return RT_EOK;
}
/**
* @brief Init bignum obj
*
* @param n bignum obj
*/
void rt_hwcrypto_bignum_init(struct hw_bignum_mpi *n)
{
if(n == RT_NULL)
return;
n->sign = 1;
n->total = 0;
n->p = RT_NULL;
}
/**
* @brief free a bignum obj
*
* @param Pointer to bignum obj
*/
void rt_hwcrypto_bignum_free(struct hw_bignum_mpi *n)
{
if (n)
{
rt_memset(n->p, 0xFF, n->total);
rt_free(n->p);
n->sign = 0;
n->total = 0;
n->p = RT_NULL;
}
}
/**
* @brief Get length of bignum as an unsigned binary buffer
*
* @param n bignum obj
*
* @return binary buffer length
*/
int rt_hwcrypto_bignum_get_len(const struct hw_bignum_mpi *n)
{
int tmp_len, total;
if (n == RT_NULL || n->p == RT_NULL)
{
return 0;
}
tmp_len = 0;
total = n->total;
while ((total > 0) && (n->p[total - 1] == 0))
{
tmp_len++;
total--;
}
return n->total - tmp_len;
}
/**
* @brief Export n into unsigned binary data, big endian
*
* @param n bignum obj
* @param buf Buffer for the binary number
* @param len Length of the buffer
*
* @return export bin length
*/
int rt_hwcrypto_bignum_export_bin(struct hw_bignum_mpi *n, rt_uint8_t *buf, int len)
{
int cp_len, i, j;
if (n == RT_NULL || buf == RT_NULL)
{
return 0;
}
rt_memset(buf, 0, len);
cp_len = (int)n->total > len ? len : (int)n->total;
for(i = cp_len, j = 0; i > 0; i--, j++)
{
buf[i - 1] = n->p[j];
}
return cp_len;
}
/**
* @brief Import n from unsigned binary data, big endian
*
* @param n bignum obj
* @param buf Buffer for the binary number
* @param len Length of the buffer
*
* @return import length.
*/
int rt_hwcrypto_bignum_import_bin(struct hw_bignum_mpi *n, rt_uint8_t *buf, int len)
{
int cp_len, i, j;
void *temp_p;
if (n == RT_NULL || buf == RT_NULL)
{
return 0;
}
if ((int)n->total < len)
{
temp_p = rt_malloc(len);
if (temp_p == RT_NULL)
{
return 0;
}
rt_free(n->p);
n->p = temp_p;
n->total = len;
}
n->sign = 1;
rt_memset(n->p, 0, n->total);
cp_len = (int)n->total > len ? len : n->total;
for(i = cp_len, j = 0; i > 0; i--, j++)
{
n->p[j] = buf[i - 1];
}
return cp_len;
}
/**
* @brief x = a + b
*
* @param a bignum obj
* @param b bignum obj
* @param c bignum obj
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_bignum_add(struct hw_bignum_mpi *x,
const struct hw_bignum_mpi *a,
const struct hw_bignum_mpi *b)
{
struct hwcrypto_bignum *bignum_ctx;
if (hwcrypto_bignum_dev_is_init() != RT_EOK)
{
return -RT_ERROR;
}
bignum_ctx = (struct hwcrypto_bignum *)bignum_default;
if (bignum_ctx->ops->add)
{
return bignum_ctx->ops->add(bignum_ctx, x, a, b);
}
return -RT_ERROR;
}
/**
* @brief x = a - b
*
* @param a bignum obj
* @param b bignum obj
* @param c bignum obj
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_bignum_sub(struct hw_bignum_mpi *x,
const struct hw_bignum_mpi *a,
const struct hw_bignum_mpi *b)
{
struct hwcrypto_bignum *bignum_ctx;
if (hwcrypto_bignum_dev_is_init() != RT_EOK)
{
return -RT_ERROR;
}
bignum_ctx = (struct hwcrypto_bignum *)bignum_default;
if (bignum_ctx->ops->sub)
{
return bignum_ctx->ops->sub(bignum_ctx, x, a, b);
}
return -RT_ERROR;
}
/**
* @brief x = a * b
*
* @param a bignum obj
* @param b bignum obj
* @param c bignum obj
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_bignum_mul(struct hw_bignum_mpi *x,
const struct hw_bignum_mpi *a,
const struct hw_bignum_mpi *b)
{
struct hwcrypto_bignum *bignum_ctx;
if (hwcrypto_bignum_dev_is_init() != RT_EOK)
{
return -RT_ERROR;
}
bignum_ctx = (struct hwcrypto_bignum *)bignum_default;
if (bignum_ctx->ops->mul)
{
return bignum_ctx->ops->mul(bignum_ctx, x, a, b);
}
return -RT_ERROR;
}
/**
* @brief x = a * b (mod c)
*
* @param a bignum obj
* @param b bignum obj
* @param c bignum obj
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_bignum_mulmod(struct hw_bignum_mpi *x,
const struct hw_bignum_mpi *a,
const struct hw_bignum_mpi *b,
const struct hw_bignum_mpi *c)
{
struct hwcrypto_bignum *bignum_ctx;
if (hwcrypto_bignum_dev_is_init() != RT_EOK)
{
return -RT_ERROR;
}
bignum_ctx = (struct hwcrypto_bignum *)bignum_default;
if (bignum_ctx->ops->mulmod)
{
return bignum_ctx->ops->mulmod(bignum_ctx, x, a, b, c);
}
return -RT_ERROR;
}
/**
* @brief x = a ^ b (mod c)
*
* @param a bignum obj
* @param b bignum obj
* @param c bignum obj
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_bignum_exptmod(struct hw_bignum_mpi *x,
const struct hw_bignum_mpi *a,
const struct hw_bignum_mpi *b,
const struct hw_bignum_mpi *c)
{
struct hwcrypto_bignum *bignum_ctx;
if (hwcrypto_bignum_dev_is_init() != RT_EOK)
{
return -RT_ERROR;
}
bignum_ctx = (struct hwcrypto_bignum *)bignum_default;
if (bignum_ctx->ops->exptmod)
{
return bignum_ctx->ops->exptmod(bignum_ctx, x, a, b, c);
}
return -RT_ERROR;
}
@@ -0,0 +1,186 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-04-25 tyx the first version
*/
#ifndef __HW_BIGNUM_H__
#define __HW_BIGNUM_H__
#include <hwcrypto.h>
#ifdef __cplusplus
extern "C" {
#endif
struct hwcrypto_bignum;
/* bignum obj */
struct hw_bignum_mpi
{
int sign; /**< integer sign. -1 or 1 */
rt_size_t total; /**< total of limbs */
rt_uint8_t *p; /**< pointer to limbs */
};
struct hwcrypto_bignum_ops
{
rt_err_t (*add)(struct hwcrypto_bignum *bignum_ctx,
struct hw_bignum_mpi *x,
const struct hw_bignum_mpi *a,
const struct hw_bignum_mpi *b); /**< x = a + b */
rt_err_t (*sub)(struct hwcrypto_bignum *bignum_ctx,
struct hw_bignum_mpi *x,
const struct hw_bignum_mpi *a,
const struct hw_bignum_mpi *b); /**< x = a - b */
rt_err_t (*mul)(struct hwcrypto_bignum *bignum_ctx,
struct hw_bignum_mpi *x,
const struct hw_bignum_mpi *a,
const struct hw_bignum_mpi *b); /**< x = a * b */
rt_err_t (*mulmod)(struct hwcrypto_bignum *bignum_ctx,
struct hw_bignum_mpi *x,
const struct hw_bignum_mpi *a,
const struct hw_bignum_mpi *b,
const struct hw_bignum_mpi *c); /**< x = a * b (mod c) */
rt_err_t (*exptmod)(struct hwcrypto_bignum *bignum_ctx,
struct hw_bignum_mpi *x,
const struct hw_bignum_mpi *a,
const struct hw_bignum_mpi *b,
const struct hw_bignum_mpi *c); /**< x = a ^ b (mod c) */
};
/**
* @brief bignum context. Hardware driver usage
*/
struct hwcrypto_bignum
{
struct rt_hwcrypto_ctx parent; /**< Inheritance from hardware crypto context */
const struct hwcrypto_bignum_ops *ops; /**< !! Hardware initializes this value when creating context !! */
};
/**
* @brief Setting bignum default devices
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_bignum_default(struct rt_hwcrypto_device *device);
/**
* @brief Init bignum obj
*/
void rt_hwcrypto_bignum_init(struct hw_bignum_mpi *n);
/**
* @brief free a bignum obj
*
* @param Pointer to bignum obj
*/
void rt_hwcrypto_bignum_free(struct hw_bignum_mpi *n);
/**
* @brief Get length of bignum as an unsigned binary buffer
*
* @param n bignum obj
*
* @return binary buffer Length
*/
int rt_hwcrypto_bignum_get_len(const struct hw_bignum_mpi *n);
/**
* @brief Export n into unsigned binary data, big endian
*
* @param n bignum obj
* @param buf Buffer for the binary number
* @param len Length of the buffer
*
* @return export bin length
*/
int rt_hwcrypto_bignum_export_bin(struct hw_bignum_mpi *n, rt_uint8_t *buf, int len);
/**
* @brief Import n from unsigned binary data, big endian
*
* @param n bignum obj
* @param buf Buffer for the binary number
* @param len Length of the buffer
*
* @return import length.
*/
int rt_hwcrypto_bignum_import_bin(struct hw_bignum_mpi *n, rt_uint8_t *buf, int len);
/**
* @brief x = a + b
*
* @param a bignum obj
* @param b bignum obj
* @param c bignum obj
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_bignum_add(struct hw_bignum_mpi *x,
const struct hw_bignum_mpi *a,
const struct hw_bignum_mpi *b);
/**
* @brief x = a - b
*
* @param a bignum obj
* @param b bignum obj
* @param c bignum obj
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_bignum_sub(struct hw_bignum_mpi *x,
const struct hw_bignum_mpi *a,
const struct hw_bignum_mpi *b);
/**
* @brief x = a * b
*
* @param a bignum obj
* @param b bignum obj
* @param c bignum obj
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_bignum_mul(struct hw_bignum_mpi *x,
const struct hw_bignum_mpi *a,
const struct hw_bignum_mpi *b);
/**
* @brief x = a * b (mod c)
*
* @param a bignum obj
* @param b bignum obj
* @param c bignum obj
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_bignum_mulmod(struct hw_bignum_mpi *x,
const struct hw_bignum_mpi *a,
const struct hw_bignum_mpi *b,
const struct hw_bignum_mpi *c);
/**
* @brief x = a ^ b (mod c)
*
* @param a bignum obj
* @param b bignum obj
* @param c bignum obj
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_bignum_exptmod(struct hw_bignum_mpi *x,
const struct hw_bignum_mpi *a,
const struct hw_bignum_mpi *b,
const struct hw_bignum_mpi *c);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,117 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-04-25 tyx the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <hw_crc.h>
/**
* @brief Creating CRC Context
*
* @param device Hardware crypto device
* @param mode Setting default mode or custom mode
*
* @return CRC context
*/
struct rt_hwcrypto_ctx *rt_hwcrypto_crc_create(struct rt_hwcrypto_device *device,
hwcrypto_crc_mode mode)
{
struct hwcrypto_crc *crc_ctx;
crc_ctx = (struct hwcrypto_crc *)rt_hwcrypto_ctx_create(device, HWCRYPTO_TYPE_CRC, sizeof(struct hwcrypto_crc));
if (crc_ctx == RT_NULL)
{
return RT_NULL;
}
switch (mode)
{
case HWCRYPTO_CRC_CRC8:
{
struct hwcrypto_crc_cfg temp = HWCRYPTO_CRC8_CFG;
crc_ctx->crc_cfg = temp;
break;
}
case HWCRYPTO_CRC_CRC16:
{
struct hwcrypto_crc_cfg temp = HWCRYPTO_CRC16_CFG;
crc_ctx->crc_cfg = temp;
break;
}
case HWCRYPTO_CRC_CRC32:
{
struct hwcrypto_crc_cfg temp = HWCRYPTO_CRC32_CFG;
crc_ctx->crc_cfg = temp;
break;
}
case HWCRYPTO_CRC_CCITT:
{
struct hwcrypto_crc_cfg temp = HWCRYPTO_CRC_CCITT_CFG;
crc_ctx->crc_cfg = temp;
break;
}
case HWCRYPTO_CRC_DNP:
{
struct hwcrypto_crc_cfg temp = HWCRYPTO_CRC_DNP_CFG;
crc_ctx->crc_cfg = temp;
break;
}
default:
break;
}
return &crc_ctx->parent;
}
/**
* @brief Destroy CRC Context
*
* @param ctx CRC context
*/
void rt_hwcrypto_crc_destroy(struct rt_hwcrypto_ctx *ctx)
{
rt_hwcrypto_ctx_destroy(ctx);
}
/**
* @brief Processing a packet of data
*
* @param ctx CRC context
* @param input Data buffer to be Processed
* @param length Data Buffer length
*
* @return CRC value
*/
rt_uint32_t rt_hwcrypto_crc_update(struct rt_hwcrypto_ctx *ctx,
const rt_uint8_t *input,
rt_size_t length)
{
struct hwcrypto_crc *crc_ctx = (struct hwcrypto_crc *)ctx;
if (ctx && crc_ctx->ops->update)
{
return crc_ctx->ops->update(crc_ctx, input, length);
}
return 0;
}
/**
* @brief CRC context configuration
*
* @param ctx CRC context
* @param cfg CRC config
*/
void rt_hwcrypto_crc_cfg(struct rt_hwcrypto_ctx *ctx,
struct hwcrypto_crc_cfg *cfg)
{
if (cfg)
{
((struct hwcrypto_crc *)ctx)->crc_cfg = *cfg;
}
}
@@ -0,0 +1,148 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-04-25 tyx the first version
*/
#ifndef __HW_CRC_H__
#define __HW_CRC_H__
#include <hwcrypto.h>
#define CRC_FLAG_REFIN (0x1 << 0)
#define CRC_FLAG_REFOUT (0x1 << 1)
#define HWCRYPTO_CRC8_CFG \
{ \
.last_val = 0x00, \
.poly = 0x07, \
.width = 8, \
.xorout = 0x00, \
.flags = 0, \
}
#define HWCRYPTO_CRC16_CFG \
{ \
.last_val = 0x0000, \
.poly = 0x8005, \
.width = 16, \
.xorout = 0x0000, \
.flags = 0, \
}
#define HWCRYPTO_CRC32_CFG \
{ \
.last_val = 0x00000000, \
.poly = 0x04C11DB7, \
.width = 32, \
.xorout = 0x00000000, \
.flags = 0, \
}
#define HWCRYPTO_CRC_CCITT_CFG \
{ \
.last_val = 0x0000, \
.poly = 0x1021, \
.width = 16, \
.xorout = 0x0000, \
.flags = CRC_FLAG_REFIN | CRC_FLAG_REFOUT, \
}
#define HWCRYPTO_CRC_DNP_CFG \
{ \
.last_val = 0x0000, \
.poly = 0x3D65, \
.width = 16, \
.xorout = 0xffff, \
.flags = CRC_FLAG_REFIN | CRC_FLAG_REFOUT, \
}
#ifdef __cplusplus
extern "C" {
#endif
struct hwcrypto_crc;
typedef enum
{
HWCRYPTO_CRC_CUSTOM, /**< Custom CRC mode */
HWCRYPTO_CRC_CRC8, /**< poly : 0x07 */
HWCRYPTO_CRC_CRC16, /**< poly : 0x8005 */
HWCRYPTO_CRC_CRC32, /**< poly : 0x04C11DB7 */
HWCRYPTO_CRC_CCITT, /**< poly : 0x1021 */
HWCRYPTO_CRC_DNP, /**< poly : 0x3D65 */
} hwcrypto_crc_mode;
struct hwcrypto_crc_cfg
{
rt_uint32_t last_val; /**< Last CRC value cache */
rt_uint32_t poly; /**< CRC polynomial */
rt_uint16_t width; /**< CRC value width */
rt_uint32_t xorout; /**< Result XOR Value */
rt_uint16_t flags; /**< Input or output data reverse. CRC_FLAG_REFIN or CRC_FLAG_REFOUT */
};
struct hwcrypto_crc_ops
{
rt_uint32_t (*update)(struct hwcrypto_crc *ctx,
const rt_uint8_t *in, rt_size_t length); /**< Perform a CRC calculation. return CRC value */
};
/**
* @brief CRC context. Hardware driver usage
*/
struct hwcrypto_crc
{
struct rt_hwcrypto_ctx parent; /**< Inherited from the standard device */
struct hwcrypto_crc_cfg crc_cfg; /**< CRC configure */
const struct hwcrypto_crc_ops *ops; /**< !! Hardware initializes this value when creating context !! */
};
/**
* @brief Creating CRC Context
*
* @param device Hardware crypto device
* @param mode Setting default mode or custom mode
*
* @return CRC context
*/
struct rt_hwcrypto_ctx *rt_hwcrypto_crc_create(struct rt_hwcrypto_device *device,
hwcrypto_crc_mode mode);
/**
* @brief Destroy CRC Context
*
* @param ctx CRC context
*/
void rt_hwcrypto_crc_destroy(struct rt_hwcrypto_ctx *ctx);
/**
* @brief Processing a packet of data
*
* @param ctx CRC context
* @param input Data buffer to be Processed
* @param length Data Buffer length
*
* @return CRC value
*/
rt_uint32_t rt_hwcrypto_crc_update(struct rt_hwcrypto_ctx *ctx,
const rt_uint8_t *input, rt_size_t length);
/**
* @brief CRC context configuration
*
* @param ctx CRC context
* @param cfg CRC config
*/
void rt_hwcrypto_crc_cfg(struct rt_hwcrypto_ctx *ctx,
struct hwcrypto_crc_cfg *cfg);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,218 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-05-14 tyx the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <hw_gcm.h>
/**
* @brief Creating GCM Context
*
* @param device Hardware crypto device
* @param type Type of symmetric crypto context
*
* @return GCM context
*/
struct rt_hwcrypto_ctx *rt_hwcrypto_gcm_create(struct rt_hwcrypto_device *device,
hwcrypto_type crypt_type)
{
struct rt_hwcrypto_ctx *ctx;
ctx = rt_hwcrypto_ctx_create(device, HWCRYPTO_TYPE_GCM, sizeof(struct hwcrypto_gcm));
if (ctx)
{
((struct hwcrypto_gcm *)ctx)->crypt_type = crypt_type;
}
return ctx;
}
/**
* @brief Destroy GCM Context
*
* @param ctx GCM context
*/
void rt_hwcrypto_gcm_destroy(struct rt_hwcrypto_ctx *ctx)
{
rt_hwcrypto_ctx_destroy(ctx);
}
/**
* @brief This function starts a GCM encryption or decryption operation
*
* @param ctx GCM context
* @param add The buffer holding the additional data
* @param add_len The length of the additional data
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_gcm_start(struct rt_hwcrypto_ctx *ctx, const rt_uint8_t *add,
rt_size_t add_len)
{
struct hwcrypto_gcm *gcm_ctx = (struct hwcrypto_gcm *)ctx;
if (gcm_ctx && gcm_ctx->ops->start)
{
return gcm_ctx->ops->start(gcm_ctx, add, add_len);
}
return -RT_EINVAL;
}
/**
* @brief This function finishes the GCM operation and generates the authentication tag
*
* @param ctx GCM context
* @param tag The buffer for holding the tag
* @param tag_len The length of the tag to generate
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_gcm_finish(struct rt_hwcrypto_ctx *ctx, const rt_uint8_t *tag,
rt_size_t tag_len)
{
struct hwcrypto_gcm *gcm_ctx = (struct hwcrypto_gcm *)ctx;
if (gcm_ctx && gcm_ctx->ops->finish)
{
return gcm_ctx->ops->finish(gcm_ctx, tag, tag_len);
}
return -RT_EINVAL;
}
/**
* @brief This function performs a symmetric encryption or decryption operation
*
* @param ctx GCM context
* @param mode Operation mode. HWCRYPTO_MODE_ENCRYPT or HWCRYPTO_MODE_DECRYPT
* @param length The length of the input data in Bytes. This must be a multiple of the block size
* @param in The buffer holding the input data
* @param out The buffer holding the output data
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_gcm_crypt(struct rt_hwcrypto_ctx *ctx, hwcrypto_mode mode,
rt_size_t length, const rt_uint8_t *in, rt_uint8_t *out)
{
return rt_hwcrypto_symmetric_crypt(ctx, mode, length, in, out);
}
/**
* @brief Set Symmetric Encryption and Decryption Key
*
* @param ctx GCM context
* @param key The crypto key
* @param bitlen The crypto key bit length
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_gcm_setkey(struct rt_hwcrypto_ctx *ctx,
const rt_uint8_t *key, rt_uint32_t bitlen)
{
return rt_hwcrypto_symmetric_setkey(ctx, key, bitlen);
}
/**
* @brief Get Symmetric Encryption and Decryption Key
*
* @param ctx GCM context
* @param key The crypto key buffer
* @param bitlen The crypto key bit length
*
* @return Key length of copy
*/
rt_err_t rt_hwcrypto_gcm_getkey(struct rt_hwcrypto_ctx *ctx,
rt_uint8_t *key, rt_uint32_t bitlen)
{
return rt_hwcrypto_symmetric_getkey(ctx, key, bitlen);
}
/**
* @brief Set Symmetric Encryption and Decryption initialization vector
*
* @param ctx GCM context
* @param iv The crypto initialization vector
* @param len The crypto initialization vector length
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_gcm_setiv(struct rt_hwcrypto_ctx *ctx,
const rt_uint8_t *iv, rt_size_t len)
{
return rt_hwcrypto_symmetric_setiv(ctx, iv, len);
}
/**
* @brief Get Symmetric Encryption and Decryption initialization vector
*
* @param ctx GCM context
* @param iv The crypto initialization vector buffer
* @param len The crypto initialization vector buffer length
*
* @return IV length of copy
*/
rt_err_t rt_hwcrypto_gcm_getiv(struct rt_hwcrypto_ctx *ctx,
rt_uint8_t *iv, rt_size_t len)
{
return rt_hwcrypto_symmetric_getiv(ctx, iv, len);
}
/**
* @brief Set offset in initialization vector
*
* @param ctx GCM context
* @param iv_off The offset in IV
*/
void rt_hwcrypto_gcm_set_ivoff(struct rt_hwcrypto_ctx *ctx, rt_int32_t iv_off)
{
rt_hwcrypto_symmetric_set_ivoff(ctx, iv_off);
}
/**
* @brief Get offset in initialization vector
*
* @param ctx GCM context
* @param iv_off It must point to a valid memory
*/
void rt_hwcrypto_gcm_get_ivoff(struct rt_hwcrypto_ctx *ctx, rt_int32_t *iv_off)
{
rt_hwcrypto_symmetric_get_ivoff(ctx, iv_off);
}
/**
* @brief This function copy GCM context
*
* @param des The destination GCM context
* @param src The GCM context to be copy
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_gcm_cpy(struct rt_hwcrypto_ctx *des,
const struct rt_hwcrypto_ctx *src)
{
struct hwcrypto_gcm *gcm_des = (struct hwcrypto_gcm *)des;
struct hwcrypto_gcm *gcm_src = (struct hwcrypto_gcm *)src;
if (des != RT_NULL && src != RT_NULL)
{
gcm_des->crypt_type = gcm_src->crypt_type;
/* symmetric crypto context copy */
return rt_hwcrypto_symmetric_cpy(des, src);
}
return -RT_EINVAL;
}
/**
* @brief Reset GCM context
*
* @param ctx GCM context
*/
void rt_hwcrypto_gcm_reset(struct rt_hwcrypto_ctx *ctx)
{
rt_hwcrypto_symmetric_reset(ctx);
}
@@ -0,0 +1,182 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-05-14 tyx the first version
*/
#ifndef __HW_GCM_H__
#define __HW_GCM_H__
#include "hw_symmetric.h"
#ifdef __cplusplus
extern "C" {
#endif
struct hwcrypto_gcm;
struct hwcrypto_gcm_ops
{
rt_err_t (*start)(struct hwcrypto_gcm *gcm_ctx,
const unsigned char *add, rt_size_t add_len); /**< Set additional data. start GCM operation */
rt_err_t (*finish)(struct hwcrypto_gcm *gcm_ctx,
const unsigned char *tag, rt_size_t tag_len); /**< finish GCM operation. get tag */
};
/**
* @brief GCM context. Hardware driver usage
*/
struct hwcrypto_gcm
{
struct hwcrypto_symmetric parent; /**< Inheritance from hardware symmetric crypto context */
hwcrypto_type crypt_type; /**< symmetric crypto type. eg: AES/DES */
const struct hwcrypto_gcm_ops *ops; /**< !! Hardware initializes this value when creating context !! */
};
/**
* @brief Creating GCM Context
*
* @param device Hardware crypto device
* @param type Type of symmetric crypto context
*
* @return GCM context
*/
struct rt_hwcrypto_ctx *rt_hwcrypto_gcm_create(struct rt_hwcrypto_device *device,
hwcrypto_type crypt_type);
/**
* @brief Destroy GCM Context
*
* @param ctx GCM context
*/
void rt_hwcrypto_gcm_destroy(struct rt_hwcrypto_ctx *ctx);
/**
* @brief This function starts a GCM encryption or decryption operation
*
* @param ctx GCM context
* @param add The buffer holding the additional data
* @param add_len The length of the additional data
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_gcm_start(struct rt_hwcrypto_ctx *ctx, const rt_uint8_t *add,
rt_size_t add_len);
/**
* @brief This function finishes the GCM operation and generates the authentication tag
*
* @param ctx GCM context
* @param tag The buffer for holding the tag
* @param tag_len The length of the tag to generate
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_gcm_finish(struct rt_hwcrypto_ctx *ctx, const rt_uint8_t *tag,
rt_size_t tag_len);
/**
* @brief This function performs a symmetric encryption or decryption operation
*
* @param ctx GCM context
* @param mode Operation mode. HWCRYPTO_MODE_ENCRYPT or HWCRYPTO_MODE_DECRYPT
* @param length The length of the input data in Bytes. This must be a multiple of the block size
* @param in The buffer holding the input data
* @param out The buffer holding the output data
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_gcm_crypt(struct rt_hwcrypto_ctx *ctx, hwcrypto_mode mode,
rt_size_t length, const rt_uint8_t *in, rt_uint8_t *out);
/**
* @brief Set Symmetric Encryption and Decryption Key
*
* @param ctx GCM context
* @param key The crypto key
* @param bitlen The crypto key bit length
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_gcm_setkey(struct rt_hwcrypto_ctx *ctx,
const rt_uint8_t *key, rt_uint32_t bitlen);
/**
* @brief Get Symmetric Encryption and Decryption Key
*
* @param ctx GCM context
* @param key The crypto key buffer
* @param bitlen The crypto key bit length
*
* @return Key length of copy
*/
rt_err_t rt_hwcrypto_gcm_getkey(struct rt_hwcrypto_ctx *ctx,
rt_uint8_t *key, rt_uint32_t bitlen);
/**
* @brief Set Symmetric Encryption and Decryption initialization vector
*
* @param ctx GCM context
* @param iv The crypto initialization vector
* @param len The crypto initialization vector length
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_gcm_setiv(struct rt_hwcrypto_ctx *ctx,
const rt_uint8_t *iv, rt_size_t len);
/**
* @brief Get Symmetric Encryption and Decryption initialization vector
*
* @param ctx GCM context
* @param iv The crypto initialization vector buffer
* @param len The crypto initialization vector buffer length
*
* @return IV length of copy
*/
rt_err_t rt_hwcrypto_gcm_getiv(struct rt_hwcrypto_ctx *ctx,
rt_uint8_t *iv, rt_size_t len);
/**
* @brief Set offset in initialization vector
*
* @param ctx GCM context
* @param iv_off The offset in IV
*/
void rt_hwcrypto_gcm_set_ivoff(struct rt_hwcrypto_ctx *ctx, rt_int32_t iv_off);
/**
* @brief Get offset in initialization vector
*
* @param ctx GCM context
* @param iv_off It must point to a valid memory
*/
void rt_hwcrypto_gcm_get_ivoff(struct rt_hwcrypto_ctx *ctx, rt_int32_t *iv_off);
/**
* @brief This function copy GCM context
*
* @param des The destination GCM context
* @param src The GCM context to be copy
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_gcm_cpy(struct rt_hwcrypto_ctx *des,
const struct rt_hwcrypto_ctx *src);
/**
* @brief Reset GCM context
*
* @param ctx GCM context
*/
void rt_hwcrypto_gcm_reset(struct rt_hwcrypto_ctx *ctx);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,111 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-04-23 tyx the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <hw_hash.h>
/**
* @brief Creating hash Context
*
* @param device Hardware crypto device
* @param type Type of hash context
*
* @return Hash context
*/
struct rt_hwcrypto_ctx *rt_hwcrypto_hash_create(struct rt_hwcrypto_device *device, hwcrypto_type type)
{
struct rt_hwcrypto_ctx *ctx;
ctx = rt_hwcrypto_ctx_create(device, type, sizeof(struct hwcrypto_hash));
return ctx;
}
/**
* @brief Destroy hash Context
*
* @param ctx Hash context
*/
void rt_hwcrypto_hash_destroy(struct rt_hwcrypto_ctx *ctx)
{
rt_hwcrypto_ctx_destroy(ctx);
}
/**
* @brief Get the final hash value
*
* @param ctx Hash context
* @param output Hash value buffer
* @param length Hash value buffer length
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_hash_finish(struct rt_hwcrypto_ctx *ctx, rt_uint8_t *output, rt_size_t length)
{
if (ctx && ((struct hwcrypto_hash *)ctx)->ops->finish)
{
return ((struct hwcrypto_hash *)ctx)->ops->finish((struct hwcrypto_hash *)ctx, output, length);
}
return -RT_ERROR;
}
/**
* @brief Processing a packet of data
*
* @param ctx Hash context
* @param input Data buffer to be Processed
* @param length Data Buffer length
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_hash_update(struct rt_hwcrypto_ctx *ctx, const rt_uint8_t *input, rt_size_t length)
{
if (ctx && ((struct hwcrypto_hash *)ctx)->ops->update)
{
return ((struct hwcrypto_hash *)ctx)->ops->update((struct hwcrypto_hash *)ctx, input, length);
}
return -RT_ERROR;
}
/**
* @brief This function copy hash context
*
* @param des The destination hash context
* @param src The hash context to be copy
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_hash_cpy(struct rt_hwcrypto_ctx *des, const struct rt_hwcrypto_ctx *src)
{
return rt_hwcrypto_ctx_cpy(des, src);
}
/**
* @brief Reset hash context
*
* @param ctx Hash context
*/
void rt_hwcrypto_hash_reset(struct rt_hwcrypto_ctx *ctx)
{
rt_hwcrypto_ctx_reset(ctx);
}
/**
* @brief Setting hash context type
*
* @param ctx Hash context
* @param type Types of settings
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_hash_set_type(struct rt_hwcrypto_ctx *ctx, hwcrypto_type type)
{
return rt_hwcrypto_set_type(ctx, type);
}
@@ -0,0 +1,110 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-04-23 tyx the first version
*/
#ifndef __HW_HASH_H__
#define __HW_HASH_H__
#include <hwcrypto.h>
#ifdef __cplusplus
extern "C" {
#endif
struct hwcrypto_hash;
struct hwcrypto_hash_ops
{
rt_err_t (*update)(struct hwcrypto_hash *hash_ctx,
const rt_uint8_t *in, rt_size_t length); /**< Processing a packet of data */
rt_err_t (*finish)(struct hwcrypto_hash *hash_ctx,
rt_uint8_t *out, rt_size_t length); /**< Get the final hash value */
};
/**
* @brief hash context. Hardware driver usage
*/
struct hwcrypto_hash
{
struct rt_hwcrypto_ctx parent; /**< Inheritance from hardware crypto context */
const struct hwcrypto_hash_ops *ops; /**< !! Hardware initializes this value when creating context !! */
};
/**
* @brief Creating hash Context
*
* @param device Hardware crypto device
* @param type Type of hash context
*
* @return Hash context
*/
struct rt_hwcrypto_ctx *rt_hwcrypto_hash_create(struct rt_hwcrypto_device *device,
hwcrypto_type type);
/**
* @brief Destroy hash Context
*
* @param ctx Hash context
*/
void rt_hwcrypto_hash_destroy(struct rt_hwcrypto_ctx *ctx);
/**
* @brief Get the final hash value
*
* @param ctx Hash context
* @param output Hash value buffer
* @param length Hash value buffer length
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_hash_finish(struct rt_hwcrypto_ctx *ctx, rt_uint8_t *output, rt_size_t length);
/**
* @brief Processing a packet of data
*
* @param ctx Hash context
* @param input Data buffer to be Processed
* @param length Data Buffer length
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_hash_update(struct rt_hwcrypto_ctx *ctx, const rt_uint8_t *input, rt_size_t length);
/**
* @brief This function copy hash context
*
* @param des The destination hash context
* @param src The hash context to be copy
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_hash_cpy(struct rt_hwcrypto_ctx *des, const struct rt_hwcrypto_ctx *src);
/**
* @brief Reset hash context
*
* @param ctx Hash context
*/
void rt_hwcrypto_hash_reset(struct rt_hwcrypto_ctx *ctx);
/**
* @brief Setting hash context type
*
* @param ctx Hash context
* @param type Types of settings
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_hash_set_type(struct rt_hwcrypto_ctx *ctx, hwcrypto_type type);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,110 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-04-25 tyx the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <hw_rng.h>
/* Used to save default RNG Context */
static struct rt_hwcrypto_ctx *ctx_default;
/**
* @brief Creating RNG Context
*
* @param device Hardware crypto device
*
* @return RNG context
*/
struct rt_hwcrypto_ctx *rt_hwcrypto_rng_create(struct rt_hwcrypto_device *device)
{
struct rt_hwcrypto_ctx *ctx;
ctx = rt_hwcrypto_ctx_create(device, HWCRYPTO_TYPE_RNG, sizeof(struct hwcrypto_rng));
return ctx;
}
/**
* @brief Destroy RNG Context
*
* @param ctx RNG context
*/
void rt_hwcrypto_rng_destroy(struct rt_hwcrypto_ctx *ctx)
{
/* Destroy the defaule RNG Context ? */
if (ctx == ctx_default)
{
ctx_default = RT_NULL;
}
rt_hwcrypto_ctx_destroy(ctx);
}
/**
* @brief Setting RNG default devices
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_rng_default(struct rt_hwcrypto_device *device)
{
struct rt_hwcrypto_ctx *tmp_ctx;
/* if device is null, destroy default RNG Context */
if (device == RT_NULL)
{
if (ctx_default)
{
rt_hwcrypto_rng_destroy(ctx_default);
ctx_default = RT_NULL;
}
return RT_EOK;
}
/* Try create RNG Context */
tmp_ctx = rt_hwcrypto_rng_create(device);
if (tmp_ctx == RT_NULL)
{
return -RT_ERROR;
}
/* create RNG Context success, update default RNG Context */
rt_hwcrypto_rng_destroy(ctx_default);
ctx_default = tmp_ctx;
return RT_EOK;
}
/**
* @brief Getting Random Numbers from RNG Context
*
* @param ctx RNG context
*
* @return Random number
*/
rt_uint32_t rt_hwcrypto_rng_update_ctx(struct rt_hwcrypto_ctx *ctx)
{
if (ctx)
{
return ((struct hwcrypto_rng *)ctx)->ops->update((struct hwcrypto_rng *)ctx);
}
return 0;
}
/**
* @brief Return a random number
*
* @return Random number
*/
rt_uint32_t rt_hwcrypto_rng_update(void)
{
/* Default device does not exist ? */
if (ctx_default == RT_NULL)
{
/* try create Context from default device */
rt_hwcrypto_rng_default(rt_hwcrypto_dev_default());
}
return rt_hwcrypto_rng_update_ctx(ctx_default);
}
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-04-25 tyx the first version
*/
#ifndef __HW_RNG_H__
#define __HW_RNG_H__
#include <hwcrypto.h>
#ifdef __cplusplus
extern "C" {
#endif
struct hwcrypto_rng;
struct hwcrypto_rng_ops
{
rt_uint32_t (*update)(struct hwcrypto_rng *ctx); /**< Return a random number */
};
/**
* @brief random context. Hardware driver usage
*/
struct hwcrypto_rng
{
struct rt_hwcrypto_ctx parent; /**< Inheritance from hardware crypto context */
const struct hwcrypto_rng_ops *ops; /**< !! Hardware initializes this value when creating context !! */
};
/**
* @brief Creating RNG Context
*
* @param device Hardware crypto device
*
* @return RNG context
*/
struct rt_hwcrypto_ctx *rt_hwcrypto_rng_create(struct rt_hwcrypto_device *device);
/**
* @brief Destroy RNG Context
*
* @param ctx RNG context
*/
void rt_hwcrypto_rng_destroy(struct rt_hwcrypto_ctx *ctx);
/**
* @brief Setting RNG default devices
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_rng_default(struct rt_hwcrypto_device *device);
/**
* @brief Getting Random Numbers from RNG Context
*
* @param ctx RNG context
*
* @return Random number
*/
rt_uint32_t rt_hwcrypto_rng_update_ctx(struct rt_hwcrypto_ctx *ctx);
/**
* @brief Return a random number
*
* @return Random number
*/
rt_uint32_t rt_hwcrypto_rng_update(void);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,276 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-04-25 tyx the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <hw_symmetric.h>
/**
* @brief Creating Symmetric Encryption and Decryption Context
*
* @param device Hardware crypto device
* @param type Type of symmetric crypto context
*
* @return Symmetric crypto context
*/
struct rt_hwcrypto_ctx *rt_hwcrypto_symmetric_create(struct rt_hwcrypto_device *device, hwcrypto_type type)
{
struct rt_hwcrypto_ctx *ctx;
ctx = rt_hwcrypto_ctx_create(device, type, sizeof(struct hwcrypto_symmetric));
return ctx;
}
/**
* @brief Destroy Symmetric Encryption and Decryption Context
*
* @param ctx Symmetric crypto context
*/
void rt_hwcrypto_symmetric_destroy(struct rt_hwcrypto_ctx *ctx)
{
rt_hwcrypto_ctx_destroy(ctx);
}
/**
* @brief This function performs a symmetric encryption or decryption operation
*
* @param ctx Symmetric crypto context
* @param mode Operation mode. HWCRYPTO_MODE_ENCRYPT or HWCRYPTO_MODE_DECRYPT
* @param length The length of the input data in Bytes. This must be a multiple of the block size
* @param in The buffer holding the input data
* @param out The buffer holding the output data
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_symmetric_crypt(struct rt_hwcrypto_ctx *ctx, hwcrypto_mode mode, rt_size_t length, const rt_uint8_t *in, rt_uint8_t *out)
{
struct hwcrypto_symmetric *symmetric_ctx;
struct hwcrypto_symmetric_info symmetric_info;
rt_err_t err;
if (ctx == RT_NULL)
{
return -RT_EINVAL;
}
symmetric_ctx = (struct hwcrypto_symmetric *)ctx;
if (symmetric_ctx->ops->crypt == RT_NULL)
{
return -RT_ERROR;
}
if (mode != HWCRYPTO_MODE_ENCRYPT && mode != HWCRYPTO_MODE_DECRYPT)
{
return -RT_EINVAL;
}
/* Input information packaging */
symmetric_info.mode = mode;
symmetric_info.in = in;
symmetric_info.out = out;
symmetric_info.length = length;
/* Calling Hardware Encryption and Decryption Function */
err = symmetric_ctx->ops->crypt(symmetric_ctx, &symmetric_info);
/* clean up flags */
symmetric_ctx->flags &= ~(SYMMTRIC_MODIFY_KEY | SYMMTRIC_MODIFY_IV | SYMMTRIC_MODIFY_IVOFF);
return err;
}
/**
* @brief Set Symmetric Encryption and Decryption Key
*
* @param ctx Symmetric crypto context
* @param key The crypto key
* @param bitlen The crypto key bit length
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_symmetric_setkey(struct rt_hwcrypto_ctx *ctx, const rt_uint8_t *key, rt_uint32_t bitlen)
{
struct hwcrypto_symmetric *symmetric_ctx;
if (ctx && bitlen <= RT_HWCRYPTO_KEYBIT_MAX_SIZE)
{
symmetric_ctx = (struct hwcrypto_symmetric *)ctx;
rt_memcpy(symmetric_ctx->key, key, bitlen >> 3);
/* Record key length */
symmetric_ctx->key_bitlen = bitlen;
/* Key change flag set up */
symmetric_ctx->flags |= SYMMTRIC_MODIFY_KEY;
return RT_EOK;
}
return -RT_EINVAL;
}
/**
* @brief Get Symmetric Encryption and Decryption Key
*
* @param ctx Symmetric crypto context
* @param key The crypto key buffer
* @param bitlen The crypto key bit length
*
* @return Key length of copy
*/
int rt_hwcrypto_symmetric_getkey(struct rt_hwcrypto_ctx *ctx, rt_uint8_t *key, rt_uint32_t bitlen)
{
struct hwcrypto_symmetric *symmetric_ctx = (struct hwcrypto_symmetric *)ctx;
if (ctx && bitlen >= symmetric_ctx->key_bitlen)
{
rt_memcpy(key, symmetric_ctx->key, symmetric_ctx->key_bitlen >> 3);
return symmetric_ctx->key_bitlen;
}
return 0;
}
/**
* @brief Set Symmetric Encryption and Decryption initialization vector
*
* @param ctx Symmetric crypto context
* @param iv The crypto initialization vector
* @param len The crypto initialization vector length
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_symmetric_setiv(struct rt_hwcrypto_ctx *ctx, const rt_uint8_t *iv, rt_size_t len)
{
struct hwcrypto_symmetric *symmetric_ctx;
if (ctx && len <= RT_HWCRYPTO_IV_MAX_SIZE)
{
symmetric_ctx = (struct hwcrypto_symmetric *)ctx;
rt_memcpy(symmetric_ctx->iv, iv, len);
symmetric_ctx->iv_len = len;
/* IV change flag set up */
symmetric_ctx->flags |= SYMMTRIC_MODIFY_IV;
return RT_EOK;
}
return -RT_EINVAL;
}
/**
* @brief Get Symmetric Encryption and Decryption initialization vector
*
* @param ctx Symmetric crypto context
* @param iv The crypto initialization vector buffer
* @param len The crypto initialization vector buffer length
*
* @return IV length of copy
*/
int rt_hwcrypto_symmetric_getiv(struct rt_hwcrypto_ctx *ctx, rt_uint8_t *iv, rt_size_t len)
{
struct hwcrypto_symmetric *symmetric_ctx = (struct hwcrypto_symmetric *)ctx;;
if (ctx && len >= symmetric_ctx->iv_len)
{
rt_memcpy(iv, symmetric_ctx->iv, symmetric_ctx->iv_len);
return symmetric_ctx->iv_len;
}
return 0;
}
/**
* @brief Set offset in initialization vector
*
* @param ctx Symmetric crypto context
* @param iv_off The offset in IV
*/
void rt_hwcrypto_symmetric_set_ivoff(struct rt_hwcrypto_ctx *ctx, rt_int32_t iv_off)
{
if (ctx)
{
((struct hwcrypto_symmetric *)ctx)->iv_off = iv_off;
/* iv_off change flag set up */
((struct hwcrypto_symmetric *)ctx)->flags |= SYMMTRIC_MODIFY_IVOFF;
}
}
/**
* @brief Get offset in initialization vector
*
* @param ctx Symmetric crypto context
* @param iv_off It must point to a valid memory
*/
void rt_hwcrypto_symmetric_get_ivoff(struct rt_hwcrypto_ctx *ctx, rt_int32_t *iv_off)
{
if (ctx && iv_off)
{
*iv_off = ((struct hwcrypto_symmetric *)ctx)->iv_off;
}
}
/**
* @brief This function copy symmetric crypto context
*
* @param des The destination symmetric crypto context
* @param src The symmetric crypto context to be copy
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_symmetric_cpy(struct rt_hwcrypto_ctx *des, const struct rt_hwcrypto_ctx *src)
{
struct hwcrypto_symmetric *symmetric_des = (struct hwcrypto_symmetric *)des;
struct hwcrypto_symmetric *symmetric_src = (struct hwcrypto_symmetric *)src;
if (des != RT_NULL && src != RT_NULL)
{
/* Copy Symmetric Encryption and Decryption Context Information */
symmetric_des->flags = symmetric_src->flags ;
symmetric_des->iv_len = symmetric_src->iv_len ;
symmetric_des->iv_off = symmetric_src->iv_off ;
symmetric_des->key_bitlen = symmetric_src->key_bitlen;
rt_memcpy(symmetric_des->iv, symmetric_src->iv, symmetric_src->iv_len);
rt_memcpy(symmetric_des->key, symmetric_src->key, symmetric_src->key_bitlen >> 3);
/* Hardware context copy */
return rt_hwcrypto_ctx_cpy(des, src);
}
return -RT_EINVAL;
}
/**
* @brief Reset symmetric crypto context
*
* @param ctx Symmetric crypto context
*/
void rt_hwcrypto_symmetric_reset(struct rt_hwcrypto_ctx *ctx)
{
struct hwcrypto_symmetric *symmetric_ctx = (struct hwcrypto_symmetric *)ctx;
if (ctx != RT_NULL)
{
/* Copy Symmetric Encryption and Decryption Context Information */
symmetric_ctx->flags = 0x00;
symmetric_ctx->iv_len = 0x00;
symmetric_ctx->iv_off = 0x00;
symmetric_ctx->key_bitlen = 0x00;
rt_memset(symmetric_ctx->iv, 0, RT_HWCRYPTO_IV_MAX_SIZE);
rt_memset(symmetric_ctx->key, 0, RT_HWCRYPTO_KEYBIT_MAX_SIZE >> 3);
/* Hardware context reset */
rt_hwcrypto_ctx_reset(ctx);
}
}
/**
* @brief Setting symmetric crypto context type
*
* @param ctx Symmetric crypto context
* @param type Types of settings
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_symmetric_set_type(struct rt_hwcrypto_ctx *ctx, hwcrypto_type type)
{
return rt_hwcrypto_set_type(ctx, type);
}
@@ -0,0 +1,189 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-04-25 tyx the first version
*/
#ifndef __HW_SYMMETRIC_H__
#define __HW_SYMMETRIC_H__
#include <hwcrypto.h>
#ifndef RT_HWCRYPTO_IV_MAX_SIZE
#define RT_HWCRYPTO_IV_MAX_SIZE (16)
#endif
#ifndef RT_HWCRYPTO_KEYBIT_MAX_SIZE
#define RT_HWCRYPTO_KEYBIT_MAX_SIZE (256)
#endif
#define SYMMTRIC_MODIFY_KEY (0x1 << 0)
#define SYMMTRIC_MODIFY_IV (0x1 << 1)
#define SYMMTRIC_MODIFY_IVOFF (0x1 << 2)
#ifdef __cplusplus
extern "C" {
#endif
struct hwcrypto_symmetric;
struct hwcrypto_symmetric_info;
struct hwcrypto_symmetric_ops
{
rt_err_t (*crypt)(struct hwcrypto_symmetric *symmetric_ctx,
struct hwcrypto_symmetric_info *symmetric_info); /**< Hardware Symmetric Encryption and Decryption Callback */
};
/**
* @brief Hardware driver usage, including input and output information
*/
struct hwcrypto_symmetric_info
{
hwcrypto_mode mode; /**< crypto mode. HWCRYPTO_MODE_ENCRYPT or HWCRYPTO_MODE_DECRYPT */
const rt_uint8_t *in; /**< Input data */
rt_uint8_t *out; /**< Output data will be written */
rt_size_t length; /**< The length of the input data in Bytes. It's a multiple of block size. */
};
/**
* @brief Symmetric crypto context. Hardware driver usage
*/
struct hwcrypto_symmetric
{
struct rt_hwcrypto_ctx parent; /**< Inheritance from hardware crypto context */
rt_uint16_t flags; /**< key or iv or ivoff has been changed. The flag will be set up */
rt_uint16_t iv_len; /**< initialization vector effective length */
rt_uint16_t iv_off; /**< The offset in IV */
rt_uint16_t key_bitlen; /**< The crypto key bit length */
rt_uint8_t iv[RT_HWCRYPTO_IV_MAX_SIZE]; /**< The initialization vector */
rt_uint8_t key[RT_HWCRYPTO_KEYBIT_MAX_SIZE >> 3]; /**< The crypto key */
const struct hwcrypto_symmetric_ops *ops; /**< !! Hardware initializes this value when creating context !! */
};
/**
* @brief Creating Symmetric Encryption and Decryption Context
*
* @param device Hardware crypto device
* @param type Type of symmetric crypto context
*
* @return Symmetric crypto context
*/
struct rt_hwcrypto_ctx *rt_hwcrypto_symmetric_create(struct rt_hwcrypto_device *device,
hwcrypto_type type);
/**
* @brief Destroy Symmetric Encryption and Decryption Context
*
* @param ctx Symmetric crypto context
*/
void rt_hwcrypto_symmetric_destroy(struct rt_hwcrypto_ctx *ctx);
/**
* @brief This function performs a symmetric encryption or decryption operation
*
* @param ctx Symmetric crypto context
* @param mode Operation mode. HWCRYPTO_MODE_ENCRYPT or HWCRYPTO_MODE_DECRYPT
* @param length The length of the input data in Bytes. This must be a multiple of the block size
* @param in The buffer holding the input data
* @param out The buffer holding the output data
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_symmetric_crypt(struct rt_hwcrypto_ctx *ctx, hwcrypto_mode mode,
rt_size_t length, const rt_uint8_t *in, rt_uint8_t *out);
/**
* @brief Set Symmetric Encryption and Decryption Key
*
* @param ctx Symmetric crypto context
* @param key The crypto key
* @param bitlen The crypto key bit length
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_symmetric_setkey(struct rt_hwcrypto_ctx *ctx, const rt_uint8_t *key, rt_uint32_t bitlen);
/**
* @brief Get Symmetric Encryption and Decryption Key
*
* @param ctx Symmetric crypto context
* @param key The crypto key buffer
* @param bitlen The crypto key bit length
*
* @return Key length of copy
*/
int rt_hwcrypto_symmetric_getkey(struct rt_hwcrypto_ctx *ctx, rt_uint8_t *key, rt_uint32_t bitlen);
/**
* @brief Set Symmetric Encryption and Decryption initialization vector
*
* @param ctx Symmetric crypto context
* @param iv The crypto initialization vector
* @param len The crypto initialization vector length
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_symmetric_setiv(struct rt_hwcrypto_ctx *ctx, const rt_uint8_t *iv, rt_size_t len);
/**
* @brief Get Symmetric Encryption and Decryption initialization vector
*
* @param ctx Symmetric crypto context
* @param iv The crypto initialization vector buffer
* @param len The crypto initialization vector buffer length
*
* @return IV length of copy
*/
int rt_hwcrypto_symmetric_getiv(struct rt_hwcrypto_ctx *ctx, rt_uint8_t *iv, rt_size_t len);
/**
* @brief Set offset in initialization vector
*
* @param ctx Symmetric crypto context
* @param iv_off The offset in IV
*/
void rt_hwcrypto_symmetric_set_ivoff(struct rt_hwcrypto_ctx *ctx, rt_int32_t iv_off);
/**
* @brief Get offset in initialization vector
*
* @param ctx Symmetric crypto context
* @param iv_off It must point to a valid memory
*/
void rt_hwcrypto_symmetric_get_ivoff(struct rt_hwcrypto_ctx *ctx, rt_int32_t *iv_off);
/**
* @brief This function copy symmetric crypto context
*
* @param des The destination symmetric crypto context
* @param src The symmetric crypto context to be copy
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_symmetric_cpy(struct rt_hwcrypto_ctx *des, const struct rt_hwcrypto_ctx *src);
/**
* @brief Reset symmetric crypto context
*
* @param ctx Symmetric crypto context
*/
void rt_hwcrypto_symmetric_reset(struct rt_hwcrypto_ctx *ctx);
/**
* @brief Setting symmetric crypto context type
*
* @param ctx Symmetric crypto context
* @param type Types of settings
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_symmetric_set_type(struct rt_hwcrypto_ctx *ctx, hwcrypto_type type);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,255 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-04-23 tyx the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <hwcrypto.h>
/**
* @brief Setting context type (Direct calls are not recommended)
*
* @param ctx Crypto context
* @param type Types of settings
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_set_type(struct rt_hwcrypto_ctx *ctx, hwcrypto_type type)
{
if (ctx)
{
/* Is it the same category? */
if ((ctx->type & HWCRYPTO_MAIN_TYPE_MASK) == (type & HWCRYPTO_MAIN_TYPE_MASK))
{
ctx->type = type;
return RT_EOK;
}
/* Context is empty type */
else if (ctx->type == HWCRYPTO_TYPE_NULL)
{
ctx->type = type;
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
return -RT_EINVAL;
}
/**
* @brief Reset context type (Direct calls are not recommended)
*
* @param ctx Crypto context
*
*/
void rt_hwcrypto_ctx_reset(struct rt_hwcrypto_ctx *ctx)
{
if (ctx && ctx->device->ops->reset)
{
ctx->device->ops->reset(ctx);
}
}
/**
* @brief Init crypto context
*
* @param ctx The context to initialize
* @param device Hardware crypto device
* @param type Type of context
* @param obj_size Size of context object
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_ctx_init(struct rt_hwcrypto_ctx *ctx, struct rt_hwcrypto_device *device, hwcrypto_type type)
{
rt_err_t err;
/* Setting context type */
rt_hwcrypto_set_type(ctx, type);
ctx->device = device;
/* Create hardware context */
err = ctx->device->ops->create(ctx);
if (err != RT_EOK)
{
return err;
}
return RT_EOK;
}
/**
* @brief Create crypto context
*
* @param device Hardware crypto device
* @param type Type of context
* @param obj_size Size of context object
*
* @return Crypto context
*/
struct rt_hwcrypto_ctx *rt_hwcrypto_ctx_create(struct rt_hwcrypto_device *device, hwcrypto_type type, rt_uint32_t obj_size)
{
struct rt_hwcrypto_ctx *ctx;
rt_err_t err;
/* Parameter checking */
if (device == RT_NULL || obj_size < sizeof(struct rt_hwcrypto_ctx))
{
return RT_NULL;
}
ctx = rt_malloc(obj_size);
if (ctx == RT_NULL)
{
return ctx;
}
rt_memset(ctx, 0, obj_size);
/* Init context */
err = rt_hwcrypto_ctx_init(ctx, device, type);
if (err != RT_EOK)
{
rt_free(ctx);
ctx = RT_NULL;
}
return ctx;
}
/**
* @brief Destroy crypto context
*
* @param device Crypto context
*/
void rt_hwcrypto_ctx_destroy(struct rt_hwcrypto_ctx *ctx)
{
if (ctx == RT_NULL)
{
return;
}
/* Destroy hardware context */
if (ctx->device->ops->destroy)
{
ctx->device->ops->destroy(ctx);
}
/* Free the resources */
rt_free(ctx);
}
/**
* @brief Copy crypto context
*
* @param des The destination context
* @param src The context to be copy
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_ctx_cpy(struct rt_hwcrypto_ctx *des, const struct rt_hwcrypto_ctx *src)
{
if (des == RT_NULL || src == RT_NULL)
{
return -RT_EINVAL;
}
/* The equipment is different or of different types and cannot be copied */
if (des->device != src->device ||
(des->type & HWCRYPTO_MAIN_TYPE_MASK) != (src->type & HWCRYPTO_MAIN_TYPE_MASK))
{
return -RT_EINVAL;
}
des->type = src->type;
/* Calling Hardware Context Copy Function */
return src->device->ops->copy(des, src);
}
/**
* @brief Get the default hardware crypto device
*
* @return Hardware crypto device
*
*/
struct rt_hwcrypto_device *rt_hwcrypto_dev_default(void)
{
static struct rt_hwcrypto_device *hwcrypto_dev;
/* If there is a default device, return the device */
if (hwcrypto_dev)
{
return hwcrypto_dev;
}
/* Find by default device name */
hwcrypto_dev = (struct rt_hwcrypto_device *)rt_device_find(RT_HWCRYPTO_DEFAULT_NAME);
return hwcrypto_dev;
}
/**
* @brief Get the unique ID of the device
*
* @param device Device object
*
* @return Device unique ID
*/
rt_uint64_t rt_hwcrypto_id(struct rt_hwcrypto_device *device)
{
if (device)
{
return device->id;
}
return 0;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops hwcrypto_ops =
{
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL
};
#endif
/**
* @brief Register hardware crypto device
*
* @param device Hardware crypto device
* @param name Name of device
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_register(struct rt_hwcrypto_device *device, const char *name)
{
rt_err_t err;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(name != RT_NULL);
RT_ASSERT(device->ops != RT_NULL);
RT_ASSERT(device->ops->create != RT_NULL);
RT_ASSERT(device->ops->destroy != RT_NULL);
RT_ASSERT(device->ops->copy != RT_NULL);
RT_ASSERT(device->ops->reset != RT_NULL);
rt_memset(&device->parent, 0, sizeof(struct rt_device));
#ifdef RT_USING_DEVICE_OPS
device->parent.ops = &hwcrypto_ops;
#else
device->parent.init = RT_NULL;
device->parent.open = RT_NULL;
device->parent.close = RT_NULL;
device->parent.read = RT_NULL;
device->parent.write = RT_NULL;
device->parent.control = RT_NULL;
#endif
device->parent.user_data = RT_NULL;
device->parent.type = RT_Device_Class_Security;
/* Register device */
err = rt_device_register(&device->parent, name, RT_DEVICE_FLAG_RDWR);
return err;
}
@@ -0,0 +1,193 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-04-23 tyx the first version
*/
#ifndef __HWCRYPTO_H__
#define __HWCRYPTO_H__
#include <rtthread.h>
#ifndef RT_HWCRYPTO_DEFAULT_NAME
#define RT_HWCRYPTO_DEFAULT_NAME ("hwcryto")
#endif
#define HWCRYPTO_MAIN_TYPE_MASK (0xffffUL << 16)
#define HWCRYPTO_SUB_TYPE_MASK (0xffUL << 8)
#ifdef __cplusplus
extern "C" {
#endif
typedef enum
{
HWCRYPTO_TYPE_NULL = 0x00000000,
/* Main Type */
/* symmetric Type */
HWCRYPTO_TYPE_HEAD = __LINE__,
HWCRYPTO_TYPE_AES = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< AES */
HWCRYPTO_TYPE_DES = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< DES */
HWCRYPTO_TYPE_3DES = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< 3DES */
HWCRYPTO_TYPE_RC4 = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< RC4 */
HWCRYPTO_TYPE_GCM = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< GCM */
/* HASH Type */
HWCRYPTO_TYPE_MD5 = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< MD5 */
HWCRYPTO_TYPE_SHA1 = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< SHA1 */
HWCRYPTO_TYPE_SHA2 = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< SHA2 */
/* Other Type */
HWCRYPTO_TYPE_RNG = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< RNG */
HWCRYPTO_TYPE_CRC = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< CRC */
HWCRYPTO_TYPE_BIGNUM = ((__LINE__ - HWCRYPTO_TYPE_HEAD) & 0xffff) << 16, /**< BIGNUM */
/* AES Subtype */
HWCRYPTO_TYPE_AES_ECB = HWCRYPTO_TYPE_AES | (0x01 << 8),
HWCRYPTO_TYPE_AES_CBC = HWCRYPTO_TYPE_AES | (0x02 << 8),
HWCRYPTO_TYPE_AES_CFB = HWCRYPTO_TYPE_AES | (0x03 << 8),
HWCRYPTO_TYPE_AES_CTR = HWCRYPTO_TYPE_AES | (0x04 << 8),
HWCRYPTO_TYPE_AES_OFB = HWCRYPTO_TYPE_AES | (0x05 << 8),
/* DES Subtype */
HWCRYPTO_TYPE_DES_ECB = HWCRYPTO_TYPE_DES | (0x01 << 8),
HWCRYPTO_TYPE_DES_CBC = HWCRYPTO_TYPE_DES | (0x02 << 8),
/* 3DES Subtype */
HWCRYPTO_TYPE_3DES_ECB = HWCRYPTO_TYPE_3DES | (0x01 << 8),
HWCRYPTO_TYPE_3DES_CBC = HWCRYPTO_TYPE_3DES | (0x02 << 8),
/* SHA2 Subtype */
HWCRYPTO_TYPE_SHA224 = HWCRYPTO_TYPE_SHA2 | (0x01 << 8),
HWCRYPTO_TYPE_SHA256 = HWCRYPTO_TYPE_SHA2 | (0x02 << 8),
HWCRYPTO_TYPE_SHA384 = HWCRYPTO_TYPE_SHA2 | (0x03 << 8),
HWCRYPTO_TYPE_SHA512 = HWCRYPTO_TYPE_SHA2 | (0x04 << 8),
} hwcrypto_type;
typedef enum
{
HWCRYPTO_MODE_ENCRYPT = 0x1, /**< Encryption operations */
HWCRYPTO_MODE_DECRYPT = 0x2, /**< Decryption operations */
HWCRYPTO_MODE_UNKNOWN = 0x7fffffff, /**< Unknown */
} hwcrypto_mode;
struct rt_hwcrypto_ctx;
struct rt_hwcrypto_ops
{
rt_err_t (*create)(struct rt_hwcrypto_ctx *ctx); /**< Creating hardware context */
void (*destroy)(struct rt_hwcrypto_ctx *ctx); /**< Delete hardware context */
rt_err_t (*copy)(struct rt_hwcrypto_ctx *des,
const struct rt_hwcrypto_ctx *src); /**< Cpoy hardware context */
void (*reset)(struct rt_hwcrypto_ctx *ctx); /**< Reset hardware context */
};
struct rt_hwcrypto_device
{
struct rt_device parent; /**< Inherited from the standard device */
const struct rt_hwcrypto_ops *ops; /**< Hardware crypto ops */
rt_uint64_t id; /**< Unique id */
void *user_data; /**< Device user data */
};
struct rt_hwcrypto_ctx
{
struct rt_hwcrypto_device *device; /**< Binding device */
hwcrypto_type type; /**< Encryption and decryption types */
void *contex; /**< Hardware context */
};
/**
* @brief Setting context type (Direct calls are not recommended)
*
* @param ctx Crypto context
* @param type Types of settings
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_set_type(struct rt_hwcrypto_ctx *ctx, hwcrypto_type type);
/**
* @brief Reset context type (Direct calls are not recommended)
*
* @param ctx Crypto context
*/
void rt_hwcrypto_ctx_reset(struct rt_hwcrypto_ctx *ctx);
/**
* @brief Init crypto context (Direct calls are not recommended)
*
* @param ctx The context to initialize
* @param device Hardware crypto device
* @param type Type of context
* @param obj_size Size of context object
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_ctx_init(struct rt_hwcrypto_ctx *ctx,
struct rt_hwcrypto_device *device, hwcrypto_type type);
/**
* @brief Create crypto context (Direct calls are not recommended)
*
* @param device Hardware crypto device
* @param type Type of context
* @param obj_size Size of context object
*
* @return Crypto context
*/
struct rt_hwcrypto_ctx *rt_hwcrypto_ctx_create(struct rt_hwcrypto_device *device,
hwcrypto_type type, rt_uint32_t obj_size);
/**
* @brief Destroy crypto context (Direct calls are not recommended)
*
* @param device Crypto context
*/
void rt_hwcrypto_ctx_destroy(struct rt_hwcrypto_ctx *ctx);
/**
* @brief Copy crypto context (Direct calls are not recommended)
*
* @param des The destination context
* @param src The context to be copy
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_ctx_cpy(struct rt_hwcrypto_ctx *des, const struct rt_hwcrypto_ctx *src);
/**
* @brief Register hardware crypto device
*
* @param device Hardware crypto device
* @param name Name of device
*
* @return RT_EOK on success.
*/
rt_err_t rt_hwcrypto_register(struct rt_hwcrypto_device *device, const char *name);
/**
* @brief Get the default hardware crypto device
*
* @return Hardware crypto device
*
*/
struct rt_hwcrypto_device *rt_hwcrypto_dev_default(void);
/**
* @brief Get the unique ID of the device
*
* @param device Device object
*
* @return Device unique ID
*/
rt_uint64_t rt_hwcrypto_id(struct rt_hwcrypto_device *device);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,10 @@
menuconfig RT_USING_HWTIMER
bool "Using Hardware Timer device drivers"
default n
config RT_HWTIMER_ARM_ARCH
bool "ARM ARCH Timer"
depends on RT_USING_DM
depends on RT_USING_HWTIMER
depends on ARCH_ARM_CORTEX_A || ARCH_ARMV8
default n
@@ -0,0 +1,18 @@
from building import *
group = []
if not GetDepend(['RT_USING_HWTIMER']):
Return('group')
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../include']
src = ['hwtimer.c']
if GetDepend(['RT_HWTIMER_ARM_ARCH']):
src += ['hwtimer-arm_arch.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
@@ -0,0 +1,383 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2021-12-20 GuEe-GUI first version
* 2022-08-24 GuEe-GUI Add OFW support
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
/* support registers access and timer registers in libcpu */
#include <cpu.h>
#include <cpuport.h>
typedef void (*timer_ctrl_handle)(rt_bool_t enable);
typedef rt_uint64_t (*timer_value_handle)(rt_uint64_t val);
static volatile rt_uint64_t timer_step;
static int arm_arch_timer_irq = -1;
static timer_ctrl_handle arm_arch_timer_ctrl_handle = RT_NULL;
static timer_value_handle arm_arch_timer_value_handle = RT_NULL;
/* CTL */
static void mon_ptimer_ctrl(rt_bool_t enable)
{
rt_hw_sysreg_write(CNTPS_CTL, !!enable);
}
static void hyp_s_ptimer_ctrl(rt_bool_t enable)
{
#if ARCH_ARMV8_EXTENSIONS > 1
rt_hw_sysreg_write(CNTHPS_CTL, !!enable);
#endif
}
static void hyp_ns_ptimer_ctrl(rt_bool_t enable)
{
rt_hw_sysreg_write(CNTHP_CTL, !!enable);
}
static void hyp_s_vtimer_ctrl(rt_bool_t enable)
{
#if ARCH_ARMV8_EXTENSIONS > 1
rt_hw_sysreg_write(CNTHVS_CTL, !!enable);
#endif
}
static void hyp_ns_vtimer_ctrl(rt_bool_t enable)
{
#if ARCH_ARMV8_EXTENSIONS > 1
rt_hw_sysreg_write(CNTHV_CTL, !!enable);
#endif
}
static void os_ptimer_ctrl(rt_bool_t enable)
{
rt_hw_sysreg_write(CNTP_CTL, !!enable);
}
static void os_vtimer_ctrl(rt_bool_t enable)
{
rt_hw_sysreg_write(CNTV_CTL, !!enable);
}
/* TVAL */
static rt_uint64_t mon_ptimer_value(rt_uint64_t val)
{
if (val)
{
rt_hw_sysreg_write(CNTPS_TVAL, val);
}
else
{
rt_hw_sysreg_read(CNTPS_TVAL, val);
}
return val;
}
static rt_uint64_t hyp_s_ptimer_value(rt_uint64_t val)
{
#if ARCH_ARMV8_EXTENSIONS > 1
if (val)
{
rt_hw_sysreg_write(CNTHPS_TVAL, val);
}
else
{
rt_hw_sysreg_read(CNTHPS_TVAL, val);
}
return val;
#else
return 0;
#endif
}
static rt_uint64_t hyp_ns_ptimer_value(rt_uint64_t val)
{
if (val)
{
rt_hw_sysreg_write(CNTHP_TVAL, val);
}
else
{
rt_hw_sysreg_read(CNTHP_TVAL, val);
}
return val;
}
static rt_uint64_t hyp_s_vtimer_value(rt_uint64_t val)
{
#if ARCH_ARMV8_EXTENSIONS > 1
if (val)
{
rt_hw_sysreg_write(CNTHVS_TVAL, val);
}
else
{
rt_hw_sysreg_read(CNTHVS_TVAL, val);
}
return val;
#else
return 0;
#endif
}
static rt_uint64_t hyp_ns_vtimer_value(rt_uint64_t val)
{
#if ARCH_ARMV8_EXTENSIONS > 1
if (val)
{
rt_hw_sysreg_write(CNTHV_TVAL, val);
}
else
{
rt_hw_sysreg_read(CNTHV_TVAL, val);
}
return val;
#else
return 0;
#endif
}
static rt_uint64_t os_ptimer_value(rt_uint64_t val)
{
if (val)
{
rt_hw_sysreg_write(CNTP_TVAL, val);
}
else
{
rt_hw_sysreg_read(CNTP_TVAL, val);
}
return val;
}
static rt_uint64_t os_vtimer_value(rt_uint64_t val)
{
if (val)
{
rt_hw_sysreg_write(CNTV_TVAL, val);
}
else
{
rt_hw_sysreg_read(CNTV_TVAL, val);
}
return val;
}
static timer_ctrl_handle ctrl_handle[] =
{
mon_ptimer_ctrl,
hyp_s_ptimer_ctrl,
hyp_ns_ptimer_ctrl,
hyp_s_vtimer_ctrl,
hyp_ns_vtimer_ctrl,
os_ptimer_ctrl,
os_vtimer_ctrl,
};
static timer_value_handle value_handle[] =
{
mon_ptimer_value,
hyp_s_ptimer_value,
hyp_ns_ptimer_value,
hyp_s_vtimer_value,
hyp_ns_vtimer_value,
os_ptimer_value,
os_vtimer_value,
};
static rt_err_t arm_arch_timer_local_enable(void)
{
rt_err_t ret = RT_EOK;
if (arm_arch_timer_irq >= 0)
{
arm_arch_timer_ctrl_handle(RT_FALSE);
arm_arch_timer_value_handle(timer_step);
rt_hw_interrupt_umask(arm_arch_timer_irq);
arm_arch_timer_ctrl_handle(RT_TRUE);
}
else
{
ret = -RT_ENOSYS;
}
return ret;
}
rt_used
static rt_err_t arm_arch_timer_local_disable(void)
{
rt_err_t ret = RT_EOK;
if (arm_arch_timer_ctrl_handle)
{
arm_arch_timer_ctrl_handle(RT_FALSE);
rt_hw_interrupt_mask(arm_arch_timer_irq);
}
else
{
ret = -RT_ENOSYS;
}
return ret;
}
rt_used
static rt_err_t arm_arch_timer_set_frequency(rt_uint64_t frq)
{
rt_err_t ret = RT_EOK;
#ifdef ARCH_SUPPORT_TEE
rt_hw_isb();
rt_hw_sysreg_write(CNTFRQ, frq);
rt_hw_dsb();
#else
ret = -RT_ENOSYS;
#endif
return ret;
}
rt_used
static rt_uint64_t arm_arch_timer_get_frequency(void)
{
rt_uint64_t frq;
rt_hw_isb();
rt_hw_sysreg_read(CNTFRQ, frq);
rt_hw_isb();
return frq;
}
rt_used
static rt_err_t arm_arch_timer_set_value(rt_uint64_t val)
{
rt_err_t ret = RT_EOK;
if (arm_arch_timer_value_handle)
{
val = arm_arch_timer_value_handle(val);
}
else
{
ret = -RT_ENOSYS;
}
return ret;
}
rt_used
static rt_uint64_t arm_arch_timer_get_value(void)
{
rt_uint64_t val = 0;
if (arm_arch_timer_value_handle)
{
val = arm_arch_timer_value_handle(0);
}
return val;
}
static void arm_arch_timer_isr(int vector, void *param)
{
arm_arch_timer_set_value(timer_step);
rt_tick_increase();
}
static int arm_arch_timer_post_init(void)
{
arm_arch_timer_local_enable();
return 0;
}
INIT_SECONDARY_CPU_EXPORT(arm_arch_timer_post_init);
static rt_err_t arm_arch_timer_probe(struct rt_platform_device *pdev)
{
int mode_idx, irq_idx;
const char *irq_name[] =
{
"phys", /* Secure Phys IRQ */
"virt", /* Non-secure Phys IRQ */
"hyp-phys", /* Virt IRQ */
"hyp-virt", /* Hyp IRQ */
};
#if defined(ARCH_SUPPORT_TEE)
mode_idx = 0;
irq_idx = 0;
#elif defined(ARCH_SUPPORT_HYP)
mode_idx = 2;
irq_idx = 3;
#else
mode_idx = 5;
irq_idx = 1;
#endif
arm_arch_timer_irq = rt_dm_dev_get_irq_by_name(&pdev->parent, irq_name[irq_idx]);
if (arm_arch_timer_irq < 0)
{
arm_arch_timer_irq = rt_dm_dev_get_irq(&pdev->parent, irq_idx);
}
if (arm_arch_timer_irq < 0)
{
return -RT_EEMPTY;
}
arm_arch_timer_ctrl_handle = ctrl_handle[mode_idx];
arm_arch_timer_value_handle = value_handle[mode_idx];
rt_hw_interrupt_install(arm_arch_timer_irq, arm_arch_timer_isr, RT_NULL, "tick-arm-timer");
timer_step = arm_arch_timer_get_frequency() / RT_TICK_PER_SECOND;
arm_arch_timer_local_enable();
return RT_EOK;
}
static const struct rt_ofw_node_id arm_arch_timer_ofw_ids[] =
{
{ .compatible = "arm,armv7-timer", },
{ .compatible = "arm,armv8-timer", },
{ /* sentinel */ }
};
static struct rt_platform_driver arm_arch_timer_driver =
{
.name = "arm-arch-timer",
.ids = arm_arch_timer_ofw_ids,
.probe = arm_arch_timer_probe,
};
static int arm_arch_timer_drv_register(void)
{
rt_platform_driver_register(&arm_arch_timer_driver);
return 0;
}
INIT_SUBSYS_EXPORT(arm_arch_timer_drv_register);
@@ -0,0 +1,417 @@
/*
* Copyright (c) 2006-2024 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2015-08-31 heyuanjie87 first version
*/
#include <rtdevice.h>
#include <rthw.h>
#define DBG_TAG "hwtimer"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#ifdef RT_USING_DM
void (*rt_device_hwtimer_us_delay)(rt_uint32_t us) = RT_NULL;
void rt_hw_us_delay(rt_uint32_t us)
{
if (rt_device_hwtimer_us_delay)
{
rt_device_hwtimer_us_delay(us);
}
else
{
LOG_E("Implemented at least in the libcpu");
RT_ASSERT(0);
}
}
#endif /* RT_USING_DM */
rt_inline rt_uint32_t timeout_calc(rt_hwtimer_t *timer, rt_hwtimerval_t *tv)
{
float overflow;
float timeout;
rt_uint32_t counter;
int i, index = 0;
float tv_sec;
float devi_min = 1;
float devi;
/* changed to second */
overflow = timer->info->maxcnt/(float)timer->freq;
tv_sec = tv->sec + tv->usec/(float)1000000;
if (tv_sec < (1/(float)timer->freq))
{
/* little timeout */
i = 0;
timeout = 1/(float)timer->freq;
}
else
{
for (i = 1; i > 0; i ++)
{
timeout = tv_sec/i;
if (timeout <= overflow)
{
counter = (rt_uint32_t)(timeout * timer->freq);
devi = tv_sec - (counter / (float)timer->freq) * i;
/* Minimum calculation error */
if (devi > devi_min)
{
i = index;
timeout = tv_sec/i;
break;
}
else if (devi == 0)
{
break;
}
else if (devi < devi_min)
{
devi_min = devi;
index = i;
}
}
}
}
timer->cycles = i;
timer->reload = i;
timer->period_sec = timeout;
counter = (rt_uint32_t)(timeout * timer->freq);
return counter;
}
static rt_err_t rt_hwtimer_init(struct rt_device *dev)
{
rt_err_t result = RT_EOK;
rt_hwtimer_t *timer;
timer = (rt_hwtimer_t *)dev;
/* try to change to 1MHz */
if ((1000000 <= timer->info->maxfreq) && (1000000 >= timer->info->minfreq))
{
timer->freq = 1000000;
}
else
{
timer->freq = timer->info->minfreq;
}
timer->mode = HWTIMER_MODE_ONESHOT;
timer->cycles = 0;
timer->overflow = 0;
if (timer->ops->init)
{
timer->ops->init(timer, 1);
}
else
{
result = -RT_ENOSYS;
}
return result;
}
static rt_err_t rt_hwtimer_open(struct rt_device *dev, rt_uint16_t oflag)
{
rt_err_t result = RT_EOK;
rt_hwtimer_t *timer;
timer = (rt_hwtimer_t *)dev;
if (timer->ops->control != RT_NULL)
{
timer->ops->control(timer, HWTIMER_CTRL_FREQ_SET, &timer->freq);
}
else
{
result = -RT_ENOSYS;
}
return result;
}
static rt_err_t rt_hwtimer_close(struct rt_device *dev)
{
rt_err_t result = RT_EOK;
rt_hwtimer_t *timer;
timer = (rt_hwtimer_t*)dev;
if (timer->ops->init != RT_NULL)
{
timer->ops->init(timer, 0);
}
else
{
result = -RT_ENOSYS;
}
dev->flag &= ~RT_DEVICE_FLAG_ACTIVATED;
dev->rx_indicate = RT_NULL;
return result;
}
static rt_ssize_t rt_hwtimer_read(struct rt_device *dev, rt_off_t pos, void *buffer, rt_size_t size)
{
rt_hwtimer_t *timer;
rt_hwtimerval_t tv;
rt_uint32_t cnt;
rt_base_t level;
rt_int32_t overflow;
float t;
timer = (rt_hwtimer_t *)dev;
if (timer->ops->count_get == RT_NULL)
return 0;
level = rt_hw_interrupt_disable();
cnt = timer->ops->count_get(timer);
overflow = timer->overflow;
rt_hw_interrupt_enable(level);
if (timer->info->cntmode == HWTIMER_CNTMODE_DW)
{
cnt = (rt_uint32_t)(timer->freq * timer->period_sec) - cnt;
}
if (timer->mode == HWTIMER_MODE_ONESHOT)
{
overflow = 0;
}
t = overflow * timer->period_sec + cnt/(float)timer->freq;
tv.sec = (rt_int32_t)t;
tv.usec = (rt_int32_t)((t - tv.sec) * 1000000);
size = size > sizeof(tv)? sizeof(tv) : size;
rt_memcpy(buffer, &tv, size);
return size;
}
static rt_ssize_t rt_hwtimer_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
rt_base_t level;
rt_uint32_t t;
rt_hwtimer_mode_t opm = HWTIMER_MODE_PERIOD;
rt_hwtimer_t *timer;
timer = (rt_hwtimer_t *)dev;
if ((timer->ops->start == RT_NULL) || (timer->ops->stop == RT_NULL))
return 0;
if (size != sizeof(rt_hwtimerval_t))
return 0;
timer->ops->stop(timer);
level = rt_hw_interrupt_disable();
timer->overflow = 0;
rt_hw_interrupt_enable(level);
t = timeout_calc(timer, (rt_hwtimerval_t*)buffer);
if ((timer->cycles <= 1) && (timer->mode == HWTIMER_MODE_ONESHOT))
{
opm = HWTIMER_MODE_ONESHOT;
}
if (timer->ops->start(timer, t, opm) != RT_EOK)
size = 0;
return size;
}
static rt_err_t rt_hwtimer_control(struct rt_device *dev, int cmd, void *args)
{
rt_base_t level;
rt_err_t result = RT_EOK;
rt_hwtimer_t *timer;
timer = (rt_hwtimer_t *)dev;
switch (cmd)
{
case HWTIMER_CTRL_STOP:
{
if (timer->ops->stop != RT_NULL)
{
timer->ops->stop(timer);
}
else
{
result = -RT_ENOSYS;
}
}
break;
case HWTIMER_CTRL_FREQ_SET:
{
rt_int32_t *f;
if (args == RT_NULL)
{
result = -RT_EEMPTY;
break;
}
f = (rt_int32_t*)args;
if ((*f > timer->info->maxfreq) || (*f < timer->info->minfreq))
{
LOG_W("frequency setting out of range! It will maintain at %d Hz", timer->freq);
result = -RT_EINVAL;
break;
}
if (timer->ops->control != RT_NULL)
{
result = timer->ops->control(timer, cmd, args);
if (result == RT_EOK)
{
level = rt_hw_interrupt_disable();
timer->freq = *f;
rt_hw_interrupt_enable(level);
}
}
else
{
result = -RT_ENOSYS;
}
}
break;
case HWTIMER_CTRL_INFO_GET:
{
if (args == RT_NULL)
{
result = -RT_EEMPTY;
break;
}
*((struct rt_hwtimer_info*)args) = *timer->info;
}
break;
case HWTIMER_CTRL_MODE_SET:
{
rt_hwtimer_mode_t *m;
if (args == RT_NULL)
{
result = -RT_EEMPTY;
break;
}
m = (rt_hwtimer_mode_t*)args;
if ((*m != HWTIMER_MODE_ONESHOT) && (*m != HWTIMER_MODE_PERIOD))
{
result = -RT_ERROR;
break;
}
level = rt_hw_interrupt_disable();
timer->mode = *m;
rt_hw_interrupt_enable(level);
}
break;
default:
{
if (timer->ops->control != RT_NULL)
{
result = timer->ops->control(timer, cmd, args);
}
else
{
result = -RT_ENOSYS;
}
}
break;
}
return result;
}
void rt_device_hwtimer_isr(rt_hwtimer_t *timer)
{
rt_base_t level;
RT_ASSERT(timer != RT_NULL);
level = rt_hw_interrupt_disable();
timer->overflow ++;
if (timer->cycles != 0)
{
timer->cycles --;
}
if (timer->cycles == 0)
{
timer->cycles = timer->reload;
rt_hw_interrupt_enable(level);
if (timer->mode == HWTIMER_MODE_ONESHOT)
{
if (timer->ops->stop != RT_NULL)
{
timer->ops->stop(timer);
}
}
if (timer->parent.rx_indicate != RT_NULL)
{
timer->parent.rx_indicate(&timer->parent, sizeof(struct rt_hwtimerval));
}
}
else
{
rt_hw_interrupt_enable(level);
}
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops hwtimer_ops =
{
rt_hwtimer_init,
rt_hwtimer_open,
rt_hwtimer_close,
rt_hwtimer_read,
rt_hwtimer_write,
rt_hwtimer_control
};
#endif
rt_err_t rt_device_hwtimer_register(rt_hwtimer_t *timer, const char *name, void *user_data)
{
struct rt_device *device;
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(timer->ops != RT_NULL);
RT_ASSERT(timer->info != RT_NULL);
device = &(timer->parent);
device->type = RT_Device_Class_Timer;
device->rx_indicate = RT_NULL;
device->tx_complete = RT_NULL;
#ifdef RT_USING_DEVICE_OPS
device->ops = &hwtimer_ops;
#else
device->init = rt_hwtimer_init;
device->open = rt_hwtimer_open;
device->close = rt_hwtimer_close;
device->read = rt_hwtimer_read;
device->write = rt_hwtimer_write;
device->control = rt_hwtimer_control;
#endif
device->user_data = user_data;
return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
}
+243
View File
@@ -0,0 +1,243 @@
config RT_USING_I2C
bool "Using I2C device drivers"
default n
if RT_USING_I2C
config RT_I2C_DEBUG
bool "Use I2C debug message"
default n
config RT_USING_I2C_BITOPS
bool "Use GPIO to simulate I2C"
default y
if RT_USING_I2C_BITOPS
config RT_I2C_BITOPS_DEBUG
bool "Use simulate I2C debug message"
default n
endif
menuconfig RT_USING_SOFT_I2C
bool "Use GPIO to soft simulate I2C"
default n
select RT_USING_PIN
select RT_USING_I2C_BITOPS
if RT_USING_SOFT_I2C
menuconfig RT_USING_SOFT_I2C0
bool "Enable I2C0 Bus (software simulation)"
default y
if RT_USING_SOFT_I2C0
config RT_SOFT_I2C0_SCL_PIN
int "SCL pin number"
range 0 32767
default 1
config RT_SOFT_I2C0_SDA_PIN
int "SDA pin number"
range 0 32767
default 2
config RT_SOFT_I2C0_BUS_NAME
string "Bus name"
default "i2c0"
config RT_SOFT_I2C0_TIMING_DELAY
int "Timing delay (us)"
range 0 32767
default 10
config RT_SOFT_I2C0_TIMING_TIMEOUT
int "Timing timeout (tick)"
range 0 32767
default 10
endif
menuconfig RT_USING_SOFT_I2C1
bool "Enable I2C1 Bus (software simulation)"
default y
if RT_USING_SOFT_I2C1
config RT_SOFT_I2C1_SCL_PIN
int "SCL pin number"
range 0 32767
default 3
config RT_SOFT_I2C1_SDA_PIN
int "SDA pin number"
range 0 32767
default 4
config RT_SOFT_I2C1_BUS_NAME
string "Bus name"
default "i2c1"
config RT_SOFT_I2C1_TIMING_DELAY
int "Timing delay (us)"
range 0 32767
default 10
config RT_SOFT_I2C1_TIMING_TIMEOUT
int "Timing timeout (tick)"
range 0 32767
default 10
endif
menuconfig RT_USING_SOFT_I2C2
bool "Enable I2C2 Bus (software simulation)"
default n
if RT_USING_SOFT_I2C2
config RT_SOFT_I2C2_SCL_PIN
int "SCL pin number"
range 0 32767
default 5
config RT_SOFT_I2C2_SDA_PIN
int "SDA pin number"
range 0 32767
default 6
config RT_SOFT_I2C2_BUS_NAME
string "Bus name"
default "i2c2"
config RT_SOFT_I2C2_TIMING_DELAY
int "Timing delay (us)"
range 0 32767
default 10
config RT_SOFT_I2C2_TIMING_TIMEOUT
int "Timing timeout (tick)"
range 0 32767
default 10
endif
menuconfig RT_USING_SOFT_I2C3
bool "Enable I2C3 Bus (software simulation)"
default n
if RT_USING_SOFT_I2C3
config RT_SOFT_I2C3_SCL_PIN
int "SCL pin number"
range 0 32767
default 7
config RT_SOFT_I2C3_SDA_PIN
int "SDA pin number"
range 0 32767
default 8
config RT_SOFT_I2C3_BUS_NAME
string "Bus name"
default "i2c3"
config RT_SOFT_I2C3_TIMING_DELAY
int "Timing delay (us)"
range 0 32767
default 10
config RT_SOFT_I2C3_TIMING_TIMEOUT
int "Timing timeout (tick)"
range 0 32767
default 10
endif
menuconfig RT_USING_SOFT_I2C4
bool "Enable I2C4 Bus (software simulation)"
default n
if RT_USING_SOFT_I2C4
config RT_SOFT_I2C4_SCL_PIN
int "SCL pin number"
range 0 32767
default 9
config RT_SOFT_I2C4_SDA_PIN
int "SDA pin number"
range 0 32767
default 10
config RT_SOFT_I2C4_BUS_NAME
string "Bus name"
default "i2c4"
config RT_SOFT_I2C4_TIMING_DELAY
int "Timing delay (us)"
range 0 32767
default 10
config RT_SOFT_I2C4_TIMING_TIMEOUT
int "Timing timeout (tick)"
range 0 32767
default 10
endif
menuconfig RT_USING_SOFT_I2C5
bool "Enable I2C5 Bus (software simulation)"
default n
if RT_USING_SOFT_I2C5
config RT_SOFT_I2C5_SCL_PIN
int "SCL pin number"
range 0 32767
default 11
config RT_SOFT_I2C5_SDA_PIN
int "SDA pin number"
range 0 32767
default 12
config RT_SOFT_I2C5_BUS_NAME
string "Bus name"
default "i2c5"
config RT_SOFT_I2C5_TIMING_DELAY
int "Timing delay (us)"
range 0 32767
default 10
config RT_SOFT_I2C5_TIMING_TIMEOUT
int "Timing timeout (tick)"
range 0 32767
default 10
endif
menuconfig RT_USING_SOFT_I2C6
bool "Enable I2C6 Bus (software simulation)"
default n
if RT_USING_SOFT_I2C6
config RT_SOFT_I2C6_SCL_PIN
int "SCL pin number"
range 0 32767
default 13
config RT_SOFT_I2C6_SDA_PIN
int "SDA pin number"
range 0 32767
default 14
config RT_SOFT_I2C6_BUS_NAME
string "Bus name"
default "i2c6"
config RT_SOFT_I2C6_TIMING_DELAY
int "Timing delay (us)"
range 0 32767
default 10
config RT_SOFT_I2C6_TIMING_TIMEOUT
int "Timing timeout (tick)"
range 0 32767
default 10
endif
menuconfig RT_USING_SOFT_I2C7
bool "Enable I2C7 Bus (software simulation)"
default n
if RT_USING_SOFT_I2C7
config RT_SOFT_I2C7_SCL_PIN
int "SCL pin number"
range 0 32767
default 15
config RT_SOFT_I2C7_SDA_PIN
int "SDA pin number"
range 0 32767
default 16
config RT_SOFT_I2C7_BUS_NAME
string "Bus name"
default "i2c7"
config RT_SOFT_I2C7_TIMING_DELAY
int "Timing delay (us)"
range 0 32767
default 10
config RT_SOFT_I2C7_TIMING_TIMEOUT
int "Timing timeout (tick)"
range 0 32767
default 10
endif
menuconfig RT_USING_SOFT_I2C8
bool "Enable I2C8 Bus (software simulation)"
default n
if RT_USING_SOFT_I2C8
config RT_SOFT_I2C8_SCL_PIN
int "SCL pin number"
range 0 32767
default 17
config RT_SOFT_I2C8_SDA_PIN
int "SDA pin number"
range 0 32767
default 18
config RT_SOFT_I2C8_BUS_NAME
string "Bus name"
default "i2c8"
config RT_SOFT_I2C8_TIMING_DELAY
int "Timing delay (us)"
range 0 32767
default 10
config RT_SOFT_I2C8_TIMING_TIMEOUT
int "Timing timeout (tick)"
range 0 32767
default 10
endif
endif
endif
@@ -0,0 +1,22 @@
Import('RTT_ROOT')
from building import *
cwd = GetCurrentDir()
src = Split("""
dev_i2c_core.c
dev_i2c_dev.c
""")
if GetDepend('RT_USING_I2C_BITOPS'):
src = src + ['dev_i2c_bit_ops.c']
if GetDepend('RT_USING_SOFT_I2C'):
src = src + ['dev_soft_i2c.c']
if GetDepend(['RT_USING_DM']):
src += ['dev_i2c_bus.c', 'dev_i2c_dm.c']
# The set of source files associated with this SConscript file.
path = [cwd + '/../include']
group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_I2C'], CPPPATH = path)
Return('group')
@@ -0,0 +1,464 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2012-04-25 weety first version
*/
#include <rtdevice.h>
#define DBG_TAG "I2C"
#ifdef RT_I2C_BITOPS_DEBUG
#define DBG_LVL DBG_LOG
#else
#define DBG_LVL DBG_INFO
#endif
#include <rtdbg.h>
#define SET_SDA(ops, val) ops->set_sda(ops->data, val)
#define SET_SCL(ops, val) ops->set_scl(ops->data, val)
#define GET_SDA(ops) ops->get_sda(ops->data)
#define GET_SCL(ops) ops->get_scl(ops->data)
rt_inline void i2c_delay(struct rt_i2c_bit_ops *ops)
{
ops->udelay((ops->delay_us + 1) >> 1);
}
rt_inline void i2c_delay2(struct rt_i2c_bit_ops *ops)
{
ops->udelay(ops->delay_us);
}
#define SDA_L(ops) SET_SDA(ops, 0)
#define SDA_H(ops) SET_SDA(ops, 1)
#define SCL_L(ops) SET_SCL(ops, 0)
/**
* release scl line, and wait scl line to high.
*/
static rt_err_t SCL_H(struct rt_i2c_bit_ops *ops)
{
rt_tick_t start;
SET_SCL(ops, 1);
if (!ops->get_scl)
goto done;
start = rt_tick_get();
while (!GET_SCL(ops))
{
if ((rt_tick_get() - start) > ops->timeout)
return -RT_ETIMEOUT;
i2c_delay(ops);
}
#ifdef RT_I2C_BITOPS_DEBUG
if (rt_tick_get() != start)
{
LOG_D("wait %ld tick for SCL line to go high",
rt_tick_get() - start);
}
#endif
done:
i2c_delay(ops);
return RT_EOK;
}
static void i2c_start(struct rt_i2c_bit_ops *ops)
{
#ifdef RT_I2C_BITOPS_DEBUG
if (ops->get_scl && !GET_SCL(ops))
{
LOG_E("I2C bus error, SCL line low");
}
if (ops->get_sda && !GET_SDA(ops))
{
LOG_E("I2C bus error, SDA line low");
}
#endif
SDA_L(ops);
i2c_delay(ops);
SCL_L(ops);
}
static void i2c_restart(struct rt_i2c_bit_ops *ops)
{
SDA_H(ops);
SCL_H(ops);
i2c_delay(ops);
SDA_L(ops);
i2c_delay(ops);
SCL_L(ops);
}
static void i2c_stop(struct rt_i2c_bit_ops *ops)
{
SDA_L(ops);
i2c_delay(ops);
SCL_H(ops);
i2c_delay(ops);
SDA_H(ops);
i2c_delay2(ops);
}
rt_inline rt_bool_t i2c_waitack(struct rt_i2c_bit_ops *ops)
{
rt_bool_t ack;
SDA_H(ops);
i2c_delay(ops);
if (SCL_H(ops) < 0)
{
LOG_W("wait ack timeout");
return -RT_ETIMEOUT;
}
ack = !GET_SDA(ops); /* ACK : SDA pin is pulled low */
LOG_D("%s", ack ? "ACK" : "NACK");
SCL_L(ops);
return ack;
}
static rt_int32_t i2c_writeb(struct rt_i2c_bus_device *bus, rt_uint8_t data)
{
rt_int32_t i;
rt_uint8_t bit;
struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;
for (i = 7; i >= 0; i--)
{
SCL_L(ops);
bit = (data >> i) & 1;
SET_SDA(ops, bit);
i2c_delay(ops);
if (SCL_H(ops) < 0)
{
LOG_D("i2c_writeb: 0x%02x, "
"wait scl pin high timeout at bit %d",
data, i);
return -RT_ETIMEOUT;
}
}
SCL_L(ops);
i2c_delay(ops);
return i2c_waitack(ops);
}
static rt_int32_t i2c_readb(struct rt_i2c_bus_device *bus)
{
rt_uint8_t i;
rt_uint8_t data = 0;
struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;
SDA_H(ops);
i2c_delay(ops);
for (i = 0; i < 8; i++)
{
data <<= 1;
if (SCL_H(ops) < 0)
{
LOG_D("i2c_readb: wait scl pin high "
"timeout at bit %d", 7 - i);
return -RT_ETIMEOUT;
}
if (GET_SDA(ops))
data |= 1;
SCL_L(ops);
i2c_delay2(ops);
}
return data;
}
static rt_ssize_t i2c_send_bytes(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg *msg)
{
rt_int32_t ret;
rt_size_t bytes = 0;
const rt_uint8_t *ptr = msg->buf;
rt_int32_t count = msg->len;
rt_uint16_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;
while (count > 0)
{
ret = i2c_writeb(bus, *ptr);
if ((ret > 0) || (ignore_nack && (ret == 0)))
{
count --;
ptr ++;
bytes ++;
}
else if (ret == 0)
{
LOG_D("send bytes: NACK.");
return 0;
}
else
{
LOG_E("send bytes: error %d", ret);
return ret;
}
}
return bytes;
}
static rt_err_t i2c_send_ack_or_nack(struct rt_i2c_bus_device *bus, int ack)
{
struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;
if (ack)
SET_SDA(ops, 0);
i2c_delay(ops);
if (SCL_H(ops) < 0)
{
LOG_E("ACK or NACK timeout.");
return -RT_ETIMEOUT;
}
SCL_L(ops);
return RT_EOK;
}
static rt_ssize_t i2c_recv_bytes(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg *msg)
{
rt_int32_t val;
rt_int32_t bytes = 0; /* actual bytes */
rt_uint8_t *ptr = msg->buf;
rt_int32_t count = msg->len;
const rt_uint32_t flags = msg->flags;
while (count > 0)
{
val = i2c_readb(bus);
if (val >= 0)
{
*ptr = val;
bytes ++;
}
else
{
break;
}
ptr ++;
count --;
LOG_D("recieve bytes: 0x%02x, %s",
val, (flags & RT_I2C_NO_READ_ACK) ?
"(No ACK/NACK)" : (count ? "ACK" : "NACK"));
if (!(flags & RT_I2C_NO_READ_ACK))
{
val = i2c_send_ack_or_nack(bus, count);
if (val < 0)
return val;
}
}
return bytes;
}
static rt_int32_t i2c_send_address(struct rt_i2c_bus_device *bus,
rt_uint8_t addr,
rt_int32_t retries)
{
struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;
rt_int32_t i;
rt_err_t ret = 0;
for (i = 0; i <= retries; i++)
{
ret = i2c_writeb(bus, addr);
if (ret == 1 || i == retries)
break;
LOG_D("send stop condition");
i2c_stop(ops);
i2c_delay2(ops);
LOG_D("send start condition");
i2c_start(ops);
}
return ret;
}
static rt_err_t i2c_bit_send_address(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg *msg)
{
rt_uint16_t flags = msg->flags;
rt_uint16_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;
struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;
rt_uint8_t addr1, addr2;
rt_int32_t retries;
rt_err_t ret;
retries = ignore_nack ? 0 : bus->retries;
if (flags & RT_I2C_ADDR_10BIT)
{
addr1 = 0xf0 | ((msg->addr >> 7) & 0x06);
addr2 = msg->addr & 0xff;
LOG_D("addr1: %d, addr2: %d", addr1, addr2);
ret = i2c_send_address(bus, addr1, retries);
if ((ret != 1) && !ignore_nack)
{
LOG_W("NACK: sending first addr");
return -RT_EIO;
}
ret = i2c_writeb(bus, addr2);
if ((ret != 1) && !ignore_nack)
{
LOG_W("NACK: sending second addr");
return -RT_EIO;
}
if (flags & RT_I2C_RD)
{
LOG_D("send repeated start condition");
i2c_restart(ops);
addr1 |= 0x01;
ret = i2c_send_address(bus, addr1, retries);
if ((ret != 1) && !ignore_nack)
{
LOG_E("NACK: sending repeated addr");
return -RT_EIO;
}
}
}
else
{
/* 7-bit addr */
addr1 = msg->addr << 1;
if (flags & RT_I2C_RD)
addr1 |= 1;
ret = i2c_send_address(bus, addr1, retries);
if ((ret != 1) && !ignore_nack)
return -RT_EIO;
}
return RT_EOK;
}
static rt_ssize_t i2c_bit_xfer(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg msgs[],
rt_uint32_t num)
{
struct rt_i2c_msg *msg;
struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;
rt_int32_t ret;
rt_uint32_t i;
rt_uint16_t ignore_nack;
if((ops->i2c_pin_init_flag == RT_FALSE) && (ops->pin_init != RT_NULL))
{
ops->pin_init();
ops->i2c_pin_init_flag = RT_TRUE;
}
if (num == 0) return 0;
for (i = 0; i < num; i++)
{
msg = &msgs[i];
ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;
if (!(msg->flags & RT_I2C_NO_START))
{
if (i)
{
i2c_restart(ops);
}
else
{
LOG_D("send start condition");
i2c_start(ops);
}
ret = i2c_bit_send_address(bus, msg);
if ((ret != RT_EOK) && !ignore_nack)
{
LOG_D("receive NACK from device addr 0x%02x msg %d",
msgs[i].addr, i);
goto out;
}
}
if (msg->flags & RT_I2C_RD)
{
ret = i2c_recv_bytes(bus, msg);
if (ret >= 1)
{
LOG_D("read %d byte%s", ret, ret == 1 ? "" : "s");
}
if (ret < msg->len)
{
if (ret >= 0)
ret = -RT_EIO;
goto out;
}
}
else
{
ret = i2c_send_bytes(bus, msg);
if (ret >= 1)
{
LOG_D("write %d byte%s", ret, ret == 1 ? "" : "s");
}
if (ret < msg->len)
{
if (ret >= 0)
ret = -RT_ERROR;
goto out;
}
}
}
ret = i;
out:
if (!(msg->flags & RT_I2C_NO_STOP))
{
LOG_D("send stop condition");
i2c_stop(ops);
}
return ret;
}
static const struct rt_i2c_bus_device_ops i2c_bit_bus_ops =
{
i2c_bit_xfer,
RT_NULL,
RT_NULL
};
rt_err_t rt_i2c_bit_add_bus(struct rt_i2c_bus_device *bus,
const char *bus_name)
{
bus->ops = &i2c_bit_bus_ops;
return rt_i2c_bus_device_register(bus, bus_name);
}
@@ -0,0 +1,182 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-12-06 GuEe-GUI first version
*/
#include <rtdevice.h>
#define DBG_TAG "dev.i2c.bus"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
static struct rt_bus i2c_bus;
void i2c_bus_scan_clients(struct rt_i2c_bus_device *bus)
{
#ifdef RT_USING_OFW
if (bus->parent.ofw_node)
{
struct rt_ofw_node *np = bus->parent.ofw_node, *child_np, *i2c_client_np;
rt_ofw_foreach_available_child_node(np, child_np)
{
rt_uint32_t client_addr;
struct rt_i2c_client *client;
if (rt_ofw_prop_read_bool(child_np, "compatible"))
{
i2c_client_np = child_np;
}
else
{
/* Maybe in i2c-mux */
i2c_client_np = rt_ofw_get_next_child(child_np, RT_NULL);
if (!rt_ofw_prop_read_bool(i2c_client_np, "compatible"))
{
continue;
}
}
client = rt_calloc(1, sizeof(*client));
if (!client)
{
rt_ofw_node_put(i2c_client_np);
LOG_E("Not memory to create i2c client: %s",
rt_ofw_node_full_name(i2c_client_np));
return;
}
rt_ofw_prop_read_u32(i2c_client_np, "reg", &client_addr);
client->parent.ofw_node = i2c_client_np;
client->name = rt_ofw_node_name(i2c_client_np);
client->bus = bus;
client->client_addr = client_addr;
rt_i2c_device_register(client);
if (i2c_client_np != child_np)
{
rt_ofw_node_put(i2c_client_np);
}
}
}
#endif /* RT_USING_OFW */
}
rt_err_t rt_i2c_driver_register(struct rt_i2c_driver *driver)
{
RT_ASSERT(driver != RT_NULL);
driver->parent.bus = &i2c_bus;
return rt_driver_register(&driver->parent);
}
rt_err_t rt_i2c_device_register(struct rt_i2c_client *client)
{
RT_ASSERT(client != RT_NULL);
return rt_bus_add_device(&i2c_bus, &client->parent);
}
static rt_bool_t i2c_match(rt_driver_t drv, rt_device_t dev)
{
const struct rt_i2c_device_id *id;
struct rt_i2c_driver *driver = rt_container_of(drv, struct rt_i2c_driver, parent);
struct rt_i2c_client *client = rt_container_of(dev, struct rt_i2c_client, parent);
if ((id = driver->ids))
{
for (; id->name[0]; ++id)
{
if (!rt_strcmp(id->name, client->name))
{
client->id = id;
client->ofw_id = RT_NULL;
return RT_TRUE;
}
}
}
#ifdef RT_USING_OFW
client->ofw_id = rt_ofw_node_match(client->parent.ofw_node, driver->ofw_ids);
if (client->ofw_id)
{
client->id = RT_NULL;
return RT_TRUE;
}
#endif
return RT_FALSE;
}
static rt_err_t i2c_probe(rt_device_t dev)
{
rt_err_t err;
struct rt_i2c_driver *driver = rt_container_of(dev->drv, struct rt_i2c_driver, parent);
struct rt_i2c_client *client = rt_container_of(dev, struct rt_i2c_client, parent);
if (!client->bus)
{
return -RT_EINVAL;
}
err = driver->probe(client);
return err;
}
static rt_err_t i2c_remove(rt_device_t dev)
{
struct rt_i2c_driver *driver = rt_container_of(dev->drv, struct rt_i2c_driver, parent);
struct rt_i2c_client *client = rt_container_of(dev, struct rt_i2c_client, parent);
if (driver && driver->remove)
{
driver->remove(client);
}
return RT_EOK;
}
static rt_err_t i2c_shutdown(rt_device_t dev)
{
struct rt_i2c_driver *driver = rt_container_of(dev->drv, struct rt_i2c_driver, parent);
struct rt_i2c_client *client = rt_container_of(dev, struct rt_i2c_client, parent);
if (driver && driver->shutdown)
{
driver->shutdown(client);
}
return RT_EOK;
}
static struct rt_bus i2c_bus =
{
.name = "i2c",
.match = i2c_match,
.probe = i2c_probe,
.remove = i2c_remove,
.shutdown = i2c_shutdown,
};
static int i2c_bus_init(void)
{
rt_bus_register(&i2c_bus);
return 0;
}
INIT_CORE_EXPORT(i2c_bus_init);
@@ -0,0 +1,153 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2012-04-25 weety first version
* 2021-04-20 RiceChen added support for bus control api
*/
#include <rtdevice.h>
#define DBG_TAG "I2C"
#ifdef RT_I2C_DEBUG
#define DBG_LVL DBG_LOG
#else
#define DBG_LVL DBG_INFO
#endif
#include <rtdbg.h>
rt_err_t rt_i2c_bus_device_register(struct rt_i2c_bus_device *bus,
const char *bus_name)
{
rt_err_t res = RT_EOK;
rt_mutex_init(&bus->lock, "i2c_bus_lock", RT_IPC_FLAG_PRIO);
if (bus->timeout == 0) bus->timeout = RT_TICK_PER_SECOND;
res = rt_i2c_bus_device_device_init(bus, bus_name);
LOG_D("I2C bus [%s] registered", bus_name);
#ifdef RT_USING_DM
if (!res)
{
i2c_bus_scan_clients(bus);
}
#endif
return res;
}
struct rt_i2c_bus_device *rt_i2c_bus_device_find(const char *bus_name)
{
struct rt_i2c_bus_device *bus;
rt_device_t dev = rt_device_find(bus_name);
if (dev == RT_NULL || dev->type != RT_Device_Class_I2CBUS)
{
LOG_E("I2C bus %s not exist", bus_name);
return RT_NULL;
}
bus = (struct rt_i2c_bus_device *)dev->user_data;
return bus;
}
rt_ssize_t rt_i2c_transfer(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg msgs[],
rt_uint32_t num)
{
rt_ssize_t ret;
rt_err_t err;
if (bus->ops->master_xfer)
{
#ifdef RT_I2C_DEBUG
for (ret = 0; ret < num; ret++)
{
LOG_D("msgs[%d] %c, addr=0x%02x, len=%d", ret,
(msgs[ret].flags & RT_I2C_RD) ? 'R' : 'W',
msgs[ret].addr, msgs[ret].len);
}
#endif
err = rt_mutex_take(&bus->lock, RT_WAITING_FOREVER);
if (err != RT_EOK)
{
return (rt_ssize_t)err;
}
ret = bus->ops->master_xfer(bus, msgs, num);
err = rt_mutex_release(&bus->lock);
if (err != RT_EOK)
{
return (rt_ssize_t)err;
}
return ret;
}
else
{
LOG_E("I2C bus operation not supported");
return -RT_EINVAL;
}
}
rt_err_t rt_i2c_control(struct rt_i2c_bus_device *bus,
int cmd,
void *args)
{
rt_err_t ret;
if(bus->ops->i2c_bus_control)
{
ret = bus->ops->i2c_bus_control(bus, cmd, args);
return ret;
}
else
{
LOG_E("I2C bus operation not supported");
return -RT_EINVAL;
}
}
rt_ssize_t rt_i2c_master_send(struct rt_i2c_bus_device *bus,
rt_uint16_t addr,
rt_uint16_t flags,
const rt_uint8_t *buf,
rt_uint32_t count)
{
rt_ssize_t ret;
struct rt_i2c_msg msg;
msg.addr = addr;
msg.flags = flags;
msg.len = count;
msg.buf = (rt_uint8_t *)buf;
ret = rt_i2c_transfer(bus, &msg, 1);
return (ret == 1) ? count : ret;
}
rt_ssize_t rt_i2c_master_recv(struct rt_i2c_bus_device *bus,
rt_uint16_t addr,
rt_uint16_t flags,
rt_uint8_t *buf,
rt_uint32_t count)
{
rt_ssize_t ret;
struct rt_i2c_msg msg;
RT_ASSERT(bus != RT_NULL);
msg.addr = addr;
msg.flags = flags | RT_I2C_RD;
msg.len = count;
msg.buf = buf;
ret = rt_i2c_transfer(bus, &msg, 1);
return (ret == 1) ? count : ret;
}
@@ -0,0 +1,137 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2012-04-25 weety first version
* 2014-08-03 bernard fix some compiling warning
* 2021-04-20 RiceChen added support for bus clock control
*/
#include <rtdevice.h>
#define DBG_TAG "I2C"
#ifdef RT_I2C_DEBUG
#define DBG_LVL DBG_LOG
#else
#define DBG_LVL DBG_INFO
#endif
#include <rtdbg.h>
static rt_ssize_t i2c_bus_device_read(rt_device_t dev,
rt_off_t pos,
void *buffer,
rt_size_t count)
{
rt_uint16_t addr;
rt_uint16_t flags;
struct rt_i2c_bus_device *bus = (struct rt_i2c_bus_device *)dev->user_data;
RT_ASSERT(bus != RT_NULL);
RT_ASSERT(buffer != RT_NULL);
LOG_D("I2C bus dev [%s] reading %u bytes.", dev->parent.name, count);
addr = pos & 0xffff;
flags = (pos >> 16) & 0xffff;
return rt_i2c_master_recv(bus, addr, flags, (rt_uint8_t *)buffer, count);
}
static rt_ssize_t i2c_bus_device_write(rt_device_t dev,
rt_off_t pos,
const void *buffer,
rt_size_t count)
{
rt_uint16_t addr;
rt_uint16_t flags;
struct rt_i2c_bus_device *bus = (struct rt_i2c_bus_device *)dev->user_data;
RT_ASSERT(bus != RT_NULL);
RT_ASSERT(buffer != RT_NULL);
LOG_D("I2C bus dev [%s] writing %u bytes.", dev->parent.name, count);
addr = pos & 0xffff;
flags = (pos >> 16) & 0xffff;
return rt_i2c_master_send(bus, addr, flags, (const rt_uint8_t *)buffer, count);
}
static rt_err_t i2c_bus_device_control(rt_device_t dev,
int cmd,
void *args)
{
rt_err_t ret;
struct rt_i2c_priv_data *priv_data;
struct rt_i2c_bus_device *bus = (struct rt_i2c_bus_device *)dev->user_data;
RT_ASSERT(bus != RT_NULL);
switch (cmd)
{
/* set 10-bit addr mode */
case RT_I2C_DEV_CTRL_10BIT:
bus->flags |= RT_I2C_ADDR_10BIT;
break;
case RT_I2C_DEV_CTRL_TIMEOUT:
bus->timeout = *(rt_uint32_t *)args;
break;
case RT_I2C_DEV_CTRL_RW:
priv_data = (struct rt_i2c_priv_data *)args;
ret = rt_i2c_transfer(bus, priv_data->msgs, priv_data->number);
if (ret < 0)
{
return -RT_EIO;
}
break;
default:
return rt_i2c_control(bus, cmd, args);
}
return RT_EOK;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops i2c_ops =
{
RT_NULL,
RT_NULL,
RT_NULL,
i2c_bus_device_read,
i2c_bus_device_write,
i2c_bus_device_control
};
#endif
rt_err_t rt_i2c_bus_device_device_init(struct rt_i2c_bus_device *bus,
const char *name)
{
struct rt_device *device;
RT_ASSERT(bus != RT_NULL);
device = &bus->parent;
device->user_data = bus;
/* set device type */
device->type = RT_Device_Class_I2CBUS;
/* initialize device interface */
#ifdef RT_USING_DEVICE_OPS
device->ops = &i2c_ops;
#else
device->init = RT_NULL;
device->open = RT_NULL;
device->close = RT_NULL;
device->read = i2c_bus_device_read;
device->write = i2c_bus_device_write;
device->control = i2c_bus_device_control;
#endif
/* register to device manager */
rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
return RT_EOK;
}
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-12-06 GuEe-GUI first version
*/
#include <rtdevice.h>
#define DBG_TAG "dev.i2c.dm"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#ifdef RT_USING_OFW
static void i2c_parse_timing(struct rt_ofw_node *dev_np, const char *propname,
rt_uint32_t *out_value, rt_uint32_t def_value, rt_bool_t use_defaults)
{
if (rt_ofw_prop_read_u32(dev_np, propname, out_value) && use_defaults)
{
*out_value = def_value;
}
}
rt_err_t i2c_timings_ofw_parse(struct rt_ofw_node *dev_np, struct i2c_timings *timings,
rt_bool_t use_defaults)
{
rt_ubase_t def;
rt_bool_t udef = use_defaults;
struct i2c_timings *t = timings;
i2c_parse_timing(dev_np, "clock-frequency", &t->bus_freq_hz, I2C_MAX_STANDARD_MODE_FREQ, udef);
def = t->bus_freq_hz <= I2C_MAX_STANDARD_MODE_FREQ ? 1000 : t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ ? 300 : 120;
i2c_parse_timing(dev_np, "i2c-scl-rising-time-ns", &t->scl_rise_ns, def, udef);
def = t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ ? 300 : 120;
i2c_parse_timing(dev_np, "i2c-scl-falling-time-ns", &t->scl_fall_ns, def, udef);
i2c_parse_timing(dev_np, "i2c-scl-internal-delay-ns", &t->scl_int_delay_ns, 0, udef);
i2c_parse_timing(dev_np, "i2c-sda-falling-time-ns", &t->sda_fall_ns, t->scl_fall_ns, udef);
i2c_parse_timing(dev_np, "i2c-sda-hold-time-ns", &t->sda_hold_ns, 0, udef);
i2c_parse_timing(dev_np, "i2c-digital-filter-width-ns", &t->digital_filter_width_ns, 0, udef);
i2c_parse_timing(dev_np, "i2c-analog-filter-cutoff-frequency", &t->analog_filter_cutoff_freq_hz, 0, udef);
return RT_EOK;
}
#endif /* RT_USING_OFW */
@@ -0,0 +1,274 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-07-30 sp-cai first version
*/
#include <rtdevice.h>
#ifdef RT_USING_SOFT_I2C
#if !defined(RT_USING_SOFT_I2C0) &&\
!defined(RT_USING_SOFT_I2C1) && !defined(RT_USING_SOFT_I2C2) &&\
!defined(RT_USING_SOFT_I2C3) && !defined(RT_USING_SOFT_I2C4) &&\
!defined(RT_USING_SOFT_I2C5) && !defined(RT_USING_SOFT_I2C6) &&\
!defined(RT_USING_SOFT_I2C7) && !defined(RT_USING_SOFT_I2C8)
#error "Please define at least one RT_USING_SOFT_I2Cx"
/*
This driver can be disabled at:
menuconfig -> RT-Thread Components -> Device Drivers -> Using I2C device drivers
*/
#endif
#define DBG_ENABLE
#define DBG_TAG "I2C_S"
#ifdef RT_I2C_BITOPS_DEBUG
#define DBG_LEVEL DBG_LOG
#endif
#include <rtdbg.h>
/* i2c config class */
struct soft_i2c_config
{
rt_base_t scl_pin;
rt_base_t sda_pin;
const char *bus_name;
rt_uint16_t timing_delay; /* scl and sda line delay */
rt_uint16_t timing_timeout; /* in tick */
};
/* i2c dirver class */
struct rt_soft_i2c
{
struct rt_i2c_bus_device i2c_bus;
struct rt_i2c_bit_ops ops;
};
struct soft_i2c_config i2c_cfg[] =
{
#ifdef RT_USING_SOFT_I2C0
{
.scl_pin = RT_SOFT_I2C0_SCL_PIN,
.sda_pin = RT_SOFT_I2C0_SDA_PIN,
.bus_name = RT_SOFT_I2C0_BUS_NAME,
.timing_delay = RT_SOFT_I2C0_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C0_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C0
#ifdef RT_USING_SOFT_I2C1
{
.scl_pin = RT_SOFT_I2C1_SCL_PIN,
.sda_pin = RT_SOFT_I2C1_SDA_PIN,
.bus_name = RT_SOFT_I2C1_BUS_NAME,
.timing_delay = RT_SOFT_I2C1_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C1_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C1
#ifdef RT_USING_SOFT_I2C2
{
.scl_pin = RT_SOFT_I2C2_SCL_PIN,
.sda_pin = RT_SOFT_I2C2_SDA_PIN,
.bus_name = RT_SOFT_I2C2_BUS_NAME,
.timing_delay = RT_SOFT_I2C2_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C2_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C2
#ifdef RT_USING_SOFT_I2C3
{
.scl_pin = RT_SOFT_I2C3_SCL_PIN,
.sda_pin = RT_SOFT_I2C3_SDA_PIN,
.bus_name = RT_SOFT_I2C3_BUS_NAME,
.timing_delay = RT_SOFT_I2C3_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C3_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C3
#ifdef RT_USING_SOFT_I2C4
{
.scl_pin = RT_SOFT_I2C4_SCL_PIN,
.sda_pin = RT_SOFT_I2C4_SDA_PIN,
.bus_name = RT_SOFT_I2C4_BUS_NAME,
.timing_delay = RT_SOFT_I2C4_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C4_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C4
#ifdef RT_USING_SOFT_I2C5
{
.scl_pin = RT_SOFT_I2C5_SCL_PIN,
.sda_pin = RT_SOFT_I2C5_SDA_PIN,
.bus_name = RT_SOFT_I2C5_BUS_NAME,
.timing_delay = RT_SOFT_I2C5_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C5_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C5
#ifdef RT_USING_SOFT_I2C6
{
.scl_pin = RT_SOFT_I2C6_SCL_PIN,
.sda_pin = RT_SOFT_I2C6_SDA_PIN,
.bus_name = RT_SOFT_I2C6_BUS_NAME,
.timing_delay = RT_SOFT_I2C6_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C6_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C6
#ifdef RT_USING_SOFT_I2C7
{
.scl_pin = RT_SOFT_I2C7_SCL_PIN,
.sda_pin = RT_SOFT_I2C7_SDA_PIN,
.bus_name = RT_SOFT_I2C7_BUS_NAME,
.timing_delay = RT_SOFT_I2C7_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C7_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C7
#ifdef RT_USING_SOFT_I2C8
{
.scl_pin = RT_SOFT_I2C8_SCL_PIN,
.sda_pin = RT_SOFT_I2C8_SDA_PIN,
.bus_name = RT_SOFT_I2C8_BUS_NAME,
.timing_delay = RT_SOFT_I2C8_TIMING_DELAY,
.timing_timeout = RT_SOFT_I2C8_TIMING_TIMEOUT,
},
#endif //RT_USING_SOFT_I2C8
};
static struct rt_soft_i2c i2c_bus_obj[sizeof(i2c_cfg) / sizeof(i2c_cfg[0])] =
{ 0 };
/**
* This function initializes the i2c pin.
* @param i2c config class.
*/
static void pin_init(const struct soft_i2c_config *cfg)
{
rt_pin_mode(cfg->scl_pin, PIN_MODE_OUTPUT_OD);
rt_pin_mode(cfg->sda_pin, PIN_MODE_OUTPUT_OD);
rt_pin_write(cfg->scl_pin, PIN_HIGH);
rt_pin_write(cfg->sda_pin, PIN_HIGH);
}
/**
* This function sets the sda pin.
* @param i2c config class.
* @param The sda pin state.
*/
static void set_sda(void *cfg, rt_int32_t value)
{
rt_pin_write(((const struct soft_i2c_config*)cfg)->sda_pin, value);
}
/**
* This function sets the scl pin.
* @param i2c config class.
* @param The sda pin state.
*/
static void set_scl(void *cfg, rt_int32_t value)
{
rt_pin_write(((const struct soft_i2c_config*)cfg)->scl_pin, value);
}
/**
* This function gets the sda pin state.
* @param i2c config class.
*/
static rt_int32_t get_sda(void *cfg)
{
return rt_pin_read(((const struct soft_i2c_config*)cfg)->sda_pin);
}
/**
* This function gets the scl pin state.
* @param i2c config class.
*/
static rt_int32_t get_scl(void *cfg)
{
return rt_pin_read(((const struct soft_i2c_config*)cfg)->scl_pin);
}
static const struct rt_i2c_bit_ops soft_i2c_ops =
{
.set_sda = set_sda,
.set_scl = set_scl,
.get_sda = get_sda,
.get_scl = get_scl,
.udelay = rt_hw_us_delay,
};
/**
* if i2c is locked, this function will unlock it
*
* @param i2c config class.
*
* @return RT_EOK indicates successful unlock.
*/
static rt_err_t i2c_bus_unlock(const struct soft_i2c_config *cfg)
{
rt_ubase_t i = 0;
if(PIN_LOW == rt_pin_read(cfg->sda_pin))
{
while(i++ < 9)
{
rt_pin_write(cfg->scl_pin, PIN_HIGH);
rt_hw_us_delay(cfg->timing_delay);
rt_pin_write(cfg->scl_pin, PIN_LOW);
rt_hw_us_delay(cfg->timing_delay);
}
}
if(PIN_LOW == rt_pin_read(cfg->sda_pin))
{
return -RT_ERROR;
}
return RT_EOK;
}
/* I2C initialization function */
int rt_soft_i2c_init(void)
{
int err = RT_EOK;
struct rt_soft_i2c *obj;
int i;
for(i = 0; i < sizeof(i2c_bus_obj) / sizeof(i2c_bus_obj[0]); i++)
{
struct soft_i2c_config *cfg = &i2c_cfg[i];
pin_init(cfg);
obj = &i2c_bus_obj[i];
obj->ops = soft_i2c_ops;
obj->ops.data = cfg;
obj->i2c_bus.priv = &obj->ops;
obj->ops.delay_us = cfg->timing_delay;
obj->ops.timeout = cfg->timing_timeout;
if(rt_i2c_bit_add_bus(&obj->i2c_bus, cfg->bus_name) == RT_EOK)
{
i2c_bus_unlock(cfg);
LOG_D("Software simulation %s init done"
", SCL pin: 0x%02X, SDA pin: 0x%02X"
, cfg->bus_name
, cfg->scl_pin
, cfg->sda_pin
);
}
else
{
err++;
LOG_E("Software simulation %s init fail"
", SCL pin: 0x%02X, SDA pin: 0x%02X"
, cfg->bus_name
, cfg->scl_pin
, cfg->sda_pin
);
}
}
return err;
}
INIT_PREV_EXPORT(rt_soft_i2c_init);
#endif // RT_USING_SOFT_I2C
@@ -0,0 +1,15 @@
from building import *
group = []
if not GetDepend(['RT_USING_DM']):
Return('group')
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../include']
src = ['iio.c',]
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
+71
View File
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-3-08 GuEe-GUI the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
static void *ofw_iio_channel_get_by_index(struct rt_ofw_node *np, int index, int *out_channel)
{
void *iio = RT_NULL;
#ifdef RT_USING_OFW
struct rt_ofw_node *iio_np;
struct rt_ofw_cell_args iio_args;
if (!rt_ofw_parse_phandle_cells(np, "io-channels", "#io-channel-cells", index, &iio_args))
{
iio_np = iio_args.data;
if (!rt_ofw_data(iio_np))
{
rt_platform_ofw_request(iio_np);
}
iio = rt_ofw_data(iio_np);
rt_ofw_node_put(iio_np);
if (out_channel)
{
*out_channel = iio_args.args[0];
}
}
#endif /* RT_USING_OFW */
return iio;
}
void *rt_iio_channel_get_by_index(struct rt_device *dev, int index, int *out_channel)
{
void *iio = RT_NULL;
if (!dev || index < 0)
{
return RT_NULL;
}
if (dev->ofw_node)
{
iio = ofw_iio_channel_get_by_index(dev->ofw_node, index, out_channel);
}
return iio;
}
void *rt_iio_channel_get_by_name(struct rt_device *dev, const char *name, int *out_channel)
{
int index;
if (!dev || !name)
{
return RT_NULL;
}
index = rt_dm_dev_prop_index_of_string(dev, "io-channel-names", name);
return rt_iio_channel_get_by_index(dev, index, out_channel);
}
@@ -0,0 +1,148 @@
/*
* Copyright (c) 2006-2024 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-05-07 aozima the first version
* 2018-11-16 Ernest Chen add finsh command and update adc function
* 2022-05-11 Stanley Lwin add finsh voltage conversion command
*/
#ifndef __ADC_H__
#define __ADC_H__
#include <rtthread.h>
/**
* @defgroup group_drivers_adc ADC
* @brief ADC driver api
* @ingroup group_device_driver
*
*
* <b>Example</b>
* @code {.c}
* #define ADC_DEV_NAME "adc1"
* #define ADC_DEV_CHANNEL 5
* #define REFER_VOLTAGE 330
* #define CONVERT_BITS (1 << 12)
*
* static int adc_vol_sample(int argc, char *argv[])
* {
* rt_adc_device_t adc_dev;
* rt_uint32_t value, vol;
*
* rt_err_t ret = RT_EOK;
*
* adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
* if (adc_dev == RT_NULL)
* {
* rt_kprintf("adc sample run failed! can't find %s device!\n", ADC_DEV_NAME);
* return -RT_ERROR;
* }
*
* ret = rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
*
* value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);
* rt_kprintf("the value is :%d \n", value);
*
* vol = value * REFER_VOLTAGE / CONVERT_BITS;
* rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
*
* ret = rt_adc_disable(adc_dev, ADC_DEV_CHANNEL);
*
* return ret;
* }
* MSH_CMD_EXPORT(adc_vol_sample, adc voltage convert sample);
*
* @endcode
*/
/*!
* @addtogroup group_drivers_adc
* @{
*/
#define RT_ADC_INTERN_CH_TEMPER (-1)
#define RT_ADC_INTERN_CH_VREF (-2)
#define RT_ADC_INTERN_CH_VBAT (-3)
struct rt_adc_device;
/**
* @brief Configure the adc device
*/
struct rt_adc_ops
{
rt_err_t (*enabled)(struct rt_adc_device *device, rt_int8_t channel, rt_bool_t enabled);
rt_err_t (*convert)(struct rt_adc_device *device, rt_int8_t channel, rt_uint32_t *value);
rt_uint8_t (*get_resolution)(struct rt_adc_device *device);
rt_int16_t (*get_vref) (struct rt_adc_device *device);
};
/**
* @brief adc device
*/
struct rt_adc_device
{
struct rt_device parent;
const struct rt_adc_ops *ops;
};
typedef struct rt_adc_device *rt_adc_device_t;
typedef enum
{
RT_ADC_CMD_ENABLE = RT_DEVICE_CTRL_BASE(ADC) + 1,
RT_ADC_CMD_DISABLE = RT_DEVICE_CTRL_BASE(ADC) + 2,
RT_ADC_CMD_GET_RESOLUTION = RT_DEVICE_CTRL_BASE(ADC) + 3, /* get the resolution in bits */
RT_ADC_CMD_GET_VREF = RT_DEVICE_CTRL_BASE(ADC) + 4, /* get reference voltage */
} rt_adc_cmd_t;
/**
* @brief register the adc device
* @param adc adc device
* @param name device name
* @param ops device ops
* @param user_data device private data
* @return rt_err_t error code
* @ingroup group_drivers_adc
*/
rt_err_t rt_hw_adc_register(rt_adc_device_t adc,const char *name, const struct rt_adc_ops *ops, const void *user_data);
/**
* @brief read the adc value
* @param dev adc device
* @param channel adc channel
* @return rt_uint32_t adc value
* @ingroup group_drivers_adc
*/
rt_uint32_t rt_adc_read(rt_adc_device_t dev, rt_int8_t channel);
/**
* @brief enable the adc channel
* @param dev adc device
* @param channel adc channel
* @return rt_err_t error code
* @ingroup group_drivers_adc
*/
rt_err_t rt_adc_enable(rt_adc_device_t dev, rt_int8_t channel);
/**
* @brief disable the adc channel
* @param dev adc device
* @param channel adc channel
* @return rt_err_t error code
* @ingroup group_drivers_adc
*/
rt_err_t rt_adc_disable(rt_adc_device_t dev, rt_int8_t channel);
/**
* @brief get the adc resolution
* @param dev adc device
* @param channel adc channel
* @return rt_int16_t adc resolution
* @ingroup group_drivers_adc
*/
rt_int16_t rt_adc_voltage(rt_adc_device_t dev, rt_int8_t channel);
/*! @}*/
#endif /* __ADC_H__ */
@@ -0,0 +1,397 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#ifndef __AHCI_H__
#define __AHCI_H__
#include <rthw.h>
#include <rtthread.h>
#include <drivers/scsi.h>
#include <drivers/misc.h>
struct rt_ahci_ops;
/* Generic Host Control */
#define RT_AHCI_HBA_CAP 0x00 /* Host capability*/
#define RT_AHCI_CAP_NP RT_GENMASK(4, 0) /* Number of Ports */
#define RT_AHCI_CAP_NCS RT_GENMASK(8, 12) /* Number of Command Slots */
#define RT_AHCI_CAP_PSC RT_BIT(13) /* Partial State Capable */
#define RT_AHCI_CAP_SSC RT_BIT(14) /* Slumber capable */
#define RT_AHCI_CAP_PMD RT_BIT(15) /* PIO Multiple DRQ Block */
#define RT_AHCI_CAP_SPM RT_BIT(17) /* Port Multiplier */
#define RT_AHCI_CAP_AHCI RT_BIT(18) /* AHCI only */
#define RT_AHCI_CAP_SNZO RT_BIT(19) /* Non-Zero DMA Offsets */
#define RT_AHCI_CAP_ISS RT_GENMASK(23, 20) /* Interface Speed Support */
#define RT_AHCI_CAP_CLO RT_BIT(24) /* Command List Override support */
#define RT_AHCI_CAP_SAL RT_BIT(25) /* Activity LED */
#define RT_AHCI_CAP_SALP RT_BIT(26) /* Aggressive Link Power Management */
#define RT_AHCI_CAP_SSS RT_BIT(27) /* Staggered Spin-up */
#define RT_AHCI_CAP_SIS RT_BIT(28) /* Interlock Switch */
#define RT_AHCI_CAP_NCQ RT_BIT(30) /* Native Command Queueing */
#define RT_AHCI_CAP_64 RT_BIT(31) /* PCI DAC (64-bit DMA) support */
#define RT_AHCI_HBA_GHC 0x04 /* Global host control */
#define RT_AHCI_GHC_RESET RT_BIT(0) /* Reset controller; self-clear */
#define RT_AHCI_GHC_IRQ_EN RT_BIT(1) /* Global IRQ enable */
#define RT_AHCI_GHC_AHCI_EN RT_BIT(31) /* AHCI enabled */
#define RT_AHCI_HBA_INTS 0x08 /* Interrupt status */
#define RT_AHCI_HBA_PI 0x0c /* Port implemented */
#define RT_AHCI_HBA_VS 0x10 /* Version */
#define RT_AHCI_HBA_CCC_CTL 0x14 /* Command completion coalescing control */
#define RT_AHCI_HBA_CCC_PTS 0x18 /* Command completion coalescing ports */
#define RT_AHCI_HBA_EM_LOC 0x1c /* Enclosure management location */
#define RT_AHCI_HBA_EM_CTL 0x20 /* Enclosure management control */
#define RT_AHCI_HBA_CAP2 0x24 /* Host capabilities extended */
#define RT_AHCI_HBA_BOHC 0x28 /* BIOS/OS handoff control and status */
#define RT_AHCI_HBA_VENDOR 0xa0 /* Vendor specific registers (0xa0 - 0xff) */
#define RT_AHCI_PORT_CLB 0x00 /* Command list base address, 1K-byte aligned */
#define RT_AHCI_PORT_CLBU 0x04 /* Command list base address upper 32 bits */
#define RT_AHCI_PORT_FB 0x08 /* FIS base address, 256-byte aligned */
#define RT_AHCI_PORT_FBU 0x0C /* FIS base address upper 32 bits */
#define RT_AHCI_PORT_INTS 0x10 /* Interrupt status */
#define RT_AHCI_PORT_INTE 0x14 /* Interrupt enable */
#define RT_AHCI_PORT_INTE_D2H_REG_FIS RT_BIT(0) /* D2H Register FIS rx'd */
#define RT_AHCI_PORT_INTE_PIOS_FIS RT_BIT(1) /* PIO Setup FIS rx'd */
#define RT_AHCI_PORT_INTE_DMAS_FIS RT_BIT(2) /* DMA Setup FIS rx'd */
#define RT_AHCI_PORT_INTE_SDB_FIS RT_BIT(3) /* Set Device Bits FIS rx'd */
#define RT_AHCI_PORT_INTE_UNK_FIS RT_BIT(4) /* Unknown FIS rx'd */
#define RT_AHCI_PORT_INTE_SG_DONE RT_BIT(5) /* Descriptor processed */
#define RT_AHCI_PORT_INTE_CONNECT RT_BIT(6) /* Port connect change status */
#define RT_AHCI_PORT_INTE_DMPS RT_BIT(7) /* Mechanical presence status */
#define RT_AHCI_PORT_INTE_PHYRDY RT_BIT(22) /* PhyRdy changed */
#define RT_AHCI_PORT_INTE_BAD_PMP RT_BIT(23) /* Incorrect port multiplier */
#define RT_AHCI_PORT_INTE_OVERFLOW RT_BIT(24) /* Xfer exhausted available S/G */
#define RT_AHCI_PORT_INTE_IF_NONFATAL RT_BIT(26) /* Interface non-fatal error */
#define RT_AHCI_PORT_INTE_IF_ERR RT_BIT(27) /* Interface fatal error */
#define RT_AHCI_PORT_INTE_HBUS_DATA_ERR RT_BIT(28) /* Host bus data error */
#define RT_AHCI_PORT_INTE_HBUS_ERR RT_BIT(29) /* Host bus fatal error */
#define RT_AHCI_PORT_INTE_TF_ERR RT_BIT(30) /* Task file error */
#define RT_AHCI_PORT_INTE_COLD_PRES RT_BIT(31) /* Cold presence detect */
#define RT_AHCI_PORT_CMD 0x18 /* Command and status */
#define RT_AHCI_PORT_CMD_START RT_BIT(0) /* Enable port DMA engine */
#define RT_AHCI_PORT_CMD_SPIN_UP RT_BIT(1) /* Spin up device */
#define RT_AHCI_PORT_CMD_POWER_ON RT_BIT(2) /* Power up device */
#define RT_AHCI_PORT_CMD_CLO RT_BIT(3) /* Command list override */
#define RT_AHCI_PORT_CMD_FIS_RX RT_BIT(4) /* Enable FIS receive DMA engine */
#define RT_AHCI_PORT_CMD_FIS_ON RT_BIT(14) /* FIS DMA engine running */
#define RT_AHCI_PORT_CMD_LIST_ON RT_BIT(15) /* cmd list DMA engine running */
#define RT_AHCI_PORT_CMD_ATAPI RT_BIT(24) /* Device is ATAPI */
#define RT_AHCI_PORT_CMD_ACTIVE RT_BIT(28) /* Active state */
#define RT_AHCI_PORT_TFD 0x20 /* Task file data */
#define RT_AHCI_PORT_TFDATA_ERR RT_BIT(0) /* Indicates an error during the transfer */
#define RT_AHCI_PORT_TFDATA_DRQ RT_BIT(3) /* Indicates a data transfer is requested */
#define RT_AHCI_PORT_TFDATA_BSY RT_BIT(7) /* Indicates the interface is busy */
#define RT_AHCI_PORT_SIG 0x24 /* Signature */
#define RT_AHCI_PORT_SIG_REG_MASK 0xff
#define RT_AHCI_PORT_SIG_SECTOR_NR_SHIFT 0 /* Sector Count Register */
#define RT_AHCI_PORT_SIG_LBA_LOW_SHIFT 8 /* LBA Low Register */
#define RT_AHCI_PORT_SIG_LBA_MID_SHIFT 16 /* LBA Mid Register */
#define RT_AHCI_PORT_SIG_LBA_HIGH_SHIFT 24 /* LBA High Register */
#define RT_AHCI_PORT_SIG_SATA_CDROM 0xeb140101
#define RT_AHCI_PORT_SIG_SATA_DISK 0x00000101
#define RT_AHCI_PORT_SSTS 0x28 /* SATA status (SCR0:SStatus) */
#define RT_AHCI_PORT_SSTS_DET_MASK 0x3
#define RT_AHCI_PORT_SSTS_DET_COMINIT 0x1
#define RT_AHCI_PORT_SSTS_DET_PHYRDY 0x3
#define RT_AHCI_PORT_SCTL 0x2c /* SATA control (SCR2:SControl) */
#define RT_AHCI_PORT_SERR 0x30 /* SATA error (SCR1:SError) */
#define RT_AHCI_PORT_SERR_ERR_I RT_BIT(0) /* Recovered Data Integrity Error */
#define RT_AHCI_PORT_SERR_ERR_M RT_BIT(1) /* Recovered Communications Error */
#define RT_AHCI_PORT_SERR_ERR_T RT_BIT(8) /* Transient Data Integrity Error */
#define RT_AHCI_PORT_SERR_ERR_C RT_BIT(9) /* Persistent Communication or Data Integrity Error */
#define RT_AHCI_PORT_SERR_ERR_P RT_BIT(10) /* Protocol Error */
#define RT_AHCI_PORT_SERR_ERR_E RT_BIT(11) /* Internal Error */
#define RT_AHCI_PORT_SERR_DIAG_N RT_BIT(16) /* PhyRdy Change */
#define RT_AHCI_PORT_SERR_DIAG_I RT_BIT(17) /* Phy Internal Error */
#define RT_AHCI_PORT_SERR_DIAG_W RT_BIT(18) /* Comm Wake */
#define RT_AHCI_PORT_SERR_DIAG_B RT_BIT(19) /* 10B to 8B Decode Error */
#define RT_AHCI_PORT_SERR_DIAG_D RT_BIT(20) /* Disparity Error */
#define RT_AHCI_PORT_SERR_DIAG_C RT_BIT(21) /* CRC Error */
#define RT_AHCI_PORT_SERR_DIAG_H RT_BIT(22) /* Handshake Error */
#define RT_AHCI_PORT_SERR_DIAG_S RT_BIT(23) /* Link Sequence Error */
#define RT_AHCI_PORT_SERR_DIAG_T RT_BIT(24) /* Transport state transition error */
#define RT_AHCI_PORT_SERR_DIAG_F RT_BIT(25) /* Unknown FIS Type */
#define RT_AHCI_PORT_SERR_DIAG_X RT_BIT(26) /* Exchanged */
#define RT_AHCI_PORT_SACT 0x34 /* SATA active (SCR3:SActive) */
#define RT_AHCI_PORT_CI 0x38 /* Command issue */
#define RT_AHCI_PORT_SNTF 0x3c /* SATA notification (SCR4:SNotification) */
#define RT_AHCI_PORT_FBS 0x40 /* FIS-based switch control */
#define RT_AHCI_PORT_VENDOR 0x70 /* Vendor specific (0x70 - 0x7f) */
#define RT_AHCI_MAX_SG 56
#define RT_AHCI_CMD_SLOT_SIZE 32
#define RT_AHCI_MAX_CMD_SLOT 32
#define RT_AHCI_RX_FIS_SIZE 256
#define RT_AHCI_CMD_TBL_HDR 0x80
#define RT_AHCI_CMD_TBL_CDB 0x40
#define RT_AHCI_CMD_TBL_SIZE RT_AHCI_CMD_TBL_HDR + (RT_AHCI_MAX_SG * 16)
#define RT_AHCI_DMA_SIZE (RT_AHCI_CMD_SLOT_SIZE * RT_AHCI_MAX_CMD_SLOT + RT_AHCI_CMD_TBL_SIZE + RT_AHCI_RX_FIS_SIZE)
#define RT_ACHI_PRDT_BYTES_MAX (4 * 1024 * 1024)
#define RT_AHCI_FIS_TYPE_REG_H2D 0x27 /* Register FIS - host to device */
#define RT_AHCI_FIS_TYPE_REG_D2H 0x34 /* Register FIS - device to host */
#define RT_AHCI_FIS_TYPE_DMA_ACT 0x39 /* DMA activate FIS - device to host */
#define RT_AHCI_FIS_TYPE_DMA_SETUP 0x41 /* DMA setup FIS - bidirectional */
#define RT_AHCI_FIS_TYPE_DATA 0x46 /* Data FIS - bidirectional */
#define RT_AHCI_FIS_TYPE_BIST 0x58 /* BIST activate FIS - bidirectional */
#define RT_AHCI_FIS_TYPE_PIO_SETUP 0x5f /* PIO setup FIS - device to host */
#define RT_AHCI_FIS_TYPE_DEV_BITS 0xa1 /* Set device bits FIS - device to host */
#define RT_AHCI_ATA_ID_WORDS 256
#define RT_AHCI_ATA_ID_CONFIG 0
#define RT_AHCI_ATA_ID_CYLS 1
#define RT_AHCI_ATA_ID_HEADS 3
#define RT_AHCI_ATA_ID_SECTORS 6
#define RT_AHCI_ATA_ID_SERNO 10
#define RT_AHCI_ATA_ID_BUF_SIZE 21
#define RT_AHCI_ATA_ID_FW_REV 23
#define RT_AHCI_ATA_ID_PROD 27
#define RT_AHCI_ATA_ID_MAX_MULTSECT 47
#define RT_AHCI_ATA_ID_DWORD_IO 48
#define RT_AHCI_ATA_ID_TRUSTED 48
#define RT_AHCI_ATA_ID_CAPABILITY 49
#define RT_AHCI_ATA_ID_OLD_PIO_MODES 51
#define RT_AHCI_ATA_ID_OLD_DMA_MODES 52
#define RT_AHCI_ATA_ID_FIELD_VALID 53
#define RT_AHCI_ATA_ID_CUR_CYLS 54
#define RT_AHCI_ATA_ID_CUR_HEADS 55
#define RT_AHCI_ATA_ID_CUR_SECTORS 56
#define RT_AHCI_ATA_ID_MULTSECT 59
#define RT_AHCI_ATA_ID_LBA_CAPACITY 60
#define RT_AHCI_ATA_ID_SWDMA_MODES 62
#define RT_AHCI_ATA_ID_MWDMA_MODES 63
#define RT_AHCI_ATA_ID_PIO_MODES 64
#define RT_AHCI_ATA_ID_EIDE_DMA_MIN 65
#define RT_AHCI_ATA_ID_EIDE_DMA_TIME 66
#define RT_AHCI_ATA_ID_EIDE_PIO 67
#define RT_AHCI_ATA_ID_EIDE_PIO_IORDY 68
#define RT_AHCI_ATA_ID_ADDITIONAL_SUPP 69
#define RT_AHCI_ATA_ID_QUEUE_DEPTH 75
#define RT_AHCI_ATA_ID_SATA_CAPABILITY 76
#define RT_AHCI_ATA_ID_SATA_CAPABILITY_2 77
#define RT_AHCI_ATA_ID_FEATURE_SUPP 78
#define RT_AHCI_ATA_ID_MAJOR_VER 80
#define RT_AHCI_ATA_ID_COMMAND_SET_1 82
#define RT_AHCI_ATA_ID_COMMAND_SET_2 83
#define RT_AHCI_ATA_ID_CFSSE 84
#define RT_AHCI_ATA_ID_CFS_ENABLE_1 85
#define RT_AHCI_ATA_ID_CFS_ENABLE_2 86
#define RT_AHCI_ATA_ID_CSF_DEFAULT 87
#define RT_AHCI_ATA_ID_UDMA_MODES 88
#define RT_AHCI_ATA_ID_HW_CONFIG 93
#define RT_AHCI_ATA_ID_SPG 98
#define RT_AHCI_ATA_ID_LBA_CAPACITY_2 100
#define RT_AHCI_ATA_ID_SECTOR_SIZE 106
#define RT_AHCI_ATA_ID_WWN 108
#define RT_AHCI_ATA_ID_LOGICAL_SECTOR_SIZE 117
#define RT_AHCI_ATA_ID_COMMAND_SET_3 119
#define RT_AHCI_ATA_ID_COMMAND_SET_4 120
#define RT_AHCI_ATA_ID_LAST_LUN 126
#define RT_AHCI_ATA_ID_DLF 128
#define RT_AHCI_ATA_ID_CSFO 129
#define RT_AHCI_ATA_ID_CFA_POWER 160
#define RT_AHCI_ATA_ID_CFA_KEY_MGMT 162
#define RT_AHCI_ATA_ID_CFA_MODES 163
#define RT_AHCI_ATA_ID_DATA_SET_MGMT 169
#define RT_AHCI_ATA_ID_SCT_CMD_XPORT 206
#define RT_AHCI_ATA_ID_ROT_SPEED 217
#define RT_AHCI_ATA_ID_PIO4 (1 << 1)
#define RT_AHCI_ATA_ID_SERNO_LEN 20
#define RT_AHCI_ATA_ID_FW_REV_LEN 8
#define RT_AHCI_ATA_ID_PROD_LEN 40
#define RT_AHCI_ATA_ID_WWN_LEN 8
#define RT_AHCI_ATA_CMD_DSM 0x06
#define RT_AHCI_ATA_CMD_DEV_RESET 0x08 /* ATAPI device reset */
#define RT_AHCI_ATA_CMD_PIO_READ 0x20 /* Read sectors with retry */
#define RT_AHCI_ATA_CMD_PIO_READ_EXT 0x24
#define RT_AHCI_ATA_CMD_READ_EXT 0x25
#define RT_AHCI_ATA_CMD_READ_NATIVE_MAX_EXT 0x27
#define RT_AHCI_ATA_CMD_READ_MULTI_EXT 0x29
#define RT_AHCI_ATA_CMD_READ_LOG_EXT 0x2f
#define RT_AHCI_ATA_CMD_PIO_WRITE 0x30 /* Write sectors with retry */
#define RT_AHCI_ATA_CMD_PIO_WRITE_EXT 0x34
#define RT_AHCI_ATA_CMD_WRITE_EXT 0x35
#define RT_AHCI_ATA_CMD_SET_MAX_EXT 0x37
#define RT_AHCI_ATA_CMD_WRITE_MULTI_EXT 0x39
#define RT_AHCI_ATA_CMD_WRITE_FUA_EXT 0x3d
#define RT_AHCI_ATA_CMD_VERIFY 0x40 /* Read verify sectors with retry */
#define RT_AHCI_ATA_CMD_VERIFY_EXT 0x42
#define RT_AHCI_ATA_CMD_FPDMA_READ 0x60
#define RT_AHCI_ATA_CMD_FPDMA_WRITE 0x61
#define RT_AHCI_ATA_CMD_EDD 0x90 /* Execute device diagnostic */
#define RT_AHCI_ATA_CMD_INIT_DEV_PARAMS 0x91 /* Initialize device parameters */
#define RT_AHCI_ATA_CMD_PACKET 0xa0 /* ATAPI packet */
#define RT_AHCI_ATA_CMD_ID_ATAPI 0xa1 /* ATAPI identify device */
#define RT_AHCI_ATA_CMD_CONF_OVERLAY 0xb1
#define RT_AHCI_ATA_CMD_READ_MULTI 0xc4 /* Read multiple */
#define RT_AHCI_ATA_CMD_WRITE_MULTI 0xc5 /* Write multiple */
#define RT_AHCI_ATA_CMD_SET_MULTI 0xc6 /* Set multiple mode */
#define RT_AHCI_ATA_CMD_READ 0xc8 /* Read DMA with retry */
#define RT_AHCI_ATA_CMD_WRITE 0xca /* Write DMA with retry */
#define RT_AHCI_ATA_CMD_WRITE_MULTI_FUA_EXT 0xce
#define RT_AHCI_ATA_CMD_STANDBYNOW1 0xe0 /* Standby immediate */
#define RT_AHCI_ATA_CMD_IDLEIMMEDIATE 0xe1 /* Idle immediate */
#define RT_AHCI_ATA_CMD_STANDBY 0xe2 /* Place in standby power mode */
#define RT_AHCI_ATA_CMD_IDLE 0xe3 /* Place in idle power mode */
#define RT_AHCI_ATA_CMD_PMP_READ 0xe4 /* Read buffer */
#define RT_AHCI_ATA_CMD_CHK_POWER 0xe5 /* Check power mode */
#define RT_AHCI_ATA_CMD_SLEEP 0xe6 /* Sleep */
#define RT_AHCI_ATA_CMD_FLUSH 0xe7
#define RT_AHCI_ATA_CMD_PMP_WRITE 0xe8 /* Write buffer */
#define RT_AHCI_ATA_CMD_FLUSH_EXT 0xea
#define RT_AHCI_ATA_CMD_ID_ATA 0xec /* Identify device */
#define RT_AHCI_ATA_CMD_SET_FEATURES 0xef /* Set features */
#define RT_AHCI_ATA_CMD_SEC_FREEZE_LOCK 0xf5 /* Security freeze */
#define RT_AHCI_ATA_CMD_READ_NATIVE_MAX 0xf8
#define RT_AHCI_ATA_CMD_SET_MAX 0xf9
#define RT_AHCI_ATA_DSM_TRIM 0x01
#define RT_AHCI_ATA_PROT_FLAG_PIO RT_BIT(0)
#define RT_AHCI_ATA_PROT_FLAG_DMA RT_BIT(1)
#define RT_AHCI_ATA_PROT_FLAG_NCQ RT_BIT(2)
#define RT_AHCI_ATA_PROT_FLAG_ATAPI RT_BIT(3)
#define rt_ahci_ata_id_is_ata(id) (((id)[0] & (1 << 15)) == 0)
#define rt_ahci_ata_id_has_lba(id) ((id)[49] & (1 << 9))
#define rt_ahci_ata_id_has_dma(id) ((id)[49] & (1 << 8))
#define rt_ahci_ata_id_has_ncq(id) ((id)[76] & (1 << 8))
#define rt_ahci_ata_id_queue_depth(id) (((id)[75] & 0x1f) + 1)
#define rt_ahci_ata_id_removeable(id) ((id)[0] & (1 << 7))
#define rt_ahci_ata_id_iordy_disable(id) ((id)[49] & (1 << 10))
#define rt_ahci_ata_id_has_iordy(id) ((id)[49] & (1 << 11))
#define rt_ahci_ata_id_u32(id, n) (((rt_uint32_t)(id)[(n) + 1] << 16) | ((rt_uint32_t) (id)[(n)]))
#define rt_ahci_ata_id_u64(id, n) (((rt_uint64_t)(id)[(n) + 3] << 48) | ((rt_uint64_t)(id)[(n) + 2] << 32) | \
((rt_uint64_t)(id)[(n) + 1] << 16) | ((rt_uint64_t)(id)[(n) + 0]) )
rt_inline rt_bool_t rt_ahci_ata_id_has_lba48(const rt_uint16_t *id)
{
if ((id[RT_AHCI_ATA_ID_COMMAND_SET_2] & 0xc000) != 0x4000 ||
!rt_ahci_ata_id_u64(id, RT_AHCI_ATA_ID_LBA_CAPACITY_2))
{
return 0;
}
return !!(id[RT_AHCI_ATA_ID_COMMAND_SET_2] & (1 << 10));
}
rt_inline rt_uint64_t rt_ahci_ata_id_n_sectors(rt_uint16_t *id)
{
if (rt_ahci_ata_id_has_lba(id))
{
if (rt_ahci_ata_id_has_lba48(id))
{
return rt_ahci_ata_id_u64(id, RT_AHCI_ATA_ID_LBA_CAPACITY_2);
}
return rt_ahci_ata_id_u32(id, RT_AHCI_ATA_ID_LBA_CAPACITY);
}
return 0;
}
rt_inline rt_bool_t rt_ahci_ata_id_wcache_enabled(const rt_uint16_t *id)
{
if ((id[RT_AHCI_ATA_ID_CSF_DEFAULT] & 0xc000) != 0x4000)
{
return RT_FALSE;
}
return id[RT_AHCI_ATA_ID_CFS_ENABLE_1] & (1 << 5);
}
rt_inline rt_bool_t rt_ahci_ata_id_has_flush(const rt_uint16_t *id)
{
if ((id[RT_AHCI_ATA_ID_COMMAND_SET_2] & 0xc000) != 0x4000)
{
return RT_FALSE;
}
return id[RT_AHCI_ATA_ID_COMMAND_SET_2] & (1 << 12);
}
rt_inline rt_bool_t rt_ahci_ata_id_has_flush_ext(const rt_uint16_t *id)
{
if ((id[RT_AHCI_ATA_ID_COMMAND_SET_2] & 0xc000) != 0x4000)
{
return RT_FALSE;
}
return id[RT_AHCI_ATA_ID_COMMAND_SET_2] & (1 << 13);
}
struct rt_ahci_cmd_hdr
{
rt_uint32_t opts;
rt_uint32_t status;
rt_uint32_t tbl_addr_lo;
rt_uint32_t tbl_addr_hi;
rt_uint32_t reserved[4];
};
struct rt_ahci_sg
{
rt_uint32_t addr_lo;
rt_uint32_t addr_hi;
rt_uint32_t reserved;
rt_uint32_t flags_size;
};
struct rt_ahci_port
{
void *regs;
void *dma;
rt_ubase_t dma_handle;
struct rt_ahci_cmd_hdr *cmd_slot;
struct rt_ahci_sg *cmd_tbl_sg;
void *cmd_tbl;
rt_ubase_t cmd_tbl_dma;
void *rx_fis;
rt_uint32_t int_enabled;
rt_size_t block_size;
rt_uint16_t *ataid;
rt_bool_t link;
struct rt_completion done;
};
struct rt_ahci_host
{
struct rt_scsi_host parent;
int irq;
void *regs;
rt_size_t ports_nr;
rt_uint32_t ports_map;
struct rt_ahci_port ports[32];
rt_uint32_t cap;
rt_uint32_t max_blocks;
const struct rt_ahci_ops *ops;
};
struct rt_ahci_ops
{
rt_err_t (*host_init)(struct rt_ahci_host *host);
rt_err_t (*port_init)(struct rt_ahci_host *host, struct rt_ahci_port *port);
rt_err_t (*port_link_up)(struct rt_ahci_host *host, struct rt_ahci_port *port);
rt_err_t (*port_dma_init)(struct rt_ahci_host *host, struct rt_ahci_port *port);
rt_err_t (*port_isr)(struct rt_ahci_host *host, struct rt_ahci_port *port, rt_uint32_t isr);
};
rt_err_t rt_ahci_host_register(struct rt_ahci_host *host);
rt_err_t rt_ahci_host_unregister(struct rt_ahci_host *host);
#endif /* __AHCI_H__ */
@@ -0,0 +1,135 @@
/*
* Copyright (c) 2006-2025 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI first version
* 2025-01-24 wumingzi add doxygen comment
*/
#ifndef __BLK_H__
#define __BLK_H__
#include <rthw.h>
#include <rtthread.h>
#include <drivers/classes/block.h>
/**
* @defgroup group_blk blk
* @brief blk driver api
* @ingroup group_device_driver
* @addtogroup group_blk
* @{
*/
struct rt_dm_ida;
struct rt_blk_device;
struct rt_blk_disk_ops;
/**
* @brief Physical blk device
*/
struct rt_blk_disk
{
struct rt_device parent;
const struct rt_blk_disk_ops *ops;
#ifdef RT_USING_DM
struct rt_dm_ida *ida;
#endif
rt_uint32_t read_only:1;
rt_uint32_t parallel_io:1;
rt_uint32_t removable:1;
#define RT_BLK_DISK_MAGIC 0xbdaabdaa
rt_uint32_t __magic;
rt_uint32_t partitions;
#define RT_BLK_PARTITION_NONE (-1)
#define RT_BLK_PARTITION_MAX (RT_UINT32_MAX >> 1)
rt_int32_t max_partitions;
rt_list_t part_nodes;
struct rt_spinlock lock;
struct rt_semaphore usr_lock;
};
/**
* @brief Configure the blk device.
*/
struct rt_blk_disk_ops
{
rt_ssize_t (*read)(struct rt_blk_disk *disk, rt_off_t sector, void *buffer,
rt_size_t sector_count);
rt_ssize_t (*write)(struct rt_blk_disk *disk, rt_off_t sector, const void *buffer,
rt_size_t sector_count);
rt_err_t (*getgeome)(struct rt_blk_disk *disk, struct rt_device_blk_geometry *geometry);
rt_err_t (*sync)(struct rt_blk_disk *disk);
rt_err_t (*erase)(struct rt_blk_disk *disk);
rt_err_t (*autorefresh)(struct rt_blk_disk *disk, rt_bool_t is_auto);
rt_err_t (*control)(struct rt_blk_disk *disk, struct rt_blk_device *blk, int cmd, void *args);
};
#ifndef __DFS_H__
#include <dfs_fs.h>
/**
* @brief Logical blk device, if you don't used DFS it will be defined by default.
*/
struct rt_blk_device
{
struct rt_device parent;
int partno;
struct dfs_partition partition;
rt_list_t list;
struct rt_blk_disk *disk;
rt_size_t sector_start;
rt_size_t sector_count;
};
#else
struct rt_blk_device;
#endif /* __DFS_H__ */
/**
* @brief Register the blk disk device
* @param disk Point to blk disk
* @return rt_err_t error code
*/
rt_err_t rt_hw_blk_disk_register(struct rt_blk_disk *disk);
/**
* @brief Unregister the blk disk device
* @param disk Point to blk disk
* @return rt_err_t error code
*/
rt_err_t rt_hw_blk_disk_unregister(struct rt_blk_disk *disk);
/**
* @brief Probe and register the blk disk partition
* @param disk Point to blk disk
* @return rt_err_t error code
*/
rt_err_t rt_blk_disk_probe_partition(struct rt_blk_disk *disk);
/**
* @brief Get the blk disk capacity
* @param disk Point to blk disk
* @return rt_ssize_t sector count or error code
*/
rt_ssize_t rt_blk_disk_get_capacity(struct rt_blk_disk *disk);
/**
* @brief Get the sector size
* @param disk Point to blk disk
* @return rt_ssize_t bytes per sector or error code
*/
rt_ssize_t rt_blk_disk_get_logical_block_size(struct rt_blk_disk *disk);
/*! @}*/
#endif /* __BLK_H__ */
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#ifndef __BYTEORDER__
#define __BYTEORDER__
#ifdef __CHECKER__
#define __bitwise __attribute__((bitwise))
#else
#define __bitwise
#endif
typedef rt_uint16_t __bitwise rt_le16_t;
typedef rt_uint32_t __bitwise rt_le32_t;
typedef rt_uint64_t __bitwise rt_le64_t;
typedef rt_uint16_t __bitwise rt_be16_t;
typedef rt_uint32_t __bitwise rt_be32_t;
typedef rt_uint64_t __bitwise rt_be64_t;
/* gcc defines __BIG_ENDIAN__ on big endian targets */
#if defined(__BIG_ENDIAN__) || defined(ARCH_CPU_BIG_ENDIAN)
#define rt_cpu_to_be16(x) (x)
#define rt_cpu_to_be32(x) (x)
#define rt_cpu_to_be64(x) (x)
#define rt_be16_to_cpu(x) (x)
#define rt_be32_to_cpu(x) (x)
#define rt_be64_to_cpu(x) (x)
#define rt_le16_to_cpu(x) __builtin_bswap16(x)
#define rt_le32_to_cpu(x) __builtin_bswap32(x)
#define rt_le64_to_cpu(x) __builtin_bswap64(x)
#define rt_cpu_to_le16(x) __builtin_bswap16(x)
#define rt_cpu_to_le32(x) __builtin_bswap32(x)
#define rt_cpu_to_le64(x) __builtin_bswap64(x)
#else
#define rt_cpu_to_be16(x) __builtin_bswap16(x)
#define rt_cpu_to_be32(x) __builtin_bswap32(x)
#define rt_cpu_to_be64(x) __builtin_bswap64(x)
#define rt_be16_to_cpu(x) __builtin_bswap16(x)
#define rt_be32_to_cpu(x) __builtin_bswap32(x)
#define rt_be64_to_cpu(x) __builtin_bswap64(x)
#define rt_le16_to_cpu(x) (x)
#define rt_le32_to_cpu(x) (x)
#define rt_le64_to_cpu(x) (x)
#define rt_cpu_to_le16(x) (x)
#define rt_cpu_to_le32(x) (x)
#define rt_cpu_to_le64(x) (x)
#endif /* __BIG_ENDIAN__ || ARCH_CPU_BIG_ENDIAN */
#undef __bitwise
#endif /* __BYTEORDER__ */
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-10-11 zmshahaha move from <rtdef.h>
*/
#ifndef __BLOCK_H__
#define __BLOCK_H__
#include <rtdef.h>
/* block device commands*/
#define RT_DEVICE_CTRL_BLK_GETGEOME (RT_DEVICE_CTRL_BASE(Block) + 1) /**< get geometry information */
#define RT_DEVICE_CTRL_BLK_SYNC (RT_DEVICE_CTRL_BASE(Block) + 2) /**< flush data to block device */
#define RT_DEVICE_CTRL_BLK_ERASE (RT_DEVICE_CTRL_BASE(Block) + 3) /**< erase block on block device */
#define RT_DEVICE_CTRL_BLK_AUTOREFRESH (RT_DEVICE_CTRL_BASE(Block) + 4) /**< block device : enter/exit auto refresh mode */
#define RT_DEVICE_CTRL_BLK_PARTITION (RT_DEVICE_CTRL_BASE(Block) + 5) /**< get block device partition */
/**
* block device geometry structure
*/
struct rt_device_blk_geometry
{
rt_uint64_t sector_count; /**< count of sectors */
rt_uint32_t bytes_per_sector; /**< number of bytes per sector */
rt_uint32_t block_size; /**< number of bytes to erase one block */
};
/**
* sector arrange struct on block device
*/
struct rt_device_blk_sectors
{
rt_uint64_t sector_begin; /**< begin sector */
rt_uint64_t sector_end; /**< end sector */
};
#endif /* __BLOCK_H__ */
@@ -0,0 +1,19 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-10-11 zmshahaha move from <rtdef.h>
*/
#ifndef __CHAR_H__
#define __CHAR_H__
#include <rtdef.h>
/* char device commands*/
#define RT_DEVICE_CTRL_CHAR_STREAM (RT_DEVICE_CTRL_BASE(Char) + 1) /**< stream mode on char device */
#endif /* __CHAR_H__ */
@@ -0,0 +1,104 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-10-11 zmshahaha move from <rtdef.h>
*/
#ifndef __GRAPHIC_H__
#define __GRAPHIC_H__
#include <rtdef.h>
/**
* cursor control command
*/
#define RT_DEVICE_CTRL_CURSOR_SET_POSITION 0x10
#define RT_DEVICE_CTRL_CURSOR_SET_TYPE 0x11
/**
* graphic device control command
*/
#define RTGRAPHIC_CTRL_RECT_UPDATE (RT_DEVICE_CTRL_BASE(Graphic) + 0)
#define RTGRAPHIC_CTRL_POWERON (RT_DEVICE_CTRL_BASE(Graphic) + 1)
#define RTGRAPHIC_CTRL_POWEROFF (RT_DEVICE_CTRL_BASE(Graphic) + 2)
#define RTGRAPHIC_CTRL_GET_INFO (RT_DEVICE_CTRL_BASE(Graphic) + 3)
#define RTGRAPHIC_CTRL_SET_MODE (RT_DEVICE_CTRL_BASE(Graphic) + 4)
#define RTGRAPHIC_CTRL_GET_EXT (RT_DEVICE_CTRL_BASE(Graphic) + 5)
#define RTGRAPHIC_CTRL_SET_BRIGHTNESS (RT_DEVICE_CTRL_BASE(Graphic) + 6)
#define RTGRAPHIC_CTRL_GET_BRIGHTNESS (RT_DEVICE_CTRL_BASE(Graphic) + 7)
#define RTGRAPHIC_CTRL_GET_MODE (RT_DEVICE_CTRL_BASE(Graphic) + 8)
#define RTGRAPHIC_CTRL_GET_STATUS (RT_DEVICE_CTRL_BASE(Graphic) + 9)
#define RTGRAPHIC_CTRL_PAN_DISPLAY (RT_DEVICE_CTRL_BASE(Graphic) + 10)
#define RTGRAPHIC_CTRL_WAIT_VSYNC (RT_DEVICE_CTRL_BASE(Graphic) + 11)
/* graphic device */
enum
{
RTGRAPHIC_PIXEL_FORMAT_MONO = 0,
RTGRAPHIC_PIXEL_FORMAT_GRAY4,
RTGRAPHIC_PIXEL_FORMAT_GRAY16,
RTGRAPHIC_PIXEL_FORMAT_RGB332,
RTGRAPHIC_PIXEL_FORMAT_RGB444,
RTGRAPHIC_PIXEL_FORMAT_RGB565,
RTGRAPHIC_PIXEL_FORMAT_RGB565P,
RTGRAPHIC_PIXEL_FORMAT_BGR565 = RTGRAPHIC_PIXEL_FORMAT_RGB565P,
RTGRAPHIC_PIXEL_FORMAT_RGB666,
RTGRAPHIC_PIXEL_FORMAT_RGB888,
RTGRAPHIC_PIXEL_FORMAT_BGR888,
RTGRAPHIC_PIXEL_FORMAT_ARGB888,
RTGRAPHIC_PIXEL_FORMAT_ABGR888,
RTGRAPHIC_PIXEL_FORMAT_RESERVED,
};
/**
* build a pixel position according to (x, y) coordinates.
*/
#define RTGRAPHIC_PIXEL_POSITION(x, y) ((x << 16) | y)
/**
* graphic device information structure
*/
struct rt_device_graphic_info
{
rt_uint8_t pixel_format; /**< graphic format */
rt_uint8_t bits_per_pixel; /**< bits per pixel */
rt_uint16_t pitch; /**< bytes per line */
rt_uint16_t width; /**< width of graphic device */
rt_uint16_t height; /**< height of graphic device */
rt_uint8_t *framebuffer; /**< frame buffer */
rt_uint32_t smem_len; /**< allocated frame buffer size */
};
/**
* rectangle information structure
*/
struct rt_device_rect_info
{
rt_uint16_t x; /**< x coordinate */
rt_uint16_t y; /**< y coordinate */
rt_uint16_t width; /**< width */
rt_uint16_t height; /**< height */
};
/**
* graphic operations
*/
struct rt_device_graphic_ops
{
void (*set_pixel) (const char *pixel, int x, int y);
void (*get_pixel) (char *pixel, int x, int y);
void (*draw_hline)(const char *pixel, int x1, int x2, int y);
void (*draw_vline)(const char *pixel, int x, int y1, int y2);
void (*blit_line) (const char *pixel, int x, int y, rt_size_t size);
};
#define rt_graphix_ops(device) ((struct rt_device_graphic_ops *)(device->user_data))
#endif /* __GRAPHIC_H__ */
@@ -0,0 +1,19 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-10-11 zmshahaha move from <rtdef.h>
*/
#ifndef __MTD_H__
#define __MTD_H__
#include <rtdef.h>
/* mtd interface device*/
#define RT_DEVICE_CTRL_MTD_FORMAT (RT_DEVICE_CTRL_BASE(MTD) + 1) /**< format a MTD device */
#endif /* __MTD_H__ */
@@ -0,0 +1,19 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-10-11 zmshahaha move from <rtdef.h>
*/
#ifndef __NET_H__
#define __NET_H__
#include <rtdef.h>
/* net interface device*/
#define RT_DEVICE_CTRL_NETIF_GETMAC (RT_DEVICE_CTRL_BASE(NetIf) + 1) /**< get mac address */
#endif /* __NET_H__ */
@@ -0,0 +1,223 @@
/*
* Copyright (c) 2006-2025 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-11-26 GuEe-GUI first version
* 2025-01-24 wumingzi add doxygen comment
*/
#ifndef __CLK_H__
#define __CLK_H__
#include <rthw.h>
#include <ref.h>
#include <drivers/ofw.h>
/**
* @defgroup group_driver_clock Clock
* @brief Clock driver API
* @ingroup group_device_driver
*/
/**
* @addtogroup group_driver_clock
* @{
*/
#define RT_CLK_NODE_OBJ_NAME "CLKNP"
struct rt_clk_ops;
struct rt_reset_control_node;
/**
* @brief Clk node, it is a pat of clk source or controller
* @note Defined as the array like this if the CLK have multi out clocks:
* @code{.c}
* struct XYZ_single_clk
* {
* struct rt_clk_node parent;
* ...
* };
*
* struct XYZ_multi_clk
* {
* struct rt_clk_node parent[N];
* ...
* };
* @endcode
* We assume the 'N' is the max value of element in 'clock-indices' if OFW.
*/
struct rt_clk_node
{
struct rt_object rt_parent;
rt_list_t list;
rt_list_t children_nodes;
const char *name;
const struct rt_clk_ops *ops;
struct rt_clk_node *parent;
struct rt_ref ref;
rt_ubase_t rate;
rt_ubase_t min_rate;
rt_ubase_t max_rate;
rt_size_t notifier_count;
void *priv;
struct rt_clk *clk;
rt_size_t multi_clk;
};
/**
* @brief Constant rate clk
*/
struct rt_clk_fixed_rate
{
struct rt_clk_node clk;
rt_ubase_t fixed_rate;
rt_ubase_t fixed_accuracy;
};
/**
* @brief Clk object, it can be clk source or controller
*/
struct rt_clk
{
struct rt_clk_node *clk_np;
const char *dev_id;
const char *con_id;
rt_ubase_t rate;
int prepare_count;
int enable_count;
void *fw_node;
void *priv;
};
/**
* @brief Clk array
*/
struct rt_clk_array
{
rt_size_t count;
struct rt_clk *clks[];
};
struct rt_clk_ops
{
rt_err_t (*init)(struct rt_clk *, void *fw_data);
rt_err_t (*finit)(struct rt_clk *);
/* API */
rt_err_t (*prepare)(struct rt_clk *);
void (*unprepare)(struct rt_clk *);
rt_bool_t (*is_prepared)(struct rt_clk *);
rt_err_t (*enable)(struct rt_clk *);
void (*disable)(struct rt_clk *);
rt_bool_t (*is_enabled)(struct rt_clk *);
rt_err_t (*set_rate)(struct rt_clk *, rt_ubase_t rate, rt_ubase_t parent_rate);
rt_err_t (*set_parent)(struct rt_clk *, struct rt_clk *parent);
rt_err_t (*set_phase)(struct rt_clk *, int degrees);
rt_base_t (*get_phase)(struct rt_clk *);
rt_base_t (*round_rate)(struct rt_clk *, rt_ubase_t drate, rt_ubase_t *prate);
};
struct rt_clk_notifier;
#define RT_CLK_MSG_PRE_RATE_CHANGE RT_BIT(0)
#define RT_CLK_MSG_POST_RATE_CHANGE RT_BIT(1)
#define RT_CLK_MSG_ABORT_RATE_CHANGE RT_BIT(2)
typedef rt_err_t (*rt_clk_notifier_callback)(struct rt_clk_notifier *notifier,
rt_ubase_t msg, rt_ubase_t old_rate, rt_ubase_t new_rate);
/**
* @brief Clock notifier, it containers of clock list and callback function
*/
struct rt_clk_notifier
{
rt_list_t list;
struct rt_clk *clk;
rt_clk_notifier_callback callback;
void *priv;
};
rt_err_t rt_clk_register(struct rt_clk_node *clk_np, struct rt_clk_node *parent_np);
rt_err_t rt_clk_unregister(struct rt_clk_node *clk_np);
rt_err_t rt_clk_notifier_register(struct rt_clk *clk, struct rt_clk_notifier *notifier);
rt_err_t rt_clk_notifier_unregister(struct rt_clk *clk, struct rt_clk_notifier *notifier);
rt_err_t rt_clk_set_parent(struct rt_clk *clk, struct rt_clk *clk_parent);
rt_err_t rt_clk_prepare(struct rt_clk *clk);
rt_err_t rt_clk_unprepare(struct rt_clk *clk);
rt_err_t rt_clk_enable(struct rt_clk *clk);
void rt_clk_disable(struct rt_clk *clk);
rt_err_t rt_clk_prepare_enable(struct rt_clk *clk);
void rt_clk_disable_unprepare(struct rt_clk *clk);
rt_err_t rt_clk_array_prepare(struct rt_clk_array *clk_arr);
rt_err_t rt_clk_array_unprepare(struct rt_clk_array *clk_arr);
rt_err_t rt_clk_array_enable(struct rt_clk_array *clk_arr);
void rt_clk_array_disable(struct rt_clk_array *clk_arr);
rt_err_t rt_clk_array_prepare_enable(struct rt_clk_array *clk_arr);
void rt_clk_array_disable_unprepare(struct rt_clk_array *clk_arr);
rt_err_t rt_clk_set_rate_range(struct rt_clk *clk, rt_ubase_t min, rt_ubase_t max);
rt_err_t rt_clk_set_min_rate(struct rt_clk *clk, rt_ubase_t rate);
rt_err_t rt_clk_set_max_rate(struct rt_clk *clk, rt_ubase_t rate);
rt_err_t rt_clk_set_rate(struct rt_clk *clk, rt_ubase_t rate);
rt_ubase_t rt_clk_get_rate(struct rt_clk *clk);
rt_err_t rt_clk_set_phase(struct rt_clk *clk, int degrees);
rt_base_t rt_clk_get_phase(struct rt_clk *clk);
rt_base_t rt_clk_round_rate(struct rt_clk *clk, rt_ubase_t rate);
struct rt_clk *rt_clk_get_parent(struct rt_clk *clk);
struct rt_clk_array *rt_clk_get_array(struct rt_device *dev);
struct rt_clk *rt_clk_get_by_index(struct rt_device *dev, int index);
struct rt_clk *rt_clk_get_by_name(struct rt_device *dev, const char *name);
void rt_clk_array_put(struct rt_clk_array *clk_arr);
void rt_clk_put(struct rt_clk *clk);
#ifdef RT_USING_OFW
struct rt_clk_array *rt_ofw_get_clk_array(struct rt_ofw_node *np);
struct rt_clk *rt_ofw_get_clk(struct rt_ofw_node *np, int index);
struct rt_clk *rt_ofw_get_clk_by_name(struct rt_ofw_node *np, const char *name);
rt_ssize_t rt_ofw_count_of_clk(struct rt_ofw_node *clk_ofw_np);
#else
rt_inline struct rt_clk *rt_ofw_get_clk(struct rt_ofw_node *np, int index)
{
return RT_NULL;
}
rt_inline struct rt_clk *rt_ofw_get_clk_by_name(struct rt_ofw_node *np, const char *name)
{
return RT_NULL;
}
rt_inline rt_ssize_t rt_ofw_count_of_clk(struct rt_ofw_node *clk_ofw_np)
{
return 0;
}
#endif /* RT_USING_OFW */
/*! @}*/
#endif /* __CLK_H__ */

Some files were not shown because too many files have changed in this diff Show More