diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/.vscode/settings.json b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/.vscode/settings.json new file mode 100644 index 0000000..a644266 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "files.associations": { + "rpmsg.h": "c", + "types.h": "c", + "ioctl.h": "c", + "wait.h": "c" + } +} \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/.~lock.Create-Kernel-Driver.odt# b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/.~lock.Create-Kernel-Driver.odt# new file mode 100644 index 0000000..58373c8 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/.~lock.Create-Kernel-Driver.odt# @@ -0,0 +1 @@ +,markus,U2204VM,02.05.2024 10:15,file:///home/markus/.config/libreoffice/4; \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/Create-Kernel-Driver.odt b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/Create-Kernel-Driver.odt new file mode 100644 index 0000000..cee5ccf Binary files /dev/null and b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/Create-Kernel-Driver.odt differ diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/LockingWaitQueues.pdf b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/LockingWaitQueues.pdf new file mode 100644 index 0000000..6d16b4b Binary files /dev/null and b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/LockingWaitQueues.pdf differ diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/files/75-mon-datafile.rules b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/files/75-mon-datafile.rules new file mode 100644 index 0000000..75eed6e --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/files/75-mon-datafile.rules @@ -0,0 +1 @@ +SUBSYSTEM=="misc", KERNEL=="mon-datafile", GROUP="dialout", MODE="0666" diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/files/Makefile b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/files/Makefile new file mode 100644 index 0000000..60c3bf0 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/files/Makefile @@ -0,0 +1,26 @@ +# Makefile for VRPMDV Monitoring Datafile +obj-m := vrpmdv-mon-datafile.o + + +SRC := $(shell pwd) + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) + +modules_install: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install + +clean: + rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c + rm -f Module.markers Module.symvers modules.order + rm -rf .tmp_versions Modules.symvers + +# obj-m = vrpmdv-monitoring-controler.o + +# KVERSION = $(shell uname -r) + +# all: +# make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules + +# clean: +# make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/files/lock.csave b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/files/lock.csave new file mode 100644 index 0000000..0de60b3 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/files/lock.csave @@ -0,0 +1,116 @@ +include +#include +#include +#include +#include + +std::mutex m; +std::condition_variable cv; +std::string data; +bool ready = false; +bool processed = false; + +void worker_thread() +{ + // wait until main() sends data + std::unique_lock lk(m); + cv.wait(lk, []{ return ready; }); + + // after the wait, we own the lock + std::cout << "Worker thread is processing data\n"; + data += " after processing"; + + // send data back to main() + processed = true; + std::cout << "Worker thread signals data processing completed\n"; + + // manual unlocking is done before notifying, to avoid waking up + // the waiting thread only to block again (see notify_one for details) + lk.unlock(); + cv.notify_one(); +} + +int main() +{ + std::thread worker(worker_thread); + + data = "Example data"; + // send data to the worker thread + { + std::lock_guard lk(m); + ready = true; + std::cout << "main() signals data ready for processing\n"; + } + cv.notify_one(); + + // wait for the worker + { + std::unique_lock lk(m); + cv.wait(lk, []{ return processed; }); + } + std::cout << "Back in main(), data = " << data << '\n'; + + worker.join(); +} + +-------------------- + +// waiting for timeout after 5 seconds +std::chrono::seconds timeoutPeriod = 5; +auto timePoint = std::chrono::system_clock::now() + timeoutPeriod; +std::unique_lock uLock(myDataMutex); +while(!DataAreReadyForProcessing()) +{ + if (myCondVar.wait_until(uLock, timePoint) //<## + == std::cv_status::timeout) + { + // data conditions where not fulfilled within + // the time period; e.g. do some error handling + break; + } +} + +-- + +if (myCondVar.wait_for(uLock, timeoutPeriod, + DataAreReadyForProcessing)) +{ + // data conditions where fulfilled + // regular processing +} +else // timeout occured, conditions are not fulfilled +{ + // e.g. do some error handling +} + + + + + + +------------- + + +static int rpmsg_sample_probe(struct rpmsg_device *rpdev) +{ + + int ret; + struct instance_data *idata; + + dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n", + rpdev->src, rpdev->dst); + + idata = devm_kzalloc(&rpdev->dev, sizeof(*idata), GFP_KERNEL); + if (!idata) + return -ENOMEM; + + dev_set_drvdata(&rpdev->dev, idata); + + /* send a message to our remote processor */ + ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG)); + if (ret) { + dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); + return ret; + } + + return 0; diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/files/vrpmdv-mon-datafile.c b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/files/vrpmdv-mon-datafile.c new file mode 100644 index 0000000..bcd4235 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/files/vrpmdv-mon-datafile.c @@ -0,0 +1,720 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Jean-Philippe Romain + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RPMSG_SDB_DRIVER_VERSION "1.0" + +/* + * Static global variables + */ +static const char rpmsg_sdb_driver_name[] = "vrpmdv-mon-datafile"; + +static int LastBufferId; + +struct rpmsg_sdb_ioctl_set_efd { + int bufferId, eventfd; +}; + +struct rpmsg_sdb_ioctl_get_data_size { + int bufferId; + uint32_t size; +}; + +/* ioctl numbers */ +/* _IOW means userland is writing and kernel is reading */ +/* _IOR means userland is reading and kernel is writing */ +/* _IOWR means userland and kernel can both read and write */ +#define RPMSG_SDB_IOCTL_SET_EFD _IOW('R', 0x00, struct rpmsg_sdb_ioctl_set_efd *) +#define RPMSG_SDB_IOCTL_GET_DATA_SIZE _IOWR('R', 0x01, struct rpmsg_sdb_ioctl_get_data_size *) + +struct sdb_buf_t { + int index; /* index of buffer */ + size_t size; /* buffer size */ + size_t writing_size; /* size of data written by copro */ + dma_addr_t paddr; /* physical address*/ + void *vaddr; /* virtual address */ + void *uaddr; /* mapped address for userland */ + struct eventfd_ctx *efd_ctx; /* eventfd context */ + struct list_head buflist; /* reference in the buffers list */ +}; + +struct rpmsg_sdb_t { + struct mutex mutex; /* mutex to protect the ioctls */ + struct miscdevice mdev; /* misc device ref */ + struct rpmsg_device *rpdev; /* handle rpmsg device */ + struct list_head buffer_list; /* buffer instances list */ +}; + +struct device *rpmsg_sdb_dev; + +struct vRCMDeviceData { + uint32_t packageNo; //current package Number + uint32_t packageCount; //complete package Number + uint32_t dataQuantity; //number of uint32_t in data + uint32_t data[]; //the data +}; + +static int rpmsg_sdb_format_txbuf_string(struct sdb_buf_t *buffer, char *bufinfo_str, size_t bufinfo_str_size) +{ + pr_info("rpmsg_sdb(%s): Buffer index:%d, addr:%08x, size:%08x\n", + __func__, + buffer->index, + buffer->paddr, + buffer->size); + return snprintf(bufinfo_str, bufinfo_str_size, "B%dA%08xL%08x", buffer->index, buffer->paddr, buffer->size); +} + +static long rpmsg_sdb_decode_rxbuf_string(char *rxbuf_str, int *buffer_id, size_t *size) +{ + int ret = 0; + char *sub_str; + long bsize; + long bufid; + const char delimiter[2] = {'L','\0'}; + //__u32* data = (__u32*) rxbuf_str; + struct vRCMDeviceData* pdata = (struct vRCMDeviceData*) rxbuf_str; + + + //pr_info("rpmsg_sdb(%s): rxbuf_str:%s\n", __func__, rxbuf_str); + pr_info("rpmsg_sdb(%s): packageno:%d\n", __func__, pdata->packageNo); + pr_info("rpmsg_sdb(%s): packageCount:%d\n", __func__, pdata->packageCount); + pr_info("rpmsg_sdb(%s): dataquantity:%d\n", __func__, pdata->dataQuantity); + pr_info("rpmsg_sdb(%s): data:%d\n", __func__, (pdata->data)[0]); + // pr_info("rpmsg_sdb(%s): rxbuf_str:%d\n", __func__, data[0]); + // pr_info("rpmsg_sdb(%s): rxbuf_str:%d\n", __func__, data[1]); + // pr_info("rpmsg_sdb(%s): rxbuf_str:%d\n", __func__, data[2]); + + + + /* Get first part containing the buffer id */ +// sub_str = strsep(&rxbuf_str, delimiter); + +// // pr_info("rpmsg_sdb(%s): sub_str:%s\n", __func__, sub_str); + +// /* Save Buffer id and size: template BxLyyyyyyyy*/ +// ret = kstrtol(&sub_str[1], 10, &bufid); +// if (ret < 0) { +// // pr_info("rpmsg_sdb(ERROR): Extract of buffer id failed(%d)", ret); +// goto out; +// } + +// ret = kstrtol(&rxbuf_str[2], 16, &bsize); +// if (ret < 0) { +// // pr_info("rpmsg_sdb(ERROR): Extract of buffer size failed(%d)", ret); +// goto out; +// } + +// *size = (size_t)bsize; +// *buffer_id = (int)bufid; + +// out: + return ret; +} + +static int rpmsg_sdb_send_buf_info(struct rpmsg_sdb_t *rpmsg_sdb, struct sdb_buf_t *buffer) +{ + int count = 0, ret = 0; + const unsigned char *tbuf; + char mybuf[32]; + int msg_size; + struct rpmsg_device *_rpdev; + + pr_info("rpmsg_sdb(%s): start rpmsg_sdb_send_buf_info\n", __func__); + _rpdev = rpmsg_sdb->rpdev; + msg_size = rpmsg_get_mtu(_rpdev->ept); + + pr_info("rpmsg_sdb(%s): checked msg site:%d\n", __func__, msg_size); + if (msg_size < 0) + return msg_size; + + pr_info("rpmsg_sdb(%s): Call rpmsg_sdb_format_txbuf_string\n", __func__); + count = rpmsg_sdb_format_txbuf_string(buffer, mybuf, 32); + tbuf = &mybuf[0]; + + do { + /* send a message to our remote processor */ + pr_info("rpmsg_sdb(%s): send a message to our remote processor\n", __func__); + ret = rpmsg_send(_rpdev->ept, (void *)tbuf, + count > msg_size ? msg_size : count); + if (ret) { + dev_err(&_rpdev->dev, "rpmsg_send failed: %d\n", ret); + pr_info("rpmsg_sdb(%s): error by send a message to our remote processor\n", __func__); + return ret; + } + else { + pr_info("rpmsg_sdb(%s): send succesfully a message to our remote processor\n", __func__); + } + + if (count > msg_size) { + count -= msg_size; + tbuf += msg_size; + } else { + count = 0; + } + } while (count > 0); + + return count; +} + +static int rpmsg_sdb_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long vsize = vma->vm_end - vma->vm_start; + unsigned long size = PAGE_ALIGN(vsize); + unsigned long NumPages = size >> PAGE_SHIFT; + unsigned long align = get_order(size); + pgprot_t prot = pgprot_noncached(vma->vm_page_prot); + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *_buffer; + + pr_info("rpmsg_sdb(%s): start rpmsg_sdb_mmap\n", __func__); + if (align > CONFIG_CMA_ALIGNMENT) + align = CONFIG_CMA_ALIGNMENT; + + if (rpmsg_sdb_dev == NULL) + return -ENOMEM; + + pr_info("rpmsg_sdb(%s): start rpmsg_sdb_mmap - coherent_dma_mask\n", __func__); + rpmsg_sdb_dev->coherent_dma_mask = DMA_BIT_MASK(32); + rpmsg_sdb_dev->dma_mask = &rpmsg_sdb_dev->coherent_dma_mask; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Field the last buffer entry which is the last one created */ + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + pr_info("rpmsg_sdb(%s): start rpmsg_sdb_mmap - bufferlist not empty\n", __func__); + _buffer = list_last_entry(&_rpmsg_sdb->buffer_list, + struct sdb_buf_t, buflist); + + _buffer->uaddr = NULL; + _buffer->size = NumPages * PAGE_SIZE; + _buffer->writing_size = -1; + _buffer->vaddr = dma_alloc_coherent(rpmsg_sdb_dev, + _buffer->size, + &_buffer->paddr, + GFP_KERNEL); + + + if (!_buffer->vaddr) { + pr_info("rpmsg_sdb(ERROR): Memory allocation issue\n"); + return -ENOMEM; + } + + pr_info("rpmsg_sdb(%s): dma_alloc_coherent done - paddr[%d]:%x - vaddr[%d]:%p\n", + __func__, + _buffer->index, + _buffer->paddr, + _buffer->index, + _buffer->vaddr); + + /* Get address for userland */ + if (remap_pfn_range(vma, vma->vm_start, + (_buffer->paddr >> PAGE_SHIFT) + vma->vm_pgoff, + size, prot)) { + + pr_info("rpmsg_sdb(%s): remap_pfn_range could not be done\n"); + return -EAGAIN; + } + + + _buffer->uaddr = (void *)vma->vm_start; + pr_info("rpmsg_sdb(%s): _buffer->uaddr = %p\n", __func__, _buffer->uaddr); + + /* Send information to remote proc */ + pr_info("rpmsg_sdb(%s): Send information to remote proc\n", __func__); + rpmsg_sdb_send_buf_info(_rpmsg_sdb, _buffer); + } else { + dev_err(rpmsg_sdb_dev, "No existing buffer entry exist in the list !!!"); + pr_debug("rpmsg_sdb(%s): No existing buffer entry exist in the list !!!\n", __func__); + return -EINVAL; + } + + /* Increment for number of requested buffer */ + LastBufferId++; + + return 0; +} + +/** + * rpmsg_sdb_open - Open Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_open(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Initialize the buffer list*/ + pr_debug("rpmsg_sdb(%s): Init bufferlist", __func__); + pr_info("rpmsg_sdb(%s): start Init bufferlist", __func__); + INIT_LIST_HEAD(&_rpmsg_sdb->buffer_list); + pr_info("rpmsg_sdb(%s): success Init bufferlist", __func__); + + mutex_init(&_rpmsg_sdb->mutex); + + return 0; +} + +/** + * rpmsg_sdb_close - Close Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_close(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *pos, *next; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + mutex_lock(&_rpmsg_sdb->mutex); + list_for_each_entry_safe(pos, next, &_rpmsg_sdb->buffer_list, buflist) { + /* Free the CMA allocation */ + pr_info("rpmsg_sdb(%s): Free the CMA allocation 3", __func__); + dma_free_coherent(rpmsg_sdb_dev, pos->size, pos->vaddr, pos->paddr); + /* Remove the buffer from the list */ + list_del(&pos->buflist); + /* Free the buffer */ + kfree(pos); + } + mutex_unlock(&_rpmsg_sdb->mutex); + pr_info("rpmsg_sdb(%s): Free the CMA allocation 4 - done", __func__); + /* Reset LastBufferId */ + LastBufferId = 0; + + return 0; +} + +/** + * rpmsg_sdb_ioctl - IOCTL + * + * @session: ibmvmc_file_session struct + * @cmd: cmd field + * @arg: Argument field + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static long rpmsg_sdb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int idx = 0; + + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *buffer, *lastbuffer; + + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + struct rpmsg_sdb_ioctl_set_efd q_set_efd; + struct rpmsg_sdb_ioctl_get_data_size q_get_dat_size; + + void __user *argp = (void __user *)arg; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + switch (cmd) { + case RPMSG_SDB_IOCTL_SET_EFD: + mutex_lock(&_rpmsg_sdb->mutex); + + /* Get index from the last buffer in the list */ + pr_info("mon-datafile: set EFD\n"); + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + lastbuffer = list_last_entry(&_rpmsg_sdb->buffer_list, struct sdb_buf_t, buflist); + idx = lastbuffer->index; + + /* Check last index was properly initiated*/ + if (lastbuffer->vaddr == NULL) { + // pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - previous buffer was not allocated\n"); + pr_info("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - previous buffer was not allocated\n"); + mutex_unlock(&_rpmsg_sdb->mutex); + return -EBADE; + } + + /* increment this index for the next buffer creation*/ + idx++; + } + + if (copy_from_user(&q_set_efd, (struct rpmsg_sdb_ioctl_set_efd *)argp, + sizeof(struct rpmsg_sdb_ioctl_set_efd))) { + // pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - copy from user failed\n"); + pr_info("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - copy from user failed\n"); + + mutex_unlock(&_rpmsg_sdb->mutex); + return -EFAULT; + } + + /* create a new buffer which will be added in the buffer list */ + buffer = kmalloc(sizeof(struct sdb_buf_t), GFP_KERNEL); + + buffer->index = idx; + buffer->vaddr = NULL; + buffer->efd_ctx = eventfd_ctx_fdget(q_set_efd.eventfd); + list_add_tail(&buffer->buflist, &_rpmsg_sdb->buffer_list); + + pr_info("rpmsg_sdb(SUCCES): RPMSG_SDB_IOCTL_SET_EFD \n"); + + mutex_unlock(&_rpmsg_sdb->mutex); + break; + + case RPMSG_SDB_IOCTL_GET_DATA_SIZE: + pr_info("mon-datafile: get datasize\n"); + if (copy_from_user(&q_get_dat_size, (struct rpmsg_sdb_ioctl_get_data_size *)argp, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy from user failed\n"); + return -EFAULT; + } + + /* Get the index of the requested buffer and then look-up in the buffer list*/ + idx = q_get_dat_size.bufferId; + + list_for_each(pos, &_rpmsg_sdb->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == idx) { + /* Get the writing size*/ + q_get_dat_size.size = datastructureptr->writing_size; + break; + } + } + + if (copy_to_user((struct rpmsg_sdb_ioctl_get_data_size *)argp, &q_get_dat_size, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy to user failed\n"); + return -EFAULT; + } + + /* Reset the writing size*/ + datastructureptr->writing_size = -1; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct file_operations rpmsg_sdb_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = rpmsg_sdb_ioctl, + .mmap = rpmsg_sdb_mmap, + .open = rpmsg_sdb_open, + .release = rpmsg_sdb_close, +}; + +static int rpmsg_sdb_drv_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + int ret = 0; + int buffer_id = 0; + size_t buffer_size; + char *rpmsg_RxBuf; + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + //pr_info("mon-datafile: receive msg from Copro: %s, len:%d\n", __func__, len); + + // struct rpmsg_sdb_t { + // struct mutex mutex; /* mutex to protect the ioctls */ + // struct miscdevice mdev; /* misc device ref */ + // struct rpmsg_device *rpdev; /* handle rpmsg device */ + // struct list_head buffer_list; /* buffer instances list */ + // }; + + + rpmsg_RxBuf = (char *)kmalloc(len+1, GFP_KERNEL); + memcpy(rpmsg_RxBuf, data, len); + + rpmsg_RxBuf[len] = 0; + + // ret = rpmsg_sdb_decode_rxbuf_string(rpmsg_RxBuf, &buffer_id, &buffer_size); + + // if (ret < 0) + // goto out; + + // if (buffer_id > LastBufferId) { + // ret = -EINVAL; + // goto out; + // } + + struct vRCMDeviceData* pdata = (struct vRCMDeviceData*) rpmsg_RxBuf; + + + //pr_info("rpmsg_sdb(%s): rxbuf_str:%s\n", __func__, rxbuf_str); + // pr_info("rpmsg_sdb(%s): packageno:%d\n", __func__, pdata->packageNo); + // pr_info("rpmsg_sdb(%s): packageCount:%d\n", __func__, pdata->packageCount); + // pr_info("rpmsg_sdb(%s): dataquantity:%d\n", __func__, pdata->dataQuantity); + // pr_info("rpmsg_sdb(%s): data:%d\n", __func__, (pdata->data)[0]); + + + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpdev->dev); + + if (len == 0) { + dev_err(rpmsg_sdb_dev, "(%s) Empty lenght requested\n", __func__); + return -EINVAL; + } + + if (drv == NULL) { + dev_err(rpmsg_sdb_dev, "(%s) no driver found\n", __func__); + return -EINVAL; + } + + //copy data to structure + //1. get buffer + + // struct vRCMDeviceData* pdata = (struct vRCMDeviceData*) kmalloc(len+1, GFP_KERNEL); + // memcpy(pdata, data, len); + + // if (ret < 0) + // goto out; + + // if (buffer_id > LastBufferId) { + // ret = -EINVAL; + // goto out; + // } + + +/** + rpmsg_RxBuf = (char *)kmalloc(len+1, GFP_KERNEL); + memcpy(rpmsg_RxBuf, data, len); + + rpmsg_RxBuf[len] = 0; + + ret = rpmsg_sdb_decode_rxbuf_string(rpmsg_RxBuf, &buffer_id, &buffer_size); + kfree(rpmsg_RxBuf); + if (ret < 0) + goto out; + + if (buffer_id > LastBufferId) { + ret = -EINVAL; + goto out; + } +*/ + /* Signal to User space application */ + mutex_lock(&drv->mutex); + list_for_each(pos, &drv->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + + + /** + * struct vRCMDeviceData { + uint32_t packageNo; //current package Number + uint32_t packageCount; //complete package Number + uint32_t dataQuantity; //number of uint32_t in data + uint32_t data[]; //the data + }; + */ + //copy data to buffer + + + // struct sdb_buf_t { + // int index; /* index of buffer */ + // size_t size; /* buffer size */ + // size_t writing_size; /* size of data written by copro */ + // dma_addr_t paddr; /* physical address*/ + // void *vaddr; /* virtual address */ + // void *uaddr; /* mapped address for userland */ + // struct eventfd_ctx *efd_ctx; /* eventfd context */ + // struct list_head buflist; /* reference in the buffers list */ + // }; + + //get the buffer as array of struct vRCMDeviceData + struct vRCMDeviceData* vrBuff = (struct vRCMDeviceData*) (datastructureptr->vaddr); + struct vRCMDeviceData* startAdress = &(vrBuff[pdata->packageNo]); + // pr_info("rpmsg_sdb(%s): startAdress:%d\n", __func__, startAdress); + + if (virt_addr_valid(startAdress)) { + if (pdata->packageNo == 1) { + //reset writing size + datastructureptr->writing_size = 0; + } + // pr_info("rpmsg_sdb(%s): startAdress:%d is valid\n", __func__, startAdress); + datastructureptr->writing_size = datastructureptr->writing_size + len; + + if (datastructureptr->writing_size > datastructureptr->size) { + pr_info("(%s) Writing size is bigger than buffer size\n", __func__); + ret = -EINVAL; + goto out; + } + + memcpy(startAdress, rpmsg_RxBuf, len); + } + else { + pr_info("rpmsg_sdb(%s): startAdress[%d]:%p is invalid!!\n", __func__, startAdress); + } + + + // if (datastructureptr->index == buffer_id) { + // datastructureptr->writing_size = buffer_size; + + // if (datastructureptr->writing_size > datastructureptr->size) { + // dev_err(rpmsg_sdb_dev, "(%s) Writing size is bigger than buffer size\n", __func__); + // ret = -EINVAL; + // goto out; + // } + + if ((pdata != NULL) && (pdata->packageNo == pdata->packageCount)) { + pr_info("rpmsg_sdb(%s): signal bufferfull!\n", __func__); + eventfd_signal(datastructureptr->efd_ctx, 1); + break; + + } + + } + mutex_unlock(&drv->mutex); + + /* Signal to User space application */ +/** list_for_each(pos, &drv->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == buffer_id) { + datastructureptr->writing_size = buffer_size; + + if (datastructureptr->writing_size > datastructureptr->size) { + dev_err(rpmsg_sdb_dev, "(%s) Writing size is bigger than buffer size\n", __func__); + ret = -EINVAL; + goto out; + } + + eventfd_signal(datastructureptr->efd_ctx, 1); + break; + } + } +*/ + +out: + kfree(rpmsg_RxBuf); + //kfree(pdata); + return ret; +} + +static int rpmsg_sdb_drv_probe(struct rpmsg_device *rpdev) +{ + int ret = 0; + struct device *dev = &rpdev->dev; + struct rpmsg_sdb_t *rpmsg_sdb; + + pr_info("mon-datafile: registering started\n"); + + rpmsg_sdb = devm_kzalloc(dev, sizeof(*rpmsg_sdb), GFP_KERNEL); + if (!rpmsg_sdb) + return -ENOMEM; + + mutex_init(&rpmsg_sdb->mutex); + + rpmsg_sdb->rpdev = rpdev; + + rpmsg_sdb->mdev.name = "mon-datafile";//"rpmsg-sdb"; + rpmsg_sdb->mdev.minor = MISC_DYNAMIC_MINOR; + rpmsg_sdb->mdev.fops = &rpmsg_sdb_fops; + + pr_info("mon-datafile: Set Driver data\n"); + dev_set_drvdata(&rpdev->dev, rpmsg_sdb); + + /* Register misc device */ + ret = misc_register(&rpmsg_sdb->mdev); + + if (ret) { + dev_err(dev, "Failed to register device\n"); + pr_info("mon-datafile: Failed to register device\n"); + goto err_out; + } + + rpmsg_sdb_dev = rpmsg_sdb->mdev.this_device; + + //pr_info("rpmsg_sdb: Failed to register device\n"); + dev_info(dev, "%s probed\n", rpmsg_sdb_driver_name); + +err_out: + return ret; +} + +static void rpmsg_sdb_drv_remove(struct rpmsg_device *rpmsgdev) +{ + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpmsgdev->dev); + pr_info("rpmsg_sdb(%s): remove misc device %s !\n", __func__, drv->mdev.name); + misc_deregister(&drv->mdev); +} + +static struct rpmsg_device_id rpmsg_driver_sdb_id_table[] = { + { .name = "vrpmdv-mon-datafile" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sdb_id_table); + +//static struct rpmsg_driver rpmsg_sdb_rmpsg_drv = { +static struct rpmsg_driver vrpmdv_monitoring_data = { + .drv.name = KBUILD_MODNAME, + .drv.owner = THIS_MODULE, + .id_table = rpmsg_driver_sdb_id_table, + .probe = rpmsg_sdb_drv_probe, + .callback = rpmsg_sdb_drv_cb, + .remove = rpmsg_sdb_drv_remove, +}; + +module_rpmsg_driver(vrpmdv_monitoring_data); + + +// static int __init rpmsg_sdb_drv_init(void) +// { +// int ret = 0; + +// /* Register rpmsg device */ +// ret = register_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + +// if (ret) { +// pr_err("rpmsg_sdb(ERROR): Failed to register device\n"); +// return ret; +// } + +// pr_info("rpmsg_sdb: Init done\n"); + +// return ret; +// } + +// static void __exit rpmsg_sdb_drv_exit(void) +// { +// unregister_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); +// pr_info("rpmsg_sdb: Exit\n"); +// } + +// module_init(rpmsg_sdb_drv_init); +// module_exit(rpmsg_sdb_drv_exit); + + +MODULE_AUTHOR("Markus Lehr "); +MODULE_DESCRIPTION("shared data buffer over RPMSG"); +MODULE_VERSION(RPMSG_SDB_DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/kernel-module-vrpmdv-monitoring-driver.bb b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/kernel-module-vrpmdv-monitoring-driver.bb new file mode 100644 index 0000000..9bd4cda --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/kernel-module-vrpmdv-monitoring-driver.bb @@ -0,0 +1,16 @@ +SUMMARY = "VRPMDV Monitoring Driver" +LICENSE = "GPL-2.0-only" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "file://Makefile \ + file://vrpmdv-monitoring.c \ + " + +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. + +RPROVIDES:${PN} += "kernel-module-vrpmdv-monitoring" diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/netlink-save.c.save b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/netlink-save.c.save new file mode 100644 index 0000000..8a32ba8 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/netlink-save.c.save @@ -0,0 +1,541 @@ +/* Copyright 2021 Philipp Schuster + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in the + * Software without restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// ################################################################################################## +/* + * This is a Linux kernel module/driver called "gnl_foobar_xmpl" which shows you the basics of using + * Generic Netlink in the kernel. It registers a Netlink family called "gnl_foobar_xmpl". See + * "gnl_foobar_xmpl_prop.h" for common properties of the family. "Generic Netlink" offers us a lot of + * convenient functions to register new/custom Netlink families on the fly during runtime. We use this + * functionality here. It implements simple IPC between Userland and Kernel (Kernel responds to userland). + * + * Check "gnl_foobar_xmpl_prop.h" for common properties of the family first, afterwards follow the code here. + * + * You can find some more interesting documentation about Generic Netlink here: + * "Generic Netlink HOW-TO based on Jamal's original doc" https://lwn.net/Articles/208755/ + */ + +// basic definitions for kernel module development +#include +// definitions for generic netlink families, policies etc; +// transitive dependencies for basic netlink, sockets etc +#include +// required for locking inside the .dumpit callback demonstration +#include + +// data/vars/enums/properties that describes our protocol that we implement +// on top of generic netlink (like functions we want to trigger on the receiving side) +#include "gnl_foobar_xmpl_prop.h" + +// Module/Driver description. +// You can see this for example when executing `$ modinfo ./gnl_foobar_xmpl.ko` (after build). +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Philipp Schuster "); +MODULE_DESCRIPTION( + "Linux driver that registers the custom Netlink family " + "\"" FAMILY_NAME "\" via Generic Netlink and responds to echo messages " + "according to the properties specified in \"gnl_foobar_xmpl_prop.h\"." +); + + +/* ######################## CONVENIENT LOGGING MACROS ######################## */ +// (Re)definition of some convenient logging macros from . You can see the logging +// messages when printing the kernel log, e.g. with `$ sudo dmesg`. +// See https://elixir.bootlin.com/linux/latest/source/include/linux/printk.h + +// with this redefinition we can easily prefix all log messages from pr_* logging macros +#ifdef pr_fmt +#undef pr_fmt +#endif +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +/* ########################################################################### */ + +/** + * Data structure required for our .dumpit callback handler to + * know about the progress of an ongoing dump. + * See the dumpit callback handler how it is used. + */ +struct { + // from + /** + * Only one process is allowed per dump process. We need a lock for that. + */ + struct mutex mtx; + /** + * Number that describes how many packets we need to send until we are done + * during an ongoing dumpit process. 0 = done. + */ + int unsigned runs_to_go; + /** + * Number that describes how many packets per dump are sent in total. + * Constant per dump. + */ + int unsigned total_runs; +} dumpit_cb_progress_data; + +// Documentation is on the implementation of this function. +int gnl_cb_echo_doit(struct sk_buff *sender_skb, struct genl_info *info); + +// Documentation is on the implementation of this function. +int gnl_cb_echo_dumpit(struct sk_buff *pre_allocated_skb, struct netlink_callback *cb); + +// Documentation is on the implementation of this function. +int gnl_cb_echo_dumpit_before(struct netlink_callback *cb); + +// Documentation is on the implementation of this function. +int gnl_cb_echo_dumpit_before_after(struct netlink_callback *cb); + +// Documentation is on the implementation of this function. +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info); + +/** + * The length of `struct genl_ops gnl_foobar_xmpl_ops[]`. Not necessarily + * the number of commands in `enum GNlFoobarXmplCommand`. It depends on your application logic. + * For example, you can use the same command multiple times and - dependent by flag - + * invoke a different callback handler. In our simple example we just use one .doit callback + * per operation/command. + */ +#define GNL_FOOBAR_OPS_LEN (GNL_FOOBAR_XMPL_COMMAND_COUNT) + +/** + * Array with all operations that our protocol on top of Generic Netlink + * supports. An operation is the glue between a command ("cmd" field in `struct genlmsghdr` of + * received Generic Netlink message) and the corresponding ".doit" callback function. + * See: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L148 + */ +struct genl_ops gnl_foobar_xmpl_ops[GNL_FOOBAR_OPS_LEN] = { + { + /* The "cmd" field in `struct genlmsghdr` of received Generic Netlink message */ + .cmd = GNL_FOOBAR_XMPL_C_ECHO_MSG, + /* TODO Use case ? */ + .flags = 0, + /* TODO Use case ? */ + .internal_flags = 0, + /* Callback handler when a request with the specified ".cmd" above is received. + * Always validates the payload except one set NO_STRICT_VALIDATION flag in ".validate" + * See: https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + * + * Quote from: https://lwn.net/Articles/208755 + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + * + */ + .doit = gnl_cb_echo_doit, + /* This callback is similar in use to the standard Netlink 'dumpit' callback. + * The 'dumpit' callback is invoked when a Generic Netlink message is received + * with the NLM_F_DUMP flag set. + * + * A dump can be understand as a "GET ALL DATA OF THE GIVEN ENTITY", i.e. + * the userland can receive as long as the .dumpit callback returns data. + * + * .dumpit is not mandatory, but either it or .doit must be provided, see + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L367 + * + * To be honest I don't know in what use case one should use .dumpit and why + * it is useful, because you can achieve the same also with .doit handlers. + * Anyway, this is just an example/tutorial. + * + * Quote from: https://lwn.net/Articles/208755 + * "The main difference between a 'dumpit' handler and a 'doit' handler is + * that a 'dumpit' handler does not allocate a message buffer for a response; + * a pre-allocated sk_buff is passed to the 'dumpit' handler as the first + * parameter. The 'dumpit' handler should fill the message buffer with the + * appropriate response message and return the size of the sk_buff, + * i.e. sk_buff->len, and the message buffer will automatically be sent to the + * Generic Netlink client that initiated the request. As long as the 'dumpit' + * handler returns a value greater than zero it will be called again with a + * newly allocated message buffer to fill, when the handler has no more data + * to send it should return zero; error conditions are indicated by returning + * a negative value. If necessary, state can be preserved in the + * netlink_callback parameter which is passed to the 'dumpit' handler; the + * netlink_callback parameter values will be preserved across handler calls + * for a single request." + * + * You can see the check for the NLM_F_DUMP-flag here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L780 + */ + .dumpit = gnl_cb_echo_dumpit, + /* Start callback for dumps. Can be used to lock data structures. */ + .start = gnl_cb_echo_dumpit_before, + /* Completion callback for dumps. Can be used for cleanup after a dump and releasing locks. */ + .done = gnl_cb_echo_dumpit_before_after, + /* + 0 (= "validate strictly") or value `enum genl_validate_flags` + * see: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L108 + */ + .validate = 0, + }, + { + .cmd = GNL_FOOBAR_XMPL_C_REPLY_WITH_NLMSG_ERR, + .flags = 0, + .internal_flags = 0, + .doit = gnl_cb_doit_reply_with_nlmsg_err, + // .dumpit is not required, only optional; application specific/dependent on your use case + // in a real application you probably have different .dumpit handlers per operation/command + .dumpit = NULL, + // in a real application you probably have different .start handlers per operation/command + .start = NULL, + // in a real application you probably have different .done handlers per operation/command + .done = NULL, + .validate = 0, + } +}; + +/** + * Attribute policy: defines which attribute has which type (e.g int, char * etc). + * This get validated for each received Generic Netlink message, if not deactivated + * in `gnl_foobar_xmpl_ops[].validate`. + * See https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + */ +static struct nla_policy gnl_foobar_xmpl_policy[GNL_FOOBAR_XMPL_ATTRIBUTE_ENUM_LEN] = { + // In case you are seeing this syntax for the first time (I also learned this just after a few years of + // coding with C myself): The following static array initiations are equivalent: + // `int a[2] = {1, 2}` <==> `int a[2] = {[0] => 1, [1] => 2}`. + + [GNL_FOOBAR_XMPL_A_UNSPEC] = {.type = NLA_UNSPEC}, + + // You can set this to NLA_U32 for testing and send an ECHO message from the userland + // It will fail in this case and you see a entry in the kernel log. + + // `enum GNL_FOOBAR_XMPL_ATTRIBUTE::GNL_FOOBAR_XMPL_A_MSG` is a null-terminated C-String + [GNL_FOOBAR_XMPL_A_MSG] = {.type = NLA_NUL_STRING}, +}; + +/** + * Definition of the Netlink family we want to register using Generic Netlink functionality + */ +static struct genl_family gnl_foobar_xmpl_family = { + // automatically assign an id + .id = 0, + // we don't use custom additional header info / user specific header + .hdrsize = 0, + // The name of this family, used by userspace application to get the numeric ID + .name = FAMILY_NAME, + // family specific version number; can be used to evolve application over time (multiple versions) + .version = 1, + // delegates all incoming requests to callback functions + .ops = gnl_foobar_xmpl_ops, + // length of array `gnl_foobar_xmpl_ops` + .n_ops = GNL_FOOBAR_OPS_LEN, + // attribute policy (for validation of messages). Enforced automatically, except ".validate" in + // corresponding ".ops"-field is set accordingly. + .policy = gnl_foobar_xmpl_policy, + // Number of attributes / bounds check for policy (array length) + .maxattr = GNL_FOOBAR_XMPL_ATTRIBUTE_ENUM_LEN, + // Owning Kernel module of the Netlink family we register. + .module = THIS_MODULE, + + // Actually not necessary because this memory region would be zeroed anyway during module load, + // but this way one sees all possible options. + + // if your application must handle multiple netlink calls in parallel (where one should not block the next + // from starting), set this to true! otherwise all netlink calls are mutually exclusive + .parallel_ops = 0, + // set to true if the family can handle network namespaces and should be presented in all of them + .netnsok = 0, + // called before an operation's doit callback, it may do additional, common, filtering and return an error + .pre_doit = NULL, + // called after an operation's doit callback, it may undo operations done by pre_doit, for example release locks + .post_doit = NULL, +}; + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_FOOBAR_XMPL_C_ECHO` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_echo_doit(struct sk_buff *sender_skb, struct genl_info *info) { + struct nlattr *na; + struct sk_buff *reply_skb; + int rc; + void *msg_head; + char *recv_msg; + + pr_info("%s() invoked\n", __func__); + + if (info == NULL) { + // should never happen + pr_err("An error occurred in %s():\n", __func__); + return -EINVAL; + } + + /* + * For each attribute there is an index in info->attrs which points to a nlattr structure + * in this structure the data is stored. + */ + na = info->attrs[GNL_FOOBAR_XMPL_A_MSG]; + + if (!na) { + pr_err("no info->attrs[%i]\n", GNL_FOOBAR_XMPL_A_MSG); + return -EINVAL; // we return here because we expect to recv a msg + } + + recv_msg = (char *) nla_data(na); + if (recv_msg == NULL) { + pr_err("error while receiving data\n"); + } else { + pr_info("received: '%s'\n", recv_msg); + } + + + // Send a message back + // --------------------- + + // Allocate some memory, since the size is not yet known use NLMSG_GOODSIZE + reply_skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (reply_skb == NULL) { + pr_err("An error occurred in %s():\n", __func__); + return -ENOMEM; + } + + // Create the message headers + + // Add header to netlink message; + // afterwards the buffer looks like this: + // ---------------------------------- + // | netlink header | + // | generic netlink header | + // | | + // ---------------------------------- + msg_head = genlmsg_put(reply_skb, // buffer for netlink message: struct sk_buff * + // According to my findings: this is not used for routing + // This can be used in an application specific way to target + // different endpoints within the same user application + // but general rule: just put sender port id here + info->snd_portid, // sending port (not process) id: int + info->snd_seq + 1, // sequence number: int (might be used by receiver, but not mandatory) + &gnl_foobar_xmpl_family, // struct genl_family * + 0, // flags for Netlink header: int; application specific and not mandatory + // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header + GNL_FOOBAR_XMPL_C_ECHO_MSG + ); + if (msg_head == NULL) { + rc = ENOMEM; + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + // Add a GNL_FOOBAR_XMPL_A_MSG attribute (actual value/payload to be sent) + // echo the value we just received + rc = nla_put_string(reply_skb, GNL_FOOBAR_XMPL_A_MSG, recv_msg); + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + // Finalize the message: + // Corrects the netlink message header (length) to include the appended + // attributes. Only necessary if attributes have been added to the message. + genlmsg_end(reply_skb, msg_head); + + // Send the message back + rc = genlmsg_reply(reply_skb, info); + // same as genlmsg_unicast(genl_info_net(info), reply_skb, info->snd_portid) + // see https://elixir.bootlin.com/linux/v5.8.9/source/include/net/genetlink.h#L326 + + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + return 0; +} + +/** + * ".dumpit"-callback function if a Generic Netlink with command ECHO_MSG and flag `NLM_F_DUMP` is received. + * Please look into the comments where this is used as ".dumpit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".dumpit" callbacks. + * + * A dump must be understand of "give me all data of a given entity" + * rather than a "dump of the netlink message itself" for debugging etc! + * + * This handler requires `gnl_cb_echo_dumpit_before` to run before a dump and `gnl_cb_echo_dumpit_after` after a dump. + * + * For the sake of simplicity, we use the ECHO_MSG command for the dump. In fact, we don't expect a + * MSG-Attribute here, unlike the regular ECHO_MSG handler. We reply with a dump of + * "all messages that we got" (application specific, hard coded in this example). +*/ +int gnl_cb_echo_dumpit(struct sk_buff *pre_allocated_skb, struct netlink_callback *cb) { + void *msg_head; + int ret; + static const char HELLO_FROM_DUMPIT_MSG[] = "You set the flag NLM_F_DUMP; this message is " + "brought to you by .dumpit callback :)"; + pr_info("Called %s()\n", __func__); + + if (dumpit_cb_progress_data.runs_to_go == 0) { + pr_info("no more data to send in dumpit cb\n"); + // mark that dump is done; + return 0; + } else { + dumpit_cb_progress_data.runs_to_go--; + pr_info("%s: %d more runs to do\n", __func__, dumpit_cb_progress_data.runs_to_go); + } + + msg_head = genlmsg_put(pre_allocated_skb, // buffer for netlink message: struct sk_buff * + // According to my findings: this is not used for routing + // This can be used in an application specific way to target + // different endpoints within the same user application + // but general rule: just put sender port id here + cb->nlh->nlmsg_pid, // sending port (not process) id: int + // sequence number: int (might be used by receiver, but not mandatory) + // sequence 0, 1, 2... + dumpit_cb_progress_data.total_runs - dumpit_cb_progress_data.runs_to_go - 1, + &gnl_foobar_xmpl_family, // struct genl_family * + 0, // flags: int (for netlink header); we don't check them in the userland; application specific + // this way we can trigger a specific command/callback on the receiving side or imply + // on which type of command we are currently answering; this is application specific + GNL_FOOBAR_XMPL_C_ECHO_MSG // cmd: u8 (for generic netlink header); + ); + if (msg_head == NULL) { + pr_info("An error occurred in %s(): genlmsg_put() failed\n", __func__); + return -ENOMEM; + } + ret = nla_put_string( + pre_allocated_skb, + GNL_FOOBAR_XMPL_A_MSG, + HELLO_FROM_DUMPIT_MSG + ); + if (ret < 0) { + pr_info("An error occurred in %s():\n", __func__); + return ret; + } + genlmsg_end(pre_allocated_skb, msg_head); + + // return the length of data we wrote into the pre-allocated buffer + return pre_allocated_skb->len; +} + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_FOOBAR_XMPL_C_REPLY_WITH_NLMSG_ERR` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info) { + pr_info("%s() invoked, a NLMSG_ERR response will be sent back\n", __func__); + + /* + * Generic Netlink is smart enough and sends a NLMSG_ERR reply automatically as reply + * Quote from https://lwn.net/Articles/208755/: + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + */ + return -EINVAL; +} + +/** + * Called before a dump with `gnl_cb_echo_dumpit()` starts. + * See where this is assigned in `struct genl_ops gnl_foobar_xmpl_ops[]` as + * `.start` callback for more comments. + * + * @return success (0) or error. + */ +int gnl_cb_echo_dumpit_before(struct netlink_callback *cb) { + int ret; + static int unsigned const dump_runs = 3; + pr_info("%s: dump started. acquire lock. initialize dump runs_to_go (number of receives userland can make) to %d runs\n", __func__, dump_runs); + // Lock the mutex like mutex_lock(), and return 0 if the mutex has been acquired or sleep until the mutex becomes available + // If a signal arrives while waiting for the lock then this function returns -EINTR. + ret = mutex_lock_interruptible(&dumpit_cb_progress_data.mtx); + if (ret != 0) { + pr_err("Failed to get lock!\n"); + return ret; + } + dumpit_cb_progress_data.total_runs = dump_runs; + dumpit_cb_progress_data.runs_to_go = dump_runs; + return 0; + +} + +/** + * Called before a dump with `gnl_cb_echo_dumpit()` starts. + * See where this is assigned in `struct genl_ops gnl_foobar_xmpl_ops[]` as + * `.start` callback for more comments. + * + * @return success (0) or error. + */ +int gnl_cb_echo_dumpit_before_after(struct netlink_callback *cb) { + pr_info("%s: dump done. release lock\n", __func__); + mutex_unlock(&dumpit_cb_progress_data.mtx); + return 0; +} + +/** + * Module/driver initializer. Called on module load/insertion. + * + * @return success (0) or error code. + */ +static int __init gnl_foobar_xmpl_module_init(void) { + int rc; + pr_info("Generic Netlink Example Module inserted.\n"); + + // Register family with its operations and policies + rc = genl_register_family(&gnl_foobar_xmpl_family); + if (rc != 0) { + pr_err("FAILED: genl_register_family(): %i\n", rc); + pr_err("An error occurred while inserting the generic netlink example module\n"); + return -1; + } else { + pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + mutex_init(&dumpit_cb_progress_data.mtx); + + return 0; +} + +/** + * Module/driver uninitializer. Called on module unload/removal. + * + * @return success (0) or error code. + */ +static void __exit gnl_foobar_xmpl_module_exit(void) { + int ret; + pr_info("Generic Netlink Example Module unloaded.\n"); + + // Unregister the family + ret = genl_unregister_family(&gnl_foobar_xmpl_family); + if (ret != 0) { + pr_err("genl_unregister_family() failed: %i\n", ret); + return; + } else { + pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + mutex_destroy(&dumpit_cb_progress_data.mtx); +} + +module_init(gnl_foobar_xmpl_module_init); +module_exit(gnl_foobar_xmpl_module_exit); \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/rpmsg_sdb.csva b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/rpmsg_sdb.csva new file mode 100644 index 0000000..857a42c --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/rpmsg_sdb.csva @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Jean-Philippe Romain + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RPMSG_SDB_DRIVER_VERSION "1.0" + +/* + * Static global variables + */ +static const char rpmsg_sdb_driver_name[] = "stm32-rpmsg-sdb"; + +static int LastBufferId; + +struct rpmsg_sdb_ioctl_set_efd { + int bufferId, eventfd; +}; + +struct rpmsg_sdb_ioctl_get_data_size { + int bufferId; + uint32_t size; +}; + +/* ioctl numbers */ +/* _IOW means userland is writing and kernel is reading */ +/* _IOR means userland is reading and kernel is writing */ +/* _IOWR means userland and kernel can both read and write */ +#define RPMSG_SDB_IOCTL_SET_EFD _IOW('R', 0x00, struct rpmsg_sdb_ioctl_set_efd *) +#define RPMSG_SDB_IOCTL_GET_DATA_SIZE _IOWR('R', 0x01, struct rpmsg_sdb_ioctl_get_data_size *) + +struct sdb_buf_t { + int index; /* index of buffer */ + size_t size; /* buffer size */ + size_t writing_size; /* size of data written by copro */ + dma_addr_t paddr; /* physical address*/ + void *vaddr; /* virtual address */ + void *uaddr; /* mapped address for userland */ + struct eventfd_ctx *efd_ctx; /* eventfd context */ + struct list_head buflist; /* reference in the buffers list */ +}; + +struct rpmsg_sdb_t { + struct mutex mutex; /* mutex to protect the ioctls */ + struct miscdevice mdev; /* misc device ref */ + struct rpmsg_device *rpdev; /* handle rpmsg device */ + struct list_head buffer_list; /* buffer instances list */ +}; + +struct device *rpmsg_sdb_dev; + +static int rpmsg_sdb_format_txbuf_string(struct sdb_buf_t *buffer, char *bufinfo_str, size_t bufinfo_str_size) +{ + pr_debug("rpmsg_sdb(%s): Buffer index:%d, addr:%08x, size:%08x\n", + __func__, + buffer->index, + buffer->paddr, + buffer->size); + return snprintf(bufinfo_str, bufinfo_str_size, "B%dA%08xL%08x", buffer->index, buffer->paddr, buffer->size); +} + +static long rpmsg_sdb_decode_rxbuf_string(char *rxbuf_str, int *buffer_id, size_t *size) +{ + int ret = 0; + char *sub_str; + long bsize; + long bufid; + const char delimiter[2] = {'L','\0'}; + + pr_debug("rpmsg_sdb(%s): rxbuf_str:%s\n", __func__, rxbuf_str); + + /* Get first part containing the buffer id */ + sub_str = strsep(&rxbuf_str, delimiter); + + pr_debug("rpmsg_sdb(%s): sub_str:%s\n", __func__, sub_str); + + /* Save Buffer id and size: template BxLyyyyyyyy*/ + ret = kstrtol(&sub_str[1], 10, &bufid); + if (ret < 0) { + pr_err("rpmsg_sdb(ERROR): Extract of buffer id failed(%d)", ret); + goto out; + } + + ret = kstrtol(&rxbuf_str[2], 16, &bsize); + if (ret < 0) { + pr_err("rpmsg_sdb(ERROR): Extract of buffer size failed(%d)", ret); + goto out; + } + + *size = (size_t)bsize; + *buffer_id = (int)bufid; + +out: + return ret; +} + +static int rpmsg_sdb_send_buf_info(struct rpmsg_sdb_t *rpmsg_sdb, struct sdb_buf_t *buffer) +{ + int count = 0, ret = 0; + const unsigned char *tbuf; + char mybuf[32]; + int msg_size; + struct rpmsg_device *_rpdev; + + _rpdev = rpmsg_sdb->rpdev; + msg_size = rpmsg_get_buffer_size(_rpdev->ept); + + if (msg_size < 0) + return msg_size; + + count = rpmsg_sdb_format_txbuf_string(buffer, mybuf, 32); + tbuf = &mybuf[0]; + + do { + /* send a message to our remote processor */ + ret = rpmsg_send(_rpdev->ept, (void *)tbuf, + count > msg_size ? msg_size : count); + if (ret) { + dev_err(&_rpdev->dev, "rpmsg_send failed: %d\n", ret); + return ret; + } + + if (count > msg_size) { + count -= msg_size; + tbuf += msg_size; + } else { + count = 0; + } + } while (count > 0); + + return count; +} + +static int rpmsg_sdb_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long vsize = vma->vm_end - vma->vm_start; + unsigned long size = PAGE_ALIGN(vsize); + unsigned long NumPages = size >> PAGE_SHIFT; + unsigned long align = get_order(size); + pgprot_t prot = pgprot_noncached(vma->vm_page_prot); + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *_buffer; + + if (align > CONFIG_CMA_ALIGNMENT) + align = CONFIG_CMA_ALIGNMENT; + + if (rpmsg_sdb_dev == NULL) + return -ENOMEM; + + rpmsg_sdb_dev->coherent_dma_mask = DMA_BIT_MASK(32); + rpmsg_sdb_dev->dma_mask = &rpmsg_sdb_dev->coherent_dma_mask; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Field the last buffer entry which is the last one created */ + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + _buffer = list_last_entry(&_rpmsg_sdb->buffer_list, + struct sdb_buf_t, buflist); + + _buffer->uaddr = NULL; + _buffer->size = NumPages * PAGE_SIZE; + _buffer->writing_size = -1; + _buffer->vaddr = dma_alloc_coherent(rpmsg_sdb_dev, + _buffer->size, + &_buffer->paddr, + GFP_KERNEL); + + if (!_buffer->vaddr) { + pr_err("rpmsg_sdb(ERROR): Memory allocation issue\n"); + return -ENOMEM; + } + + pr_debug("rpmsg_sdb(%s): dma_alloc_coherent done - paddr[%d]:%x - vaddr[%d]:%p\n", + __func__, + _buffer->index, + _buffer->paddr, + _buffer->index, + _buffer->vaddr); + + /* Get address for userland */ + if (remap_pfn_range(vma, vma->vm_start, + (_buffer->paddr >> PAGE_SHIFT) + vma->vm_pgoff, + size, prot)) + return -EAGAIN; + + _buffer->uaddr = (void *)vma->vm_start; + + /* Send information to remote proc */ + rpmsg_sdb_send_buf_info(_rpmsg_sdb, _buffer); + } else { + dev_err(rpmsg_sdb_dev, "No existing buffer entry exist in the list !!!"); + return -EINVAL; + } + + /* Increment for number of requested buffer */ + LastBufferId++; + + return 0; +} + +/** + * rpmsg_sdb_open - Open Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_open(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Initialize the buffer list*/ + INIT_LIST_HEAD(&_rpmsg_sdb->buffer_list); + + mutex_init(&_rpmsg_sdb->mutex); + + return 0; +} + +/** + * rpmsg_sdb_close - Close Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_close(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *pos, *next; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + list_for_each_entry_safe(pos, next, &_rpmsg_sdb->buffer_list, buflist) { + /* Free the CMA allocation */ + pr_debug("rpmsg_sdb(%s): Free the CMA allocation: pos->size:%08x, pos->vaddr:%08x, pos->paddr:%08x\n", + __func__, + pos->size, + pos->vaddr, + pos->paddr); + + dma_free_coherent(rpmsg_sdb_dev, pos->size, pos->vaddr, + pos->paddr); + /* Remove the buffer from the list */ + list_del(&pos->buflist); + /* Free the buffer */ + kfree(pos); + } + + /* Reset LastBufferId */ + LastBufferId = 0; + + return 0; +} + +/** + * rpmsg_sdb_ioctl - IOCTL + * + * @session: ibmvmc_file_session struct + * @cmd: cmd field + * @arg: Argument field + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static long rpmsg_sdb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int idx = 0; + + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *buffer, *lastbuffer; + + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + struct rpmsg_sdb_ioctl_set_efd q_set_efd; + struct rpmsg_sdb_ioctl_get_data_size q_get_dat_size; + + void __user *argp = (void __user *)arg; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + switch (cmd) { + case RPMSG_SDB_IOCTL_SET_EFD: + mutex_lock(&_rpmsg_sdb->mutex); + + /* Get index from the last buffer in the list */ + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + lastbuffer = list_last_entry(&_rpmsg_sdb->buffer_list, struct sdb_buf_t, buflist); + idx = lastbuffer->index; + + /* Check last index was properly initiated*/ + if (lastbuffer->vaddr == NULL) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - previous buffer was not allocated\n"); + mutex_unlock(&_rpmsg_sdb->mutex); + return -EBADE; + } + + /* increment this index for the next buffer creation*/ + idx++; + } + + if (copy_from_user(&q_set_efd, (struct rpmsg_sdb_ioctl_set_efd *)argp, + sizeof(struct rpmsg_sdb_ioctl_set_efd))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - copy from user failed\n"); + mutex_unlock(&_rpmsg_sdb->mutex); + return -EFAULT; + } + + /* create a new buffer which will be added in the buffer list */ + buffer = kmalloc(sizeof(struct sdb_buf_t), GFP_KERNEL); + + buffer->index = idx; + buffer->vaddr = NULL; + buffer->efd_ctx = eventfd_ctx_fdget(q_set_efd.eventfd); + list_add_tail(&buffer->buflist, &_rpmsg_sdb->buffer_list); + + mutex_unlock(&_rpmsg_sdb->mutex); + break; + + case RPMSG_SDB_IOCTL_GET_DATA_SIZE: + if (copy_from_user(&q_get_dat_size, (struct rpmsg_sdb_ioctl_get_data_size *)argp, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy from user failed\n"); + return -EFAULT; + } + + /* Get the index of the requested buffer and then look-up in the buffer list*/ + idx = q_get_dat_size.bufferId; + + list_for_each(pos, &_rpmsg_sdb->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == idx) { + /* Get the writing size*/ + q_get_dat_size.size = datastructureptr->writing_size; + break; + } + } + + if (copy_to_user((struct rpmsg_sdb_ioctl_get_data_size *)argp, &q_get_dat_size, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy to user failed\n"); + return -EFAULT; + } + + /* Reset the writing size*/ + datastructureptr->writing_size = -1; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct file_operations rpmsg_sdb_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = rpmsg_sdb_ioctl, + .mmap = rpmsg_sdb_mmap, + .open = rpmsg_sdb_open, + .release = rpmsg_sdb_close, +}; + +static int rpmsg_sdb_drv_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + int ret = 0; + int buffer_id = 0; + size_t buffer_size; + char *rpmsg_RxBuf; + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpdev->dev); + + if (len == 0) { + dev_err(rpmsg_sdb_dev, "(%s) Empty lenght requested\n", __func__); + return -EINVAL; + } + + //dev_err(rpmsg_sdb_dev, "(%s) lenght: %d\n", __func__,len); + + rpmsg_RxBuf = (char *)kmalloc(len+1, GFP_KERNEL); + memcpy(rpmsg_RxBuf, data, len); + + rpmsg_RxBuf[len] = 0; + + ret = rpmsg_sdb_decode_rxbuf_string(rpmsg_RxBuf, &buffer_id, &buffer_size); + kfree(rpmsg_RxBuf); + if (ret < 0) + goto out; + + if (buffer_id > LastBufferId) { + ret = -EINVAL; + goto out; + } + + /* Signal to User space application */ + list_for_each(pos, &drv->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == buffer_id) { + datastructureptr->writing_size = buffer_size; + + if (datastructureptr->writing_size > datastructureptr->size) { + dev_err(rpmsg_sdb_dev, "(%s) Writing size is bigger than buffer size\n", __func__); + ret = -EINVAL; + goto out; + } + + eventfd_signal(datastructureptr->efd_ctx, 1); + break; + } + /* TODO: quid if nothing find during the loop ? */ + } + +out: + return ret; +} + +static int rpmsg_sdb_drv_probe(struct rpmsg_device *rpdev) +{ + int ret = 0; + struct device *dev = &rpdev->dev; + struct rpmsg_sdb_t *rpmsg_sdb; + + rpmsg_sdb = devm_kzalloc(dev, sizeof(*rpmsg_sdb), GFP_KERNEL); + if (!rpmsg_sdb) + return -ENOMEM; + + mutex_init(&rpmsg_sdb->mutex); + + rpmsg_sdb->rpdev = rpdev; + + rpmsg_sdb->mdev.name = "rpmsg-sdb"; + rpmsg_sdb->mdev.minor = MISC_DYNAMIC_MINOR; + rpmsg_sdb->mdev.fops = &rpmsg_sdb_fops; + + dev_set_drvdata(&rpdev->dev, rpmsg_sdb); + + /* Register misc device */ + ret = misc_register(&rpmsg_sdb->mdev); + + if (ret) { + dev_err(dev, "Failed to register device\n"); + goto err_out; + } + + rpmsg_sdb_dev = rpmsg_sdb->mdev.this_device; + + dev_info(dev, "%s probed\n", rpmsg_sdb_driver_name); + +err_out: + return ret; +} + +static void rpmsg_sdb_drv_remove(struct rpmsg_device *rpmsgdev) +{ + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpmsgdev->dev); + + misc_deregister(&drv->mdev); +} + +static struct rpmsg_device_id rpmsg_driver_sdb_id_table[] = { + { .name = "rpmsg-sdb-channel" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sdb_id_table); + +static struct rpmsg_driver rpmsg_sdb_rmpsg_drv = { + .drv.name = KBUILD_MODNAME, + .drv.owner = THIS_MODULE, + .id_table = rpmsg_driver_sdb_id_table, + .probe = rpmsg_sdb_drv_probe, + .callback = rpmsg_sdb_drv_cb, + .remove = rpmsg_sdb_drv_remove, +}; + +static int __init rpmsg_sdb_drv_init(void) +{ + int ret = 0; + + /* Register rpmsg device */ + ret = register_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + + if (ret) { + pr_err("rpmsg_sdb(ERROR): Failed to register device\n"); + return ret; + } + + pr_info("rpmsg_sdb: Init done\n"); + + return ret; +} + +static void __exit rpmsg_sdb_drv_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + pr_info("rpmsg_sdb: Exit\n"); +} + +module_init(rpmsg_sdb_drv_init); +module_exit(rpmsg_sdb_drv_exit); + + +MODULE_AUTHOR("Jean-Philippe Romain "); +MODULE_DESCRIPTION("shared data buffer over RPMSG"); +MODULE_VERSION(RPMSG_SDB_DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/stm32_rpmsg_sdb (Kopie).c.old b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/stm32_rpmsg_sdb (Kopie).c.old new file mode 100644 index 0000000..5062cef --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/stm32_rpmsg_sdb (Kopie).c.old @@ -0,0 +1,541 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Jean-Philippe Romain + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RPMSG_SDB_DRIVER_VERSION "1.0" + +/* + * Static global variables + */ +static const char rpmsg_sdb_driver_name[] = "stm32-rpmsg-sdb"; + +static int LastBufferId; + +struct rpmsg_sdb_ioctl_set_efd { + int bufferId, eventfd; +}; + +struct rpmsg_sdb_ioctl_get_data_size { + int bufferId; + uint32_t size; +}; + +/* ioctl numbers */ +/* _IOW means userland is writing and kernel is reading */ +/* _IOR means userland is reading and kernel is writing */ +/* _IOWR means userland and kernel can both read and write */ +#define RPMSG_SDB_IOCTL_SET_EFD _IOW('R', 0x00, struct rpmsg_sdb_ioctl_set_efd *) +#define RPMSG_SDB_IOCTL_GET_DATA_SIZE _IOWR('R', 0x01, struct rpmsg_sdb_ioctl_get_data_size *) + +struct sdb_buf_t { + int index; /* index of buffer */ + size_t size; /* buffer size */ + size_t writing_size; /* size of data written by copro */ + dma_addr_t paddr; /* physical address*/ + void *vaddr; /* virtual address */ + void *uaddr; /* mapped address for userland */ + struct eventfd_ctx *efd_ctx; /* eventfd context */ + struct list_head buflist; /* reference in the buffers list */ +}; + +struct rpmsg_sdb_t { + struct mutex mutex; /* mutex to protect the ioctls */ + struct miscdevice mdev; /* misc device ref */ + struct rpmsg_device *rpdev; /* handle rpmsg device */ + struct list_head buffer_list; /* buffer instances list */ +}; + +struct device *rpmsg_sdb_dev; + +static int rpmsg_sdb_format_txbuf_string(struct sdb_buf_t *buffer, char *bufinfo_str, size_t bufinfo_str_size) +{ + pr_debug("rpmsg_sdb(%s): Buffer index:%d, addr:%08x, size:%08x\n", + __func__, + buffer->index, + buffer->paddr, + buffer->size); + return snprintf(bufinfo_str, bufinfo_str_size, "B%dA%08xL%08x", buffer->index, buffer->paddr, buffer->size); +} + +static long rpmsg_sdb_decode_rxbuf_string(char *rxbuf_str, int *buffer_id, size_t *size) +{ + int ret = 0; + char *sub_str; + long bsize; + long bufid; + const char delimiter[2] = {'L','\0'}; + + pr_debug("rpmsg_sdb(%s): rxbuf_str:%s\n", __func__, rxbuf_str); + + /* Get first part containing the buffer id */ + sub_str = strsep(&rxbuf_str, delimiter); + + pr_debug("rpmsg_sdb(%s): sub_str:%s\n", __func__, sub_str); + + /* Save Buffer id and size: template BxLyyyyyyyy*/ + ret = kstrtol(&sub_str[1], 10, &bufid); + if (ret < 0) { + pr_err("rpmsg_sdb(ERROR): Extract of buffer id failed(%d)", ret); + goto out; + } + + ret = kstrtol(&rxbuf_str[2], 16, &bsize); + if (ret < 0) { + pr_err("rpmsg_sdb(ERROR): Extract of buffer size failed(%d)", ret); + goto out; + } + + *size = (size_t)bsize; + *buffer_id = (int)bufid; + +out: + return ret; +} + +static int rpmsg_sdb_send_buf_info(struct rpmsg_sdb_t *rpmsg_sdb, struct sdb_buf_t *buffer) +{ + int count = 0, ret = 0; + const unsigned char *tbuf; + char mybuf[32]; + int msg_size; + struct rpmsg_device *_rpdev; + + _rpdev = rpmsg_sdb->rpdev; + msg_size = rpmsg_get_mtu(_rpdev->ept); + + if (msg_size < 0) + return msg_size; + + count = rpmsg_sdb_format_txbuf_string(buffer, mybuf, 32); + tbuf = &mybuf[0]; + + do { + /* send a message to our remote processor */ + ret = rpmsg_send(_rpdev->ept, (void *)tbuf, + count > msg_size ? msg_size : count); + if (ret) { + dev_err(&_rpdev->dev, "rpmsg_send failed: %d\n", ret); + return ret; + } + + if (count > msg_size) { + count -= msg_size; + tbuf += msg_size; + } else { + count = 0; + } + } while (count > 0); + + return count; +} + +static int rpmsg_sdb_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long vsize = vma->vm_end - vma->vm_start; + unsigned long size = PAGE_ALIGN(vsize); + unsigned long NumPages = size >> PAGE_SHIFT; + unsigned long align = get_order(size); + pgprot_t prot = pgprot_noncached(vma->vm_page_prot); + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *_buffer; + + if (align > CONFIG_CMA_ALIGNMENT) + align = CONFIG_CMA_ALIGNMENT; + + if (rpmsg_sdb_dev == NULL) + return -ENOMEM; + + rpmsg_sdb_dev->coherent_dma_mask = DMA_BIT_MASK(32); + rpmsg_sdb_dev->dma_mask = &rpmsg_sdb_dev->coherent_dma_mask; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Field the last buffer entry which is the last one created */ + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + _buffer = list_last_entry(&_rpmsg_sdb->buffer_list, + struct sdb_buf_t, buflist); + + _buffer->uaddr = NULL; + _buffer->size = NumPages * PAGE_SIZE; + _buffer->writing_size = -1; + _buffer->vaddr = dma_alloc_coherent(rpmsg_sdb_dev, + _buffer->size, + &_buffer->paddr, + GFP_KERNEL); + + if (!_buffer->vaddr) { + pr_err("rpmsg_sdb(ERROR): Memory allocation issue\n"); + return -ENOMEM; + } + + pr_debug("rpmsg_sdb(%s): dma_alloc_coherent done - paddr[%d]:%x - vaddr[%d]:%p\n", + __func__, + _buffer->index, + _buffer->paddr, + _buffer->index, + _buffer->vaddr); + + /* Get address for userland */ + if (remap_pfn_range(vma, vma->vm_start, + (_buffer->paddr >> PAGE_SHIFT) + vma->vm_pgoff, + size, prot)) + return -EAGAIN; + + _buffer->uaddr = (void *)vma->vm_start; + + /* Send information to remote proc */ + rpmsg_sdb_send_buf_info(_rpmsg_sdb, _buffer); + } else { + dev_err(rpmsg_sdb_dev, "No existing buffer entry exist in the list !!!"); + return -EINVAL; + } + + /* Increment for number of requested buffer */ + LastBufferId++; + + return 0; +} + +/** + * rpmsg_sdb_open - Open Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_open(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Initialize the buffer list*/ + INIT_LIST_HEAD(&_rpmsg_sdb->buffer_list); + + mutex_init(&_rpmsg_sdb->mutex); + + return 0; +} + +/** + * rpmsg_sdb_close - Close Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_close(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *pos, *next; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + list_for_each_entry_safe(pos, next, &_rpmsg_sdb->buffer_list, buflist) { + /* Free the CMA allocation */ + pr_debug("rpmsg_sdb(%s): Free the CMA allocation: pos->size:%08x, pos->vaddr:%08x, pos->paddr:%08x\n", + __func__, + pos->size, + pos->vaddr, + pos->paddr); + + dma_free_coherent(rpmsg_sdb_dev, pos->size, pos->vaddr, + pos->paddr); + /* Remove the buffer from the list */ + list_del(&pos->buflist); + /* Free the buffer */ + kfree(pos); + } + + /* Reset LastBufferId */ + LastBufferId = 0; + + return 0; +} + +/** + * rpmsg_sdb_ioctl - IOCTL + * + * @session: ibmvmc_file_session struct + * @cmd: cmd field + * @arg: Argument field + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static long rpmsg_sdb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int idx = 0; + + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *buffer, *lastbuffer; + + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + struct rpmsg_sdb_ioctl_set_efd q_set_efd; + struct rpmsg_sdb_ioctl_get_data_size q_get_dat_size; + + void __user *argp = (void __user *)arg; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + switch (cmd) { + case RPMSG_SDB_IOCTL_SET_EFD: + mutex_lock(&_rpmsg_sdb->mutex); + + /* Get index from the last buffer in the list */ + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + lastbuffer = list_last_entry(&_rpmsg_sdb->buffer_list, struct sdb_buf_t, buflist); + idx = lastbuffer->index; + + /* Check last index was properly initiated*/ + if (lastbuffer->vaddr == NULL) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - previous buffer was not allocated\n"); + mutex_unlock(&_rpmsg_sdb->mutex); + return -EBADE; + } + + /* increment this index for the next buffer creation*/ + idx++; + } + + if (copy_from_user(&q_set_efd, (struct rpmsg_sdb_ioctl_set_efd *)argp, + sizeof(struct rpmsg_sdb_ioctl_set_efd))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - copy from user failed\n"); + mutex_unlock(&_rpmsg_sdb->mutex); + return -EFAULT; + } + + /* create a new buffer which will be added in the buffer list */ + buffer = kmalloc(sizeof(struct sdb_buf_t), GFP_KERNEL); + + buffer->index = idx; + buffer->vaddr = NULL; + buffer->efd_ctx = eventfd_ctx_fdget(q_set_efd.eventfd); + list_add_tail(&buffer->buflist, &_rpmsg_sdb->buffer_list); + + mutex_unlock(&_rpmsg_sdb->mutex); + break; + + case RPMSG_SDB_IOCTL_GET_DATA_SIZE: + if (copy_from_user(&q_get_dat_size, (struct rpmsg_sdb_ioctl_get_data_size *)argp, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy from user failed\n"); + return -EFAULT; + } + + /* Get the index of the requested buffer and then look-up in the buffer list*/ + idx = q_get_dat_size.bufferId; + + list_for_each(pos, &_rpmsg_sdb->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == idx) { + /* Get the writing size*/ + q_get_dat_size.size = datastructureptr->writing_size; + break; + } + } + + if (copy_to_user((struct rpmsg_sdb_ioctl_get_data_size *)argp, &q_get_dat_size, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy to user failed\n"); + return -EFAULT; + } + + /* Reset the writing size*/ + datastructureptr->writing_size = -1; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct file_operations rpmsg_sdb_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = rpmsg_sdb_ioctl, + .mmap = rpmsg_sdb_mmap, + .open = rpmsg_sdb_open, + .release = rpmsg_sdb_close, +}; + +static int rpmsg_sdb_drv_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + int ret = 0; + int buffer_id = 0; + size_t buffer_size; + char *rpmsg_RxBuf; + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpdev->dev); + + if (len == 0) { + dev_err(rpmsg_sdb_dev, "(%s) Empty lenght requested\n", __func__); + return -EINVAL; + } + + //dev_err(rpmsg_sdb_dev, "(%s) lenght: %d\n", __func__,len); + + rpmsg_RxBuf = (char *)kmalloc(len+1, GFP_KERNEL); + memcpy(rpmsg_RxBuf, data, len); + + rpmsg_RxBuf[len] = 0; + + ret = rpmsg_sdb_decode_rxbuf_string(rpmsg_RxBuf, &buffer_id, &buffer_size); + kfree(rpmsg_RxBuf); + if (ret < 0) + goto out; + + if (buffer_id > LastBufferId) { + ret = -EINVAL; + goto out; + } + + /* Signal to User space application */ + list_for_each(pos, &drv->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == buffer_id) { + datastructureptr->writing_size = buffer_size; + + if (datastructureptr->writing_size > datastructureptr->size) { + dev_err(rpmsg_sdb_dev, "(%s) Writing size is bigger than buffer size\n", __func__); + ret = -EINVAL; + goto out; + } + + eventfd_signal(datastructureptr->efd_ctx, 1); + break; + } + /* TODO: quid if nothing find during the loop ? */ + } + +out: + return ret; +} + +static int rpmsg_sdb_drv_probe(struct rpmsg_device *rpdev) +{ + int ret = 0; + struct device *dev = &rpdev->dev; + struct rpmsg_sdb_t *rpmsg_sdb; + + pr_info("rpmsg_sdb: registering startetd\n"); + + rpmsg_sdb = devm_kzalloc(dev, sizeof(*rpmsg_sdb), GFP_KERNEL); + if (!rpmsg_sdb) + return -ENOMEM; + + mutex_init(&rpmsg_sdb->mutex); + + rpmsg_sdb->rpdev = rpdev; + + rpmsg_sdb->mdev.name = "rpmsg-sdb"; + rpmsg_sdb->mdev.minor = MISC_DYNAMIC_MINOR; + rpmsg_sdb->mdev.fops = &rpmsg_sdb_fops; + + pr_info("rpmsg_sdb: Set Driver data\n"); + dev_set_drvdata(&rpdev->dev, rpmsg_sdb); + + /* Register misc device */ + ret = misc_register(&rpmsg_sdb->mdev); + + if (ret) { + dev_err(dev, "Failed to register device\n"); + pr_info("rpmsg_sdb: Failed to register device\n"); + goto err_out; + } + + rpmsg_sdb_dev = rpmsg_sdb->mdev.this_device; + + pr_info("rpmsg_sdb: Failed to register device\n"); + dev_info(dev, "%s probed\n", rpmsg_sdb_driver_name); + +err_out: + return ret; +} + +static void rpmsg_sdb_drv_remove(struct rpmsg_device *rpmsgdev) +{ + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpmsgdev->dev); + + misc_deregister(&drv->mdev); +} + +static struct rpmsg_device_id rpmsg_driver_sdb_id_table[] = { + { .name = "rpmsg-sdb-channel" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sdb_id_table); + +static struct rpmsg_driver rpmsg_sdb_rmpsg_drv = { + .drv.name = KBUILD_MODNAME, + .drv.owner = THIS_MODULE, + .id_table = rpmsg_driver_sdb_id_table, + .probe = rpmsg_sdb_drv_probe, + .callback = rpmsg_sdb_drv_cb, + .remove = rpmsg_sdb_drv_remove, +}; + +static int __init rpmsg_sdb_drv_init(void) +{ + int ret = 0; + + /* Register rpmsg device */ + ret = register_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + + if (ret) { + pr_err("rpmsg_sdb(ERROR): Failed to register device\n"); + return ret; + } + + pr_info("rpmsg_sdb: Init done\n"); + + return ret; +} + +static void __exit rpmsg_sdb_drv_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + pr_info("rpmsg_sdb: Exit\n"); +} + +module_init(rpmsg_sdb_drv_init); +module_exit(rpmsg_sdb_drv_exit); + + +MODULE_AUTHOR("Jean-Philippe Romain "); +MODULE_DESCRIPTION("shared data buffer over RPMSG"); +MODULE_VERSION(RPMSG_SDB_DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/ttyrpmsg.csave b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/ttyrpmsg.csave new file mode 100644 index 0000000..ec89035 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/ttyrpmsg.csave @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 STMicroelectronics - All Rights Reserved + * + * The rpmsg tty driver implements serial communication on the RPMsg bus to makes + * possible for user-space programs to send and receive rpmsg messages as a standard + * tty protocol. + * + * The remote processor can instantiate a new tty by requesting a "rpmsg-tty" RPMsg service. + * The "rpmsg-tty" service is directly used for data exchange. No flow control is implemented yet. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#define RPMSG_TTY_NAME "ttyRPMSG" +#define MAX_TTY_RPMSG 32 + +static DEFINE_IDR(tty_idr); /* tty instance id */ +static DEFINE_MUTEX(idr_lock); /* protects tty_idr */ + +static struct tty_driver *rpmsg_tty_driver; + +struct rpmsg_tty_port { + struct tty_port port; /* TTY port data */ + int id; /* TTY rpmsg index */ + struct rpmsg_device *rpdev; /* rpmsg device */ +}; + +static int rpmsg_tty_cb(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 src) +{ + struct rpmsg_tty_port *cport = dev_get_drvdata(&rpdev->dev); + int copied; + + if (!len) + return -EINVAL; + copied = tty_insert_flip_string(&cport->port, data, len); + if (copied != len) + dev_err_ratelimited(&rpdev->dev, "Trunc buffer: available space is %d\n", copied); + tty_flip_buffer_push(&cport->port); + + return 0; +} + +static int rpmsg_tty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct rpmsg_tty_port *cport = idr_find(&tty_idr, tty->index); + struct tty_port *port; + + tty->driver_data = cport; + + port = tty_port_get(&cport->port); + return tty_port_install(port, driver, tty); +} + +static void rpmsg_tty_cleanup(struct tty_struct *tty) +{ + tty_port_put(tty->port); +} + +static int rpmsg_tty_open(struct tty_struct *tty, struct file *filp) +{ + return tty_port_open(tty->port, tty, filp); +} + +static void rpmsg_tty_close(struct tty_struct *tty, struct file *filp) +{ + return tty_port_close(tty->port, tty, filp); +} + +static int rpmsg_tty_write(struct tty_struct *tty, const u8 *buf, int len) +{ + struct rpmsg_tty_port *cport = tty->driver_data; + struct rpmsg_device *rpdev; + int msg_max_size, msg_size; + int ret; + + rpdev = cport->rpdev; + + msg_max_size = rpmsg_get_mtu(rpdev->ept); + if (msg_max_size < 0) + return msg_max_size; + + msg_size = min(len, msg_max_size); + + /* + * Use rpmsg_trysend instead of rpmsg_send to send the message so the caller is not + * hung until a rpmsg buffer is available. In such case rpmsg_trysend returns -ENOMEM. + */ + ret = rpmsg_trysend(rpdev->ept, (void *)buf, msg_size); + if (ret) { + dev_dbg_ratelimited(&rpdev->dev, "rpmsg_send failed: %d\n", ret); + return ret; + } + + return msg_size; +} + +static unsigned int rpmsg_tty_write_room(struct tty_struct *tty) +{ + struct rpmsg_tty_port *cport = tty->driver_data; + int size; + + size = rpmsg_get_mtu(cport->rpdev->ept); + if (size < 0) + return 0; + + return size; +} + +static void rpmsg_tty_hangup(struct tty_struct *tty) +{ + tty_port_hangup(tty->port); +} + +static const struct tty_operations rpmsg_tty_ops = { + .install = rpmsg_tty_install, + .open = rpmsg_tty_open, + .close = rpmsg_tty_close, + .write = rpmsg_tty_write, + .write_room = rpmsg_tty_write_room, + .hangup = rpmsg_tty_hangup, + .cleanup = rpmsg_tty_cleanup, +}; + +static struct rpmsg_tty_port *rpmsg_tty_alloc_cport(void) +{ + struct rpmsg_tty_port *cport; + int ret; + + cport = kzalloc(sizeof(*cport), GFP_KERNEL); + if (!cport) + return ERR_PTR(-ENOMEM); + + mutex_lock(&idr_lock); + ret = idr_alloc(&tty_idr, cport, 0, MAX_TTY_RPMSG, GFP_KERNEL); + mutex_unlock(&idr_lock); + + if (ret < 0) { + kfree(cport); + return ERR_PTR(ret); + } + + cport->id = ret; + + return cport; +} + +static void rpmsg_tty_destruct_port(struct tty_port *port) +{ + struct rpmsg_tty_port *cport = container_of(port, struct rpmsg_tty_port, port); + + mutex_lock(&idr_lock); + idr_remove(&tty_idr, cport->id); + mutex_unlock(&idr_lock); + + kfree(cport); +} + +static const struct tty_port_operations rpmsg_tty_port_ops = { + .destruct = rpmsg_tty_destruct_port, +}; + + +static int rpmsg_tty_probe(struct rpmsg_device *rpdev) +{ + struct rpmsg_tty_port *cport; + struct device *dev = &rpdev->dev; + struct device *tty_dev; + int ret; + + cport = rpmsg_tty_alloc_cport(); + if (IS_ERR(cport)) + return dev_err_probe(dev, PTR_ERR(cport), "Failed to alloc tty port\n"); + + tty_port_init(&cport->port); + cport->port.ops = &rpmsg_tty_port_ops; + + tty_dev = tty_port_register_device(&cport->port, rpmsg_tty_driver, + cport->id, dev); + if (IS_ERR(tty_dev)) { + ret = dev_err_probe(dev, PTR_ERR(tty_dev), "Failed to register tty port\n"); + tty_port_put(&cport->port); + return ret; + } + + cport->rpdev = rpdev; + + dev_set_drvdata(dev, cport); + + dev_dbg(dev, "New channel: 0x%x -> 0x%x: " RPMSG_TTY_NAME "%d\n", + rpdev->src, rpdev->dst, cport->id); + + return 0; +} + +static void rpmsg_tty_remove(struct rpmsg_device *rpdev) +{ + struct rpmsg_tty_port *cport = dev_get_drvdata(&rpdev->dev); + + dev_dbg(&rpdev->dev, "Removing rpmsg tty device %d\n", cport->id); + + /* User hang up to release the tty */ + tty_port_tty_hangup(&cport->port, false); + + tty_unregister_device(rpmsg_tty_driver, cport->id); + + tty_port_put(&cport->port); +} + +static struct rpmsg_device_id rpmsg_driver_tty_id_table[] = { + { .name = "rpmsg-tty" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_tty_id_table); + +static struct rpmsg_driver rpmsg_tty_rpmsg_drv = { + .drv.name = KBUILD_MODNAME, + .id_table = rpmsg_driver_tty_id_table, + .probe = rpmsg_tty_probe, + .callback = rpmsg_tty_cb, + .remove = rpmsg_tty_remove, +}; + +static int __init rpmsg_tty_init(void) +{ + int ret; + + rpmsg_tty_driver = tty_alloc_driver(MAX_TTY_RPMSG, TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(rpmsg_tty_driver)) + return PTR_ERR(rpmsg_tty_driver); + + rpmsg_tty_driver->driver_name = "rpmsg_tty"; + rpmsg_tty_driver->name = RPMSG_TTY_NAME; + rpmsg_tty_driver->major = 0; + rpmsg_tty_driver->type = TTY_DRIVER_TYPE_CONSOLE; + + /* Disable unused mode by default */ + rpmsg_tty_driver->init_termios = tty_std_termios; + rpmsg_tty_driver->init_termios.c_lflag &= ~(ECHO | ICANON); + rpmsg_tty_driver->init_termios.c_oflag &= ~(OPOST | ONLCR); + + tty_set_operations(rpmsg_tty_driver, &rpmsg_tty_ops); + + ret = tty_register_driver(rpmsg_tty_driver); + if (ret < 0) { + pr_err("Couldn't install driver: %d\n", ret); + goto error_put; + } + + ret = register_rpmsg_driver(&rpmsg_tty_rpmsg_drv); + if (ret < 0) { + pr_err("Couldn't register driver: %d\n", ret); + goto error_unregister; + } + + return 0; + +error_unregister: + tty_unregister_driver(rpmsg_tty_driver); + +error_put: + tty_driver_kref_put(rpmsg_tty_driver); + + return ret; +} + +static void __exit rpmsg_tty_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_tty_rpmsg_drv); + tty_unregister_driver(rpmsg_tty_driver); + tty_driver_kref_put(rpmsg_tty_driver); + idr_destroy(&tty_idr); +} + +module_init(rpmsg_tty_init); +module_exit(rpmsg_tty_exit); + +MODULE_AUTHOR("Arnaud Pouliquen "); +MODULE_DESCRIPTION("remote processor messaging tty driver"); +MODULE_LICENSE("GPL v2"); \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-mon-datafile (Kopie).c.save b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-mon-datafile (Kopie).c.save new file mode 100644 index 0000000..844a67d --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-mon-datafile (Kopie).c.save @@ -0,0 +1,553 @@ +/* Copyright 2024 Markus Lehr + * + * + * This Software is owned by Markus Lehr. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// ################################################################################################## +/* + * This is a Linux kernel module/driver called "vrpmdv-monitoring-cmd " which holds the family ID + * and functions to handle the monitoring in a Generic Netlink in the kernel. + * "It registers a Netlink family called "vrpmdv-monitoring_cmd". + * + * + * You can find some more interesting documentation about Generic Netlink here: + * "Generic Netlink HOW-TO based on Jamal's original doc" https://lwn.net/Articles/208755/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VRPMDV_MON_DRIVER_VERSION "1.0" + +/* + * Static global variables + */ +static const char rpmsg_sdb_driver_name[] = "vrpmdv-mon-datafile"; + +static int LastBufferId; + +struct rpmsg_sdb_ioctl_set_efd { + int bufferId, eventfd; +}; + +struct rpmsg_sdb_ioctl_get_data_size { + int bufferId; + uint32_t size; +}; + +/* ioctl numbers */ +/* _IOW means userland is writing and kernel is reading */ +/* _IOR means userland is reading and kernel is writing */ +/* _IOWR means userland and kernel can both read and write */ +#define RPMSG_SDB_IOCTL_SET_EFD _IOW('R', 0x00, struct rpmsg_sdb_ioctl_set_efd *) +#define RPMSG_SDB_IOCTL_GET_DATA_SIZE _IOWR('R', 0x01, struct rpmsg_sdb_ioctl_get_data_size *) + +struct sdb_buf_t { + int index; /* index of buffer */ + size_t size; /* buffer size */ + size_t writing_size; /* size of data written by copro */ + dma_addr_t paddr; /* physical address*/ + void *vaddr; /* virtual address */ + void *uaddr; /* mapped address for userland */ + struct eventfd_ctx *efd_ctx; /* eventfd context */ + struct list_head buflist; /* reference in the buffers list */ +}; + +struct rpmsg_sdb_t { + struct mutex mutex; /* mutex to protect the ioctls */ + struct miscdevice mdev; /* misc device ref */ + struct rpmsg_device *rpdev; /* handle rpmsg device */ + struct list_head buffer_list; /* buffer instances list */ +}; + +struct device *rpmsg_sdb_dev; + +static int rpmsg_sdb_format_txbuf_string(struct sdb_buf_t *buffer, char *bufinfo_str, size_t bufinfo_str_size) +{ + pr_debug("rpmsg_sdb(%s): Buffer index:%d, addr:%08x, size:%08x\n", + __func__, + buffer->index, + buffer->paddr, + buffer->size); + return snprintf(bufinfo_str, bufinfo_str_size, "B%dA%08xL%08x", buffer->index, buffer->paddr, buffer->size); +} + +static long rpmsg_sdb_decode_rxbuf_string(char *rxbuf_str, int *buffer_id, size_t *size) +{ + int ret = 0; + char *sub_str; + long bsize; + long bufid; + const char delimiter[2] = {'L','\0'}; + + pr_debug("rpmsg_sdb(%s): rxbuf_str:%s\n", __func__, rxbuf_str); + + /* Get first part containing the buffer id */ + sub_str = strsep(&rxbuf_str, delimiter); + + pr_debug("rpmsg_sdb(%s): sub_str:%s\n", __func__, sub_str); + + /* Save Buffer id and size: template BxLyyyyyyyy*/ + ret = kstrtol(&sub_str[1], 10, &bufid); + if (ret < 0) { + pr_err("rpmsg_sdb(ERROR): Extract of buffer id failed(%d)", ret); + goto out; + } + + ret = kstrtol(&rxbuf_str[2], 16, &bsize); + if (ret < 0) { + pr_err("rpmsg_sdb(ERROR): Extract of buffer size failed(%d)", ret); + goto out; + } + + *size = (size_t)bsize; + *buffer_id = (int)bufid; + +out: + return ret; +} + +static int rpmsg_sdb_send_buf_info(struct rpmsg_sdb_t *rpmsg_sdb, struct sdb_buf_t *buffer) +{ + int count = 0, ret = 0; + const unsigned char *tbuf; + char mybuf[32]; + int msg_size; + struct rpmsg_device *_rpdev; + + _rpdev = rpmsg_sdb->rpdev; + msg_size = rpmsg_get_mtu(_rpdev->ept); + + if (msg_size < 0) + return msg_size; + + count = rpmsg_sdb_format_txbuf_string(buffer, mybuf, 32); + tbuf = &mybuf[0]; + + do { + /* send a message to our remote processor */ + ret = rpmsg_send(_rpdev->ept, (void *)tbuf, + count > msg_size ? msg_size : count); + if (ret) { + dev_err(&_rpdev->dev, "rpmsg_send failed: %d\n", ret); + return ret; + } + + if (count > msg_size) { + count -= msg_size; + tbuf += msg_size; + } else { + count = 0; + } + } while (count > 0); + + return count; +} + +static int rpmsg_sdb_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long vsize = vma->vm_end - vma->vm_start; + unsigned long size = PAGE_ALIGN(vsize); + unsigned long NumPages = size >> PAGE_SHIFT; + unsigned long align = get_order(size); + pgprot_t prot = pgprot_noncached(vma->vm_page_prot); + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *_buffer; + + if (align > CONFIG_CMA_ALIGNMENT) + align = CONFIG_CMA_ALIGNMENT; + + if (rpmsg_sdb_dev == NULL) + return -ENOMEM; + + rpmsg_sdb_dev->coherent_dma_mask = DMA_BIT_MASK(32); + rpmsg_sdb_dev->dma_mask = &rpmsg_sdb_dev->coherent_dma_mask; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Field the last buffer entry which is the last one created */ + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + _buffer = list_last_entry(&_rpmsg_sdb->buffer_list, + struct sdb_buf_t, buflist); + + _buffer->uaddr = NULL; + _buffer->size = NumPages * PAGE_SIZE; + _buffer->writing_size = -1; + _buffer->vaddr = dma_alloc_coherent(rpmsg_sdb_dev, + _buffer->size, + &_buffer->paddr, + GFP_KERNEL); + + if (!_buffer->vaddr) { + pr_err("rpmsg_sdb(ERROR): Memory allocation issue\n"); + return -ENOMEM; + } + + pr_debug("rpmsg_sdb(%s): dma_alloc_coherent done - paddr[%d]:%x - vaddr[%d]:%p\n", + __func__, + _buffer->index, + _buffer->paddr, + _buffer->index, + _buffer->vaddr); + + /* Get address for userland */ + if (remap_pfn_range(vma, vma->vm_start, + (_buffer->paddr >> PAGE_SHIFT) + vma->vm_pgoff, + size, prot)) + return -EAGAIN; + + _buffer->uaddr = (void *)vma->vm_start; + + /* Send information to remote proc */ + rpmsg_sdb_send_buf_info(_rpmsg_sdb, _buffer); + } else { + dev_err(rpmsg_sdb_dev, "No existing buffer entry exist in the list !!!"); + return -EINVAL; + } + + /* Increment for number of requested buffer */ + LastBufferId++; + + return 0; +} + +/** + * rpmsg_sdb_open - Open Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_open(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Initialize the buffer list*/ + INIT_LIST_HEAD(&_rpmsg_sdb->buffer_list); + + mutex_init(&_rpmsg_sdb->mutex); + + return 0; +} + +/** + * rpmsg_sdb_close - Close Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_close(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *pos, *next; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + list_for_each_entry_safe(pos, next, &_rpmsg_sdb->buffer_list, buflist) { + /* Free the CMA allocation */ + pr_debug("rpmsg_sdb(%s): Free the CMA allocation: pos->size:%08x, pos->vaddr:%08x, pos->paddr:%08x\n", + __func__, + pos->size, + pos->vaddr, + pos->paddr); + + dma_free_coherent(rpmsg_sdb_dev, pos->size, pos->vaddr, + pos->paddr); + /* Remove the buffer from the list */ + list_del(&pos->buflist); + /* Free the buffer */ + kfree(pos); + } + + /* Reset LastBufferId */ + LastBufferId = 0; + + return 0; +} + +/** + * rpmsg_sdb_ioctl - IOCTL + * + * @session: ibmvmc_file_session struct + * @cmd: cmd field + * @arg: Argument field + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static long rpmsg_sdb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int idx = 0; + + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *buffer, *lastbuffer; + + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + struct rpmsg_sdb_ioctl_set_efd q_set_efd; + struct rpmsg_sdb_ioctl_get_data_size q_get_dat_size; + + void __user *argp = (void __user *)arg; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + switch (cmd) { + case RPMSG_SDB_IOCTL_SET_EFD: + mutex_lock(&_rpmsg_sdb->mutex); + + /* Get index from the last buffer in the list */ + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + lastbuffer = list_last_entry(&_rpmsg_sdb->buffer_list, struct sdb_buf_t, buflist); + idx = lastbuffer->index; + + /* Check last index was properly initiated*/ + if (lastbuffer->vaddr == NULL) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - previous buffer was not allocated\n"); + mutex_unlock(&_rpmsg_sdb->mutex); + return -EBADE; + } + + /* increment this index for the next buffer creation*/ + idx++; + } + + if (copy_from_user(&q_set_efd, (struct rpmsg_sdb_ioctl_set_efd *)argp, + sizeof(struct rpmsg_sdb_ioctl_set_efd))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - copy from user failed\n"); + mutex_unlock(&_rpmsg_sdb->mutex); + return -EFAULT; + } + + /* create a new buffer which will be added in the buffer list */ + buffer = kmalloc(sizeof(struct sdb_buf_t), GFP_KERNEL); + + buffer->index = idx; + buffer->vaddr = NULL; + buffer->efd_ctx = eventfd_ctx_fdget(q_set_efd.eventfd); + list_add_tail(&buffer->buflist, &_rpmsg_sdb->buffer_list); + + mutex_unlock(&_rpmsg_sdb->mutex); + break; + + case RPMSG_SDB_IOCTL_GET_DATA_SIZE: + if (copy_from_user(&q_get_dat_size, (struct rpmsg_sdb_ioctl_get_data_size *)argp, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy from user failed\n"); + return -EFAULT; + } + + /* Get the index of the requested buffer and then look-up in the buffer list*/ + idx = q_get_dat_size.bufferId; + + list_for_each(pos, &_rpmsg_sdb->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == idx) { + /* Get the writing size*/ + q_get_dat_size.size = datastructureptr->writing_size; + break; + } + } + + if (copy_to_user((struct rpmsg_sdb_ioctl_get_data_size *)argp, &q_get_dat_size, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy to user failed\n"); + return -EFAULT; + } + + /* Reset the writing size*/ + datastructureptr->writing_size = -1; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct file_operations rpmsg_sdb_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = rpmsg_sdb_ioctl, + .mmap = rpmsg_sdb_mmap, + .open = rpmsg_sdb_open, + .release = rpmsg_sdb_close, +}; + +static int rpmsg_sdb_drv_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + int ret = 0; + int buffer_id = 0; + size_t buffer_size; + char *rpmsg_RxBuf; + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpdev->dev); + + if (len == 0) { + dev_err(rpmsg_sdb_dev, "(%s) Empty lenght requested\n", __func__); + return -EINVAL; + } + + //dev_err(rpmsg_sdb_dev, "(%s) lenght: %d\n", __func__,len); + + rpmsg_RxBuf = (char *)kmalloc(len+1, GFP_KERNEL); + memcpy(rpmsg_RxBuf, data, len); + + rpmsg_RxBuf[len] = 0; + + ret = rpmsg_sdb_decode_rxbuf_string(rpmsg_RxBuf, &buffer_id, &buffer_size); + kfree(rpmsg_RxBuf); + if (ret < 0) + goto out; + + if (buffer_id > LastBufferId) { + ret = -EINVAL; + goto out; + } + + /* Signal to User space application */ + list_for_each(pos, &drv->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == buffer_id) { + datastructureptr->writing_size = buffer_size; + + if (datastructureptr->writing_size > datastructureptr->size) { + dev_err(rpmsg_sdb_dev, "(%s) Writing size is bigger than buffer size\n", __func__); + ret = -EINVAL; + goto out; + } + + eventfd_signal(datastructureptr->efd_ctx, 1); + break; + } + /* TODO: quid if nothing find during the loop ? */ + } + +out: + return ret; +} + +static int rpmsg_sdb_drv_probe(struct rpmsg_device *rpdev) +{ + int ret = 0; + struct device *dev = &rpdev->dev; + struct rpmsg_sdb_t *rpmsg_sdb; + + rpmsg_sdb = devm_kzalloc(dev, sizeof(*rpmsg_sdb), GFP_KERNEL); + if (!rpmsg_sdb) + return -ENOMEM; + + mutex_init(&rpmsg_sdb->mutex); + + rpmsg_sdb->rpdev = rpdev; + + rpmsg_sdb->mdev.name = "rpmsg-sdb"; + rpmsg_sdb->mdev.minor = MISC_DYNAMIC_MINOR; + rpmsg_sdb->mdev.fops = &rpmsg_sdb_fops; + + dev_set_drvdata(&rpdev->dev, rpmsg_sdb); + + /* Register misc device */ + ret = misc_register(&rpmsg_sdb->mdev); + + if (ret) { + dev_err(dev, "Failed to register device\n"); + goto err_out; + } + + rpmsg_sdb_dev = rpmsg_sdb->mdev.this_device; + + dev_info(dev, "%s probed\n", rpmsg_sdb_driver_name); + +err_out: + return ret; +} + +static void rpmsg_sdb_drv_remove(struct rpmsg_device *rpmsgdev) +{ + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpmsgdev->dev); + + misc_deregister(&drv->mdev); +} + +static struct rpmsg_device_id rpmsg_driver_sdb_id_table[] = { + { .name = "rpmsg-sdb-channel" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sdb_id_table); + +static struct rpmsg_driver rpmsg_sdb_rmpsg_drv = { + .drv.name = KBUILD_MODNAME, + .drv.owner = THIS_MODULE, + .id_table = rpmsg_driver_sdb_id_table, + .probe = rpmsg_sdb_drv_probe, + .callback = rpmsg_sdb_drv_cb, + .remove = rpmsg_sdb_drv_remove, +}; + +static int __init rpmsg_sdb_drv_init(void) +{ + int ret = 0; + + /* Register rpmsg device */ + ret = register_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + + if (ret) { + pr_err("rpmsg_sdb(ERROR): Failed to register device\n"); + return ret; + } + + pr_info("rpmsg_sdb: Init done\n"); + + return ret; +} + +static void __exit rpmsg_sdb_drv_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + pr_info("rpmsg_sdb: Exit\n"); +} + +module_init(rpmsg_sdb_drv_init); +module_exit(rpmsg_sdb_drv_exit); + + +MODULE_AUTHOR("Jean-Philippe Romain "); +MODULE_DESCRIPTION("shared data buffer over RPMSG"); +MODULE_VERSION(RPMSG_SDB_DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-mon-datafile copy.c b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-mon-datafile copy.c new file mode 100644 index 0000000..ebe1af1 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-mon-datafile copy.c @@ -0,0 +1,552 @@ +/* Copyright 2024 Markus Lehr + * + * + * This Software is owned by Markus Lehr. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// ################################################################################################## +/* + * This is a Linux kernel module/driver called "vrpmdv-monitoring-cmd " which holds the family ID + * and functions to handle the monitoring in a Generic Netlink in the kernel. + * "It registers a Netlink family called "vrpmdv-monitoring_cmd". + * + * + * You can find some more interesting documentation about Generic Netlink here: + * "Generic Netlink HOW-TO based on Jamal's original doc" https://lwn.net/Articles/208755/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VRPMDV_MON_DRIVER_VERSION "1.0" + +/* + * Static global variables + */ +static const char vrpmdv_mon_datafile[] = "vrpmdv-mon-datafile"; + +static int LastBufferId; + +struct rpmsg_sdb_ioctl_set_efd { + int bufferId, eventfd; +}; + +struct rpmsg_sdb_ioctl_get_data_size { + int bufferId; + uint32_t size; +}; + +/* ioctl numbers */ +/* _IOW means userland is writing and kernel is reading */ +/* _IOR means userland is reading and kernel is writing */ +/* _IOWR means userland and kernel can both read and write */ +#define RPMSG_SDB_IOCTL_SET_EFD _IOW('R', 0x00, struct rpmsg_sdb_ioctl_set_efd *) +#define RPMSG_SDB_IOCTL_GET_DATA_SIZE _IOWR('R', 0x01, struct rpmsg_sdb_ioctl_get_data_size *) + +struct sdb_buf_t { + int index; /* index of buffer */ + size_t size; /* buffer size */ + size_t writing_size; /* size of data written by copro */ + dma_addr_t paddr; /* physical address*/ + void *vaddr; /* virtual address */ + void *uaddr; /* mapped address for userland */ + struct eventfd_ctx *efd_ctx; /* eventfd context */ + struct list_head buflist; /* reference in the buffers list */ +}; + +struct rpmsg_sdb_t { + struct mutex mutex; /* mutex to protect the ioctls */ + struct miscdevice mdev; /* misc device ref */ + struct rpmsg_device *rpdev; /* handle rpmsg device */ + struct list_head buffer_list; /* buffer instances list */ +}; + +struct device *rpmsg_sdb_dev; + +static int rpmsg_sdb_format_txbuf_string(struct sdb_buf_t *buffer, char *bufinfo_str, size_t bufinfo_str_size) +{ + pr_debug("vrpmdv-mon-datafile(%s): Buffer index:%d, addr:%08x, size:%08x\n", + __func__, + buffer->index, + buffer->paddr, + buffer->size); + return snprintf(bufinfo_str, bufinfo_str_size, "B%dA%08xL%08x", buffer->index, buffer->paddr, buffer->size); +} + +static long rpmsg_sdb_decode_rxbuf_string(char *rxbuf_str, int *buffer_id, size_t *size) +{ + int ret = 0; + char *sub_str; + long bsize; + long bufid; + const char delimiter[2] = {'L','\0'}; + + pr_debug("vrpmdv-mon-datafile(%s): rxbuf_str:%s\n", __func__, rxbuf_str); + + /* Get first part containing the buffer id */ + sub_str = strsep(&rxbuf_str, delimiter); + + pr_debug("vrpmdv-mon-datafile(%s): sub_str:%s\n", __func__, sub_str); + + /* Save Buffer id and size: template BxLyyyyyyyy*/ + ret = kstrtol(&sub_str[1], 10, &bufid); + if (ret < 0) { + pr_err("vrpmdv-mon-datafile(ERROR): Extract of buffer id failed(%d)", ret); + goto out; + } + + ret = kstrtol(&rxbuf_str[2], 16, &bsize); + if (ret < 0) { + pr_err("vrpmdv-mon-datafile(ERROR): Extract of buffer size failed(%d)", ret); + goto out; + } + + *size = (size_t)bsize; + *buffer_id = (int)bufid; + +out: + return ret; +} + +static int rpmsg_sdb_send_buf_info(struct rpmsg_sdb_t *rpmsg_sdb, struct sdb_buf_t *buffer) +{ + int count = 0, ret = 0; + const unsigned char *tbuf; + char mybuf[32]; + int msg_size; + struct rpmsg_device *_rpdev; + + _rpdev = rpmsg_sdb->rpdev; + msg_size = rpmsg_get_mtu(_rpdev->ept); + + if (msg_size < 0) + return msg_size; + + count = rpmsg_sdb_format_txbuf_string(buffer, mybuf, 32); + tbuf = &mybuf[0]; + + do { + /* send a message to our remote processor */ + ret = rpmsg_send(_rpdev->ept, (void *)tbuf, + count > msg_size ? msg_size : count); + if (ret) { + dev_err(&_rpdev->dev, "vrpmdv-mon-datafile failed: %d\n", ret); + return ret; + } + + if (count > msg_size) { + count -= msg_size; + tbuf += msg_size; + } else { + count = 0; + } + } while (count > 0); + + return count; +} + +static int rpmsg_sdb_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long vsize = vma->vm_end - vma->vm_start; + unsigned long size = PAGE_ALIGN(vsize); + unsigned long NumPages = size >> PAGE_SHIFT; + unsigned long align = get_order(size); + pgprot_t prot = pgprot_noncached(vma->vm_page_prot); + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *_buffer; + + if (align > CONFIG_CMA_ALIGNMENT) + align = CONFIG_CMA_ALIGNMENT; + + if (rpmsg_sdb_dev == NULL) + return -ENOMEM; + + rpmsg_sdb_dev->coherent_dma_mask = DMA_BIT_MASK(32); + rpmsg_sdb_dev->dma_mask = &rpmsg_sdb_dev->coherent_dma_mask; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Field the last buffer entry which is the last one created */ + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + _buffer = list_last_entry(&_rpmsg_sdb->buffer_list, + struct sdb_buf_t, buflist); + + _buffer->uaddr = NULL; + _buffer->size = NumPages * PAGE_SIZE; + _buffer->writing_size = -1; + _buffer->vaddr = dma_alloc_coherent(rpmsg_sdb_dev, + _buffer->size, + &_buffer->paddr, + GFP_KERNEL); + + if (!_buffer->vaddr) { + pr_err("vrpmdv-mon-datafile(ERROR): Memory allocation issue\n"); + return -ENOMEM; + } + + pr_debug("vrpmdv-mon-datafile(%s): dma_alloc_coherent done - paddr[%d]:%x - vaddr[%d]:%p\n", + __func__, + _buffer->index, + _buffer->paddr, + _buffer->index, + _buffer->vaddr); + + /* Get address for userland */ + if (remap_pfn_range(vma, vma->vm_start, + (_buffer->paddr >> PAGE_SHIFT) + vma->vm_pgoff, + size, prot)) + return -EAGAIN; + + _buffer->uaddr = (void *)vma->vm_start; + + /* Send information to remote proc */ + rpmsg_sdb_send_buf_info(_rpmsg_sdb, _buffer); + } else { + dev_err(rpmsg_sdb_dev, "No existing buffer entry exist in the list !!!"); + return -EINVAL; + } + + /* Increment for number of requested buffer */ + LastBufferId++; + + return 0; +} + +/** + * rpmsg_sdb_open - Open Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_open(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Initialize the buffer list*/ + INIT_LIST_HEAD(&_rpmsg_sdb->buffer_list); + + mutex_init(&_rpmsg_sdb->mutex); + + return 0; +} + +/** + * rpmsg_sdb_close - Close Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_close(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *pos, *next; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t,mdev); + + list_for_each_entry_safe(pos, next, &_rpmsg_sdb->buffer_list, buflist) { + /* Free the CMA allocation */ + pr_debug("vrpmdv-mon-datafile(%s): Free the CMA allocation: pos->size:%08x, pos->vaddr:%08x, pos->paddr:%08x\n", + __func__, + pos->size, + pos->vaddr, + pos->paddr); + + dma_free_coherent(rpmsg_sdb_dev, pos->size, pos->vaddr, + pos->paddr); + /* Remove the buffer from the list */ + list_del(&pos->buflist); + /* Free the buffer */ + kfree(pos); + } + + /* Reset LastBufferId */ + LastBufferId = 0; + + return 0; +} + +/** + * rpmsg_sdb_ioctl - IOCTL + * + * @session: ibmvmc_file_session struct + * @cmd: cmd field + * @arg: Argument field + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static long rpmsg_sdb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int idx = 0; + + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *buffer, *lastbuffer; + + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + struct rpmsg_sdb_ioctl_set_efd q_set_efd; + struct rpmsg_sdb_ioctl_get_data_size q_get_dat_size; + + void __user *argp = (void __user *)arg; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + switch (cmd) { + case RPMSG_SDB_IOCTL_SET_EFD: + mutex_lock(&_rpmsg_sdb->mutex); + + /* Get index from the last buffer in the list */ + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + lastbuffer = list_last_entry(&_rpmsg_sdb->buffer_list, struct sdb_buf_t, buflist); + idx = lastbuffer->index; + + /* Check last index was properly initiated*/ + if (lastbuffer->vaddr == NULL) { + pr_err("vrpmdv-mon-datafile(ERROR): RPMSG_SDB_IOCTL_SET_EFD - previous buffer was not allocated\n"); + mutex_unlock(&_rpmsg_sdb->mutex); + return -EBADE; + } + + /* increment this index for the next buffer creation*/ + idx++; + } + + if (copy_from_user(&q_set_efd, (struct rpmsg_sdb_ioctl_set_efd *)argp, + sizeof(struct rpmsg_sdb_ioctl_set_efd))) { + pr_err("vrpmdv-mon-datafile(ERROR): RPMSG_SDB_IOCTL_SET_EFD - copy from user failed\n"); + mutex_unlock(&_rpmsg_sdb->mutex); + return -EFAULT; + } + + /* create a new buffer which will be added in the buffer list */ + buffer = kmalloc(sizeof(struct sdb_buf_t), GFP_KERNEL); + + buffer->index = idx; + buffer->vaddr = NULL; + buffer->efd_ctx = eventfd_ctx_fdget(q_set_efd.eventfd); + list_add_tail(&buffer->buflist, &_rpmsg_sdb->buffer_list); + + mutex_unlock(&_rpmsg_sdb->mutex); + break; + + case RPMSG_SDB_IOCTL_GET_DATA_SIZE: + if (copy_from_user(&q_get_dat_size, (struct rpmsg_sdb_ioctl_get_data_size *)argp, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("vrpmdv-mon-datafile(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy from user failed\n"); + return -EFAULT; + } + + /* Get the index of the requested buffer and then look-up in the buffer list*/ + idx = q_get_dat_size.bufferId; + + list_for_each(pos, &_rpmsg_sdb->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == idx) { + /* Get the writing size*/ + q_get_dat_size.size = datastructureptr->writing_size; + break; + } + } + + if (copy_to_user((struct rpmsg_sdb_ioctl_get_data_size *)argp, &q_get_dat_size, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("vrpmdv-mon-datafile(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy to user failed\n"); + return -EFAULT; + } + + /* Reset the writing size*/ + datastructureptr->writing_size = -1; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct file_operations rpmsg_sdb_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = rpmsg_sdb_ioctl, + .mmap = rpmsg_sdb_mmap, + .open = rpmsg_sdb_open, + .release = rpmsg_sdb_close, +}; + +static int rpmsg_sdb_drv_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + int ret = 0; + int buffer_id = 0; + size_t buffer_size; + char *rpmsg_RxBuf; + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpdev->dev); + + if (len == 0) { + dev_err(rpmsg_sdb_dev, "(%s) Empty lenght requested\n", __func__); + return -EINVAL; + } + + //dev_err(rpmsg_sdb_dev, "(%s) lenght: %d\n", __func__,len); + + rpmsg_RxBuf = (char *)kmalloc(len+1, GFP_KERNEL); + memcpy(rpmsg_RxBuf, data, len); + + rpmsg_RxBuf[len] = 0; + + ret = rpmsg_sdb_decode_rxbuf_string(rpmsg_RxBuf, &buffer_id, &buffer_size); + kfree(rpmsg_RxBuf); + if (ret < 0) + goto out; + + if (buffer_id > LastBufferId) { + ret = -EINVAL; + goto out; + } + + /* Signal to User space application */ + list_for_each(pos, &drv->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == buffer_id) { + datastructureptr->writing_size = buffer_size; + + if (datastructureptr->writing_size > datastructureptr->size) { + dev_err(rpmsg_sdb_dev, "(%s) Writing size is bigger than buffer size\n", __func__); + ret = -EINVAL; + goto out; + } + + eventfd_signal(datastructureptr->efd_ctx, 1); + break; + } + /* TODO: quid if nothing find during the loop ? */ + } + +out: + return ret; +} + +static int rpmsg_sdb_drv_probe(struct rpmsg_device *rpdev) +{ + int ret = 0; + struct device *dev = &rpdev->dev; + struct rpmsg_sdb_t *rpmsg_sdb; + + rpmsg_sdb = devm_kzalloc(dev, sizeof(*rpmsg_sdb), GFP_KERNEL); + if (!rpmsg_sdb) + return -ENOMEM; + + mutex_init(&rpmsg_sdb->mutex); + + rpmsg_sdb->rpdev = rpdev; + + rpmsg_sdb->mdev.name = "vrpmdv-mon-datafile"; //rpmsg-sdb + rpmsg_sdb->mdev.minor = MISC_DYNAMIC_MINOR; + rpmsg_sdb->mdev.fops = &rpmsg_sdb_fops; + + dev_set_drvdata(&rpdev->dev, rpmsg_sdb); + + /* Register misc device */ + ret = misc_register(&rpmsg_sdb->mdev); + + if (ret) { + dev_err(dev, "Failed to register device\n"); + goto err_out; + } + + rpmsg_sdb_dev = rpmsg_sdb->mdev.this_device; + + dev_info(dev, "%s probed\n", vrpmdv_mon_datafile); + +err_out: + return ret; +} + +static void rpmsg_sdb_drv_remove(struct rpmsg_device *rpmsgdev) +{ + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpmsgdev->dev); + + misc_deregister(&drv->mdev); +} + +static struct rpmsg_device_id rpmsg_driver_sdb_id_table[] = { + { .name = "vrpmdv-mon-datafile" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sdb_id_table); + +static struct rpmsg_driver rpmsg_sdb_rmpsg_drv = { + .drv.name = KBUILD_MODNAME, + .drv.owner = THIS_MODULE, + .id_table = rpmsg_driver_sdb_id_table, + .probe = rpmsg_sdb_drv_probe, + .callback = rpmsg_sdb_drv_cb, + .remove = rpmsg_sdb_drv_remove, +}; + +static int __init rpmsg_sdb_drv_init(void) +{ + int ret = 0; + + /* Register rpmsg device */ + ret = register_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + + if (ret) { + pr_err("vrpmdv-mon-datafile(ERROR): Failed to register device\n"); + return ret; + } + + pr_info("vrpmdv-mon-datafile: Init done\n"); + + return ret; +} + +static void __exit rpmsg_sdb_drv_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + pr_info("vrpmdv-mon-datafile: Exit\n"); +} + +module_init(rpmsg_sdb_drv_init); +module_exit(rpmsg_sdb_drv_exit); + + +MODULE_AUTHOR("Markus Lehr "); +MODULE_DESCRIPTION("shared data buffer over RPMSG"); +MODULE_VERSION(VRPMDV_MON_DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-mon-datafile-before-DMA-Change.c b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-mon-datafile-before-DMA-Change.c new file mode 100644 index 0000000..b76446c --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-mon-datafile-before-DMA-Change.c @@ -0,0 +1,706 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Jean-Philippe Romain + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RPMSG_SDB_DRIVER_VERSION "1.0" + +/* + * Static global variables + */ +static const char rpmsg_sdb_driver_name[] = "vrpmdv-mon-datafile"; + +static int LastBufferId; + +struct rpmsg_sdb_ioctl_set_efd { + int bufferId, eventfd; +}; + +struct rpmsg_sdb_ioctl_get_data_size { + int bufferId; + uint32_t size; +}; + +/* ioctl numbers */ +/* _IOW means userland is writing and kernel is reading */ +/* _IOR means userland is reading and kernel is writing */ +/* _IOWR means userland and kernel can both read and write */ +#define RPMSG_SDB_IOCTL_SET_EFD _IOW('R', 0x00, struct rpmsg_sdb_ioctl_set_efd *) +#define RPMSG_SDB_IOCTL_GET_DATA_SIZE _IOWR('R', 0x01, struct rpmsg_sdb_ioctl_get_data_size *) + +struct sdb_buf_t { + int index; /* index of buffer */ + size_t size; /* buffer size */ + size_t writing_size; /* size of data written by copro */ + dma_addr_t paddr; /* physical address*/ + void *vaddr; /* virtual address */ + void *uaddr; /* mapped address for userland */ + struct eventfd_ctx *efd_ctx; /* eventfd context */ + struct list_head buflist; /* reference in the buffers list */ +}; + +struct rpmsg_sdb_t { + struct mutex mutex; /* mutex to protect the ioctls */ + struct miscdevice mdev; /* misc device ref */ + struct rpmsg_device *rpdev; /* handle rpmsg device */ + struct list_head buffer_list; /* buffer instances list */ +}; + +struct device *rpmsg_sdb_dev; + +struct vRCMDeviceData { + uint32_t packageNo; //current package Number + uint32_t packageCount; //complete package Number + uint32_t dataQuantity; //number of uint32_t in data + uint32_t data[]; //the data +}; + +static int rpmsg_sdb_format_txbuf_string(struct sdb_buf_t *buffer, char *bufinfo_str, size_t bufinfo_str_size) +{ + pr_info("rpmsg_sdb(%s): Buffer index:%d, addr:%08x, size:%08x\n", + __func__, + buffer->index, + buffer->paddr, + buffer->size); + return snprintf(bufinfo_str, bufinfo_str_size, "B%dA%08xL%08x", buffer->index, buffer->paddr, buffer->size); +} + +static long rpmsg_sdb_decode_rxbuf_string(char *rxbuf_str, int *buffer_id, size_t *size) +{ + int ret = 0; + char *sub_str; + long bsize; + long bufid; + const char delimiter[2] = {'L','\0'}; + //__u32* data = (__u32*) rxbuf_str; + struct vRCMDeviceData* pdata = (struct vRCMDeviceData*) rxbuf_str; + + + //pr_info("rpmsg_sdb(%s): rxbuf_str:%s\n", __func__, rxbuf_str); + pr_info("rpmsg_sdb(%s): packageno:%d\n", __func__, pdata->packageNo); + pr_info("rpmsg_sdb(%s): packageCount:%d\n", __func__, pdata->packageCount); + pr_info("rpmsg_sdb(%s): dataquantity:%d\n", __func__, pdata->dataQuantity); + pr_info("rpmsg_sdb(%s): data:%d\n", __func__, (pdata->data)[0]); + // pr_info("rpmsg_sdb(%s): rxbuf_str:%d\n", __func__, data[0]); + // pr_info("rpmsg_sdb(%s): rxbuf_str:%d\n", __func__, data[1]); + // pr_info("rpmsg_sdb(%s): rxbuf_str:%d\n", __func__, data[2]); + + + + /* Get first part containing the buffer id */ +// sub_str = strsep(&rxbuf_str, delimiter); + +// // pr_info("rpmsg_sdb(%s): sub_str:%s\n", __func__, sub_str); + +// /* Save Buffer id and size: template BxLyyyyyyyy*/ +// ret = kstrtol(&sub_str[1], 10, &bufid); +// if (ret < 0) { +// // pr_info("rpmsg_sdb(ERROR): Extract of buffer id failed(%d)", ret); +// goto out; +// } + +// ret = kstrtol(&rxbuf_str[2], 16, &bsize); +// if (ret < 0) { +// // pr_info("rpmsg_sdb(ERROR): Extract of buffer size failed(%d)", ret); +// goto out; +// } + +// *size = (size_t)bsize; +// *buffer_id = (int)bufid; + +// out: + return ret; +} + +static int rpmsg_sdb_send_buf_info(struct rpmsg_sdb_t *rpmsg_sdb, struct sdb_buf_t *buffer) +{ + int count = 0, ret = 0; + const unsigned char *tbuf; + char mybuf[32]; + int msg_size; + struct rpmsg_device *_rpdev; + + pr_info("rpmsg_sdb(%s): start rpmsg_sdb_send_buf_info\n", __func__); + _rpdev = rpmsg_sdb->rpdev; + msg_size = rpmsg_get_mtu(_rpdev->ept); + + pr_info("rpmsg_sdb(%s): checked msg site:%d\n", __func__, msg_size); + if (msg_size < 0) + return msg_size; + + pr_info("rpmsg_sdb(%s): Call rpmsg_sdb_format_txbuf_string\n", __func__); + count = rpmsg_sdb_format_txbuf_string(buffer, mybuf, 32); + tbuf = &mybuf[0]; + + do { + /* send a message to our remote processor */ + pr_info("rpmsg_sdb(%s): send a message to our remote processor\n", __func__); + ret = rpmsg_send(_rpdev->ept, (void *)tbuf, + count > msg_size ? msg_size : count); + if (ret) { + dev_err(&_rpdev->dev, "rpmsg_send failed: %d\n", ret); + pr_info("rpmsg_sdb(%s): error by send a message to our remote processor\n", __func__); + return ret; + } + else { + pr_info("rpmsg_sdb(%s): send succesfully a message to our remote processor\n", __func__); + } + + if (count > msg_size) { + count -= msg_size; + tbuf += msg_size; + } else { + count = 0; + } + } while (count > 0); + + return count; +} + +static int rpmsg_sdb_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long vsize = vma->vm_end - vma->vm_start; + unsigned long size = PAGE_ALIGN(vsize); + unsigned long NumPages = size >> PAGE_SHIFT; + unsigned long align = get_order(size); + pgprot_t prot = pgprot_noncached(vma->vm_page_prot); + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *_buffer; + + pr_info("rpmsg_sdb(%s): start rpmsg_sdb_mmap\n", __func__); + if (align > CONFIG_CMA_ALIGNMENT) + align = CONFIG_CMA_ALIGNMENT; + + if (rpmsg_sdb_dev == NULL) + return -ENOMEM; + + pr_info("rpmsg_sdb(%s): start rpmsg_sdb_mmap - coherent_dma_mask\n", __func__); + rpmsg_sdb_dev->coherent_dma_mask = DMA_BIT_MASK(32); + rpmsg_sdb_dev->dma_mask = &rpmsg_sdb_dev->coherent_dma_mask; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Field the last buffer entry which is the last one created */ + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + pr_info("rpmsg_sdb(%s): start rpmsg_sdb_mmap - bufferlist not empty\n", __func__); + _buffer = list_last_entry(&_rpmsg_sdb->buffer_list, + struct sdb_buf_t, buflist); + + _buffer->uaddr = NULL; + _buffer->size = NumPages * PAGE_SIZE; + _buffer->writing_size = -1; + _buffer->vaddr = dma_alloc_coherent(rpmsg_sdb_dev, + _buffer->size, + &_buffer->paddr, + GFP_KERNEL); + + + if (!_buffer->vaddr) { + pr_info("rpmsg_sdb(ERROR): Memory allocation issue\n"); + return -ENOMEM; + } + + pr_info("rpmsg_sdb(%s): dma_alloc_coherent done - paddr[%d]:%x - vaddr[%d]:%p\n", + __func__, + _buffer->index, + _buffer->paddr, + _buffer->index, + _buffer->vaddr); + + /* Get address for userland */ + if (remap_pfn_range(vma, vma->vm_start, + (_buffer->paddr >> PAGE_SHIFT) + vma->vm_pgoff, + size, prot)) { + + pr_info("rpmsg_sdb(%s): remap_pfn_range could not be done\n"); + return -EAGAIN; + } + + + _buffer->uaddr = (void *)vma->vm_start; + pr_info("rpmsg_sdb(%s): _buffer->uaddr = %p\n", __func__, _buffer->uaddr); + + /* Send information to remote proc */ + pr_info("rpmsg_sdb(%s): Send information to remote proc\n", __func__); + rpmsg_sdb_send_buf_info(_rpmsg_sdb, _buffer); + } else { + dev_err(rpmsg_sdb_dev, "No existing buffer entry exist in the list !!!"); + pr_debug("rpmsg_sdb(%s): No existing buffer entry exist in the list !!!\n", __func__); + return -EINVAL; + } + + /* Increment for number of requested buffer */ + LastBufferId++; + + return 0; +} + +/** + * rpmsg_sdb_open - Open Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_open(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Initialize the buffer list*/ + pr_debug("rpmsg_sdb(%s): Init bufferlist", __func__); + pr_info("rpmsg_sdb(%s): start Init bufferlist", __func__); + INIT_LIST_HEAD(&_rpmsg_sdb->buffer_list); + pr_info("rpmsg_sdb(%s): success Init bufferlist", __func__); + + mutex_init(&_rpmsg_sdb->mutex); + + return 0; +} + +/** + * rpmsg_sdb_close - Close Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_close(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *pos, *next; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + list_for_each_entry_safe(pos, next, &_rpmsg_sdb->buffer_list, buflist) { + /* Free the CMA allocation */ + pr_info("rpmsg_sdb(%s): Free the CMA allocation 3", __func__); + dma_free_coherent(rpmsg_sdb_dev, pos->size, pos->vaddr, pos->paddr); + /* Remove the buffer from the list */ + list_del(&pos->buflist); + /* Free the buffer */ + kfree(pos); + } + + pr_info("rpmsg_sdb(%s): Free the CMA allocation 4 - done", __func__); + /* Reset LastBufferId */ + LastBufferId = 0; + + return 0; +} + +/** + * rpmsg_sdb_ioctl - IOCTL + * + * @session: ibmvmc_file_session struct + * @cmd: cmd field + * @arg: Argument field + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static long rpmsg_sdb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int idx = 0; + + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *buffer, *lastbuffer; + + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + struct rpmsg_sdb_ioctl_set_efd q_set_efd; + struct rpmsg_sdb_ioctl_get_data_size q_get_dat_size; + + void __user *argp = (void __user *)arg; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + switch (cmd) { + case RPMSG_SDB_IOCTL_SET_EFD: + mutex_lock(&_rpmsg_sdb->mutex); + + /* Get index from the last buffer in the list */ + pr_info("mon-datafile: set EFD\n"); + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + lastbuffer = list_last_entry(&_rpmsg_sdb->buffer_list, struct sdb_buf_t, buflist); + idx = lastbuffer->index; + + /* Check last index was properly initiated*/ + if (lastbuffer->vaddr == NULL) { + // pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - previous buffer was not allocated\n"); + pr_info("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - previous buffer was not allocated\n"); + mutex_unlock(&_rpmsg_sdb->mutex); + return -EBADE; + } + + /* increment this index for the next buffer creation*/ + idx++; + } + + if (copy_from_user(&q_set_efd, (struct rpmsg_sdb_ioctl_set_efd *)argp, + sizeof(struct rpmsg_sdb_ioctl_set_efd))) { + // pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - copy from user failed\n"); + pr_info("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - copy from user failed\n"); + + mutex_unlock(&_rpmsg_sdb->mutex); + return -EFAULT; + } + + /* create a new buffer which will be added in the buffer list */ + buffer = kmalloc(sizeof(struct sdb_buf_t), GFP_KERNEL); + + buffer->index = idx; + buffer->vaddr = NULL; + buffer->efd_ctx = eventfd_ctx_fdget(q_set_efd.eventfd); + list_add_tail(&buffer->buflist, &_rpmsg_sdb->buffer_list); + + pr_info("rpmsg_sdb(SUCCES): RPMSG_SDB_IOCTL_SET_EFD \n"); + + mutex_unlock(&_rpmsg_sdb->mutex); + break; + + case RPMSG_SDB_IOCTL_GET_DATA_SIZE: + pr_info("mon-datafile: get datasize\n"); + if (copy_from_user(&q_get_dat_size, (struct rpmsg_sdb_ioctl_get_data_size *)argp, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy from user failed\n"); + return -EFAULT; + } + + /* Get the index of the requested buffer and then look-up in the buffer list*/ + idx = q_get_dat_size.bufferId; + + list_for_each(pos, &_rpmsg_sdb->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == idx) { + /* Get the writing size*/ + q_get_dat_size.size = datastructureptr->writing_size; + break; + } + } + + if (copy_to_user((struct rpmsg_sdb_ioctl_get_data_size *)argp, &q_get_dat_size, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy to user failed\n"); + return -EFAULT; + } + + /* Reset the writing size*/ + datastructureptr->writing_size = -1; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct file_operations rpmsg_sdb_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = rpmsg_sdb_ioctl, + .mmap = rpmsg_sdb_mmap, + .open = rpmsg_sdb_open, + .release = rpmsg_sdb_close, +}; + +static int rpmsg_sdb_drv_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + int ret = 0; + int buffer_id = 0; + size_t buffer_size; + char *rpmsg_RxBuf; + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + //pr_info("mon-datafile: receive msg from Copro: %s, len:%d\n", __func__, len); + + // struct rpmsg_sdb_t { + // struct mutex mutex; /* mutex to protect the ioctls */ + // struct miscdevice mdev; /* misc device ref */ + // struct rpmsg_device *rpdev; /* handle rpmsg device */ + // struct list_head buffer_list; /* buffer instances list */ + // }; + + + rpmsg_RxBuf = (char *)kmalloc(len+1, GFP_KERNEL); + memcpy(rpmsg_RxBuf, data, len); + + rpmsg_RxBuf[len] = 0; + + // ret = rpmsg_sdb_decode_rxbuf_string(rpmsg_RxBuf, &buffer_id, &buffer_size); + + // if (ret < 0) + // goto out; + + // if (buffer_id > LastBufferId) { + // ret = -EINVAL; + // goto out; + // } + + struct vRCMDeviceData* pdata = (struct vRCMDeviceData*) rpmsg_RxBuf; + + + //pr_info("rpmsg_sdb(%s): rxbuf_str:%s\n", __func__, rxbuf_str); + // pr_info("rpmsg_sdb(%s): packageno:%d\n", __func__, pdata->packageNo); + // pr_info("rpmsg_sdb(%s): packageCount:%d\n", __func__, pdata->packageCount); + // pr_info("rpmsg_sdb(%s): dataquantity:%d\n", __func__, pdata->dataQuantity); + // pr_info("rpmsg_sdb(%s): data:%d\n", __func__, (pdata->data)[0]); + + + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpdev->dev); + + if (len == 0) { + dev_err(rpmsg_sdb_dev, "(%s) Empty lenght requested\n", __func__); + return -EINVAL; + } + + if (drv == NULL) { + dev_err(rpmsg_sdb_dev, "(%s) no driver found\n", __func__); + return -EINVAL; + } + + //copy data to structure + //1. get buffer + + // struct vRCMDeviceData* pdata = (struct vRCMDeviceData*) kmalloc(len+1, GFP_KERNEL); + // memcpy(pdata, data, len); + + // if (ret < 0) + // goto out; + + // if (buffer_id > LastBufferId) { + // ret = -EINVAL; + // goto out; + // } + + +/** + rpmsg_RxBuf = (char *)kmalloc(len+1, GFP_KERNEL); + memcpy(rpmsg_RxBuf, data, len); + + rpmsg_RxBuf[len] = 0; + + ret = rpmsg_sdb_decode_rxbuf_string(rpmsg_RxBuf, &buffer_id, &buffer_size); + kfree(rpmsg_RxBuf); + if (ret < 0) + goto out; + + if (buffer_id > LastBufferId) { + ret = -EINVAL; + goto out; + } +*/ + /* Signal to User space application */ + list_for_each(pos, &drv->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + + + /** + * struct vRCMDeviceData { + uint32_t packageNo; //current package Number + uint32_t packageCount; //complete package Number + uint32_t dataQuantity; //number of uint32_t in data + uint32_t data[]; //the data + }; + */ + //copy data to buffer + + + // struct sdb_buf_t { + // int index; /* index of buffer */ + // size_t size; /* buffer size */ + // size_t writing_size; /* size of data written by copro */ + // dma_addr_t paddr; /* physical address*/ + // void *vaddr; /* virtual address */ + // void *uaddr; /* mapped address for userland */ + // struct eventfd_ctx *efd_ctx; /* eventfd context */ + // struct list_head buflist; /* reference in the buffers list */ + // }; + + //get the buffer as array of struct vRCMDeviceData + struct vRCMDeviceData* vrBuff = (struct vRCMDeviceData*) (datastructureptr->vaddr); + struct vRCMDeviceData* startAdress = &(vrBuff[pdata->packageNo]); + // pr_info("rpmsg_sdb(%s): startAdress:%d\n", __func__, startAdress); + + if (virt_addr_valid(startAdress)) { + // pr_info("rpmsg_sdb(%s): startAdress:%d is valid\n", __func__, startAdress); + memcpy(startAdress, rpmsg_RxBuf, len); + } + else { + pr_info("rpmsg_sdb(%s): startAdress[%d]:%p is invalid!!\n", __func__, startAdress); + } + + + // if (datastructureptr->index == buffer_id) { + // datastructureptr->writing_size = buffer_size; + + // if (datastructureptr->writing_size > datastructureptr->size) { + // dev_err(rpmsg_sdb_dev, "(%s) Writing size is bigger than buffer size\n", __func__); + // ret = -EINVAL; + // goto out; + // } + + if ((pdata != NULL) && (pdata->packageNo == pdata->packageCount)) { + pr_info("rpmsg_sdb(%s): signal bufferfull!\n", __func__); + eventfd_signal(datastructureptr->efd_ctx, 1); + break; + + } + + } + + + /* Signal to User space application */ +/** list_for_each(pos, &drv->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == buffer_id) { + datastructureptr->writing_size = buffer_size; + + if (datastructureptr->writing_size > datastructureptr->size) { + dev_err(rpmsg_sdb_dev, "(%s) Writing size is bigger than buffer size\n", __func__); + ret = -EINVAL; + goto out; + } + + eventfd_signal(datastructureptr->efd_ctx, 1); + break; + } + } +*/ + +out: + kfree(rpmsg_RxBuf); + kfree(pdata); + return ret; +} + +static int rpmsg_sdb_drv_probe(struct rpmsg_device *rpdev) +{ + int ret = 0; + struct device *dev = &rpdev->dev; + struct rpmsg_sdb_t *rpmsg_sdb; + + pr_info("mon-datafile: registering started\n"); + + rpmsg_sdb = devm_kzalloc(dev, sizeof(*rpmsg_sdb), GFP_KERNEL); + if (!rpmsg_sdb) + return -ENOMEM; + + mutex_init(&rpmsg_sdb->mutex); + + rpmsg_sdb->rpdev = rpdev; + + rpmsg_sdb->mdev.name = "mon-datafile";//"rpmsg-sdb"; + rpmsg_sdb->mdev.minor = MISC_DYNAMIC_MINOR; + rpmsg_sdb->mdev.fops = &rpmsg_sdb_fops; + + pr_info("mon-datafile: Set Driver data\n"); + dev_set_drvdata(&rpdev->dev, rpmsg_sdb); + + /* Register misc device */ + ret = misc_register(&rpmsg_sdb->mdev); + + if (ret) { + dev_err(dev, "Failed to register device\n"); + pr_info("mon-datafile: Failed to register device\n"); + goto err_out; + } + + rpmsg_sdb_dev = rpmsg_sdb->mdev.this_device; + + //pr_info("rpmsg_sdb: Failed to register device\n"); + dev_info(dev, "%s probed\n", rpmsg_sdb_driver_name); + +err_out: + return ret; +} + +static void rpmsg_sdb_drv_remove(struct rpmsg_device *rpmsgdev) +{ + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpmsgdev->dev); + + misc_deregister(&drv->mdev); +} + +static struct rpmsg_device_id rpmsg_driver_sdb_id_table[] = { + { .name = "vrpmdv-mon-datafile" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sdb_id_table); + +//static struct rpmsg_driver rpmsg_sdb_rmpsg_drv = { +static struct rpmsg_driver vrpmdv_monitoring_data = { + .drv.name = KBUILD_MODNAME, + .drv.owner = THIS_MODULE, + .id_table = rpmsg_driver_sdb_id_table, + .probe = rpmsg_sdb_drv_probe, + .callback = rpmsg_sdb_drv_cb, + .remove = rpmsg_sdb_drv_remove, +}; + +module_rpmsg_driver(vrpmdv_monitoring_data); + + +// static int __init rpmsg_sdb_drv_init(void) +// { +// int ret = 0; + +// /* Register rpmsg device */ +// ret = register_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + +// if (ret) { +// pr_err("rpmsg_sdb(ERROR): Failed to register device\n"); +// return ret; +// } + +// pr_info("rpmsg_sdb: Init done\n"); + +// return ret; +// } + +// static void __exit rpmsg_sdb_drv_exit(void) +// { +// unregister_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); +// pr_info("rpmsg_sdb: Exit\n"); +// } + +// module_init(rpmsg_sdb_drv_init); +// module_exit(rpmsg_sdb_drv_exit); + + +MODULE_AUTHOR("Markus Lehr "); +MODULE_DESCRIPTION("shared data buffer over RPMSG"); +MODULE_VERSION(RPMSG_SDB_DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-mon-datafile.c.old b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-mon-datafile.c.old new file mode 100644 index 0000000..e62b109 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-mon-datafile.c.old @@ -0,0 +1,28 @@ +/* Copyright 2024 Markus Lehr + * + * + * This Software is owned by Markus Lehr. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// ################################################################################################## +/* + * This is a Linux kernel module/driver called "vrpmdv-monitoring-cmd " which holds the family ID + * and functions to handle the monitoring in a Generic Netlink in the kernel. + * "It registers a Netlink family called "vrpmdv-monitoring_cmd". + * + * + * You can find some more interesting documentation about Generic Netlink here: + * "Generic Netlink HOW-TO based on Jamal's original doc" https://lwn.net/Articles/208755/ + */ + + +MODULE_AUTHOR("Markus Lehr "); +MODULE_DESCRIPTION("shared data buffer over RPMSG"); +MODULE_VERSION(VRPMDV_MON_DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-monitoring-controler.cold b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-monitoring-controler.cold new file mode 100644 index 0000000..12438fe --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-monitoring-controler.cold @@ -0,0 +1,804 @@ +/* Copyright 2024 Markus Lehr + * + * + * This Software is owned by Markus Lehr. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// ################################################################################################## +/* + * This is a Linux kernel module/driver called "vrpmdv-monitoring-cmd " which holds the family ID + * and functions to handle the monitoring in a Generic Netlink in the kernel. + * "It registers a Netlink family called "vrpmdv-monitoring_cmd". + * + * + * You can find some more interesting documentation about Generic Netlink here: + * "Generic Netlink HOW-TO based on Jamal's original doc" https://lwn.net/Articles/208755/ + */ + + +#include +#include + +// basic definitions for kernel module development +#include +// definitions for generic netlink families, policies etc; +// transitive dependencies for basic netlink, sockets etc +#include +// required for locking inside the .dumpit callback demonstration +#include +#include + + +// data/vars/enums/properties that describes our protocol that we implement +// on top of generic netlink (like functions we want to trigger on the receiving side) +//#include "vrpmdv-monitoring-cmd.h" + + +/* ######################## CONVENIENT LOGGING MACROS ######################## */ +// (Re)definition of some convenient logging macros from . You can see the logging +// messages when printing the kernel log, e.g. with `$ sudo dmesg`. +// See https://elixir.bootlin.com/linux/latest/source/include/linux/printk.h + +// with this redefinition we can easily prefix all log messages from pr_* logging macros +#ifdef pr_fmt +#undef pr_fmt +#endif +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +/* ########################################################################### */ + + +#define MSG "hello Monitoring!" + +static int count = 100; +module_param(count, int, 0644); + +struct instance_data { + int rx_count; +}; + + +// std::chrono::seconds timeoutPeriod = 5; +// auto timePoint = std::chrono::system_clock::now() + timeoutPeriod; + + +/** ----- NETLINK Driver defintion ------------------*/ + + +/** + * Generic Netlink will create a Netlink family with this name. Kernel will asign + * a numeric ID and afterwards we can talk to the family with its ID. To get + * the ID we use Generic Netlink in the userland and pass the family name. + * + * Short for: Generic Netlink VRPMDV Monitoring gnl_foobar_mcmd + */ +#define FAMILY_NAME "gnl-vrpmdv-mcmd" + +/** + * These are the attributes that we want to share in gnl_foobar_xmpl. + * You can understand an attribute as a semantic type. This is + * the payload of Netlink messages. + * GNl: Generic Netlink + */ +enum GNL_VRPMDV_XMPL_ATTRIBUTE { + /** + * 0 is never used (=> UNSPEC), you can also see this in other family definitions in Linux code. + * We do the same, although I'm not sure, if this is really enforced by code. + */ + GNL_VRPMDV_MCMD_A_UNSPEC, + /** We expect a MSG to be a null-terminated C-string. */ + GNL_VRPMDV_MCMD_A_MSG, + /** Unused marker field to get the length/count of enum entries. No real attribute. */ + __GNL_VRPMDV_MCMD_A_MAX, +}; +/** + * Number of elements in `enum GNL_VRPMDV_MCMD_COMMAND`. + */ +#define GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN (__GNL_VRPMDV_MCMD_A_MAX) +/** + * The number of actual usable attributes in `enum GNL_VRPMDV_MCMD_ATTRIBUTE`. + * This is `GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN` - 1 because "UNSPEC" is never used. + */ +#define GNL_VRPMDV_MCMD_ATTRIBUTE_COUNT (GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN - 1) + +/** + * Enumeration of all commands (functions) that our custom protocol on top + * of generic netlink supports. This can be understood as the action that + * we want to trigger on the receiving side. + */ +enum GNL_VRPMDV_MCMD_COMMAND { + /** + * 0 is never used (=> UNSPEC), you can also see this in other family definitions in Linux code. + * We do the same, although I'm not sure, if this is really enforced by code. + */ + GNL_VRPMDV_MCMD_C_UNSPEC, + + // first real command is "1" (>0) + /** + * When this command is received, we expect the attribute `GNL_VRPMDV_MCMD_ATTRIBUTE::GNL_VRPMDV_MCMD_A_MSG` to + * be present in the Generic Netlink request message. The kernel reads the message from the packet and + * sent it to the copro. THe result will be return by creating a new Generic Netlink response message + * with an corresponding attribute/payload. + * + * This command/signaling mechanism is independent of the Netlink flag `NLM_F_ECHO (0x08)`. We use it as + * "echo specific data" instead of return a 1:1 copy of the package, which you could do with + * `NLM_F_ECHO (0x08)` for example. + */ + GNL_VRPMDV_MCMD_C_MSG, + + /** + * Provokes a NLMSG_ERR answer to this request as described in netlink manpage + * (https://man7.org/linux/man-pages/man7/netlink.7.html). + */ + GNL_VRPMDV_MCMD_C_REPLY_WITH_NLMSG_ERR, + + /** Unused marker field to get the length/count of enum entries. No real attribute. */ + __GNL_VRPMDV_MCMD_C_MAX, +}; +/** + * Number of elements in `enum GNL_VRPMDV_MCMD_COMMAND`. + */ +#define GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN (__GNL_VRPMDV_MCMD_C_MAX) +/** + * The number of actual usable commands in `enum GNL_FOOBAR_XMPL_COMMAND`. + * This is `GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN` - 1 because "UNSPEC" is never used. + */ +#define GNL_VRPMDV_MCMD_COMMAND_COUNT (GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN - 1) + +#define NODATARECEIVED 0 +#define DATARECEIVED 1 + +/** + * Data structure required for our .doit callback handler to + * know about the progress of an ongoing cmd execution. + * See the cmd callback handler how it is used. + */ +// struct { +// // from +// /** +// * rpmsg wait for response from copro side. +// */ +// struct mutex sendMTX; +// /** +// * rpmsg wait for response from copro side. +// */ +// struct mutex receiveCV; +// /** +// * Wait Queue: if it is signaled we have received data from copro +// */ +// wait_queue_head_t receive_queue; +// /** +// * Waitflag: 0= no data received, 1 = data received +// */ +// int receive_queue_flag = NODATARECEIVED; +// /** +// * Condition vaiable signal we have received data from copro +// */ +// // std::condition_variable cv; +// // /** +// // * Number that describes how many packets we need to send until we are done +// // * during an ongoing dumpit process. 0 = done. +// // */ +// // int unsigned runs_to_go; +// // /** +// // * Number that describes how many packets per dump are sent in total. +// // * Constant per dump. +// // */ +// // int unsigned total_runs; + +// //the rpmsg device which sends the data to the copro +// struct rpmsg_device *rpdev; /* handle rpmsg device */ +// } cmd_cb_progress_data; + + +struct rpmsg_vrpmdv_mon_t{ + // from + /** + * rpmsg wait for response from copro side. + */ + struct mutex sendMTX; + /** + * rpmsg wait for response from copro side. + */ + struct mutex receiveCV; + /** + * Wait Queue: if it is signaled we have received data from copro + */ + wait_queue_head_t receive_queue; + /** + * Waitflag: 0= no data received, 1 = data received + */ + int receive_queue_flag; + //the rpmsg device which sends the data to the copro + struct rpmsg_device* rpdev; /* handle rpmsg device */ +}; + +struct rpmsg_vrpmdv_mon_t vrpmdv_mon; + + // struct mutex mutex; /* mutex to protect the ioctls */ + // struct miscdevice mdev; /* misc device ref */ + // struct rpmsg_device *rpdev; /* handle rpmsg device */ + // struct list_head buffer_list; /* buffer instances list */ + + + +// Documentation is on the implementation of this function. +int gnl_cb_vrpmdv_doit(struct sk_buff *sender_skb, struct genl_info *info); + +// Documentation is on the implementation of this function. +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info); + +/** + * The length of `struct genl_ops gnl_foobar_xmpl_ops[]`. Not necessarily + * the number of commands in `enum GNlFoobarXmplCommand`. It depends on your application logic. + * For example, you can use the same command multiple times and - dependent by flag - + * invoke a different callback handler. In our simple example we just use one .doit callback + * per operation/command. + */ +#define GNL_VRPMDV_OPS_LEN (GNL_VRPMDV_MCMD_COMMAND_COUNT) + +/** + * Array with all operations that our protocol on top of Generic Netlink + * supports. An operation is the glue between a command ("cmd" field in `struct genlmsghdr` of + * received Generic Netlink message) and the corresponding ".doit" callback function. + * See: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L148 + */ +struct genl_ops gnl_vrpmdv_mcmd_ops[GNL_VRPMDV_OPS_LEN] = { + { + /* The "cmd" field in `struct genlmsghdr` of received Generic Netlink message */ + .cmd = GNL_VRPMDV_MCMD_C_MSG, + /* TODO Use case ? */ + .flags = 0, + /* TODO Use case ? */ + .internal_flags = 0, + /* Callback handler when a request with the specified ".cmd" above is received. + * Always validates the payload except one set NO_STRICT_VALIDATION flag in ".validate" + * See: https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + * + * Quote from: https://lwn.net/Articles/208755 + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + * + */ + .doit = gnl_cb_vrpmdv_doit, + /* This callback is similar in use to the standard Netlink 'dumpit' callback. + * The 'dumpit' callback is invoked when a Generic Netlink message is received + * with the NLM_F_DUMP flag set. + * + * A dump can be understand as a "GET ALL DATA OF THE GIVEN ENTITY", i.e. + * the userland can receive as long as the .dumpit callback returns data. + * + * .dumpit is not mandatory, but either it or .doit must be provided, see + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L367 + * + * To be honest I don't know in what use case one should use .dumpit and why + * it is useful, because you can achieve the same also with .doit handlers. + * Anyway, this is just an example/tutorial. + * + * Quote from: https://lwn.net/Articles/208755 + * "The main difference between a 'dumpit' handler and a 'doit' handler is + * that a 'dumpit' handler does not allocate a message buffer for a response; + * a pre-allocated sk_buff is passed to the 'dumpit' handler as the first + * parameter. The 'dumpit' handler should fill the message buffer with the + * appropriate response message and return the size of the sk_buff, + * i.e. sk_buff->len, and the message buffer will automatically be sent to the + * Generic Netlink client that initiated the request. As long as the 'dumpit' + * handler returns a value greater than zero it will be called again with a + * newly allocated message buffer to fill, when the handler has no more data + * to send it should return zero; error conditions are indicated by returning + * a negative value. If necessary, state can be preserved in the + * netlink_callback parameter which is passed to the 'dumpit' handler; the + * netlink_callback parameter values will be preserved across handler calls + * for a single request." + * + * You can see the check for the NLM_F_DUMP-flag here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L780 + */ + .dumpit = NULL, //gnl_cb_echo_dumpit, + /* Start callback for dumps. Can be used to lock data structures. */ + .start = NULL, //gnl_cb_echo_dumpit_before, + /* Completion callback for dumps. Can be used for cleanup after a dump and releasing locks. */ + .done = NULL, //gnl_cb_echo_dumpit_before_after, + /* + 0 (= "validate strictly") or value `enum genl_validate_flags` + * see: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L108 + */ + .validate = 0, + }, + { + .cmd = GNL_VRPMDV_MCMD_C_REPLY_WITH_NLMSG_ERR, + .flags = 0, + .internal_flags = 0, + .doit = gnl_cb_doit_reply_with_nlmsg_err, + // .dumpit is not required, only optional; application specific/dependent on your use case + // in a real application you probably have different .dumpit handlers per operation/command + .dumpit = NULL, + // in a real application you probably have different .start handlers per operation/command + .start = NULL, + // in a real application you probably have different .done handlers per operation/command + .done = NULL, + .validate = 0, + } +}; + + +/** + * Attribute policy: defines which attribute has which type (e.g int, char * etc). + * This get validated for each received Generic Netlink message, if not deactivated + * in `gnl_foobar_xmpl_ops[].validate`. + * See https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + */ +static struct nla_policy gnl_vrpmdv_mcmd_policy[GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN] = { + // In case you are seeing this syntax for the first time (I also learned this just after a few years of + // coding with C myself): The following static array initiations are equivalent: + // `int a[2] = {1, 2}` <==> `int a[2] = {[0] => 1, [1] => 2}`. + + [GNL_VRPMDV_MCMD_A_UNSPEC] = {.type = NLA_UNSPEC}, + + // You can set this to NLA_U32 for testing and send an ECHO message from the userland + // It will fail in this case and you see a entry in the kernel log. + + // `enum GNL_FOOBAR_XMPL_ATTRIBUTE::GNL_FOOBAR_XMPL_A_MSG` is a null-terminated C-String + [GNL_VRPMDV_MCMD_A_MSG] = {.type = NLA_NUL_STRING}, +}; + + + +/** + * Definition of the Netlink family we want to register using Generic Netlink functionality + */ +static struct genl_family gnl_vrpmdv_mcmd_family = { + // automatically assign an id + .id = 0, + // we don't use custom additional header info / user specific header + .hdrsize = 0, + // The name of this family, used by userspace application to get the numeric ID + .name = FAMILY_NAME, + // family specific version number; can be used to evolve application over time (multiple versions) + .version = 1, + // delegates all incoming requests to callback functions + .ops = gnl_vrpmdv_mcmd_ops, + // length of array `gnl_foobar_xmpl_ops` + .n_ops = GNL_VRPMDV_OPS_LEN, + // attribute policy (for validation of messages). Enforced automatically, except ".validate" in + // corresponding ".ops"-field is set accordingly. + .policy = gnl_vrpmdv_mcmd_policy, + // Number of attributes / bounds check for policy (array length) + .maxattr = GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN, + // Owning Kernel module of the Netlink family we register. + .module = THIS_MODULE, + + // Actually not necessary because this memory region would be zeroed anyway during module load, + // but this way one sees all possible options. + + // if your application must handle multiple netlink calls in parallel (where one should not block the next + // from starting), set this to true! otherwise all netlink calls are mutually exclusive + .parallel_ops = 0, + // set to true if the family can handle network namespaces and should be presented in all of them + .netnsok = 0, + // called before an operation's doit callback, it may do additional, common, filtering and return an error + .pre_doit = NULL, + // called after an operation's doit callback, it may undo operations done by pre_doit, for example release locks + .post_doit = NULL, +}; + + + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_VRPMDV_MCMD_C_MSG` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_vrpmdv_mcmd_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_vrpmdv_doit(struct sk_buff *sender_skb, struct genl_info *info) { + struct nlattr *na; + struct sk_buff *reply_skb; + int rc; + void *msg_head; + char *recv_msg; + + pr_info("%s() invoked\n", __func__); + + if (info == NULL) { + // should never happen + pr_err("An error occurred in %s():\n", __func__); + return -EINVAL; + } + + /* + * For each attribute there is an index in info->attrs which points to a nlattr structure + * in this structure the data is stored. + */ + na = info->attrs[GNL_VRPMDV_MCMD_A_MSG]; + + if (!na) { + pr_err("no info->attrs[%i]\n", GNL_VRPMDV_MCMD_A_MSG); + return -EINVAL; // we return here because we expect to recv a msg + } + + recv_msg = (char *) nla_data(na); + if (recv_msg == NULL) { + pr_err("error while receiving data\n"); + } else { + pr_info("received: '%s'\n", recv_msg); + } + + //aquire lock for cmd repmsg channel + // std::lock sendlk(cmd_cb_progress_data.sendmtx); + + //send the message to the copro over RPMSG + rc = rpmsg_send(vrpmdv_mon.rpdev->ept, recv_msg, strlen(recv_msg)); + if (rc) { + pr_err("rpmsg_send failed: %d\n", rc); + return rc; + } + + // struct rpmsg_vrpmdv_mon_t *drv = dev_get_drvdata(&rpdev->dev); + + { + pr_info("wait for response\n"); + // wait until receive_queue_flag=1 , that means we have received data from Copro + wait_event_interruptible(vrpmdv_mon.receive_queue, vrpmdv_mon.receive_queue_flag != 0 ); + pr_info("received data \n"); + //Copy data + vrpmdv_mon.receive_queue_flag = NODATARECEIVED; + + + + // std::unique_lock lk(cmd_cb_progress_data.receivemtx); + // if (myCondVar.wait_until(uLock, timePoint) == std::cv_status::timeout) + // { + // dev_err(&cmd_cb_progress_data.rpdev, "rpmsg_send failed, timeout: \n"); + // return -1: + // } + + pr_info("get response: '%s'\n", recv_msg); + } + + + + // Send a message back after we receive the reply from rpmsg channel + // --------------------- + + // Allocate some memory, since the size is not yet known use NLMSG_GOODSIZE + reply_skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (reply_skb == NULL) { + pr_err("An error occurred in %s():\n", __func__); + return -ENOMEM; + } + + // Create the message headers + + // Add header to netlink message; + // afterwards the buffer looks like this: + // ---------------------------------- + // | netlink header | + // | generic netlink header | + // | | + // ---------------------------------- + // msg_head = genlmsg_put(reply_skb, // buffer for netlink message: struct sk_buff * + // // According to my findings: this is not used for routing + // // This can be used in an application specific way to target + // // different endpoints within the same user application + // // but general rule: just put sender port id here + // info->snd_portid, // sending port (not process) id: int + // info->snd_seq + 1, // sequence number: int (might be used by receiver, but not mandatory) + // &gnl_vrpmdv_mcmd_family, // struct genl_family * + // 0, // flags for Netlink header: int; application specific and not mandatory + // // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header + // GNL_VRPMDV_MCMD_C_MSG + // ); + + msg_head = genlmsg_put_reply(reply_skb, // buffer for netlink message: struct sk_buff * + info, // info + &gnl_vrpmdv_mcmd_family, // struct genl_family * + 0, // flags for Netlink header: int; application specific and not mandatory + // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header + info->genlhdr->cmd + ); + + if (msg_head == NULL) { + rc = ENOMEM; + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + // Add a GNL_VRPMDV_MCMD_A_MSG attribute (actual value/payload to be sent) + // echo the value we just received + rc = nla_put_string(reply_skb, GNL_VRPMDV_MCMD_A_MSG, recv_msg); + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + // Finalize the message: + // Corrects the netlink message header (length) to include the appended + // attributes. Only necessary if attributes have been added to the message. + genlmsg_end(reply_skb, msg_head); + + // Send the message back + rc = genlmsg_reply(reply_skb, info); + // same as genlmsg_unicast(genl_info_net(info), reply_skb, info->snd_portid) + // see https://elixir.bootlin.com/linux/v5.8.9/source/include/net/genetlink.h#L326 + + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + return 0; +} + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_FOOBAR_XMPL_C_REPLY_WITH_NLMSG_ERR` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info) { + pr_info("%s() invoked, a NLMSG_ERR response will be sent back\n", __func__); + + /* + * Generic Netlink is smart enough and sends a NLMSG_ERR reply automatically as reply + * Quote from https://lwn.net/Articles/208755/: + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + */ + return -EINVAL; +} + + + +/** ----- NETLINK Driver defintion ------------------*/ + + + + + +/** + * callback that is called after the copro send data + * we have to copy it in a buffer for the netlink and later send it back to the userland + * +*/ +static int vrpmdv_monitoring_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + int ret = 0; + struct instance_data *idata = dev_get_drvdata(&rpdev->dev); + + dev_info(&rpdev->dev, "incoming msg %d (src: 0x%x)\n", + ++idata->rx_count, src); + + print_hex_dump_debug(__func__, DUMP_PREFIX_NONE, 16, 1, data, len, + true); + + vrpmdv_mon.receive_queue_flag= DATARECEIVED; + wake_up_interruptible(&(vrpmdv_mon.receive_queue)); + + // /* samples should not live forever */ + // if (idata->rx_count >= count) { + // dev_info(&rpdev->dev, "goodbye!\n"); + // return 0; + // } + + /* send a new message now */ + // ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG)); + // if (ret) + // dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); + + return ret; +} + +static int vrpmdv_monitoring_probe(struct rpmsg_device *rpdev) +{ + int rc; + + // int ret; + // struct instance_data *idata; + + dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n", + rpdev->src, rpdev->dst); + + // idata = devm_kzalloc(&rpdev->dev, sizeof(*idata), GFP_KERNEL); + // if (!idata) + // return -ENOMEM; + + // dev_set_drvdata(&rpdev->dev, idata); + + // /* send a message to our remote processor to */ + // ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG)); + // if (ret) { + // dev_err(&rpdev->dev, "vrpmdv_monitoring_controler_send failed: %d\n", ret); + // return ret; + // } + + // return 0; + + // struct device *dev; + // dev = &rpdev->dev; + // struct rpmsg_vrpmdv_mon_t *rpmsg_vrpmdv_mon; + + // rpmsg_vrpmdv_mon = devm_kzalloc(dev, sizeof(*rpmsg_vrpmdv_mon), GFP_KERNEL); + // if (!rpmsg_vrpmdv_mon) + // return -ENOMEM; + + mutex_init(&(vrpmdv_mon.sendMTX)); + init_waitqueue_head (&(vrpmdv_mon.receive_queue)); + vrpmdv_mon.rpdev = rpdev; + + // dev_set_drvdata(&rpdev->dev, rpmsg_vrpmdv_mon); + pr_info("RPMSG CMD Device set.\n"); + + + /** NEU **/ + // if (cmd_cb_progress_data.rpdev == NULL) { + // cmd_cb_progress_data.rpdev = rpdev; + // pr_info("RPMSG CMD Device set.\n"); + // } + // else { + // pr_info("Error: RPMSG CMD Device already set. Don't set it twice\n"); + // } + + pr_info("Generic Netlink VRPMDV-Monitroring_Controler Module started.\n"); + + // Register family with its operations and policies + rc = genl_register_family(&gnl_vrpmdv_mcmd_family); + if (rc != 0) { + pr_err("FAILED: genl_register_family(): %i\n", rc); + pr_err("An error occurred while inserting the generic netlink example module\n"); + return -1; + } else { + pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + return 0; + + + +} + +static void vrpmdv_monitoring_remove(struct rpmsg_device *rpdev) +{ + int ret; + pr_info("Generic Netlink Example Module unloaded.\n"); + + // Unregister the family + ret = genl_unregister_family(&gnl_vrpmdv_mcmd_family); + if (ret != 0) { + pr_err("genl_unregister_family() failed: %i\n", ret); + return; + } else { + pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + mutex_destroy(&(vrpmdv_mon.sendMTX)); + wake_up_interruptible(&(vrpmdv_mon.receive_queue)); + + dev_info(&rpdev->dev, "vrpmdv-monitoring controler driver is removed\n"); + +} + +static struct rpmsg_device_id vrpmdv_monitoring_controler_id_table[] = { + { .name = "vrpmdv-monitoring-controler" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, vrpmdv_monitoring_controler_id_table); + +static struct rpmsg_driver vrpmdv_monitoring_controler = { + .drv.name = KBUILD_MODNAME, + .id_table = vrpmdv_monitoring_controler_id_table, + .probe = vrpmdv_monitoring_probe, + .callback = vrpmdv_monitoring_cb, + .remove = vrpmdv_monitoring_remove, +}; +module_rpmsg_driver(vrpmdv_monitoring_controler); + + +// static struct rpmsg_driver vrpmdv_monitoring_data = { +// .drv.name = KBUILD_MODNAME, +// .id_table = vrpmdv_monitoring_controler_id_table, +// .probe = vrpmdv_monitoring_probe, +// .callback = vrpmdv_monitoring_cb, +// .remove = vrpmdv_monitoring_remove, +// }; + +// module_rpmsg_driver(vrpmdv_monitoring_data); + +MODULE_DESCRIPTION("Remote processor messaging vrpmdv monitoring controler"); +MODULE_LICENSE("GPL v2"); + + + +// /** +// * Module/driver initializer. Called on module load/insertion. +// * +// * @return success (0) or error code. +// */ +// static int __init gnl_foobar_xmpl_module_init(void) { +// int rc; +// pr_info("Generic Netlink Example Module inserted.\n"); + +// // Register family with its operations and policies +// rc = genl_register_family(&gnl_foobar_xmpl_family); +// if (rc != 0) { +// pr_err("FAILED: genl_register_family(): %i\n", rc); +// pr_err("An error occurred while inserting the generic netlink example module\n"); +// return -1; +// } else { +// pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); +// } + +// mutex_init(&dumpit_cb_progress_data.mtx); + +// return 0; +// } + +// /** +// * Module/driver uninitializer. Called on module unload/removal. +// * +// * @return success (0) or error code. +// */ +// static void __exit gnl_foobar_xmpl_module_exit(void) { +// int ret; +// pr_info("Generic Netlink Example Module unloaded.\n"); + +// // Unregister the family +// ret = genl_unregister_family(&gnl_foobar_xmpl_family); +// if (ret != 0) { +// pr_err("genl_unregister_family() failed: %i\n", ret); +// return; +// } else { +// pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); +// } + +// mutex_destroy(&dumpit_cb_progress_data.mtx); +// } + + +// ---- + +// static int __init rpmsg_sdb_drv_init(void) +// { +// int ret = 0; + +// /* Register rpmsg device */ +// ret = register_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + +// if (ret) { +// pr_err("rpmsg_sdb(ERROR): Failed to register device\n"); +// return ret; +// } + +// pr_info("rpmsg_sdb: Init done\n"); + +// return ret; +// } + +// static void __exit rpmsg_sdb_drv_exit(void) +// { +// unregister_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); +// pr_info("rpmsg_sdb: Exit\n"); +// } + +// module_init(rpmsg_sdb_drv_init); +// module_exit(rpmsg_sdb_drv_exit); diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-monitoring-driver copy.bb.old b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-monitoring-driver copy.bb.old new file mode 100644 index 0000000..6aeebb9 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-monitoring-driver copy.bb.old @@ -0,0 +1,24 @@ +SUMMARY = "VRPMDV Monitoring Driver" +LICENSE = "CLOSED" +# LICENSE = "GPL-2.0-only" +# LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "file://Makefile \ + file://stm32_rpmsg_sdb.c \ + " +# file://75-rpmsg-sdb.rules \ +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. + +# do_install:append() { + # udev rules for rpmsg-sdb + # install -d ${D}${sysconfdir}/udev/rules.d/ + # install -m 0644 ${WORKDIR}/75-rpmsg-sdb.rules ${D}${sysconfdir}/udev/rules.d/75-rpmsg-sdb.rules +# } +#FILES:${PN} += "${sysconfdir}/udev/rules.d/" + +RPROVIDES:${PN} += "kernel-module-vrpmdv-monitoring-driver" \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-monitoring-driver copy1.bb.old b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-monitoring-driver copy1.bb.old new file mode 100644 index 0000000..2f245db --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-monitoring-driver copy1.bb.old @@ -0,0 +1,26 @@ +SUMMARY = "VRPMDV Monitoring Driver" +LICENSE = "GPL-2.0-only" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "file://Makefile \ + file://vrpmdv-monitoring.c \ + file://75-vrpmdv-monitoring.rules \ + " + +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. + +do_install:append() { + # udev rules for vrpmdv-monitoring + install -d ${D}${sysconfdir}/udev/rules.d/ + install -m 0644 ${WORKDIR}/75-vrpmdv-monitoring.rules ${D}${sysconfdir}/udev/rules.d/75-vrpmdv-monitoring.rules +} + +FILES:${PN} += "${sysconfdir}/udev/rules.d/" + + +RPROVIDES:${PN} += "kernel-module-vrpmdv-monitoring" \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-monitoring-driverv2.bb.old b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-monitoring-driverv2.bb.old new file mode 100644 index 0000000..03bdb32 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/save/vrpmdv-monitoring-driverv2.bb.old @@ -0,0 +1,26 @@ +SUMMARY = "VRPMDV Monitoring Driver" +LICENSE = "GPL-2.0-only" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "file://Makefile \ + file://vrpmdv-monitoring.c \ + file://75-vrpmdv-monitoring.rules \ + " + +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. + +do_install:append() { + # udev rules for vrpmdv-monitoring + install -d ${D}${sysconfdir}/udev/rules.d/ + install -m 0644 ${WORKDIR}/75-vrpmdv-monitoring.rules ${D}${sysconfdir}/udev/rules.d/75-vrpmdv-monitoring.rules +} + +FILES:${PN} += "${sysconfdir}/udev/rules.d/" +FILES:${PN} += "${base_libdir}/modules/" + +RPROVIDES:${PN} += "vrpmdv-monitoring" diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/vrpmdv-mon-datafile.bb b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/vrpmdv-mon-datafile.bb new file mode 100644 index 0000000..c80b221 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-datafile/vrpmdv-mon-datafile.bb @@ -0,0 +1,25 @@ +SUMMARY = "VRPMDV Monitoring Controler" +LICENSE = "GPL-2.0-only" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "file://Makefile \ + file://vrpmdv-mon-datafile.c \ + file://75-mon-datafile.rules \ + " + +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. + +do_install:append() { + # udev rules for rpmsg-sdb + install -d ${D}${sysconfdir}/udev/rules.d/ + install -m 0644 ${WORKDIR}/75-mon-datafile.rules ${D}${sysconfdir}/udev/rules.d/75-mon-datafile.rules +} +FILES:${PN} += "${sysconfdir}/udev/rules.d/" + + +RPROVIDES:${PN} += "kernel-module-vrpmdv-mon-datafile" diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/.vscode/settings.json b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/.vscode/settings.json new file mode 100644 index 0000000..a644266 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "files.associations": { + "rpmsg.h": "c", + "types.h": "c", + "ioctl.h": "c", + "wait.h": "c" + } +} \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/Create-Kernel-Driver.odt b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/Create-Kernel-Driver.odt new file mode 100644 index 0000000..cee5ccf Binary files /dev/null and b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/Create-Kernel-Driver.odt differ diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/LockingWaitQueues.pdf b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/LockingWaitQueues.pdf new file mode 100644 index 0000000..6d16b4b Binary files /dev/null and b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/LockingWaitQueues.pdf differ diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/75-mon-tty.rules b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/75-mon-tty.rules new file mode 100644 index 0000000..a573a73 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/75-mon-tty.rules @@ -0,0 +1 @@ +SUBSYSTEM=="misc", KERNEL=="ttyMON", GROUP="tty", MODE="0666" diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/Makefile b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/Makefile new file mode 100644 index 0000000..86fda31 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/Makefile @@ -0,0 +1,26 @@ +# Makefile for VRPMDV Monitoring Controler +obj-m := vrpmdv-mon-tty.o + + +SRC := $(shell pwd) + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) + +modules_install: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install + +clean: + rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c + rm -f Module.markers Module.symvers modules.order + rm -rf .tmp_versions Modules.symvers + +# obj-m = vrpmdv-monitoring-controler.o + +# KVERSION = $(shell uname -r) + +# all: +# make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules + +# clean: +# make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/lock.csave b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/lock.csave new file mode 100644 index 0000000..0de60b3 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/lock.csave @@ -0,0 +1,116 @@ +include +#include +#include +#include +#include + +std::mutex m; +std::condition_variable cv; +std::string data; +bool ready = false; +bool processed = false; + +void worker_thread() +{ + // wait until main() sends data + std::unique_lock lk(m); + cv.wait(lk, []{ return ready; }); + + // after the wait, we own the lock + std::cout << "Worker thread is processing data\n"; + data += " after processing"; + + // send data back to main() + processed = true; + std::cout << "Worker thread signals data processing completed\n"; + + // manual unlocking is done before notifying, to avoid waking up + // the waiting thread only to block again (see notify_one for details) + lk.unlock(); + cv.notify_one(); +} + +int main() +{ + std::thread worker(worker_thread); + + data = "Example data"; + // send data to the worker thread + { + std::lock_guard lk(m); + ready = true; + std::cout << "main() signals data ready for processing\n"; + } + cv.notify_one(); + + // wait for the worker + { + std::unique_lock lk(m); + cv.wait(lk, []{ return processed; }); + } + std::cout << "Back in main(), data = " << data << '\n'; + + worker.join(); +} + +-------------------- + +// waiting for timeout after 5 seconds +std::chrono::seconds timeoutPeriod = 5; +auto timePoint = std::chrono::system_clock::now() + timeoutPeriod; +std::unique_lock uLock(myDataMutex); +while(!DataAreReadyForProcessing()) +{ + if (myCondVar.wait_until(uLock, timePoint) //<## + == std::cv_status::timeout) + { + // data conditions where not fulfilled within + // the time period; e.g. do some error handling + break; + } +} + +-- + +if (myCondVar.wait_for(uLock, timeoutPeriod, + DataAreReadyForProcessing)) +{ + // data conditions where fulfilled + // regular processing +} +else // timeout occured, conditions are not fulfilled +{ + // e.g. do some error handling +} + + + + + + +------------- + + +static int rpmsg_sample_probe(struct rpmsg_device *rpdev) +{ + + int ret; + struct instance_data *idata; + + dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n", + rpdev->src, rpdev->dst); + + idata = devm_kzalloc(&rpdev->dev, sizeof(*idata), GFP_KERNEL); + if (!idata) + return -ENOMEM; + + dev_set_drvdata(&rpdev->dev, idata); + + /* send a message to our remote processor */ + ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG)); + if (ret) { + dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); + return ret; + } + + return 0; diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/vrpmdv-mon-log.c.old b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/vrpmdv-mon-log.c.old new file mode 100644 index 0000000..dab7cc2 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/vrpmdv-mon-log.c.old @@ -0,0 +1,795 @@ +/* Copyright 2024 Markus Lehr + * + * + * This Software is owned by Markus Lehr. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// ################################################################################################## +/* + * This is a Linux kernel module/driver called "vrpmdv-monitoring-cmd " which holds the family ID + * and functions to handle the monitoring in a Generic Netlink in the kernel. + * "It registers a Netlink family called "vrpmdv-monitoring_cmd". + * + * + * You can find some more interesting documentation about Generic Netlink here: + * "Generic Netlink HOW-TO based on Jamal's original doc" https://lwn.net/Articles/208755/ + */ + + +#include +#include + +// basic definitions for kernel module development +#include +// definitions for generic netlink families, policies etc; +// transitive dependencies for basic netlink, sockets etc +#include +// required for locking inside the .dumpit callback demonstration +#include +#include + + +// data/vars/enums/properties that describes our protocol that we implement +// on top of generic netlink (like functions we want to trigger on the receiving side) +//#include "vrpmdv-monitoring-cmd.h" + + +/* ######################## CONVENIENT LOGGING MACROS ######################## */ +// (Re)definition of some convenient logging macros from . You can see the logging +// messages when printing the kernel log, e.g. with `$ sudo dmesg`. +// See https://elixir.bootlin.com/linux/latest/source/include/linux/printk.h + +// with this redefinition we can easily prefix all log messages from pr_* logging macros +#ifdef pr_fmt +#undef pr_fmt +#endif +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +/* ########################################################################### */ + + +#define MSG "hello Monitoring!" +static bool retok = true; + +static int count = 100; +module_param(count, int, 0644); + +struct instance_data { + int rx_count; +}; + + +// std::chrono::seconds timeoutPeriod = 5; +// auto timePoint = std::chrono::system_clock::now() + timeoutPeriod; + + +/** ----- NETLINK Driver defintion ------------------*/ + + +/** + * Generic Netlink will create a Netlink family with this name. Kernel will asign + * a numeric ID and afterwards we can talk to the family with its ID. To get + * the ID we use Generic Netlink in the userland and pass the family name. + * + * Short for: Generic Netlink VRPMDV Monitoring gnl_foobar_mcmd + */ +#define FAMILY_NAME "gnl-vrpmdv-mcmd" + +/** + * These are the attributes that we want to share in gnl_foobar_xmpl. + * You can understand an attribute as a semantic type. This is + * the payload of Netlink messages. + * GNl: Generic Netlink + */ +enum GNL_VRPMDV_XMPL_ATTRIBUTE { + /** + * 0 is never used (=> UNSPEC), you can also see this in other family definitions in Linux code. + * We do the same, although I'm not sure, if this is really enforced by code. + */ + GNL_VRPMDV_MCMD_A_UNSPEC, + /** We expect a MSG to be a null-terminated C-string. */ + GNL_VRPMDV_MCMD_A_MSG, + /** Unused marker field to get the length/count of enum entries. No real attribute. */ + __GNL_VRPMDV_MCMD_A_MAX, +}; +/** + * Number of elements in `enum GNL_VRPMDV_MCMD_COMMAND`. + */ +#define GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN (__GNL_VRPMDV_MCMD_A_MAX) +/** + * The number of actual usable attributes in `enum GNL_VRPMDV_MCMD_ATTRIBUTE`. + * This is `GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN` - 1 because "UNSPEC" is never used. + */ +#define GNL_VRPMDV_MCMD_ATTRIBUTE_COUNT (GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN - 1) + +/** + * Enumeration of all commands (functions) that our custom protocol on top + * of generic netlink supports. This can be understood as the action that + * we want to trigger on the receiving side. + */ +enum GNL_VRPMDV_MCMD_COMMAND { + /** + * 0 is never used (=> UNSPEC), you can also see this in other family definitions in Linux code. + * We do the same, although I'm not sure, if this is really enforced by code. + */ + GNL_VRPMDV_MCMD_C_UNSPEC, + + // first real command is "1" (>0) + /** + * When this command is received, we expect the attribute `GNL_VRPMDV_MCMD_ATTRIBUTE::GNL_VRPMDV_MCMD_A_MSG` to + * be present in the Generic Netlink request message. The kernel reads the message from the packet and + * sent it to the copro. THe result will be return by creating a new Generic Netlink response message + * with an corresponding attribute/payload. + * + * This command/signaling mechanism is independent of the Netlink flag `NLM_F_ECHO (0x08)`. We use it as + * "echo specific data" instead of return a 1:1 copy of the package, which you could do with + * `NLM_F_ECHO (0x08)` for example. + */ + GNL_VRPMDV_MCMD_C_MSG, + + /** + * Provokes a NLMSG_ERR answer to this request as described in netlink manpage + * (https://man7.org/linux/man-pages/man7/netlink.7.html). + */ + GNL_VRPMDV_MCMD_C_REPLY_WITH_NLMSG_ERR, + + /** Unused marker field to get the length/count of enum entries. No real attribute. */ + __GNL_VRPMDV_MCMD_C_MAX, +}; +/** + * Number of elements in `enum GNL_VRPMDV_MCMD_COMMAND`. + */ +#define GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN (__GNL_VRPMDV_MCMD_C_MAX) +/** + * The number of actual usable commands in `enum GNL_FOOBAR_XMPL_COMMAND`. + * This is `GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN` - 1 because "UNSPEC" is never used. + */ +#define GNL_VRPMDV_MCMD_COMMAND_COUNT (GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN - 1) + +#define NODATARECEIVED 0 +#define DATARECEIVED 1 + + + + /** + * rpmsg wait for response from copro side. + */ + struct mutex sendMTX; + /** + * rpmsg wait for response from copro side. + */ + struct mutex receiveCV; + /** + * Wait Queue: if it is signaled we have received data from copro + */ + wait_queue_head_t receive_queue; + /** + * Waitflag: 0= no data received, 1 = data received + */ + int receive_queue_flag = NODATARECEIVED; + + char* received_bytes = NULL; + int received_len = 0; + //the rpmsg device which sends the data to the copro + struct rpmsg_device* prpdev = NULL; /* handle rpmsg device */ + + +// Documentation is on the implementation of this function. +int gnl_cb_vrpmdv_doit(struct sk_buff *sender_skb, struct genl_info *info); + +// Documentation is on the implementation of this function. +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info); + +/** + * The length of `struct genl_ops gnl_foobar_xmpl_ops[]`. Not necessarily + * the number of commands in `enum GNlFoobarXmplCommand`. It depends on your application logic. + * For example, you can use the same command multiple times and - dependent by flag - + * invoke a different callback handler. In our simple example we just use one .doit callback + * per operation/command. + */ +#define GNL_VRPMDV_OPS_LEN (GNL_VRPMDV_MCMD_COMMAND_COUNT) + +/** + * Array with all operations that our protocol on top of Generic Netlink + * supports. An operation is the glue between a command ("cmd" field in `struct genlmsghdr` of + * received Generic Netlink message) and the corresponding ".doit" callback function. + * See: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L148 + */ +struct genl_ops gnl_vrpmdv_mcmd_ops[GNL_VRPMDV_OPS_LEN] = { + { + /* The "cmd" field in `struct genlmsghdr` of received Generic Netlink message */ + .cmd = GNL_VRPMDV_MCMD_C_MSG, + /* TODO Use case ? */ + .flags = 0, + /* TODO Use case ? */ + .internal_flags = 0, + /* Callback handler when a request with the specified ".cmd" above is received. + * Always validates the payload except one set NO_STRICT_VALIDATION flag in ".validate" + * See: https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + * + * Quote from: https://lwn.net/Articles/208755 + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + * + */ + .doit = gnl_cb_vrpmdv_doit, + /* This callback is similar in use to the standard Netlink 'dumpit' callback. + * The 'dumpit' callback is invoked when a Generic Netlink message is received + * with the NLM_F_DUMP flag set. + * + * A dump can be understand as a "GET ALL DATA OF THE GIVEN ENTITY", i.e. + * the userland can receive as long as the .dumpit callback returns data. + * + * .dumpit is not mandatory, but either it or .doit must be provided, see + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L367 + * + * To be honest I don't know in what use case one should use .dumpit and why + * it is useful, because you can achieve the same also with .doit handlers. + * Anyway, this is just an example/tutorial. + * + * Quote from: https://lwn.net/Articles/208755 + * "The main difference between a 'dumpit' handler and a 'doit' handler is + * that a 'dumpit' handler does not allocate a message buffer for a response; + * a pre-allocated sk_buff is passed to the 'dumpit' handler as the first + * parameter. The 'dumpit' handler should fill the message buffer with the + * appropriate response message and return the size of the sk_buff, + * i.e. sk_buff->len, and the message buffer will automatically be sent to the + * Generic Netlink client that initiated the request. As long as the 'dumpit' + * handler returns a value greater than zero it will be called again with a + * newly allocated message buffer to fill, when the handler has no more data + * to send it should return zero; error conditions are indicated by returning + * a negative value. If necessary, state can be preserved in the + * netlink_callback parameter which is passed to the 'dumpit' handler; the + * netlink_callback parameter values will be preserved across handler calls + * for a single request." + * + * You can see the check for the NLM_F_DUMP-flag here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L780 + */ + .dumpit = NULL, //gnl_cb_echo_dumpit, + /* Start callback for dumps. Can be used to lock data structures. */ + .start = NULL, //gnl_cb_echo_dumpit_before, + /* Completion callback for dumps. Can be used for cleanup after a dump and releasing locks. */ + .done = NULL, //gnl_cb_echo_dumpit_before_after, + /* + 0 (= "validate strictly") or value `enum genl_validate_flags` + * see: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L108 + */ + .validate = 0, + }, + { + .cmd = GNL_VRPMDV_MCMD_C_REPLY_WITH_NLMSG_ERR, + .flags = 0, + .internal_flags = 0, + .doit = gnl_cb_doit_reply_with_nlmsg_err, + // .dumpit is not required, only optional; application specific/dependent on your use case + // in a real application you probably have different .dumpit handlers per operation/command + .dumpit = NULL, + // in a real application you probably have different .start handlers per operation/command + .start = NULL, + // in a real application you probably have different .done handlers per operation/command + .done = NULL, + .validate = 0, + } +}; + + +/** + * Attribute policy: defines which attribute has which type (e.g int, char * etc). + * This get validated for each received Generic Netlink message, if not deactivated + * in `gnl_foobar_xmpl_ops[].validate`. + * See https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + */ +static struct nla_policy gnl_vrpmdv_mcmd_policy[GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN] = { + // In case you are seeing this syntax for the first time (I also learned this just after a few years of + // coding with C myself): The following static array initiations are equivalent: + // `int a[2] = {1, 2}` <==> `int a[2] = {[0] => 1, [1] => 2}`. + + [GNL_VRPMDV_MCMD_A_UNSPEC] = {.type = NLA_UNSPEC}, + + // You can set this to NLA_U32 for testing and send an ECHO message from the userland + // It will fail in this case and you see a entry in the kernel log. + + // `enum GNL_FOOBAR_XMPL_ATTRIBUTE::GNL_FOOBAR_XMPL_A_MSG` is a null-terminated C-String + [GNL_VRPMDV_MCMD_A_MSG] = {.type = NLA_NUL_STRING}, +}; + + + +/** + * Definition of the Netlink family we want to register using Generic Netlink functionality + */ +static struct genl_family gnl_vrpmdv_mcmd_family = { + // automatically assign an id + .id = 0, + // we don't use custom additional header info / user specific header + .hdrsize = 0, + // The name of this family, used by userspace application to get the numeric ID + .name = FAMILY_NAME, + // family specific version number; can be used to evolve application over time (multiple versions) + .version = 1, + // delegates all incoming requests to callback functions + .ops = gnl_vrpmdv_mcmd_ops, + // length of array `gnl_foobar_xmpl_ops` + .n_ops = GNL_VRPMDV_OPS_LEN, + // attribute policy (for validation of messages). Enforced automatically, except ".validate" in + // corresponding ".ops"-field is set accordingly. + .policy = gnl_vrpmdv_mcmd_policy, + // Number of attributes / bounds check for policy (array length) + .maxattr = GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN, + // Owning Kernel module of the Netlink family we register. + .module = THIS_MODULE, + + // Actually not necessary because this memory region would be zeroed anyway during module load, + // but this way one sees all possible options. + + // if your application must handle multiple netlink calls in parallel (where one should not block the next + // from starting), set this to true! otherwise all netlink calls are mutually exclusive + .parallel_ops = 0, + // set to true if the family can handle network namespaces and should be presented in all of them + .netnsok = 0, + // called before an operation's doit callback, it may do additional, common, filtering and return an error + .pre_doit = NULL, + // called after an operation's doit callback, it may undo operations done by pre_doit, for example release locks + .post_doit = NULL, +}; + + + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_VRPMDV_MCMD_C_MSG` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_vrpmdv_mcmd_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_vrpmdv_doit(struct sk_buff *sender_skb, struct genl_info *info) { + struct nlattr *na; + struct sk_buff *reply_skb; + int rc; + void *msg_head; + char *recv_msg; + + pr_info("%s() invoked\n", __func__); + + if (info == NULL) { + // should never happen + pr_err("An error occurred in %s():\n", __func__); + return -EINVAL; + } + + /* + * For each attribute there is an index in info->attrs which points to a nlattr structure + * in this structure the data is stored. + */ + na = info->attrs[GNL_VRPMDV_MCMD_A_MSG]; + + if (!na) { + pr_err("no info->attrs[%i]\n", GNL_VRPMDV_MCMD_A_MSG); + return -EINVAL; // we return here because we expect to recv a msg + } + + recv_msg = (char *) nla_data(na); + if (recv_msg == NULL) { + pr_err("error while receiving data\n"); + } else { + pr_info("received: '%s'\n", recv_msg); + } + + //aquire lock for cmd repmsg channel + // std::lock sendlk(cmd_cb_progress_data.sendmtx); + + //send the message to the copro over RPMSG + if (prpdev) { + rc = rpmsg_send(prpdev->ept, recv_msg, strlen(recv_msg)); + if (rc) { + pr_err("rpmsg_send failed: %d\n", rc); + return rc; + } + + // struct rpmsg_vrpmdv_mon_t *drv = dev_get_drvdata(&rpdev->dev); + + // Send a message back after we receive the reply from rpmsg channel + // --------------------- + + // Allocate some memory, since the size is not yet known use NLMSG_GOODSIZE + reply_skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (reply_skb == NULL) { + pr_err("An error occurred in %s():\n", __func__); + return -ENOMEM; + } + + // Create the message headersprpdev + + // Add header to netlink message; + // afterwards the buffer looks like this: + // ---------------------------------- + // | netlink header | + // | generic netlink header | + // | | + // ---------------------------------- + // msg_head = genlmsg_put(reply_skb, // buffer for netlink message: struct sk_buff * + // // According to my findings: this is not used for routing + // // This can be used in an application specific way to target + // // different endpoints within the same user application + // // but general rule: just put sender port id here + // info->snd_portid, // sending port (not process) id: int + // info->snd_seq + 1, // sequence number: int (might be used by receiver, but not mandatory) + // &gnl_vrpmdv_mcmd_family, // struct genl_family * + // 0, // flags for Netlink header: int; application specific and not mandatory + // // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header + // GNL_VRPMDV_MCMD_C_MSG + // ); + + msg_head = genlmsg_put_reply(reply_skb, // buffer for netlink message: struct sk_buff * + info, // info + &gnl_vrpmdv_mcmd_family, // struct genl_family * + 0, // flags for Netlink header: int; application specific and not mandatory + // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header + info->genlhdr->cmd + ); + + if (msg_head == NULL) { + rc = ENOMEM; + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + { + pr_info("wait for response\n"); + // wait until receive_queue_flag=1 , that means we have received data from Copro + wait_event_interruptible(receive_queue, receive_queue_flag != 0 ); + + //Copy data + receive_queue_flag = NODATARECEIVED; + + + + // std::unique_lock lk(cmd_cb_progress_data.receivemtx); + // if (myCondVar.wait_until(uLock, timePoint) == std::cv_status::timeout) + // { + // dev_err(&cmd_cb_progress_data.rpdev, "rpmsg_send failed, timeout: \n"); + // return -1: + // } + + //pr_info("get response: '%s'\n", recv_msg); + if (received_len > 0) { + pr_info("received data from copro %s\n", received_bytes); + } + else { + pr_err("don't received data from Copro \n"); + } + + } + + // Add a GNL_VRPMDV_MCMD_A_MSG attribute (actual value/payload to be sent) + // echo the value we just received + rc = nla_put_string(reply_skb, GNL_VRPMDV_MCMD_A_MSG, received_bytes); + + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + //free the buffer + kfree(received_bytes); + received_bytes = NULL; + return -rc; + } + + // Finalize the message: + // Corrects the netlink message header (length) to include the appended + // attributes. Only necessary if attributes have been added to the message. + genlmsg_end(reply_skb, msg_head); + + // Send the message back + rc = genlmsg_reply(reply_skb, info); + // same as genlmsg_unicast(genl_info_net(info), reply_skb, info->snd_portid) + // see https://elixir.bootlin.com/linux/v5.8.9/source/include/net/genetlink.h#L326 + + kfree(received_bytes); + received_bytes = NULL; + + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + return 0; + + } + pr_info("Device not set in Probe. Should not happen"); + return -1; +} + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_FOOBAR_XMPL_C_REPLY_WITH_NLMSG_ERR` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info) { + pr_info("%s() invoked, a NLMSG_ERR response will be sent back\n", __func__); + + /* + * Generic Netlink is smart enough and sends a NLMSG_ERR reply automatically as reply + * Quote from https://lwn.net/Articles/208755/: + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + */ + return -EINVAL; +} + + + +/** ----- NETLINK Driver defintion ------------------*/ + + + + + +/** + * callback that is called after the copro send data + * we have to copy it in a buffer for the netlink and later send it back to the userland + * +*/ +static int vrpmdv_monitoring_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + int ret = 0; + // struct instance_data *idata = dev_get_drvdata(&rpdev->dev); + + // dev_info(&rpdev->dev, "incoming msg %d (src: 0x%x)\n", + // ++idata->rx_count, src); + + print_hex_dump_debug(__func__, DUMP_PREFIX_NONE, 16, 1, data, len, + true); + if (len == 0) { + pr_err("(%s) Empty lenght requested\n", __func__); + return -EINVAL; + } + else { + pr_info("received logging: %s\n", (char*) data); + } + + ret = rpmsg_send(rpdev->ept, retok, sizeof(retok)); + + // received_bytes = (char *)kmalloc(len+1, GFP_KERNEL); + // memcpy(received_bytes, data, len); + + // rpmsg_RxBuf[len] = 0; + + + + // received_bytes = (char *)kmalloc(len, GFP_KERNEL); + // memcpy(received_bytes, data, len); + // received_len = len; + // receive_queue_flag= DATARECEIVED; + // wake_up_interruptible(&receive_queue); + + // /* samples should not live forever */ + // if (idata->rx_count >= count) { + // dev_info(&rpdev->dev, "goodbye!\n"); + // return 0; + // } + + /* send a new message now */ + // ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG)); + // if (ret) + // dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); + + return ret; +} + +static int vrpmdv_mon_log_probe(struct rpmsg_device *rpdev) +{ + //int rc; + + // int ret; + // struct instance_data *idata; + + // dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n", + // rpdev->src, rpdev->dst); + + pr_info("RPMSG mon logger device driver started.\n"); + + // idata = devm_kzalloc(&rpdev->dev, sizeof(*idata), GFP_KERNEL); + // if (!idata) + // return -ENOMEM; + + // dev_set_drvdata(&rpdev->dev, idata); + + // /* send a message to our remote processor to */ + // ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG)); + // if (ret) { + // dev_err(&rpdev->dev, "vrpmdv_monitoring_controler_send failed: %d\n", ret); + // return ret; + // } + + // return 0; + + // struct device *dev; + // dev = &rpdev->dev; + // struct rpmsg_vrpmdv_mon_t *rpmsg_vrpmdv_mon; + + // rpmsg_vrpmdv_mon = devm_kzalloc(dev, sizeof(*rpmsg_vrpmdv_mon), GFP_KERNEL); + // if (!rpmsg_vrpmdv_mon) + // return -ENOMEM; + + // mutex_init(&sendMTX); + // init_waitqueue_head (&receive_queue); + prpdev = rpdev; + + // dev_set_drvdata(&rpdev->dev, rpmsg_vrpmdv_mon); + pr_info("RPMSG Logger Device set.\n"); + + + /** NEU **/ + // if (cmd_cb_progress_data.rpdev == NULL) { + // cmd_cb_progress_data.rpdev = rpdev; + // pr_info("RPMSG CMD Device set.\n"); + // } + // else { + // pr_info("Error: RPMSG CMD Device already set. Don't set it twice\n"); + // } + + //pr_info("Generic Netlink VRPMDV-Mon-Log Module started.\n"); + + // Register family with its operations and policies + // rc = genl_register_family(&gnl_vrpmdv_mcmd_family); + // if (rc != 0) { + // pr_err("FAILED: genl_register_family(): %i\n", rc); + // pr_err("An error occurred while inserting the generic netlink example module\n"); + // return -1; + // } else { + // pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + // } + + return 0; + + + +} + +static void vrpmdv_mon_log_remove(struct rpmsg_device *rpdev) +{ + //int ret; + pr_info("Mon Logger unloaded.\n"); + + // Unregister the family + // ret = genl_unregister_family(&gnl_vrpmdv_mcmd_family); + // if (ret != 0) { + // pr_err("genl_unregister_family() failed: %i\n", ret); + // return; + // } else { + // pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + // } + + // mutex_destroy(&sendMTX); + // wake_up_interruptible(&receive_queue); + + pr_info("vrpmdv-mon logger driver is removed\n"); + // dev_info(&rpdev->dev, "vrpmdv-monitoring controler driver is removed\n"); + +} + +static struct rpmsg_device_id vrpmdv_mon_log_id_table[] = { + { .name = "vrpmdv-mon-log" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, vrpmdv_mon_log_id_table); + +static struct rpmsg_driver vrpmdv_mon_log = { + .drv.name = KBUILD_MODNAME, + .id_table = vrpmdv_mon_log_id_table, + .probe = vrpmdv_mon_log_probe, + .callback = vrpmdv_mon_log_cb, + .remove = vrpmdv_mon_log_remove, +}; +module_rpmsg_driver(vrpmdv_mon_log); + + +// static struct rpmsg_driver vrpmdv_monitoring_data = { +// .drv.name = KBUILD_MODNAME, +// .id_table = vrpmdv_monitoring_controler_id_table, +// .probe = vrpmdv_monitoring_probe, +// .callback = vrpmdv_monitoring_cb, +// .remove = vrpmdv_monitoring_remove, +// }; + +// module_rpmsg_driver(vrpmdv_monitoring_data); + +MODULE_DESCRIPTION("Remote processor messaging vrpmdv monitoring controler"); +MODULE_LICENSE("GPL v2"); + + + +// /** +// * Module/driver initializer. Called on module load/insertion. +// * +// * @return success (0) or error code. +// */ +// static int __init gnl_foobar_xmpl_module_init(void) { +// int rc; +// pr_info("Generic Netlink Example Module inserted.\n"); + +// // Register family with its operations and policies +// rc = genl_register_family(&gnl_foobar_xmpl_family); +// if (rc != 0) { +// pr_err("FAILED: genl_register_family(): %i\n", rc); +// pr_err("An error occurred while inserting the generic netlink example module\n"); +// return -1; +// } else { +// pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); +// } + +// mutex_init(&dumpit_cb_progress_data.mtx); + +// return 0; +// } + +// /** +// * Module/driver uninitializer. Called on module unload/removal. +// * +// * @return success (0) or error code. +// */ +// static void __exit gnl_foobar_xmpl_module_exit(void) { +// int ret; +// pr_info("Generic Netlink Example Module unloaded.\n"); + +// // Unregister the family +// ret = genl_unregister_family(&gnl_foobar_xmpl_family); +// if (ret != 0) { +// pr_err("genl_unregister_family() failed: %i\n", ret); +// return; +// } else { +// pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); +// } + +// mutex_destroy(&dumpit_cb_progress_data.mtx); +// } + + +// ---- + +// static int __init rpmsg_sdb_drv_init(void) +// { +// int ret = 0; + +// /* Register rpmsg device */ +// ret = register_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + +// if (ret) { +// pr_err("rpmsg_sdb(ERROR): Failed to register device\n"); +// return ret; +// } + +// pr_info("rpmsg_sdb: Init done\n"); + +// return ret; +// } + +// static void __exit rpmsg_sdb_drv_exit(void) +// { +// unregister_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); +// pr_info("rpmsg_sdb: Exit\n"); +// } + +// module_init(rpmsg_sdb_drv_init); +// module_exit(rpmsg_sdb_drv_exit); diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/vrpmdv-mon-tty.c b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/vrpmdv-mon-tty.c new file mode 100644 index 0000000..d122414 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/vrpmdv-mon-tty.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 STMicroelectronics - All Rights Reserved + * + * The rpmsg tty driver implements serial communication on the RPMsg bus to makes + * possible for user-space programs to send and receive rpmsg messages as a standard + * tty protocol. + * + * The remote processor can instantiate a new tty by requesting a "rpmsg-tty" RPMsg service. + * The "rpmsg-tty" service is directly used for data exchange. No flow control is implemented yet. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#define RPMSG_TTY_NAME "ttyMON" +#define MAX_TTY_RPMSG 32 + +static DEFINE_IDR(tty_idr); /* tty instance id */ +static DEFINE_MUTEX(idr_lock); /* protects tty_idr */ + +static struct tty_driver *rpmsg_tty_driver; + +struct rpmsg_tty_port { + struct tty_port port; /* TTY port data */ + int id; /* TTY rpmsg index */ + struct rpmsg_device *rpdev; /* rpmsg device */ +}; + +static int rpmsg_tty_cb(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 src) +{ + struct rpmsg_tty_port *cport = dev_get_drvdata(&rpdev->dev); + int copied; + + pr_info("%s() invoked\n", __func__); + + if (!len) + return -EINVAL; + + copied = tty_insert_flip_string(&cport->port, data, len); + pr_info("%s() received\n", data); + + if (copied != len) + dev_err_ratelimited(&rpdev->dev, "Trunc buffer: available space is %d\n", copied); + pr_info("%s() error cop\n", __func__); + + tty_flip_buffer_push(&cport->port); + pr_info("%s() invoked. succes!!\n", __func__); + return 0; +} + +static int rpmsg_tty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + pr_info("%s() invoked\n", __func__); + struct rpmsg_tty_port *cport = idr_find(&tty_idr, tty->index); + struct tty_port *port; + + tty->driver_data = cport; + + port = tty_port_get(&cport->port); + return tty_port_install(port, driver, tty); +} + +static void rpmsg_tty_cleanup(struct tty_struct *tty) +{ + pr_info("%s() invoked\n", __func__); + tty_port_put(tty->port); +} + +static int rpmsg_tty_open(struct tty_struct *tty, struct file *filp) +{ + pr_info("%s() invoked\n", __func__); + return tty_port_open(tty->port, tty, filp); +} + +static void rpmsg_tty_close(struct tty_struct *tty, struct file *filp) +{ + pr_info("%s() invoked\n", __func__); + return tty_port_close(tty->port, tty, filp); +} + +static int rpmsg_tty_write(struct tty_struct *tty, const u8 *buf, int len) +{ + pr_info("%s() invoked\n", __func__); + struct rpmsg_tty_port *cport = tty->driver_data; + struct rpmsg_device *rpdev; + int msg_max_size, msg_size; + int ret; + + rpdev = cport->rpdev; + + msg_max_size = rpmsg_get_mtu(rpdev->ept); + if (msg_max_size < 0) + return msg_max_size; + + msg_size = min(len, msg_max_size); + + /* + * Use rpmsg_trysend instead of rpmsg_send to send the message so the caller is not + * hung until a rpmsg buffer is available. In such case rpmsg_trysend returns -ENOMEM. + */ + ret = rpmsg_trysend(rpdev->ept, (void *)buf, msg_size); + if (ret) { + dev_dbg_ratelimited(&rpdev->dev, "rpmsg_send failed: %d\n", ret); + return ret; + } + pr_info("%s() invoked. send succes!!\n", __func__); + return msg_size; +} + +static unsigned int rpmsg_tty_write_room(struct tty_struct *tty) +{ + pr_info("%s() invoked\n", __func__); + struct rpmsg_tty_port *cport = tty->driver_data; + int size; + + size = rpmsg_get_mtu(cport->rpdev->ept); + if (size < 0) + return 0; + pr_info("%s() invoked. write success\n", __func__); + return size; +} + +static void rpmsg_tty_hangup(struct tty_struct *tty) +{ + pr_info("%s() invoked\n", __func__); + tty_port_hangup(tty->port); +} + +static const struct tty_operations rpmsg_tty_ops = { + .install = rpmsg_tty_install, + .open = rpmsg_tty_open, + .close = rpmsg_tty_close, + .write = rpmsg_tty_write, + .write_room = rpmsg_tty_write_room, + .hangup = rpmsg_tty_hangup, + .cleanup = rpmsg_tty_cleanup, +}; + +static struct rpmsg_tty_port *rpmsg_tty_alloc_cport(void) +{ + pr_info("%s() invoked\n", __func__); + struct rpmsg_tty_port *cport; + int ret; + + cport = kzalloc(sizeof(*cport), GFP_KERNEL); + if (!cport) + return ERR_PTR(-ENOMEM); + + mutex_lock(&idr_lock); + ret = idr_alloc(&tty_idr, cport, 0, MAX_TTY_RPMSG, GFP_KERNEL); + mutex_unlock(&idr_lock); + + if (ret < 0) { + kfree(cport); + return ERR_PTR(ret); + } + + cport->id = ret; + + return cport; +} + +static void rpmsg_tty_destruct_port(struct tty_port *port) +{ + pr_info("%s() invoked\n", __func__); + struct rpmsg_tty_port *cport = container_of(port, struct rpmsg_tty_port, port); + + mutex_lock(&idr_lock); + idr_remove(&tty_idr, cport->id); + mutex_unlock(&idr_lock); + + kfree(cport); +} + +static const struct tty_port_operations rpmsg_tty_port_ops = { + .destruct = rpmsg_tty_destruct_port, +}; + + +static int rpmsg_tty_probe(struct rpmsg_device *rpdev) +{ + pr_info("%s() invoked\n", __func__); + struct rpmsg_tty_port *cport; + struct device *dev = &rpdev->dev; + struct device *tty_dev; + int ret; + + cport = rpmsg_tty_alloc_cport(); + if (IS_ERR(cport)) + return dev_err_probe(dev, PTR_ERR(cport), "Failed to alloc tty mon port\n"); + + tty_port_init(&cport->port); + cport->port.ops = &rpmsg_tty_port_ops; + + tty_dev = tty_port_register_device(&cport->port, rpmsg_tty_driver, + cport->id, dev); + if (IS_ERR(tty_dev)) { + ret = dev_err_probe(dev, PTR_ERR(tty_dev), "Failed to register tty mon port\n"); + tty_port_put(&cport->port); + return ret; + } + + cport->rpdev = rpdev; + + dev_set_drvdata(dev, cport); + + dev_dbg(dev, "New channel: 0x%x -> 0x%x: " RPMSG_TTY_NAME "%d\n", + rpdev->src, rpdev->dst, cport->id); + + return 0; +} + +static void rpmsg_tty_remove(struct rpmsg_device *rpdev) +{ + pr_info("%s() invoked\n", __func__); + struct rpmsg_tty_port *cport = dev_get_drvdata(&rpdev->dev); + + dev_dbg(&rpdev->dev, "Removing mon tty device %d\n", cport->id); + + /* User hang up to release the tty */ + tty_port_tty_hangup(&cport->port, false); + + tty_unregister_device(rpmsg_tty_driver, cport->id); + + tty_port_put(&cport->port); +} + +static struct rpmsg_device_id rpmsg_driver_tty_id_table[] = { + { .name = "vrpmdv-mon-tty" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_tty_id_table); + +static struct rpmsg_driver rpmsg_tty_rpmsg_drv = { + .drv.name = KBUILD_MODNAME, + .id_table = rpmsg_driver_tty_id_table, + .probe = rpmsg_tty_probe, + .callback = rpmsg_tty_cb, + .remove = rpmsg_tty_remove, +}; + +static int __init rpmsg_tty_init(void) +{ + pr_info("%s() invoked\n", __func__); + int ret; + + rpmsg_tty_driver = tty_alloc_driver(MAX_TTY_RPMSG, TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(rpmsg_tty_driver)) + return PTR_ERR(rpmsg_tty_driver); + + rpmsg_tty_driver->driver_name = "vrpmdv-mon-tty"; + rpmsg_tty_driver->name = RPMSG_TTY_NAME; + rpmsg_tty_driver->major = 0; + rpmsg_tty_driver->type = TTY_DRIVER_TYPE_CONSOLE; + + /* Disable unused mode by default */ + rpmsg_tty_driver->init_termios = tty_std_termios; + rpmsg_tty_driver->init_termios.c_lflag &= ~(ECHO | ICANON); + rpmsg_tty_driver->init_termios.c_oflag &= ~(OPOST | ONLCR); + + tty_set_operations(rpmsg_tty_driver, &rpmsg_tty_ops); + + ret = tty_register_driver(rpmsg_tty_driver); + if (ret < 0) { + pr_err("Couldn't install mon tty driver: %d\n", ret); + goto error_put; + } + + ret = register_rpmsg_driver(&rpmsg_tty_rpmsg_drv); + if (ret < 0) { + pr_err("Couldn't register mon tty driver: %d\n", ret); + goto error_unregister; + } + pr_info("%s() invoked. Install complete!\n", __func__); + return 0; + +error_unregister: + pr_info("%s() invoked unregister.\n", __func__); + tty_unregister_driver(rpmsg_tty_driver); + +error_put: + pr_info("%s() invoked. error put\n", __func__); + tty_driver_kref_put(rpmsg_tty_driver); + + return ret; +} + +static void __exit rpmsg_tty_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_tty_rpmsg_drv); + tty_unregister_driver(rpmsg_tty_driver); + tty_driver_kref_put(rpmsg_tty_driver); + idr_destroy(&tty_idr); +} + +module_init(rpmsg_tty_init); +module_exit(rpmsg_tty_exit); + +MODULE_AUTHOR("Arnaud Pouliquen "); +MODULE_DESCRIPTION("remote processor messaging tty driver"); +MODULE_LICENSE("GPL v2"); diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/vrpmdv_mon_tty.c.old b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/vrpmdv_mon_tty.c.old new file mode 100644 index 0000000..2bf7717 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/files/vrpmdv_mon_tty.c.old @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 STMicroelectronics - All Rights Reserved + * + * The rpmsg tty driver implements serial communication on the RPMsg bus to makes + * possible for user-space programs to send and receive rpmsg messages as a standard + * tty protocol. + * + * The remote processor can instantiate a new tty by requesting a "rpmsg-tty" RPMsg service. + * The "rpmsg-tty" service is directly used for data exchange. No flow control is implemented yet. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#define RPMSG_TTY_NAME "ttyMON" +#define MAX_TTY_RPMSG 32 + +static DEFINE_IDR(tty_idr); /* tty instance id */ +static DEFINE_MUTEX(idr_lock); /* protects tty_idr */ + +static struct tty_driver *rpmsg_tty_driver; + +struct rpmsg_tty_port { + struct tty_port port; /* TTY port data */ + int id; /* TTY rpmsg index */ + struct rpmsg_device *rpdev; /* rpmsg device */ +}; + +static int rpmsg_tty_cb(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 src) +{ + struct rpmsg_tty_port *cport = dev_get_drvdata(&rpdev->dev); + int copied; + + if (!len) + return -EINVAL; + copied = tty_insert_flip_string(&cport->port, data, len); + if (copied != len) + dev_err_ratelimited(&rpdev->dev, "Trunc buffer: available space is %d\n", copied); + tty_flip_buffer_push(&cport->port); + + return 0; +} + +static int rpmsg_tty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct rpmsg_tty_port *cport = idr_find(&tty_idr, tty->index); + struct tty_port *port; + + tty->driver_data = cport; + + port = tty_port_get(&cport->port); + return tty_port_install(port, driver, tty); +} + +static void rpmsg_tty_cleanup(struct tty_struct *tty) +{ + tty_port_put(tty->port); +} + +static int rpmsg_tty_open(struct tty_struct *tty, struct file *filp) +{ + return tty_port_open(tty->port, tty, filp); +} + +static void rpmsg_tty_close(struct tty_struct *tty, struct file *filp) +{ + return tty_port_close(tty->port, tty, filp); +} + +static int rpmsg_tty_write(struct tty_struct *tty, const u8 *buf, int len) +{ + struct rpmsg_tty_port *cport = tty->driver_data; + struct rpmsg_device *rpdev; + int msg_max_size, msg_size; + int ret; + + rpdev = cport->rpdev; + + msg_max_size = rpmsg_get_mtu(rpdev->ept); + if (msg_max_size < 0) + return msg_max_size; + + msg_size = min(len, msg_max_size); + + /* + * Use rpmsg_trysend instead of rpmsg_send to send the message so the caller is not + * hung until a rpmsg buffer is available. In such case rpmsg_trysend returns -ENOMEM. + */ + ret = rpmsg_trysend(rpdev->ept, (void *)buf, msg_size); + if (ret) { + dev_dbg_ratelimited(&rpdev->dev, "rpmsg_send failed: %d\n", ret); + return ret; + } + + return msg_size; +} + +static unsigned int rpmsg_tty_write_room(struct tty_struct *tty) +{ + struct rpmsg_tty_port *cport = tty->driver_data; + int size; + + size = rpmsg_get_mtu(cport->rpdev->ept); + if (size < 0) + return 0; + + return size; +} + +static void rpmsg_tty_hangup(struct tty_struct *tty) +{ + tty_port_hangup(tty->port); +} + +static const struct tty_operations rpmsg_tty_ops = { + .install = rpmsg_tty_install, + .open = rpmsg_tty_open, + .close = rpmsg_tty_close, + .write = rpmsg_tty_write, + .write_room = rpmsg_tty_write_room, + .hangup = rpmsg_tty_hangup, + .cleanup = rpmsg_tty_cleanup, +}; + +static struct rpmsg_tty_port *rpmsg_tty_alloc_cport(void) +{ + struct rpmsg_tty_port *cport; + int ret; + + cport = kzalloc(sizeof(*cport), GFP_KERNEL); + if (!cport) + return ERR_PTR(-ENOMEM); + + mutex_lock(&idr_lock); + ret = idr_alloc(&tty_idr, cport, 0, MAX_TTY_RPMSG, GFP_KERNEL); + mutex_unlock(&idr_lock); + + if (ret < 0) { + kfree(cport); + return ERR_PTR(ret); + } + + cport->id = ret; + + return cport; +} + +static void rpmsg_tty_destruct_port(struct tty_port *port) +{ + struct rpmsg_tty_port *cport = container_of(port, struct rpmsg_tty_port, port); + + mutex_lock(&idr_lock); + idr_remove(&tty_idr, cport->id); + mutex_unlock(&idr_lock); + + kfree(cport); +} + +static const struct tty_port_operations rpmsg_tty_port_ops = { + .destruct = rpmsg_tty_destruct_port, +}; + + +static int rpmsg_tty_probe(struct rpmsg_device *rpdev) +{ + struct rpmsg_tty_port *cport; + struct device *dev = &rpdev->dev; + struct device *tty_dev; + int ret; + + cport = rpmsg_tty_alloc_cport(); + if (IS_ERR(cport)) + return dev_err_probe(dev, PTR_ERR(cport), "Failed to alloc tty mon port\n"); + + tty_port_init(&cport->port); + cport->port.ops = &rpmsg_tty_port_ops; + + tty_dev = tty_port_register_device(&cport->port, rpmsg_tty_driver, + cport->id, dev); + if (IS_ERR(tty_dev)) { + ret = dev_err_probe(dev, PTR_ERR(tty_dev), "Failed to register tty mon port\n"); + tty_port_put(&cport->port); + return ret; + } + + cport->rpdev = rpdev; + + dev_set_drvdata(dev, cport); + + dev_dbg(dev, "New channel: 0x%x -> 0x%x: " RPMSG_TTY_NAME "%d\n", + rpdev->src, rpdev->dst, cport->id); + + return 0; +} + +static void rpmsg_tty_remove(struct rpmsg_device *rpdev) +{ + struct rpmsg_tty_port *cport = dev_get_drvdata(&rpdev->dev); + + dev_dbg(&rpdev->dev, "Removing mon tty device %d\n", cport->id); + + /* User hang up to release the tty */ + tty_port_tty_hangup(&cport->port, false); + + tty_unregister_device(rpmsg_tty_driver, cport->id); + + tty_port_put(&cport->port); +} + +static struct rpmsg_device_id rpmsg_driver_tty_id_table[] = { + { .name = "vrpmdv-mon-tty" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_tty_id_table); + +static struct rpmsg_driver rpmsg_tty_rpmsg_drv = { + .drv.name = KBUILD_MODNAME, + .id_table = rpmsg_driver_tty_id_table, + .probe = rpmsg_tty_probe, + .callback = rpmsg_tty_cb, + .remove = rpmsg_tty_remove, +}; + +static int __init rpmsg_tty_init(void) +{ + int ret; + + rpmsg_tty_driver = tty_alloc_driver(MAX_TTY_RPMSG, TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(rpmsg_tty_driver)) + return PTR_ERR(rpmsg_tty_driver); + + rpmsg_tty_driver->driver_name = "vrpmdv-mon-tty"; + rpmsg_tty_driver->name = RPMSG_TTY_NAME; + rpmsg_tty_driver->major = 0; + rpmsg_tty_driver->type = TTY_DRIVER_TYPE_CONSOLE; + + /* Disable unused mode by default */ + rpmsg_tty_driver->init_termios = tty_std_termios; + rpmsg_tty_driver->init_termios.c_lflag &= ~(ECHO | ICANON); + rpmsg_tty_driver->init_termios.c_oflag &= ~(OPOST | ONLCR); + + tty_set_operations(rpmsg_tty_driver, &rpmsg_tty_ops); + + ret = tty_register_driver(rpmsg_tty_driver); + if (ret < 0) { + pr_err("Couldn't install mon tty driver: %d\n", ret); + goto error_put; + } + + ret = register_rpmsg_driver(&rpmsg_tty_rpmsg_drv); + if (ret < 0) { + pr_err("Couldn't register mon tty driver: %d\n", ret); + goto error_unregister; + } + + return 0; + +error_unregister: + tty_unregister_driver(rpmsg_tty_driver); + +error_put: + tty_driver_kref_put(rpmsg_tty_driver); + + return ret; +} + +static void __exit rpmsg_tty_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_tty_rpmsg_drv); + tty_unregister_driver(rpmsg_tty_driver); + tty_driver_kref_put(rpmsg_tty_driver); + idr_destroy(&tty_idr); +} + +module_init(rpmsg_tty_init); +module_exit(rpmsg_tty_exit); + +MODULE_AUTHOR("Arnaud Pouliquen "); +MODULE_DESCRIPTION("remote processor messaging tty driver"); +MODULE_LICENSE("GPL v2"); diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/kernel-module-vrpmdv-monitoring-driver.bb b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/kernel-module-vrpmdv-monitoring-driver.bb new file mode 100644 index 0000000..9bd4cda --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/kernel-module-vrpmdv-monitoring-driver.bb @@ -0,0 +1,16 @@ +SUMMARY = "VRPMDV Monitoring Driver" +LICENSE = "GPL-2.0-only" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "file://Makefile \ + file://vrpmdv-monitoring.c \ + " + +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. + +RPROVIDES:${PN} += "kernel-module-vrpmdv-monitoring" diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/netlink-save.c.save b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/netlink-save.c.save new file mode 100644 index 0000000..8a32ba8 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/netlink-save.c.save @@ -0,0 +1,541 @@ +/* Copyright 2021 Philipp Schuster + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in the + * Software without restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// ################################################################################################## +/* + * This is a Linux kernel module/driver called "gnl_foobar_xmpl" which shows you the basics of using + * Generic Netlink in the kernel. It registers a Netlink family called "gnl_foobar_xmpl". See + * "gnl_foobar_xmpl_prop.h" for common properties of the family. "Generic Netlink" offers us a lot of + * convenient functions to register new/custom Netlink families on the fly during runtime. We use this + * functionality here. It implements simple IPC between Userland and Kernel (Kernel responds to userland). + * + * Check "gnl_foobar_xmpl_prop.h" for common properties of the family first, afterwards follow the code here. + * + * You can find some more interesting documentation about Generic Netlink here: + * "Generic Netlink HOW-TO based on Jamal's original doc" https://lwn.net/Articles/208755/ + */ + +// basic definitions for kernel module development +#include +// definitions for generic netlink families, policies etc; +// transitive dependencies for basic netlink, sockets etc +#include +// required for locking inside the .dumpit callback demonstration +#include + +// data/vars/enums/properties that describes our protocol that we implement +// on top of generic netlink (like functions we want to trigger on the receiving side) +#include "gnl_foobar_xmpl_prop.h" + +// Module/Driver description. +// You can see this for example when executing `$ modinfo ./gnl_foobar_xmpl.ko` (after build). +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Philipp Schuster "); +MODULE_DESCRIPTION( + "Linux driver that registers the custom Netlink family " + "\"" FAMILY_NAME "\" via Generic Netlink and responds to echo messages " + "according to the properties specified in \"gnl_foobar_xmpl_prop.h\"." +); + + +/* ######################## CONVENIENT LOGGING MACROS ######################## */ +// (Re)definition of some convenient logging macros from . You can see the logging +// messages when printing the kernel log, e.g. with `$ sudo dmesg`. +// See https://elixir.bootlin.com/linux/latest/source/include/linux/printk.h + +// with this redefinition we can easily prefix all log messages from pr_* logging macros +#ifdef pr_fmt +#undef pr_fmt +#endif +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +/* ########################################################################### */ + +/** + * Data structure required for our .dumpit callback handler to + * know about the progress of an ongoing dump. + * See the dumpit callback handler how it is used. + */ +struct { + // from + /** + * Only one process is allowed per dump process. We need a lock for that. + */ + struct mutex mtx; + /** + * Number that describes how many packets we need to send until we are done + * during an ongoing dumpit process. 0 = done. + */ + int unsigned runs_to_go; + /** + * Number that describes how many packets per dump are sent in total. + * Constant per dump. + */ + int unsigned total_runs; +} dumpit_cb_progress_data; + +// Documentation is on the implementation of this function. +int gnl_cb_echo_doit(struct sk_buff *sender_skb, struct genl_info *info); + +// Documentation is on the implementation of this function. +int gnl_cb_echo_dumpit(struct sk_buff *pre_allocated_skb, struct netlink_callback *cb); + +// Documentation is on the implementation of this function. +int gnl_cb_echo_dumpit_before(struct netlink_callback *cb); + +// Documentation is on the implementation of this function. +int gnl_cb_echo_dumpit_before_after(struct netlink_callback *cb); + +// Documentation is on the implementation of this function. +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info); + +/** + * The length of `struct genl_ops gnl_foobar_xmpl_ops[]`. Not necessarily + * the number of commands in `enum GNlFoobarXmplCommand`. It depends on your application logic. + * For example, you can use the same command multiple times and - dependent by flag - + * invoke a different callback handler. In our simple example we just use one .doit callback + * per operation/command. + */ +#define GNL_FOOBAR_OPS_LEN (GNL_FOOBAR_XMPL_COMMAND_COUNT) + +/** + * Array with all operations that our protocol on top of Generic Netlink + * supports. An operation is the glue between a command ("cmd" field in `struct genlmsghdr` of + * received Generic Netlink message) and the corresponding ".doit" callback function. + * See: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L148 + */ +struct genl_ops gnl_foobar_xmpl_ops[GNL_FOOBAR_OPS_LEN] = { + { + /* The "cmd" field in `struct genlmsghdr` of received Generic Netlink message */ + .cmd = GNL_FOOBAR_XMPL_C_ECHO_MSG, + /* TODO Use case ? */ + .flags = 0, + /* TODO Use case ? */ + .internal_flags = 0, + /* Callback handler when a request with the specified ".cmd" above is received. + * Always validates the payload except one set NO_STRICT_VALIDATION flag in ".validate" + * See: https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + * + * Quote from: https://lwn.net/Articles/208755 + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + * + */ + .doit = gnl_cb_echo_doit, + /* This callback is similar in use to the standard Netlink 'dumpit' callback. + * The 'dumpit' callback is invoked when a Generic Netlink message is received + * with the NLM_F_DUMP flag set. + * + * A dump can be understand as a "GET ALL DATA OF THE GIVEN ENTITY", i.e. + * the userland can receive as long as the .dumpit callback returns data. + * + * .dumpit is not mandatory, but either it or .doit must be provided, see + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L367 + * + * To be honest I don't know in what use case one should use .dumpit and why + * it is useful, because you can achieve the same also with .doit handlers. + * Anyway, this is just an example/tutorial. + * + * Quote from: https://lwn.net/Articles/208755 + * "The main difference between a 'dumpit' handler and a 'doit' handler is + * that a 'dumpit' handler does not allocate a message buffer for a response; + * a pre-allocated sk_buff is passed to the 'dumpit' handler as the first + * parameter. The 'dumpit' handler should fill the message buffer with the + * appropriate response message and return the size of the sk_buff, + * i.e. sk_buff->len, and the message buffer will automatically be sent to the + * Generic Netlink client that initiated the request. As long as the 'dumpit' + * handler returns a value greater than zero it will be called again with a + * newly allocated message buffer to fill, when the handler has no more data + * to send it should return zero; error conditions are indicated by returning + * a negative value. If necessary, state can be preserved in the + * netlink_callback parameter which is passed to the 'dumpit' handler; the + * netlink_callback parameter values will be preserved across handler calls + * for a single request." + * + * You can see the check for the NLM_F_DUMP-flag here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L780 + */ + .dumpit = gnl_cb_echo_dumpit, + /* Start callback for dumps. Can be used to lock data structures. */ + .start = gnl_cb_echo_dumpit_before, + /* Completion callback for dumps. Can be used for cleanup after a dump and releasing locks. */ + .done = gnl_cb_echo_dumpit_before_after, + /* + 0 (= "validate strictly") or value `enum genl_validate_flags` + * see: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L108 + */ + .validate = 0, + }, + { + .cmd = GNL_FOOBAR_XMPL_C_REPLY_WITH_NLMSG_ERR, + .flags = 0, + .internal_flags = 0, + .doit = gnl_cb_doit_reply_with_nlmsg_err, + // .dumpit is not required, only optional; application specific/dependent on your use case + // in a real application you probably have different .dumpit handlers per operation/command + .dumpit = NULL, + // in a real application you probably have different .start handlers per operation/command + .start = NULL, + // in a real application you probably have different .done handlers per operation/command + .done = NULL, + .validate = 0, + } +}; + +/** + * Attribute policy: defines which attribute has which type (e.g int, char * etc). + * This get validated for each received Generic Netlink message, if not deactivated + * in `gnl_foobar_xmpl_ops[].validate`. + * See https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + */ +static struct nla_policy gnl_foobar_xmpl_policy[GNL_FOOBAR_XMPL_ATTRIBUTE_ENUM_LEN] = { + // In case you are seeing this syntax for the first time (I also learned this just after a few years of + // coding with C myself): The following static array initiations are equivalent: + // `int a[2] = {1, 2}` <==> `int a[2] = {[0] => 1, [1] => 2}`. + + [GNL_FOOBAR_XMPL_A_UNSPEC] = {.type = NLA_UNSPEC}, + + // You can set this to NLA_U32 for testing and send an ECHO message from the userland + // It will fail in this case and you see a entry in the kernel log. + + // `enum GNL_FOOBAR_XMPL_ATTRIBUTE::GNL_FOOBAR_XMPL_A_MSG` is a null-terminated C-String + [GNL_FOOBAR_XMPL_A_MSG] = {.type = NLA_NUL_STRING}, +}; + +/** + * Definition of the Netlink family we want to register using Generic Netlink functionality + */ +static struct genl_family gnl_foobar_xmpl_family = { + // automatically assign an id + .id = 0, + // we don't use custom additional header info / user specific header + .hdrsize = 0, + // The name of this family, used by userspace application to get the numeric ID + .name = FAMILY_NAME, + // family specific version number; can be used to evolve application over time (multiple versions) + .version = 1, + // delegates all incoming requests to callback functions + .ops = gnl_foobar_xmpl_ops, + // length of array `gnl_foobar_xmpl_ops` + .n_ops = GNL_FOOBAR_OPS_LEN, + // attribute policy (for validation of messages). Enforced automatically, except ".validate" in + // corresponding ".ops"-field is set accordingly. + .policy = gnl_foobar_xmpl_policy, + // Number of attributes / bounds check for policy (array length) + .maxattr = GNL_FOOBAR_XMPL_ATTRIBUTE_ENUM_LEN, + // Owning Kernel module of the Netlink family we register. + .module = THIS_MODULE, + + // Actually not necessary because this memory region would be zeroed anyway during module load, + // but this way one sees all possible options. + + // if your application must handle multiple netlink calls in parallel (where one should not block the next + // from starting), set this to true! otherwise all netlink calls are mutually exclusive + .parallel_ops = 0, + // set to true if the family can handle network namespaces and should be presented in all of them + .netnsok = 0, + // called before an operation's doit callback, it may do additional, common, filtering and return an error + .pre_doit = NULL, + // called after an operation's doit callback, it may undo operations done by pre_doit, for example release locks + .post_doit = NULL, +}; + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_FOOBAR_XMPL_C_ECHO` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_echo_doit(struct sk_buff *sender_skb, struct genl_info *info) { + struct nlattr *na; + struct sk_buff *reply_skb; + int rc; + void *msg_head; + char *recv_msg; + + pr_info("%s() invoked\n", __func__); + + if (info == NULL) { + // should never happen + pr_err("An error occurred in %s():\n", __func__); + return -EINVAL; + } + + /* + * For each attribute there is an index in info->attrs which points to a nlattr structure + * in this structure the data is stored. + */ + na = info->attrs[GNL_FOOBAR_XMPL_A_MSG]; + + if (!na) { + pr_err("no info->attrs[%i]\n", GNL_FOOBAR_XMPL_A_MSG); + return -EINVAL; // we return here because we expect to recv a msg + } + + recv_msg = (char *) nla_data(na); + if (recv_msg == NULL) { + pr_err("error while receiving data\n"); + } else { + pr_info("received: '%s'\n", recv_msg); + } + + + // Send a message back + // --------------------- + + // Allocate some memory, since the size is not yet known use NLMSG_GOODSIZE + reply_skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (reply_skb == NULL) { + pr_err("An error occurred in %s():\n", __func__); + return -ENOMEM; + } + + // Create the message headers + + // Add header to netlink message; + // afterwards the buffer looks like this: + // ---------------------------------- + // | netlink header | + // | generic netlink header | + // | | + // ---------------------------------- + msg_head = genlmsg_put(reply_skb, // buffer for netlink message: struct sk_buff * + // According to my findings: this is not used for routing + // This can be used in an application specific way to target + // different endpoints within the same user application + // but general rule: just put sender port id here + info->snd_portid, // sending port (not process) id: int + info->snd_seq + 1, // sequence number: int (might be used by receiver, but not mandatory) + &gnl_foobar_xmpl_family, // struct genl_family * + 0, // flags for Netlink header: int; application specific and not mandatory + // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header + GNL_FOOBAR_XMPL_C_ECHO_MSG + ); + if (msg_head == NULL) { + rc = ENOMEM; + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + // Add a GNL_FOOBAR_XMPL_A_MSG attribute (actual value/payload to be sent) + // echo the value we just received + rc = nla_put_string(reply_skb, GNL_FOOBAR_XMPL_A_MSG, recv_msg); + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + // Finalize the message: + // Corrects the netlink message header (length) to include the appended + // attributes. Only necessary if attributes have been added to the message. + genlmsg_end(reply_skb, msg_head); + + // Send the message back + rc = genlmsg_reply(reply_skb, info); + // same as genlmsg_unicast(genl_info_net(info), reply_skb, info->snd_portid) + // see https://elixir.bootlin.com/linux/v5.8.9/source/include/net/genetlink.h#L326 + + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + return 0; +} + +/** + * ".dumpit"-callback function if a Generic Netlink with command ECHO_MSG and flag `NLM_F_DUMP` is received. + * Please look into the comments where this is used as ".dumpit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".dumpit" callbacks. + * + * A dump must be understand of "give me all data of a given entity" + * rather than a "dump of the netlink message itself" for debugging etc! + * + * This handler requires `gnl_cb_echo_dumpit_before` to run before a dump and `gnl_cb_echo_dumpit_after` after a dump. + * + * For the sake of simplicity, we use the ECHO_MSG command for the dump. In fact, we don't expect a + * MSG-Attribute here, unlike the regular ECHO_MSG handler. We reply with a dump of + * "all messages that we got" (application specific, hard coded in this example). +*/ +int gnl_cb_echo_dumpit(struct sk_buff *pre_allocated_skb, struct netlink_callback *cb) { + void *msg_head; + int ret; + static const char HELLO_FROM_DUMPIT_MSG[] = "You set the flag NLM_F_DUMP; this message is " + "brought to you by .dumpit callback :)"; + pr_info("Called %s()\n", __func__); + + if (dumpit_cb_progress_data.runs_to_go == 0) { + pr_info("no more data to send in dumpit cb\n"); + // mark that dump is done; + return 0; + } else { + dumpit_cb_progress_data.runs_to_go--; + pr_info("%s: %d more runs to do\n", __func__, dumpit_cb_progress_data.runs_to_go); + } + + msg_head = genlmsg_put(pre_allocated_skb, // buffer for netlink message: struct sk_buff * + // According to my findings: this is not used for routing + // This can be used in an application specific way to target + // different endpoints within the same user application + // but general rule: just put sender port id here + cb->nlh->nlmsg_pid, // sending port (not process) id: int + // sequence number: int (might be used by receiver, but not mandatory) + // sequence 0, 1, 2... + dumpit_cb_progress_data.total_runs - dumpit_cb_progress_data.runs_to_go - 1, + &gnl_foobar_xmpl_family, // struct genl_family * + 0, // flags: int (for netlink header); we don't check them in the userland; application specific + // this way we can trigger a specific command/callback on the receiving side or imply + // on which type of command we are currently answering; this is application specific + GNL_FOOBAR_XMPL_C_ECHO_MSG // cmd: u8 (for generic netlink header); + ); + if (msg_head == NULL) { + pr_info("An error occurred in %s(): genlmsg_put() failed\n", __func__); + return -ENOMEM; + } + ret = nla_put_string( + pre_allocated_skb, + GNL_FOOBAR_XMPL_A_MSG, + HELLO_FROM_DUMPIT_MSG + ); + if (ret < 0) { + pr_info("An error occurred in %s():\n", __func__); + return ret; + } + genlmsg_end(pre_allocated_skb, msg_head); + + // return the length of data we wrote into the pre-allocated buffer + return pre_allocated_skb->len; +} + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_FOOBAR_XMPL_C_REPLY_WITH_NLMSG_ERR` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info) { + pr_info("%s() invoked, a NLMSG_ERR response will be sent back\n", __func__); + + /* + * Generic Netlink is smart enough and sends a NLMSG_ERR reply automatically as reply + * Quote from https://lwn.net/Articles/208755/: + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + */ + return -EINVAL; +} + +/** + * Called before a dump with `gnl_cb_echo_dumpit()` starts. + * See where this is assigned in `struct genl_ops gnl_foobar_xmpl_ops[]` as + * `.start` callback for more comments. + * + * @return success (0) or error. + */ +int gnl_cb_echo_dumpit_before(struct netlink_callback *cb) { + int ret; + static int unsigned const dump_runs = 3; + pr_info("%s: dump started. acquire lock. initialize dump runs_to_go (number of receives userland can make) to %d runs\n", __func__, dump_runs); + // Lock the mutex like mutex_lock(), and return 0 if the mutex has been acquired or sleep until the mutex becomes available + // If a signal arrives while waiting for the lock then this function returns -EINTR. + ret = mutex_lock_interruptible(&dumpit_cb_progress_data.mtx); + if (ret != 0) { + pr_err("Failed to get lock!\n"); + return ret; + } + dumpit_cb_progress_data.total_runs = dump_runs; + dumpit_cb_progress_data.runs_to_go = dump_runs; + return 0; + +} + +/** + * Called before a dump with `gnl_cb_echo_dumpit()` starts. + * See where this is assigned in `struct genl_ops gnl_foobar_xmpl_ops[]` as + * `.start` callback for more comments. + * + * @return success (0) or error. + */ +int gnl_cb_echo_dumpit_before_after(struct netlink_callback *cb) { + pr_info("%s: dump done. release lock\n", __func__); + mutex_unlock(&dumpit_cb_progress_data.mtx); + return 0; +} + +/** + * Module/driver initializer. Called on module load/insertion. + * + * @return success (0) or error code. + */ +static int __init gnl_foobar_xmpl_module_init(void) { + int rc; + pr_info("Generic Netlink Example Module inserted.\n"); + + // Register family with its operations and policies + rc = genl_register_family(&gnl_foobar_xmpl_family); + if (rc != 0) { + pr_err("FAILED: genl_register_family(): %i\n", rc); + pr_err("An error occurred while inserting the generic netlink example module\n"); + return -1; + } else { + pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + mutex_init(&dumpit_cb_progress_data.mtx); + + return 0; +} + +/** + * Module/driver uninitializer. Called on module unload/removal. + * + * @return success (0) or error code. + */ +static void __exit gnl_foobar_xmpl_module_exit(void) { + int ret; + pr_info("Generic Netlink Example Module unloaded.\n"); + + // Unregister the family + ret = genl_unregister_family(&gnl_foobar_xmpl_family); + if (ret != 0) { + pr_err("genl_unregister_family() failed: %i\n", ret); + return; + } else { + pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + mutex_destroy(&dumpit_cb_progress_data.mtx); +} + +module_init(gnl_foobar_xmpl_module_init); +module_exit(gnl_foobar_xmpl_module_exit); \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/rpmsg_sdb.csva b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/rpmsg_sdb.csva new file mode 100644 index 0000000..857a42c --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/rpmsg_sdb.csva @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Jean-Philippe Romain + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RPMSG_SDB_DRIVER_VERSION "1.0" + +/* + * Static global variables + */ +static const char rpmsg_sdb_driver_name[] = "stm32-rpmsg-sdb"; + +static int LastBufferId; + +struct rpmsg_sdb_ioctl_set_efd { + int bufferId, eventfd; +}; + +struct rpmsg_sdb_ioctl_get_data_size { + int bufferId; + uint32_t size; +}; + +/* ioctl numbers */ +/* _IOW means userland is writing and kernel is reading */ +/* _IOR means userland is reading and kernel is writing */ +/* _IOWR means userland and kernel can both read and write */ +#define RPMSG_SDB_IOCTL_SET_EFD _IOW('R', 0x00, struct rpmsg_sdb_ioctl_set_efd *) +#define RPMSG_SDB_IOCTL_GET_DATA_SIZE _IOWR('R', 0x01, struct rpmsg_sdb_ioctl_get_data_size *) + +struct sdb_buf_t { + int index; /* index of buffer */ + size_t size; /* buffer size */ + size_t writing_size; /* size of data written by copro */ + dma_addr_t paddr; /* physical address*/ + void *vaddr; /* virtual address */ + void *uaddr; /* mapped address for userland */ + struct eventfd_ctx *efd_ctx; /* eventfd context */ + struct list_head buflist; /* reference in the buffers list */ +}; + +struct rpmsg_sdb_t { + struct mutex mutex; /* mutex to protect the ioctls */ + struct miscdevice mdev; /* misc device ref */ + struct rpmsg_device *rpdev; /* handle rpmsg device */ + struct list_head buffer_list; /* buffer instances list */ +}; + +struct device *rpmsg_sdb_dev; + +static int rpmsg_sdb_format_txbuf_string(struct sdb_buf_t *buffer, char *bufinfo_str, size_t bufinfo_str_size) +{ + pr_debug("rpmsg_sdb(%s): Buffer index:%d, addr:%08x, size:%08x\n", + __func__, + buffer->index, + buffer->paddr, + buffer->size); + return snprintf(bufinfo_str, bufinfo_str_size, "B%dA%08xL%08x", buffer->index, buffer->paddr, buffer->size); +} + +static long rpmsg_sdb_decode_rxbuf_string(char *rxbuf_str, int *buffer_id, size_t *size) +{ + int ret = 0; + char *sub_str; + long bsize; + long bufid; + const char delimiter[2] = {'L','\0'}; + + pr_debug("rpmsg_sdb(%s): rxbuf_str:%s\n", __func__, rxbuf_str); + + /* Get first part containing the buffer id */ + sub_str = strsep(&rxbuf_str, delimiter); + + pr_debug("rpmsg_sdb(%s): sub_str:%s\n", __func__, sub_str); + + /* Save Buffer id and size: template BxLyyyyyyyy*/ + ret = kstrtol(&sub_str[1], 10, &bufid); + if (ret < 0) { + pr_err("rpmsg_sdb(ERROR): Extract of buffer id failed(%d)", ret); + goto out; + } + + ret = kstrtol(&rxbuf_str[2], 16, &bsize); + if (ret < 0) { + pr_err("rpmsg_sdb(ERROR): Extract of buffer size failed(%d)", ret); + goto out; + } + + *size = (size_t)bsize; + *buffer_id = (int)bufid; + +out: + return ret; +} + +static int rpmsg_sdb_send_buf_info(struct rpmsg_sdb_t *rpmsg_sdb, struct sdb_buf_t *buffer) +{ + int count = 0, ret = 0; + const unsigned char *tbuf; + char mybuf[32]; + int msg_size; + struct rpmsg_device *_rpdev; + + _rpdev = rpmsg_sdb->rpdev; + msg_size = rpmsg_get_buffer_size(_rpdev->ept); + + if (msg_size < 0) + return msg_size; + + count = rpmsg_sdb_format_txbuf_string(buffer, mybuf, 32); + tbuf = &mybuf[0]; + + do { + /* send a message to our remote processor */ + ret = rpmsg_send(_rpdev->ept, (void *)tbuf, + count > msg_size ? msg_size : count); + if (ret) { + dev_err(&_rpdev->dev, "rpmsg_send failed: %d\n", ret); + return ret; + } + + if (count > msg_size) { + count -= msg_size; + tbuf += msg_size; + } else { + count = 0; + } + } while (count > 0); + + return count; +} + +static int rpmsg_sdb_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long vsize = vma->vm_end - vma->vm_start; + unsigned long size = PAGE_ALIGN(vsize); + unsigned long NumPages = size >> PAGE_SHIFT; + unsigned long align = get_order(size); + pgprot_t prot = pgprot_noncached(vma->vm_page_prot); + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *_buffer; + + if (align > CONFIG_CMA_ALIGNMENT) + align = CONFIG_CMA_ALIGNMENT; + + if (rpmsg_sdb_dev == NULL) + return -ENOMEM; + + rpmsg_sdb_dev->coherent_dma_mask = DMA_BIT_MASK(32); + rpmsg_sdb_dev->dma_mask = &rpmsg_sdb_dev->coherent_dma_mask; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Field the last buffer entry which is the last one created */ + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + _buffer = list_last_entry(&_rpmsg_sdb->buffer_list, + struct sdb_buf_t, buflist); + + _buffer->uaddr = NULL; + _buffer->size = NumPages * PAGE_SIZE; + _buffer->writing_size = -1; + _buffer->vaddr = dma_alloc_coherent(rpmsg_sdb_dev, + _buffer->size, + &_buffer->paddr, + GFP_KERNEL); + + if (!_buffer->vaddr) { + pr_err("rpmsg_sdb(ERROR): Memory allocation issue\n"); + return -ENOMEM; + } + + pr_debug("rpmsg_sdb(%s): dma_alloc_coherent done - paddr[%d]:%x - vaddr[%d]:%p\n", + __func__, + _buffer->index, + _buffer->paddr, + _buffer->index, + _buffer->vaddr); + + /* Get address for userland */ + if (remap_pfn_range(vma, vma->vm_start, + (_buffer->paddr >> PAGE_SHIFT) + vma->vm_pgoff, + size, prot)) + return -EAGAIN; + + _buffer->uaddr = (void *)vma->vm_start; + + /* Send information to remote proc */ + rpmsg_sdb_send_buf_info(_rpmsg_sdb, _buffer); + } else { + dev_err(rpmsg_sdb_dev, "No existing buffer entry exist in the list !!!"); + return -EINVAL; + } + + /* Increment for number of requested buffer */ + LastBufferId++; + + return 0; +} + +/** + * rpmsg_sdb_open - Open Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_open(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Initialize the buffer list*/ + INIT_LIST_HEAD(&_rpmsg_sdb->buffer_list); + + mutex_init(&_rpmsg_sdb->mutex); + + return 0; +} + +/** + * rpmsg_sdb_close - Close Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_close(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *pos, *next; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + list_for_each_entry_safe(pos, next, &_rpmsg_sdb->buffer_list, buflist) { + /* Free the CMA allocation */ + pr_debug("rpmsg_sdb(%s): Free the CMA allocation: pos->size:%08x, pos->vaddr:%08x, pos->paddr:%08x\n", + __func__, + pos->size, + pos->vaddr, + pos->paddr); + + dma_free_coherent(rpmsg_sdb_dev, pos->size, pos->vaddr, + pos->paddr); + /* Remove the buffer from the list */ + list_del(&pos->buflist); + /* Free the buffer */ + kfree(pos); + } + + /* Reset LastBufferId */ + LastBufferId = 0; + + return 0; +} + +/** + * rpmsg_sdb_ioctl - IOCTL + * + * @session: ibmvmc_file_session struct + * @cmd: cmd field + * @arg: Argument field + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static long rpmsg_sdb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int idx = 0; + + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *buffer, *lastbuffer; + + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + struct rpmsg_sdb_ioctl_set_efd q_set_efd; + struct rpmsg_sdb_ioctl_get_data_size q_get_dat_size; + + void __user *argp = (void __user *)arg; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + switch (cmd) { + case RPMSG_SDB_IOCTL_SET_EFD: + mutex_lock(&_rpmsg_sdb->mutex); + + /* Get index from the last buffer in the list */ + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + lastbuffer = list_last_entry(&_rpmsg_sdb->buffer_list, struct sdb_buf_t, buflist); + idx = lastbuffer->index; + + /* Check last index was properly initiated*/ + if (lastbuffer->vaddr == NULL) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - previous buffer was not allocated\n"); + mutex_unlock(&_rpmsg_sdb->mutex); + return -EBADE; + } + + /* increment this index for the next buffer creation*/ + idx++; + } + + if (copy_from_user(&q_set_efd, (struct rpmsg_sdb_ioctl_set_efd *)argp, + sizeof(struct rpmsg_sdb_ioctl_set_efd))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - copy from user failed\n"); + mutex_unlock(&_rpmsg_sdb->mutex); + return -EFAULT; + } + + /* create a new buffer which will be added in the buffer list */ + buffer = kmalloc(sizeof(struct sdb_buf_t), GFP_KERNEL); + + buffer->index = idx; + buffer->vaddr = NULL; + buffer->efd_ctx = eventfd_ctx_fdget(q_set_efd.eventfd); + list_add_tail(&buffer->buflist, &_rpmsg_sdb->buffer_list); + + mutex_unlock(&_rpmsg_sdb->mutex); + break; + + case RPMSG_SDB_IOCTL_GET_DATA_SIZE: + if (copy_from_user(&q_get_dat_size, (struct rpmsg_sdb_ioctl_get_data_size *)argp, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy from user failed\n"); + return -EFAULT; + } + + /* Get the index of the requested buffer and then look-up in the buffer list*/ + idx = q_get_dat_size.bufferId; + + list_for_each(pos, &_rpmsg_sdb->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == idx) { + /* Get the writing size*/ + q_get_dat_size.size = datastructureptr->writing_size; + break; + } + } + + if (copy_to_user((struct rpmsg_sdb_ioctl_get_data_size *)argp, &q_get_dat_size, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy to user failed\n"); + return -EFAULT; + } + + /* Reset the writing size*/ + datastructureptr->writing_size = -1; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct file_operations rpmsg_sdb_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = rpmsg_sdb_ioctl, + .mmap = rpmsg_sdb_mmap, + .open = rpmsg_sdb_open, + .release = rpmsg_sdb_close, +}; + +static int rpmsg_sdb_drv_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + int ret = 0; + int buffer_id = 0; + size_t buffer_size; + char *rpmsg_RxBuf; + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpdev->dev); + + if (len == 0) { + dev_err(rpmsg_sdb_dev, "(%s) Empty lenght requested\n", __func__); + return -EINVAL; + } + + //dev_err(rpmsg_sdb_dev, "(%s) lenght: %d\n", __func__,len); + + rpmsg_RxBuf = (char *)kmalloc(len+1, GFP_KERNEL); + memcpy(rpmsg_RxBuf, data, len); + + rpmsg_RxBuf[len] = 0; + + ret = rpmsg_sdb_decode_rxbuf_string(rpmsg_RxBuf, &buffer_id, &buffer_size); + kfree(rpmsg_RxBuf); + if (ret < 0) + goto out; + + if (buffer_id > LastBufferId) { + ret = -EINVAL; + goto out; + } + + /* Signal to User space application */ + list_for_each(pos, &drv->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == buffer_id) { + datastructureptr->writing_size = buffer_size; + + if (datastructureptr->writing_size > datastructureptr->size) { + dev_err(rpmsg_sdb_dev, "(%s) Writing size is bigger than buffer size\n", __func__); + ret = -EINVAL; + goto out; + } + + eventfd_signal(datastructureptr->efd_ctx, 1); + break; + } + /* TODO: quid if nothing find during the loop ? */ + } + +out: + return ret; +} + +static int rpmsg_sdb_drv_probe(struct rpmsg_device *rpdev) +{ + int ret = 0; + struct device *dev = &rpdev->dev; + struct rpmsg_sdb_t *rpmsg_sdb; + + rpmsg_sdb = devm_kzalloc(dev, sizeof(*rpmsg_sdb), GFP_KERNEL); + if (!rpmsg_sdb) + return -ENOMEM; + + mutex_init(&rpmsg_sdb->mutex); + + rpmsg_sdb->rpdev = rpdev; + + rpmsg_sdb->mdev.name = "rpmsg-sdb"; + rpmsg_sdb->mdev.minor = MISC_DYNAMIC_MINOR; + rpmsg_sdb->mdev.fops = &rpmsg_sdb_fops; + + dev_set_drvdata(&rpdev->dev, rpmsg_sdb); + + /* Register misc device */ + ret = misc_register(&rpmsg_sdb->mdev); + + if (ret) { + dev_err(dev, "Failed to register device\n"); + goto err_out; + } + + rpmsg_sdb_dev = rpmsg_sdb->mdev.this_device; + + dev_info(dev, "%s probed\n", rpmsg_sdb_driver_name); + +err_out: + return ret; +} + +static void rpmsg_sdb_drv_remove(struct rpmsg_device *rpmsgdev) +{ + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpmsgdev->dev); + + misc_deregister(&drv->mdev); +} + +static struct rpmsg_device_id rpmsg_driver_sdb_id_table[] = { + { .name = "rpmsg-sdb-channel" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sdb_id_table); + +static struct rpmsg_driver rpmsg_sdb_rmpsg_drv = { + .drv.name = KBUILD_MODNAME, + .drv.owner = THIS_MODULE, + .id_table = rpmsg_driver_sdb_id_table, + .probe = rpmsg_sdb_drv_probe, + .callback = rpmsg_sdb_drv_cb, + .remove = rpmsg_sdb_drv_remove, +}; + +static int __init rpmsg_sdb_drv_init(void) +{ + int ret = 0; + + /* Register rpmsg device */ + ret = register_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + + if (ret) { + pr_err("rpmsg_sdb(ERROR): Failed to register device\n"); + return ret; + } + + pr_info("rpmsg_sdb: Init done\n"); + + return ret; +} + +static void __exit rpmsg_sdb_drv_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + pr_info("rpmsg_sdb: Exit\n"); +} + +module_init(rpmsg_sdb_drv_init); +module_exit(rpmsg_sdb_drv_exit); + + +MODULE_AUTHOR("Jean-Philippe Romain "); +MODULE_DESCRIPTION("shared data buffer over RPMSG"); +MODULE_VERSION(RPMSG_SDB_DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/ttyrpmsg.csave b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/ttyrpmsg.csave new file mode 100644 index 0000000..ec89035 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/ttyrpmsg.csave @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 STMicroelectronics - All Rights Reserved + * + * The rpmsg tty driver implements serial communication on the RPMsg bus to makes + * possible for user-space programs to send and receive rpmsg messages as a standard + * tty protocol. + * + * The remote processor can instantiate a new tty by requesting a "rpmsg-tty" RPMsg service. + * The "rpmsg-tty" service is directly used for data exchange. No flow control is implemented yet. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#define RPMSG_TTY_NAME "ttyRPMSG" +#define MAX_TTY_RPMSG 32 + +static DEFINE_IDR(tty_idr); /* tty instance id */ +static DEFINE_MUTEX(idr_lock); /* protects tty_idr */ + +static struct tty_driver *rpmsg_tty_driver; + +struct rpmsg_tty_port { + struct tty_port port; /* TTY port data */ + int id; /* TTY rpmsg index */ + struct rpmsg_device *rpdev; /* rpmsg device */ +}; + +static int rpmsg_tty_cb(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 src) +{ + struct rpmsg_tty_port *cport = dev_get_drvdata(&rpdev->dev); + int copied; + + if (!len) + return -EINVAL; + copied = tty_insert_flip_string(&cport->port, data, len); + if (copied != len) + dev_err_ratelimited(&rpdev->dev, "Trunc buffer: available space is %d\n", copied); + tty_flip_buffer_push(&cport->port); + + return 0; +} + +static int rpmsg_tty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct rpmsg_tty_port *cport = idr_find(&tty_idr, tty->index); + struct tty_port *port; + + tty->driver_data = cport; + + port = tty_port_get(&cport->port); + return tty_port_install(port, driver, tty); +} + +static void rpmsg_tty_cleanup(struct tty_struct *tty) +{ + tty_port_put(tty->port); +} + +static int rpmsg_tty_open(struct tty_struct *tty, struct file *filp) +{ + return tty_port_open(tty->port, tty, filp); +} + +static void rpmsg_tty_close(struct tty_struct *tty, struct file *filp) +{ + return tty_port_close(tty->port, tty, filp); +} + +static int rpmsg_tty_write(struct tty_struct *tty, const u8 *buf, int len) +{ + struct rpmsg_tty_port *cport = tty->driver_data; + struct rpmsg_device *rpdev; + int msg_max_size, msg_size; + int ret; + + rpdev = cport->rpdev; + + msg_max_size = rpmsg_get_mtu(rpdev->ept); + if (msg_max_size < 0) + return msg_max_size; + + msg_size = min(len, msg_max_size); + + /* + * Use rpmsg_trysend instead of rpmsg_send to send the message so the caller is not + * hung until a rpmsg buffer is available. In such case rpmsg_trysend returns -ENOMEM. + */ + ret = rpmsg_trysend(rpdev->ept, (void *)buf, msg_size); + if (ret) { + dev_dbg_ratelimited(&rpdev->dev, "rpmsg_send failed: %d\n", ret); + return ret; + } + + return msg_size; +} + +static unsigned int rpmsg_tty_write_room(struct tty_struct *tty) +{ + struct rpmsg_tty_port *cport = tty->driver_data; + int size; + + size = rpmsg_get_mtu(cport->rpdev->ept); + if (size < 0) + return 0; + + return size; +} + +static void rpmsg_tty_hangup(struct tty_struct *tty) +{ + tty_port_hangup(tty->port); +} + +static const struct tty_operations rpmsg_tty_ops = { + .install = rpmsg_tty_install, + .open = rpmsg_tty_open, + .close = rpmsg_tty_close, + .write = rpmsg_tty_write, + .write_room = rpmsg_tty_write_room, + .hangup = rpmsg_tty_hangup, + .cleanup = rpmsg_tty_cleanup, +}; + +static struct rpmsg_tty_port *rpmsg_tty_alloc_cport(void) +{ + struct rpmsg_tty_port *cport; + int ret; + + cport = kzalloc(sizeof(*cport), GFP_KERNEL); + if (!cport) + return ERR_PTR(-ENOMEM); + + mutex_lock(&idr_lock); + ret = idr_alloc(&tty_idr, cport, 0, MAX_TTY_RPMSG, GFP_KERNEL); + mutex_unlock(&idr_lock); + + if (ret < 0) { + kfree(cport); + return ERR_PTR(ret); + } + + cport->id = ret; + + return cport; +} + +static void rpmsg_tty_destruct_port(struct tty_port *port) +{ + struct rpmsg_tty_port *cport = container_of(port, struct rpmsg_tty_port, port); + + mutex_lock(&idr_lock); + idr_remove(&tty_idr, cport->id); + mutex_unlock(&idr_lock); + + kfree(cport); +} + +static const struct tty_port_operations rpmsg_tty_port_ops = { + .destruct = rpmsg_tty_destruct_port, +}; + + +static int rpmsg_tty_probe(struct rpmsg_device *rpdev) +{ + struct rpmsg_tty_port *cport; + struct device *dev = &rpdev->dev; + struct device *tty_dev; + int ret; + + cport = rpmsg_tty_alloc_cport(); + if (IS_ERR(cport)) + return dev_err_probe(dev, PTR_ERR(cport), "Failed to alloc tty port\n"); + + tty_port_init(&cport->port); + cport->port.ops = &rpmsg_tty_port_ops; + + tty_dev = tty_port_register_device(&cport->port, rpmsg_tty_driver, + cport->id, dev); + if (IS_ERR(tty_dev)) { + ret = dev_err_probe(dev, PTR_ERR(tty_dev), "Failed to register tty port\n"); + tty_port_put(&cport->port); + return ret; + } + + cport->rpdev = rpdev; + + dev_set_drvdata(dev, cport); + + dev_dbg(dev, "New channel: 0x%x -> 0x%x: " RPMSG_TTY_NAME "%d\n", + rpdev->src, rpdev->dst, cport->id); + + return 0; +} + +static void rpmsg_tty_remove(struct rpmsg_device *rpdev) +{ + struct rpmsg_tty_port *cport = dev_get_drvdata(&rpdev->dev); + + dev_dbg(&rpdev->dev, "Removing rpmsg tty device %d\n", cport->id); + + /* User hang up to release the tty */ + tty_port_tty_hangup(&cport->port, false); + + tty_unregister_device(rpmsg_tty_driver, cport->id); + + tty_port_put(&cport->port); +} + +static struct rpmsg_device_id rpmsg_driver_tty_id_table[] = { + { .name = "rpmsg-tty" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_tty_id_table); + +static struct rpmsg_driver rpmsg_tty_rpmsg_drv = { + .drv.name = KBUILD_MODNAME, + .id_table = rpmsg_driver_tty_id_table, + .probe = rpmsg_tty_probe, + .callback = rpmsg_tty_cb, + .remove = rpmsg_tty_remove, +}; + +static int __init rpmsg_tty_init(void) +{ + int ret; + + rpmsg_tty_driver = tty_alloc_driver(MAX_TTY_RPMSG, TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(rpmsg_tty_driver)) + return PTR_ERR(rpmsg_tty_driver); + + rpmsg_tty_driver->driver_name = "rpmsg_tty"; + rpmsg_tty_driver->name = RPMSG_TTY_NAME; + rpmsg_tty_driver->major = 0; + rpmsg_tty_driver->type = TTY_DRIVER_TYPE_CONSOLE; + + /* Disable unused mode by default */ + rpmsg_tty_driver->init_termios = tty_std_termios; + rpmsg_tty_driver->init_termios.c_lflag &= ~(ECHO | ICANON); + rpmsg_tty_driver->init_termios.c_oflag &= ~(OPOST | ONLCR); + + tty_set_operations(rpmsg_tty_driver, &rpmsg_tty_ops); + + ret = tty_register_driver(rpmsg_tty_driver); + if (ret < 0) { + pr_err("Couldn't install driver: %d\n", ret); + goto error_put; + } + + ret = register_rpmsg_driver(&rpmsg_tty_rpmsg_drv); + if (ret < 0) { + pr_err("Couldn't register driver: %d\n", ret); + goto error_unregister; + } + + return 0; + +error_unregister: + tty_unregister_driver(rpmsg_tty_driver); + +error_put: + tty_driver_kref_put(rpmsg_tty_driver); + + return ret; +} + +static void __exit rpmsg_tty_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_tty_rpmsg_drv); + tty_unregister_driver(rpmsg_tty_driver); + tty_driver_kref_put(rpmsg_tty_driver); + idr_destroy(&tty_idr); +} + +module_init(rpmsg_tty_init); +module_exit(rpmsg_tty_exit); + +MODULE_AUTHOR("Arnaud Pouliquen "); +MODULE_DESCRIPTION("remote processor messaging tty driver"); +MODULE_LICENSE("GPL v2"); \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/vrpmdv-monitoring-controler.cold b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/vrpmdv-monitoring-controler.cold new file mode 100644 index 0000000..12438fe --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/vrpmdv-monitoring-controler.cold @@ -0,0 +1,804 @@ +/* Copyright 2024 Markus Lehr + * + * + * This Software is owned by Markus Lehr. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// ################################################################################################## +/* + * This is a Linux kernel module/driver called "vrpmdv-monitoring-cmd " which holds the family ID + * and functions to handle the monitoring in a Generic Netlink in the kernel. + * "It registers a Netlink family called "vrpmdv-monitoring_cmd". + * + * + * You can find some more interesting documentation about Generic Netlink here: + * "Generic Netlink HOW-TO based on Jamal's original doc" https://lwn.net/Articles/208755/ + */ + + +#include +#include + +// basic definitions for kernel module development +#include +// definitions for generic netlink families, policies etc; +// transitive dependencies for basic netlink, sockets etc +#include +// required for locking inside the .dumpit callback demonstration +#include +#include + + +// data/vars/enums/properties that describes our protocol that we implement +// on top of generic netlink (like functions we want to trigger on the receiving side) +//#include "vrpmdv-monitoring-cmd.h" + + +/* ######################## CONVENIENT LOGGING MACROS ######################## */ +// (Re)definition of some convenient logging macros from . You can see the logging +// messages when printing the kernel log, e.g. with `$ sudo dmesg`. +// See https://elixir.bootlin.com/linux/latest/source/include/linux/printk.h + +// with this redefinition we can easily prefix all log messages from pr_* logging macros +#ifdef pr_fmt +#undef pr_fmt +#endif +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +/* ########################################################################### */ + + +#define MSG "hello Monitoring!" + +static int count = 100; +module_param(count, int, 0644); + +struct instance_data { + int rx_count; +}; + + +// std::chrono::seconds timeoutPeriod = 5; +// auto timePoint = std::chrono::system_clock::now() + timeoutPeriod; + + +/** ----- NETLINK Driver defintion ------------------*/ + + +/** + * Generic Netlink will create a Netlink family with this name. Kernel will asign + * a numeric ID and afterwards we can talk to the family with its ID. To get + * the ID we use Generic Netlink in the userland and pass the family name. + * + * Short for: Generic Netlink VRPMDV Monitoring gnl_foobar_mcmd + */ +#define FAMILY_NAME "gnl-vrpmdv-mcmd" + +/** + * These are the attributes that we want to share in gnl_foobar_xmpl. + * You can understand an attribute as a semantic type. This is + * the payload of Netlink messages. + * GNl: Generic Netlink + */ +enum GNL_VRPMDV_XMPL_ATTRIBUTE { + /** + * 0 is never used (=> UNSPEC), you can also see this in other family definitions in Linux code. + * We do the same, although I'm not sure, if this is really enforced by code. + */ + GNL_VRPMDV_MCMD_A_UNSPEC, + /** We expect a MSG to be a null-terminated C-string. */ + GNL_VRPMDV_MCMD_A_MSG, + /** Unused marker field to get the length/count of enum entries. No real attribute. */ + __GNL_VRPMDV_MCMD_A_MAX, +}; +/** + * Number of elements in `enum GNL_VRPMDV_MCMD_COMMAND`. + */ +#define GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN (__GNL_VRPMDV_MCMD_A_MAX) +/** + * The number of actual usable attributes in `enum GNL_VRPMDV_MCMD_ATTRIBUTE`. + * This is `GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN` - 1 because "UNSPEC" is never used. + */ +#define GNL_VRPMDV_MCMD_ATTRIBUTE_COUNT (GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN - 1) + +/** + * Enumeration of all commands (functions) that our custom protocol on top + * of generic netlink supports. This can be understood as the action that + * we want to trigger on the receiving side. + */ +enum GNL_VRPMDV_MCMD_COMMAND { + /** + * 0 is never used (=> UNSPEC), you can also see this in other family definitions in Linux code. + * We do the same, although I'm not sure, if this is really enforced by code. + */ + GNL_VRPMDV_MCMD_C_UNSPEC, + + // first real command is "1" (>0) + /** + * When this command is received, we expect the attribute `GNL_VRPMDV_MCMD_ATTRIBUTE::GNL_VRPMDV_MCMD_A_MSG` to + * be present in the Generic Netlink request message. The kernel reads the message from the packet and + * sent it to the copro. THe result will be return by creating a new Generic Netlink response message + * with an corresponding attribute/payload. + * + * This command/signaling mechanism is independent of the Netlink flag `NLM_F_ECHO (0x08)`. We use it as + * "echo specific data" instead of return a 1:1 copy of the package, which you could do with + * `NLM_F_ECHO (0x08)` for example. + */ + GNL_VRPMDV_MCMD_C_MSG, + + /** + * Provokes a NLMSG_ERR answer to this request as described in netlink manpage + * (https://man7.org/linux/man-pages/man7/netlink.7.html). + */ + GNL_VRPMDV_MCMD_C_REPLY_WITH_NLMSG_ERR, + + /** Unused marker field to get the length/count of enum entries. No real attribute. */ + __GNL_VRPMDV_MCMD_C_MAX, +}; +/** + * Number of elements in `enum GNL_VRPMDV_MCMD_COMMAND`. + */ +#define GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN (__GNL_VRPMDV_MCMD_C_MAX) +/** + * The number of actual usable commands in `enum GNL_FOOBAR_XMPL_COMMAND`. + * This is `GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN` - 1 because "UNSPEC" is never used. + */ +#define GNL_VRPMDV_MCMD_COMMAND_COUNT (GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN - 1) + +#define NODATARECEIVED 0 +#define DATARECEIVED 1 + +/** + * Data structure required for our .doit callback handler to + * know about the progress of an ongoing cmd execution. + * See the cmd callback handler how it is used. + */ +// struct { +// // from +// /** +// * rpmsg wait for response from copro side. +// */ +// struct mutex sendMTX; +// /** +// * rpmsg wait for response from copro side. +// */ +// struct mutex receiveCV; +// /** +// * Wait Queue: if it is signaled we have received data from copro +// */ +// wait_queue_head_t receive_queue; +// /** +// * Waitflag: 0= no data received, 1 = data received +// */ +// int receive_queue_flag = NODATARECEIVED; +// /** +// * Condition vaiable signal we have received data from copro +// */ +// // std::condition_variable cv; +// // /** +// // * Number that describes how many packets we need to send until we are done +// // * during an ongoing dumpit process. 0 = done. +// // */ +// // int unsigned runs_to_go; +// // /** +// // * Number that describes how many packets per dump are sent in total. +// // * Constant per dump. +// // */ +// // int unsigned total_runs; + +// //the rpmsg device which sends the data to the copro +// struct rpmsg_device *rpdev; /* handle rpmsg device */ +// } cmd_cb_progress_data; + + +struct rpmsg_vrpmdv_mon_t{ + // from + /** + * rpmsg wait for response from copro side. + */ + struct mutex sendMTX; + /** + * rpmsg wait for response from copro side. + */ + struct mutex receiveCV; + /** + * Wait Queue: if it is signaled we have received data from copro + */ + wait_queue_head_t receive_queue; + /** + * Waitflag: 0= no data received, 1 = data received + */ + int receive_queue_flag; + //the rpmsg device which sends the data to the copro + struct rpmsg_device* rpdev; /* handle rpmsg device */ +}; + +struct rpmsg_vrpmdv_mon_t vrpmdv_mon; + + // struct mutex mutex; /* mutex to protect the ioctls */ + // struct miscdevice mdev; /* misc device ref */ + // struct rpmsg_device *rpdev; /* handle rpmsg device */ + // struct list_head buffer_list; /* buffer instances list */ + + + +// Documentation is on the implementation of this function. +int gnl_cb_vrpmdv_doit(struct sk_buff *sender_skb, struct genl_info *info); + +// Documentation is on the implementation of this function. +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info); + +/** + * The length of `struct genl_ops gnl_foobar_xmpl_ops[]`. Not necessarily + * the number of commands in `enum GNlFoobarXmplCommand`. It depends on your application logic. + * For example, you can use the same command multiple times and - dependent by flag - + * invoke a different callback handler. In our simple example we just use one .doit callback + * per operation/command. + */ +#define GNL_VRPMDV_OPS_LEN (GNL_VRPMDV_MCMD_COMMAND_COUNT) + +/** + * Array with all operations that our protocol on top of Generic Netlink + * supports. An operation is the glue between a command ("cmd" field in `struct genlmsghdr` of + * received Generic Netlink message) and the corresponding ".doit" callback function. + * See: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L148 + */ +struct genl_ops gnl_vrpmdv_mcmd_ops[GNL_VRPMDV_OPS_LEN] = { + { + /* The "cmd" field in `struct genlmsghdr` of received Generic Netlink message */ + .cmd = GNL_VRPMDV_MCMD_C_MSG, + /* TODO Use case ? */ + .flags = 0, + /* TODO Use case ? */ + .internal_flags = 0, + /* Callback handler when a request with the specified ".cmd" above is received. + * Always validates the payload except one set NO_STRICT_VALIDATION flag in ".validate" + * See: https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + * + * Quote from: https://lwn.net/Articles/208755 + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + * + */ + .doit = gnl_cb_vrpmdv_doit, + /* This callback is similar in use to the standard Netlink 'dumpit' callback. + * The 'dumpit' callback is invoked when a Generic Netlink message is received + * with the NLM_F_DUMP flag set. + * + * A dump can be understand as a "GET ALL DATA OF THE GIVEN ENTITY", i.e. + * the userland can receive as long as the .dumpit callback returns data. + * + * .dumpit is not mandatory, but either it or .doit must be provided, see + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L367 + * + * To be honest I don't know in what use case one should use .dumpit and why + * it is useful, because you can achieve the same also with .doit handlers. + * Anyway, this is just an example/tutorial. + * + * Quote from: https://lwn.net/Articles/208755 + * "The main difference between a 'dumpit' handler and a 'doit' handler is + * that a 'dumpit' handler does not allocate a message buffer for a response; + * a pre-allocated sk_buff is passed to the 'dumpit' handler as the first + * parameter. The 'dumpit' handler should fill the message buffer with the + * appropriate response message and return the size of the sk_buff, + * i.e. sk_buff->len, and the message buffer will automatically be sent to the + * Generic Netlink client that initiated the request. As long as the 'dumpit' + * handler returns a value greater than zero it will be called again with a + * newly allocated message buffer to fill, when the handler has no more data + * to send it should return zero; error conditions are indicated by returning + * a negative value. If necessary, state can be preserved in the + * netlink_callback parameter which is passed to the 'dumpit' handler; the + * netlink_callback parameter values will be preserved across handler calls + * for a single request." + * + * You can see the check for the NLM_F_DUMP-flag here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L780 + */ + .dumpit = NULL, //gnl_cb_echo_dumpit, + /* Start callback for dumps. Can be used to lock data structures. */ + .start = NULL, //gnl_cb_echo_dumpit_before, + /* Completion callback for dumps. Can be used for cleanup after a dump and releasing locks. */ + .done = NULL, //gnl_cb_echo_dumpit_before_after, + /* + 0 (= "validate strictly") or value `enum genl_validate_flags` + * see: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L108 + */ + .validate = 0, + }, + { + .cmd = GNL_VRPMDV_MCMD_C_REPLY_WITH_NLMSG_ERR, + .flags = 0, + .internal_flags = 0, + .doit = gnl_cb_doit_reply_with_nlmsg_err, + // .dumpit is not required, only optional; application specific/dependent on your use case + // in a real application you probably have different .dumpit handlers per operation/command + .dumpit = NULL, + // in a real application you probably have different .start handlers per operation/command + .start = NULL, + // in a real application you probably have different .done handlers per operation/command + .done = NULL, + .validate = 0, + } +}; + + +/** + * Attribute policy: defines which attribute has which type (e.g int, char * etc). + * This get validated for each received Generic Netlink message, if not deactivated + * in `gnl_foobar_xmpl_ops[].validate`. + * See https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + */ +static struct nla_policy gnl_vrpmdv_mcmd_policy[GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN] = { + // In case you are seeing this syntax for the first time (I also learned this just after a few years of + // coding with C myself): The following static array initiations are equivalent: + // `int a[2] = {1, 2}` <==> `int a[2] = {[0] => 1, [1] => 2}`. + + [GNL_VRPMDV_MCMD_A_UNSPEC] = {.type = NLA_UNSPEC}, + + // You can set this to NLA_U32 for testing and send an ECHO message from the userland + // It will fail in this case and you see a entry in the kernel log. + + // `enum GNL_FOOBAR_XMPL_ATTRIBUTE::GNL_FOOBAR_XMPL_A_MSG` is a null-terminated C-String + [GNL_VRPMDV_MCMD_A_MSG] = {.type = NLA_NUL_STRING}, +}; + + + +/** + * Definition of the Netlink family we want to register using Generic Netlink functionality + */ +static struct genl_family gnl_vrpmdv_mcmd_family = { + // automatically assign an id + .id = 0, + // we don't use custom additional header info / user specific header + .hdrsize = 0, + // The name of this family, used by userspace application to get the numeric ID + .name = FAMILY_NAME, + // family specific version number; can be used to evolve application over time (multiple versions) + .version = 1, + // delegates all incoming requests to callback functions + .ops = gnl_vrpmdv_mcmd_ops, + // length of array `gnl_foobar_xmpl_ops` + .n_ops = GNL_VRPMDV_OPS_LEN, + // attribute policy (for validation of messages). Enforced automatically, except ".validate" in + // corresponding ".ops"-field is set accordingly. + .policy = gnl_vrpmdv_mcmd_policy, + // Number of attributes / bounds check for policy (array length) + .maxattr = GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN, + // Owning Kernel module of the Netlink family we register. + .module = THIS_MODULE, + + // Actually not necessary because this memory region would be zeroed anyway during module load, + // but this way one sees all possible options. + + // if your application must handle multiple netlink calls in parallel (where one should not block the next + // from starting), set this to true! otherwise all netlink calls are mutually exclusive + .parallel_ops = 0, + // set to true if the family can handle network namespaces and should be presented in all of them + .netnsok = 0, + // called before an operation's doit callback, it may do additional, common, filtering and return an error + .pre_doit = NULL, + // called after an operation's doit callback, it may undo operations done by pre_doit, for example release locks + .post_doit = NULL, +}; + + + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_VRPMDV_MCMD_C_MSG` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_vrpmdv_mcmd_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_vrpmdv_doit(struct sk_buff *sender_skb, struct genl_info *info) { + struct nlattr *na; + struct sk_buff *reply_skb; + int rc; + void *msg_head; + char *recv_msg; + + pr_info("%s() invoked\n", __func__); + + if (info == NULL) { + // should never happen + pr_err("An error occurred in %s():\n", __func__); + return -EINVAL; + } + + /* + * For each attribute there is an index in info->attrs which points to a nlattr structure + * in this structure the data is stored. + */ + na = info->attrs[GNL_VRPMDV_MCMD_A_MSG]; + + if (!na) { + pr_err("no info->attrs[%i]\n", GNL_VRPMDV_MCMD_A_MSG); + return -EINVAL; // we return here because we expect to recv a msg + } + + recv_msg = (char *) nla_data(na); + if (recv_msg == NULL) { + pr_err("error while receiving data\n"); + } else { + pr_info("received: '%s'\n", recv_msg); + } + + //aquire lock for cmd repmsg channel + // std::lock sendlk(cmd_cb_progress_data.sendmtx); + + //send the message to the copro over RPMSG + rc = rpmsg_send(vrpmdv_mon.rpdev->ept, recv_msg, strlen(recv_msg)); + if (rc) { + pr_err("rpmsg_send failed: %d\n", rc); + return rc; + } + + // struct rpmsg_vrpmdv_mon_t *drv = dev_get_drvdata(&rpdev->dev); + + { + pr_info("wait for response\n"); + // wait until receive_queue_flag=1 , that means we have received data from Copro + wait_event_interruptible(vrpmdv_mon.receive_queue, vrpmdv_mon.receive_queue_flag != 0 ); + pr_info("received data \n"); + //Copy data + vrpmdv_mon.receive_queue_flag = NODATARECEIVED; + + + + // std::unique_lock lk(cmd_cb_progress_data.receivemtx); + // if (myCondVar.wait_until(uLock, timePoint) == std::cv_status::timeout) + // { + // dev_err(&cmd_cb_progress_data.rpdev, "rpmsg_send failed, timeout: \n"); + // return -1: + // } + + pr_info("get response: '%s'\n", recv_msg); + } + + + + // Send a message back after we receive the reply from rpmsg channel + // --------------------- + + // Allocate some memory, since the size is not yet known use NLMSG_GOODSIZE + reply_skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (reply_skb == NULL) { + pr_err("An error occurred in %s():\n", __func__); + return -ENOMEM; + } + + // Create the message headers + + // Add header to netlink message; + // afterwards the buffer looks like this: + // ---------------------------------- + // | netlink header | + // | generic netlink header | + // | | + // ---------------------------------- + // msg_head = genlmsg_put(reply_skb, // buffer for netlink message: struct sk_buff * + // // According to my findings: this is not used for routing + // // This can be used in an application specific way to target + // // different endpoints within the same user application + // // but general rule: just put sender port id here + // info->snd_portid, // sending port (not process) id: int + // info->snd_seq + 1, // sequence number: int (might be used by receiver, but not mandatory) + // &gnl_vrpmdv_mcmd_family, // struct genl_family * + // 0, // flags for Netlink header: int; application specific and not mandatory + // // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header + // GNL_VRPMDV_MCMD_C_MSG + // ); + + msg_head = genlmsg_put_reply(reply_skb, // buffer for netlink message: struct sk_buff * + info, // info + &gnl_vrpmdv_mcmd_family, // struct genl_family * + 0, // flags for Netlink header: int; application specific and not mandatory + // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header + info->genlhdr->cmd + ); + + if (msg_head == NULL) { + rc = ENOMEM; + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + // Add a GNL_VRPMDV_MCMD_A_MSG attribute (actual value/payload to be sent) + // echo the value we just received + rc = nla_put_string(reply_skb, GNL_VRPMDV_MCMD_A_MSG, recv_msg); + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + // Finalize the message: + // Corrects the netlink message header (length) to include the appended + // attributes. Only necessary if attributes have been added to the message. + genlmsg_end(reply_skb, msg_head); + + // Send the message back + rc = genlmsg_reply(reply_skb, info); + // same as genlmsg_unicast(genl_info_net(info), reply_skb, info->snd_portid) + // see https://elixir.bootlin.com/linux/v5.8.9/source/include/net/genetlink.h#L326 + + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + return 0; +} + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_FOOBAR_XMPL_C_REPLY_WITH_NLMSG_ERR` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info) { + pr_info("%s() invoked, a NLMSG_ERR response will be sent back\n", __func__); + + /* + * Generic Netlink is smart enough and sends a NLMSG_ERR reply automatically as reply + * Quote from https://lwn.net/Articles/208755/: + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + */ + return -EINVAL; +} + + + +/** ----- NETLINK Driver defintion ------------------*/ + + + + + +/** + * callback that is called after the copro send data + * we have to copy it in a buffer for the netlink and later send it back to the userland + * +*/ +static int vrpmdv_monitoring_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + int ret = 0; + struct instance_data *idata = dev_get_drvdata(&rpdev->dev); + + dev_info(&rpdev->dev, "incoming msg %d (src: 0x%x)\n", + ++idata->rx_count, src); + + print_hex_dump_debug(__func__, DUMP_PREFIX_NONE, 16, 1, data, len, + true); + + vrpmdv_mon.receive_queue_flag= DATARECEIVED; + wake_up_interruptible(&(vrpmdv_mon.receive_queue)); + + // /* samples should not live forever */ + // if (idata->rx_count >= count) { + // dev_info(&rpdev->dev, "goodbye!\n"); + // return 0; + // } + + /* send a new message now */ + // ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG)); + // if (ret) + // dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); + + return ret; +} + +static int vrpmdv_monitoring_probe(struct rpmsg_device *rpdev) +{ + int rc; + + // int ret; + // struct instance_data *idata; + + dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n", + rpdev->src, rpdev->dst); + + // idata = devm_kzalloc(&rpdev->dev, sizeof(*idata), GFP_KERNEL); + // if (!idata) + // return -ENOMEM; + + // dev_set_drvdata(&rpdev->dev, idata); + + // /* send a message to our remote processor to */ + // ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG)); + // if (ret) { + // dev_err(&rpdev->dev, "vrpmdv_monitoring_controler_send failed: %d\n", ret); + // return ret; + // } + + // return 0; + + // struct device *dev; + // dev = &rpdev->dev; + // struct rpmsg_vrpmdv_mon_t *rpmsg_vrpmdv_mon; + + // rpmsg_vrpmdv_mon = devm_kzalloc(dev, sizeof(*rpmsg_vrpmdv_mon), GFP_KERNEL); + // if (!rpmsg_vrpmdv_mon) + // return -ENOMEM; + + mutex_init(&(vrpmdv_mon.sendMTX)); + init_waitqueue_head (&(vrpmdv_mon.receive_queue)); + vrpmdv_mon.rpdev = rpdev; + + // dev_set_drvdata(&rpdev->dev, rpmsg_vrpmdv_mon); + pr_info("RPMSG CMD Device set.\n"); + + + /** NEU **/ + // if (cmd_cb_progress_data.rpdev == NULL) { + // cmd_cb_progress_data.rpdev = rpdev; + // pr_info("RPMSG CMD Device set.\n"); + // } + // else { + // pr_info("Error: RPMSG CMD Device already set. Don't set it twice\n"); + // } + + pr_info("Generic Netlink VRPMDV-Monitroring_Controler Module started.\n"); + + // Register family with its operations and policies + rc = genl_register_family(&gnl_vrpmdv_mcmd_family); + if (rc != 0) { + pr_err("FAILED: genl_register_family(): %i\n", rc); + pr_err("An error occurred while inserting the generic netlink example module\n"); + return -1; + } else { + pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + return 0; + + + +} + +static void vrpmdv_monitoring_remove(struct rpmsg_device *rpdev) +{ + int ret; + pr_info("Generic Netlink Example Module unloaded.\n"); + + // Unregister the family + ret = genl_unregister_family(&gnl_vrpmdv_mcmd_family); + if (ret != 0) { + pr_err("genl_unregister_family() failed: %i\n", ret); + return; + } else { + pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + mutex_destroy(&(vrpmdv_mon.sendMTX)); + wake_up_interruptible(&(vrpmdv_mon.receive_queue)); + + dev_info(&rpdev->dev, "vrpmdv-monitoring controler driver is removed\n"); + +} + +static struct rpmsg_device_id vrpmdv_monitoring_controler_id_table[] = { + { .name = "vrpmdv-monitoring-controler" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, vrpmdv_monitoring_controler_id_table); + +static struct rpmsg_driver vrpmdv_monitoring_controler = { + .drv.name = KBUILD_MODNAME, + .id_table = vrpmdv_monitoring_controler_id_table, + .probe = vrpmdv_monitoring_probe, + .callback = vrpmdv_monitoring_cb, + .remove = vrpmdv_monitoring_remove, +}; +module_rpmsg_driver(vrpmdv_monitoring_controler); + + +// static struct rpmsg_driver vrpmdv_monitoring_data = { +// .drv.name = KBUILD_MODNAME, +// .id_table = vrpmdv_monitoring_controler_id_table, +// .probe = vrpmdv_monitoring_probe, +// .callback = vrpmdv_monitoring_cb, +// .remove = vrpmdv_monitoring_remove, +// }; + +// module_rpmsg_driver(vrpmdv_monitoring_data); + +MODULE_DESCRIPTION("Remote processor messaging vrpmdv monitoring controler"); +MODULE_LICENSE("GPL v2"); + + + +// /** +// * Module/driver initializer. Called on module load/insertion. +// * +// * @return success (0) or error code. +// */ +// static int __init gnl_foobar_xmpl_module_init(void) { +// int rc; +// pr_info("Generic Netlink Example Module inserted.\n"); + +// // Register family with its operations and policies +// rc = genl_register_family(&gnl_foobar_xmpl_family); +// if (rc != 0) { +// pr_err("FAILED: genl_register_family(): %i\n", rc); +// pr_err("An error occurred while inserting the generic netlink example module\n"); +// return -1; +// } else { +// pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); +// } + +// mutex_init(&dumpit_cb_progress_data.mtx); + +// return 0; +// } + +// /** +// * Module/driver uninitializer. Called on module unload/removal. +// * +// * @return success (0) or error code. +// */ +// static void __exit gnl_foobar_xmpl_module_exit(void) { +// int ret; +// pr_info("Generic Netlink Example Module unloaded.\n"); + +// // Unregister the family +// ret = genl_unregister_family(&gnl_foobar_xmpl_family); +// if (ret != 0) { +// pr_err("genl_unregister_family() failed: %i\n", ret); +// return; +// } else { +// pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); +// } + +// mutex_destroy(&dumpit_cb_progress_data.mtx); +// } + + +// ---- + +// static int __init rpmsg_sdb_drv_init(void) +// { +// int ret = 0; + +// /* Register rpmsg device */ +// ret = register_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + +// if (ret) { +// pr_err("rpmsg_sdb(ERROR): Failed to register device\n"); +// return ret; +// } + +// pr_info("rpmsg_sdb: Init done\n"); + +// return ret; +// } + +// static void __exit rpmsg_sdb_drv_exit(void) +// { +// unregister_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); +// pr_info("rpmsg_sdb: Exit\n"); +// } + +// module_init(rpmsg_sdb_drv_init); +// module_exit(rpmsg_sdb_drv_exit); diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/vrpmdv-monitoring-driver copy.bb.old b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/vrpmdv-monitoring-driver copy.bb.old new file mode 100644 index 0000000..6aeebb9 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/vrpmdv-monitoring-driver copy.bb.old @@ -0,0 +1,24 @@ +SUMMARY = "VRPMDV Monitoring Driver" +LICENSE = "CLOSED" +# LICENSE = "GPL-2.0-only" +# LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "file://Makefile \ + file://stm32_rpmsg_sdb.c \ + " +# file://75-rpmsg-sdb.rules \ +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. + +# do_install:append() { + # udev rules for rpmsg-sdb + # install -d ${D}${sysconfdir}/udev/rules.d/ + # install -m 0644 ${WORKDIR}/75-rpmsg-sdb.rules ${D}${sysconfdir}/udev/rules.d/75-rpmsg-sdb.rules +# } +#FILES:${PN} += "${sysconfdir}/udev/rules.d/" + +RPROVIDES:${PN} += "kernel-module-vrpmdv-monitoring-driver" \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/vrpmdv-monitoring-driver copy1.bb.old b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/vrpmdv-monitoring-driver copy1.bb.old new file mode 100644 index 0000000..2f245db --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/vrpmdv-monitoring-driver copy1.bb.old @@ -0,0 +1,26 @@ +SUMMARY = "VRPMDV Monitoring Driver" +LICENSE = "GPL-2.0-only" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "file://Makefile \ + file://vrpmdv-monitoring.c \ + file://75-vrpmdv-monitoring.rules \ + " + +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. + +do_install:append() { + # udev rules for vrpmdv-monitoring + install -d ${D}${sysconfdir}/udev/rules.d/ + install -m 0644 ${WORKDIR}/75-vrpmdv-monitoring.rules ${D}${sysconfdir}/udev/rules.d/75-vrpmdv-monitoring.rules +} + +FILES:${PN} += "${sysconfdir}/udev/rules.d/" + + +RPROVIDES:${PN} += "kernel-module-vrpmdv-monitoring" \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/vrpmdv-monitoring-driverv2.bb.old b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/vrpmdv-monitoring-driverv2.bb.old new file mode 100644 index 0000000..03bdb32 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/save/vrpmdv-monitoring-driverv2.bb.old @@ -0,0 +1,26 @@ +SUMMARY = "VRPMDV Monitoring Driver" +LICENSE = "GPL-2.0-only" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "file://Makefile \ + file://vrpmdv-monitoring.c \ + file://75-vrpmdv-monitoring.rules \ + " + +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. + +do_install:append() { + # udev rules for vrpmdv-monitoring + install -d ${D}${sysconfdir}/udev/rules.d/ + install -m 0644 ${WORKDIR}/75-vrpmdv-monitoring.rules ${D}${sysconfdir}/udev/rules.d/75-vrpmdv-monitoring.rules +} + +FILES:${PN} += "${sysconfdir}/udev/rules.d/" +FILES:${PN} += "${base_libdir}/modules/" + +RPROVIDES:${PN} += "vrpmdv-monitoring" diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/vrpmdv-mon-tty.bb b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/vrpmdv-mon-tty.bb new file mode 100644 index 0000000..8db283a --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-mon-tty/vrpmdv-mon-tty.bb @@ -0,0 +1,24 @@ +SUMMARY = "VRPMDV Monitoring TTY" +LICENSE = "GPL-2.0-only" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "file://Makefile \ + file://vrpmdv-mon-tty.c \ + file://75-mon-tty.rules \ + " + +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. +do_install:append() { + # udev rules for tty + install -d ${D}${sysconfdir}/udev/rules.d/ + install -m 0644 ${WORKDIR}/75-mon-tty.rules ${D}${sysconfdir}/udev/rules.d/75-mon-tty.rules +} +FILES:${PN} += "${sysconfdir}/udev/rules.d/" + + +RPROVIDES:${PN} += "kernel-module-vrpmdv-mon-tty" diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/.gitignore b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/.gitignore new file mode 100644 index 0000000..610580e --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/.gitignore @@ -0,0 +1 @@ +/.~lock.Create-Kernel-Driver.odt# diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/.vscode/settings.json b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/.vscode/settings.json new file mode 100644 index 0000000..a644266 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "files.associations": { + "rpmsg.h": "c", + "types.h": "c", + "ioctl.h": "c", + "wait.h": "c" + } +} \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/Create-Kernel-Driver.odt b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/Create-Kernel-Driver.odt new file mode 100644 index 0000000..cee5ccf Binary files /dev/null and b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/Create-Kernel-Driver.odt differ diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/LockingWaitQueues.pdf b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/LockingWaitQueues.pdf new file mode 100644 index 0000000..6d16b4b Binary files /dev/null and b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/LockingWaitQueues.pdf differ diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/files/Makefile b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/files/Makefile new file mode 100644 index 0000000..c7a64dd --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/files/Makefile @@ -0,0 +1,26 @@ +# Makefile for VRPMDV Monitoring Controler +obj-m := vrpmdv-monitoring-controler.o + + +SRC := $(shell pwd) + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) + +modules_install: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install + +clean: + rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c + rm -f Module.markers Module.symvers modules.order + rm -rf .tmp_versions Modules.symvers + +# obj-m = vrpmdv-monitoring-controler.o + +# KVERSION = $(shell uname -r) + +# all: +# make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules + +# clean: +# make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/files/lock.csave b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/files/lock.csave new file mode 100644 index 0000000..0de60b3 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/files/lock.csave @@ -0,0 +1,116 @@ +include +#include +#include +#include +#include + +std::mutex m; +std::condition_variable cv; +std::string data; +bool ready = false; +bool processed = false; + +void worker_thread() +{ + // wait until main() sends data + std::unique_lock lk(m); + cv.wait(lk, []{ return ready; }); + + // after the wait, we own the lock + std::cout << "Worker thread is processing data\n"; + data += " after processing"; + + // send data back to main() + processed = true; + std::cout << "Worker thread signals data processing completed\n"; + + // manual unlocking is done before notifying, to avoid waking up + // the waiting thread only to block again (see notify_one for details) + lk.unlock(); + cv.notify_one(); +} + +int main() +{ + std::thread worker(worker_thread); + + data = "Example data"; + // send data to the worker thread + { + std::lock_guard lk(m); + ready = true; + std::cout << "main() signals data ready for processing\n"; + } + cv.notify_one(); + + // wait for the worker + { + std::unique_lock lk(m); + cv.wait(lk, []{ return processed; }); + } + std::cout << "Back in main(), data = " << data << '\n'; + + worker.join(); +} + +-------------------- + +// waiting for timeout after 5 seconds +std::chrono::seconds timeoutPeriod = 5; +auto timePoint = std::chrono::system_clock::now() + timeoutPeriod; +std::unique_lock uLock(myDataMutex); +while(!DataAreReadyForProcessing()) +{ + if (myCondVar.wait_until(uLock, timePoint) //<## + == std::cv_status::timeout) + { + // data conditions where not fulfilled within + // the time period; e.g. do some error handling + break; + } +} + +-- + +if (myCondVar.wait_for(uLock, timeoutPeriod, + DataAreReadyForProcessing)) +{ + // data conditions where fulfilled + // regular processing +} +else // timeout occured, conditions are not fulfilled +{ + // e.g. do some error handling +} + + + + + + +------------- + + +static int rpmsg_sample_probe(struct rpmsg_device *rpdev) +{ + + int ret; + struct instance_data *idata; + + dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n", + rpdev->src, rpdev->dst); + + idata = devm_kzalloc(&rpdev->dev, sizeof(*idata), GFP_KERNEL); + if (!idata) + return -ENOMEM; + + dev_set_drvdata(&rpdev->dev, idata); + + /* send a message to our remote processor */ + ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG)); + if (ret) { + dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); + return ret; + } + + return 0; diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/files/vrpmdv-monitoring-cmd.c b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/files/vrpmdv-monitoring-cmd.c new file mode 100644 index 0000000..0524f6b --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/files/vrpmdv-monitoring-cmd.c @@ -0,0 +1,541 @@ +/* Copyright 2024 Markus Lehr + * + * + * This Software is owned by Markus Lehr. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// ################################################################################################## +/* + * This is a Linux kernel module/driver called "vrpmdv-monitoring-cmd " which holds the family ID + * and functions to handle the monitoring in a Generic Netlink in the kernel. + * "It registers a Netlink family called "vrpmdv-monitoring_cmd". + * + * + * You can find some more interesting documentation about Generic Netlink here: + * "Generic Netlink HOW-TO based on Jamal's original doc" https://lwn.net/Articles/208755/ + */ + +// basic definitions for kernel module development +#include +// definitions for generic netlink families, policies etc; +// transitive dependencies for basic netlink, sockets etc +#include +// required for locking inside the .dumpit callback demonstration +#include + +// data/vars/enums/properties that describes our protocol that we implement +// on top of generic netlink (like functions we want to trigger on the receiving side) +#include "gnl_vrpmdv_mcmd.h" + +// Module/Driver description. +// You can see this for example when executing `$ modinfo ./gnl_vromdv_cmd.ko` (after build). +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("MArkus Lehr "); +MODULE_DESCRIPTION( + "Linux driver that registers the custom Netlink family " + "\"" FAMILY_NAME "\" via Generic Netlink and responds to echo messages " + "according to the properties specified in \"gnl_vrpmd_mcmd.h\"." +); + + +/* ######################## CONVENIENT LOGGING MACROS ######################## */ +// (Re)definition of some convenient logging macros from . You can see the logging +// messages when printing the kernel log, e.g. with `$ sudo dmesg`. +// See https://elixir.bootlin.com/linux/latest/source/include/linux/printk.h + +// with this redefinition we can easily prefix all log messages from pr_* logging macros +#ifdef pr_fmt +#undef pr_fmt +#endif +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +/* ########################################################################### */ + +/** + * Data structure required for our .dumpit callback handler to + * know about the progress of an ongoing dump. + * See the dumpit callback handler how it is used. + */ +struct { + // from + /** + * Only one process is allowed per dump process. We need a lock for that. + */ + struct mutex mtx; + /** + * Number that describes how many packets we need to send until we are done + * during an ongoing dumpit process. 0 = done. + */ + int unsigned runs_to_go; + /** + * Number that describes how many packets per dump are sent in total. + * Constant per dump. + */ + int unsigned total_runs; +} dumpit_cb_progress_data; + +// Documentation is on the implementation of this function. +int gnl_cb_echo_doit(struct sk_buff *sender_skb, struct genl_info *info); + +// Documentation is on the implementation of this function. +int gnl_cb_echo_dumpit(struct sk_buff *pre_allocated_skb, struct netlink_callback *cb); + +// Documentation is on the implementation of this function. +int gnl_cb_echo_dumpit_before(struct netlink_callback *cb); + +// Documentation is on the implementation of this function. +int gnl_cb_echo_dumpit_before_after(struct netlink_callback *cb); + +// Documentation is on the implementation of this function. +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info); + +/** + * The length of `struct genl_ops gnl_foobar_xmpl_ops[]`. Not necessarily + * the number of commands in `enum GNlFoobarXmplCommand`. It depends on your application logic. + * For example, you can use the same command multiple times and - dependent by flag - + * invoke a different callback handler. In our simple example we just use one .doit callback + * per operation/command. + */ +#define GNL_VRPMDV_OPS_LEN (GNL_VRPMDV_MCMD_COMMAND_COUNT) + +/** + * Array with all operations that our protocol on top of Generic Netlink + * supports. An operation is the glue between a command ("cmd" field in `struct genlmsghdr` of + * received Generic Netlink message) and the corresponding ".doit" callback function. + * See: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L148 + */ +struct genl_ops gnl_vrpmdv_mcmd_ops[GNL_VRPMDV_OPS_LEN] = { + { + /* The "cmd" field in `struct genlmsghdr` of received Generic Netlink message */ + .cmd = GNL_VRPMDV_MCMD_C_MSG, + /* TODO Use case ? */ + .flags = 0, + /* TODO Use case ? */ + .internal_flags = 0, + /* Callback handler when a request with the specified ".cmd" above is received. + * Always validates the payload except one set NO_STRICT_VALIDATION flag in ".validate" + * See: https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + * + * Quote from: https://lwn.net/Articles/208755 + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + * + */ + .doit = gnl_cb_vrpmdv_doit, + /* This callback is similar in use to the standard Netlink 'dumpit' callback. + * The 'dumpit' callback is invoked when a Generic Netlink message is received + * with the NLM_F_DUMP flag set. + * + * A dump can be understand as a "GET ALL DATA OF THE GIVEN ENTITY", i.e. + * the userland can receive as long as the .dumpit callback returns data. + * + * .dumpit is not mandatory, but either it or .doit must be provided, see + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L367 + * + * To be honest I don't know in what use case one should use .dumpit and why + * it is useful, because you can achieve the same also with .doit handlers. + * Anyway, this is just an example/tutorial. + * + * Quote from: https://lwn.net/Articles/208755 + * "The main difference between a 'dumpit' handler and a 'doit' handler is + * that a 'dumpit' handler does not allocate a message buffer for a response; + * a pre-allocated sk_buff is passed to the 'dumpit' handler as the first + * parameter. The 'dumpit' handler should fill the message buffer with the + * appropriate response message and return the size of the sk_buff, + * i.e. sk_buff->len, and the message buffer will automatically be sent to the + * Generic Netlink client that initiated the request. As long as the 'dumpit' + * handler returns a value greater than zero it will be called again with a + * newly allocated message buffer to fill, when the handler has no more data + * to send it should return zero; error conditions are indicated by returning + * a negative value. If necessary, state can be preserved in the + * netlink_callback parameter which is passed to the 'dumpit' handler; the + * netlink_callback parameter values will be preserved across handler calls + * for a single request." + * + * You can see the check for the NLM_F_DUMP-flag here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L780 + */ + .dumpit = gnl_cb_echo_dumpit, + /* Start callback for dumps. Can be used to lock data structures. */ + .start = gnl_cb_echo_dumpit_before, + /* Completion callback for dumps. Can be used for cleanup after a dump and releasing locks. */ + .done = gnl_cb_echo_dumpit_before_after, + /* + 0 (= "validate strictly") or value `enum genl_validate_flags` + * see: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L108 + */ + .validate = 0, + }, + { + .cmd = GNL_VRPMDV_MCMD_C_REPLY_WITH_NLMSG_ERR, + .flags = 0, + .internal_flags = 0, + .doit = gnl_cb_doit_reply_with_nlmsg_err, + // .dumpit is not required, only optional; application specific/dependent on your use case + // in a real application you probably have different .dumpit handlers per operation/command + .dumpit = NULL, + // in a real application you probably have different .start handlers per operation/command + .start = NULL, + // in a real application you probably have different .done handlers per operation/command + .done = NULL, + .validate = 0, + } +}; + +/** + * Attribute policy: defines which attribute has which type (e.g int, char * etc). + * This get validated for each received Generic Netlink message, if not deactivated + * in `gnl_foobar_xmpl_ops[].validate`. + * See https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + */ +static struct nla_policy gnl_vrpmdv_mcmd_policy[GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN] = { + // In case you are seeing this syntax for the first time (I also learned this just after a few years of + // coding with C myself): The following static array initiations are equivalent: + // `int a[2] = {1, 2}` <==> `int a[2] = {[0] => 1, [1] => 2}`. + + [GNL_VRPMDV_MCMD_A_UNSPEC] = {.type = NLA_UNSPEC}, + + // You can set this to NLA_U32 for testing and send an ECHO message from the userland + // It will fail in this case and you see a entry in the kernel log. + + // `enum GNL_FOOBAR_XMPL_ATTRIBUTE::GNL_FOOBAR_XMPL_A_MSG` is a null-terminated C-String + [GNL_VRPMDV_MCMD_A_MSG] = {.type = NLA_NUL_STRING}, +}; + +/** + * Definition of the Netlink family we want to register using Generic Netlink functionality + */ +static struct genl_family gnl_vrpmdv_mcmd_family = { + // automatically assign an id + .id = 0, + // we don't use custom additional header info / user specific header + .hdrsize = 0, + // The name of this family, used by userspace application to get the numeric ID + .name = FAMILY_NAME, + // family specific version number; can be used to evolve application over time (multiple versions) + .version = 1, + // delegates all incoming requests to callback functions + .ops = gnl_vrpmdv_mcmd_ops, + // length of array `gnl_foobar_xmpl_ops` + .n_ops = GNL_VRPMDV_OPS_LEN, + // attribute policy (for validation of messages). Enforced automatically, except ".validate" in + // corresponding ".ops"-field is set accordingly. + .policy = gnl_vrpmdv_mcmd_policy, + // Number of attributes / bounds check for policy (array length) + .maxattr = GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN, + // Owning Kernel module of the Netlink family we register. + .module = THIS_MODULE, + + // Actually not necessary because this memory region would be zeroed anyway during module load, + // but this way one sees all possible options. + + // if your application must handle multiple netlink calls in parallel (where one should not block the next + // from starting), set this to true! otherwise all netlink calls are mutually exclusive + .parallel_ops = 0, + // set to true if the family can handle network namespaces and should be presented in all of them + .netnsok = 0, + // called before an operation's doit callback, it may do additional, common, filtering and return an error + .pre_doit = NULL, + // called after an operation's doit callback, it may undo operations done by pre_doit, for example release locks + .post_doit = NULL, +}; + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_FOOBAR_XMPL_C_ECHO` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_echo_doit(struct sk_buff *sender_skb, struct genl_info *info) { + struct nlattr *na; + struct sk_buff *reply_skb; + int rc; + void *msg_head; + char *recv_msg; + + pr_info("%s() invoked\n", __func__); + + if (info == NULL) { + // should never happen + pr_err("An error occurred in %s():\n", __func__); + return -EINVAL; + } + + /* + * For each attribute there is an index in info->attrs which points to a nlattr structure + * in this structure the data is stored. + */ + na = info->attrs[GNL_FOOBAR_XMPL_A_MSG]; + + if (!na) { + pr_err("no info->attrs[%i]\n", GNL_FOOBAR_XMPL_A_MSG); + return -EINVAL; // we return here because we expect to recv a msg + } + + recv_msg = (char *) nla_data(na); + if (recv_msg == NULL) { + pr_err("error while receiving data\n"); + } else { + pr_info("received: '%s'\n", recv_msg); + } + + + // Send a message back + // --------------------- + + // Allocate some memory, since the size is not yet known use NLMSG_GOODSIZE + reply_skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (reply_skb == NULL) { + pr_err("An error occurred in %s():\n", __func__); + return -ENOMEM; + } + + // Create the message headers + + // Add header to netlink message; + // afterwards the buffer looks like this: + // ---------------------------------- + // | netlink header | + // | generic netlink header | + // | | + // ---------------------------------- + msg_head = genlmsg_put(reply_skb, // buffer for netlink message: struct sk_buff * + // According to my findings: this is not used for routing + // This can be used in an application specific way to target + // different endpoints within the same user application + // but general rule: just put sender port id here + info->snd_portid, // sending port (not process) id: int + info->snd_seq + 1, // sequence number: int (might be used by receiver, but not mandatory) + &gnl_foobar_xmpl_family, // struct genl_family * + 0, // flags for Netlink header: int; application specific and not mandatory + // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header + GNL_FOOBAR_XMPL_C_ECHO_MSG + ); + if (msg_head == NULL) { + rc = ENOMEM; + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + // Add a GNL_FOOBAR_XMPL_A_MSG attribute (actual value/payload to be sent) + // echo the value we just received + rc = nla_put_string(reply_skb, GNL_FOOBAR_XMPL_A_MSG, recv_msg); + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + // Finalize the message: + // Corrects the netlink message header (length) to include the appended + // attributes. Only necessary if attributes have been added to the message. + genlmsg_end(reply_skb, msg_head); + + // Send the message back + rc = genlmsg_reply(reply_skb, info); + // same as genlmsg_unicast(genl_info_net(info), reply_skb, info->snd_portid) + // see https://elixir.bootlin.com/linux/v5.8.9/source/include/net/genetlink.h#L326 + + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + return 0; +} + +/** ---------------------- not needed ML */ + +/** + * ".dumpit"-callback function if a Generic Netlink with command ECHO_MSG and flag `NLM_F_DUMP` is received. + * Please look into the comments where this is used as ".dumpit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".dumpit" callbacks. + * + * A dump must be understand of "give me all data of a given entity" + * rather than a "dump of the netlink message itself" for debugging etc! + * + * This handler requires `gnl_cb_echo_dumpit_before` to run before a dump and `gnl_cb_echo_dumpit_after` after a dump. + * + * For the sake of simplicity, we use the ECHO_MSG command for the dump. In fact, we don't expect a + * MSG-Attribute here, unlike the regular ECHO_MSG handler. We reply with a dump of + * "all messages that we got" (application specific, hard coded in this example). +*/ +int gnl_cb_echo_dumpit(struct sk_buff *pre_allocated_skb, struct netlink_callback *cb) { + void *msg_head; + int ret; + static const char HELLO_FROM_DUMPIT_MSG[] = "You set the flag NLM_F_DUMP; this message is " + "brought to you by .dumpit callback :)"; + pr_info("Called %s()\n", __func__); + + if (dumpit_cb_progress_data.runs_to_go == 0) { + pr_info("no more data to send in dumpit cb\n"); + // mark that dump is done; + return 0; + } else { + dumpit_cb_progress_data.runs_to_go--; + pr_info("%s: %d more runs to do\n", __func__, dumpit_cb_progress_data.runs_to_go); + } + + msg_head = genlmsg_put(pre_allocated_skb, // buffer for netlink message: struct sk_buff * + // According to my findings: this is not used for routing + // This can be used in an application specific way to target + // different endpoints within the same user application + // but general rule: just put sender port id here + cb->nlh->nlmsg_pid, // sending port (not process) id: int + // sequence number: int (might be used by receiver, but not mandatory) + // sequence 0, 1, 2... + dumpit_cb_progress_data.total_runs - dumpit_cb_progress_data.runs_to_go - 1, + &gnl_foobar_xmpl_family, // struct genl_family * + 0, // flags: int (for netlink header); we don't check them in the userland; application specific + // this way we can trigger a specific command/callback on the receiving side or imply + // on which type of command we are currently answering; this is application specific + GNL_FOOBAR_XMPL_C_ECHO_MSG // cmd: u8 (for generic netlink header); + ); + if (msg_head == NULL) { + pr_info("An error occurred in %s(): genlmsg_put() failed\n", __func__); + return -ENOMEM; + } + ret = nla_put_string( + pre_allocated_skb, + GNL_FOOBAR_XMPL_A_MSG, + HELLO_FROM_DUMPIT_MSG + ); + if (ret < 0) { + pr_info("An error occurred in %s():\n", __func__); + return ret; + } + genlmsg_end(pre_allocated_skb, msg_head); + + // return the length of data we wrote into the pre-allocated buffer + return pre_allocated_skb->len; +} + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_FOOBAR_XMPL_C_REPLY_WITH_NLMSG_ERR` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info) { + pr_info("%s() invoked, a NLMSG_ERR response will be sent back\n", __func__); + + /* + * Generic Netlink is smart enough and sends a NLMSG_ERR reply automatically as reply + * Quote from https://lwn.net/Articles/208755/: + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + */ + return -EINVAL; +} + +/** + * Called before a dump with `gnl_cb_echo_dumpit()` starts. + * See where this is assigned in `struct genl_ops gnl_foobar_xmpl_ops[]` as + * `.start` callback for more comments. + * + * @return success (0) or error. + */ +int gnl_cb_echo_dumpit_before(struct netlink_callback *cb) { + int ret; + static int unsigned const dump_runs = 3; + pr_info("%s: dump started. acquire lock. initialize dump runs_to_go (number of receives userland can make) to %d runs\n", __func__, dump_runs); + // Lock the mutex like mutex_lock(), and return 0 if the mutex has been acquired or sleep until the mutex becomes available + // If a signal arrives while waiting for the lock then this function returns -EINTR. + ret = mutex_lock_interruptible(&dumpit_cb_progress_data.mtx); + if (ret != 0) { + pr_err("Failed to get lock!\n"); + return ret; + } + dumpit_cb_progress_data.total_runs = dump_runs; + dumpit_cb_progress_data.runs_to_go = dump_runs; + return 0; + +} + +/** + * Called before a dump with `gnl_cb_echo_dumpit()` starts. + * See where this is assigned in `struct genl_ops gnl_foobar_xmpl_ops[]` as + * `.start` callback for more comments. + * + * @return success (0) or error. + */ +int gnl_cb_echo_dumpit_before_after(struct netlink_callback *cb) { + pr_info("%s: dump done. release lock\n", __func__); + mutex_unlock(&dumpit_cb_progress_data.mtx); + return 0; +} + + +/** ---------------------- not needed ML */ + + +/** ---------------------- chenge this in the probe of the rpmsg driver ML */ + +/** + * Module/driver initializer. Called on module load/insertion. + * + * @return success (0) or error code. + */ +static int __init gnl_foobar_xmpl_module_init(void) { + int rc; + pr_info("Generic Netlink Example Module inserted.\n"); + + // Register family with its operations and policies + rc = genl_register_family(&gnl_foobar_xmpl_family); + if (rc != 0) { + pr_err("FAILED: genl_register_family(): %i\n", rc); + pr_err("An error occurred while inserting the generic netlink example module\n"); + return -1; + } else { + pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + mutex_init(&dumpit_cb_progress_data.mtx); + + return 0; +} + +/** + * Module/driver uninitializer. Called on module unload/removal. + * + * @return success (0) or error code. + */ +static void __exit gnl_foobar_xmpl_module_exit(void) { + int ret; + pr_info("Generic Netlink Example Module unloaded.\n"); + + // Unregister the family + ret = genl_unregister_family(&gnl_foobar_xmpl_family); + if (ret != 0) { + pr_err("genl_unregister_family() failed: %i\n", ret); + return; + } else { + pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + mutex_destroy(&dumpit_cb_progress_data.mtx); +} + +module_init(gnl_foobar_xmpl_module_init); +module_exit(gnl_foobar_xmpl_module_exit); + +/** ---------------------- chenge this in the probe of the rpmsg driver ML */ diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/files/vrpmdv-monitoring-cmd.h b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/files/vrpmdv-monitoring-cmd.h new file mode 100644 index 0000000..bba93ff --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/files/vrpmdv-monitoring-cmd.h @@ -0,0 +1,98 @@ +/* Copyright 2021 Philipp Schuster + * + * This Software is owned by Markus Lehr. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +/* + * This file describes the common properties of our custom Netlink family on top of Generic Netlink. + * It is used by all C projects, i.e. the Kernel driver, and the userland components. + */ + +/** + * Generic Netlink will create a Netlink family with this name. Kernel will asign + * a numeric ID and afterwards we can talk to the family with its ID. To get + * the ID we use Generic Netlink in the userland and pass the family name. + * + * Short for: Generic Netlink VRPMDV Monitoring gnl_foobar_mcmd + */ +#define FAMILY_NAME "gnl-vrpmdv-mcmd" + +/** + * These are the attributes that we want to share in gnl_foobar_xmpl. + * You can understand an attribute as a semantic type. This is + * the payload of Netlink messages. + * GNl: Generic Netlink + */ +enum GNL_VRPMDV_XMPL_ATTRIBUTE { + /** + * 0 is never used (=> UNSPEC), you can also see this in other family definitions in Linux code. + * We do the same, although I'm not sure, if this is really enforced by code. + */ + GNL_VRPMDV_MCMD_A_UNSPEC, + /** We expect a MSG to be a null-terminated C-string. */ + GNL_VRPMDV_MCMD_A_MSG, + /** Unused marker field to get the length/count of enum entries. No real attribute. */ + __GNL_VRPMDV_MCMD_A_MAX, +}; +/** + * Number of elements in `enum GNL_VRPMDV_MCMD_COMMAND`. + */ +#define GNL_FOOBAR_XMPL_ATTRIBUTE_ENUM_LEN (__GNL_VRPMDV_MCMD_A_MAX) +/** + * The number of actual usable attributes in `enum GNL_VRPMDV_MCMD_ATTRIBUTE`. + * This is `GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN` - 1 because "UNSPEC" is never used. + */ +#define GNL_VRPMDV_MCMD_ATTRIBUTE_COUNT (GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN - 1) + +/** + * Enumeration of all commands (functions) that our custom protocol on top + * of generic netlink supports. This can be understood as the action that + * we want to trigger on the receiving side. + */ +enum GNL_VRPMDV_MCMD_COMMAND { + /** + * 0 is never used (=> UNSPEC), you can also see this in other family definitions in Linux code. + * We do the same, although I'm not sure, if this is really enforced by code. + */ + GNL_VRPMDV_MCMD_C_UNSPEC, + + // first real command is "1" (>0) + /** + * When this command is received, we expect the attribute `GNL_VRPMDV_MCMD_ATTRIBUTE::GNL_VRPMDV_MCMD_A_MSG` to + * be present in the Generic Netlink request message. The kernel reads the message from the packet and + * sent it to the copro. THe result will be return by creating a new Generic Netlink response message + * with an corresponding attribute/payload. + * + * This command/signaling mechanism is independent of the Netlink flag `NLM_F_ECHO (0x08)`. We use it as + * "echo specific data" instead of return a 1:1 copy of the package, which you could do with + * `NLM_F_ECHO (0x08)` for example. + */ + GNL_VRPMDV_MCMD_C_MSG, + + /** + * Provokes a NLMSG_ERR answer to this request as described in netlink manpage + * (https://man7.org/linux/man-pages/man7/netlink.7.html). + */ + GNL_VRPMDV_MCMD_C_REPLY_WITH_NLMSG_ERR, + + /** Unused marker field to get the length/count of enum entries. No real attribute. */ + __GNL_VRPMDV_MCMD_C_MAX, +}; +/** + * Number of elements in `enum GNL_VRPMDV_MCMD_COMMAND`. + */ +#define GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN (__GNL_VRPMDV_MCMD_C_MAX) +/** + * The number of actual usable commands in `enum GNL_FOOBAR_XMPL_COMMAND`. + * This is `GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN` - 1 because "UNSPEC" is never used. + */ +#define GNL_VRPMDV_MCMD_COMMAND_COUNT (GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN - 1) \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/files/vrpmdv-monitoring-controler.c b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/files/vrpmdv-monitoring-controler.c new file mode 100644 index 0000000..86d6e26 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/files/vrpmdv-monitoring-controler.c @@ -0,0 +1,865 @@ +/* Copyright 2024 Markus Lehr + * + * + * This Software is owned by Markus Lehr. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// ################################################################################################## +/* + * This is a Linux kernel module/driver called "vrpmdv-monitoring-cmd " which holds the family ID + * and functions to handle the monitoring in a Generic Netlink in the kernel. + * "It registers a Netlink family called "vrpmdv-monitoring_cmd". + * + * + * You can find some more interesting documentation about Generic Netlink here: + * "Generic Netlink HOW-TO based on Jamal's original doc" https://lwn.net/Articles/208755/ + */ + + +#include +#include + +// basic definitions for kernel module development +#include +// definitions for generic netlink families, policies etc; +// transitive dependencies for basic netlink, sockets etc +#include +// required for locking inside the .dumpit callback demonstration +#include +#include + + +// data/vars/enums/properties that describes our protocol that we implement +// on top of generic netlink (like functions we want to trigger on the receiving side) +//#include "vrpmdv-monitoring-cmd.h" + + +/* ######################## CONVENIENT LOGGING MACROS ######################## */ +// (Re)definition of some convenient logging macros from . You can see the logging +// messages when printing the kernel log, e.g. with `$ sudo dmesg`. +// See https://elixir.bootlin.com/linux/latest/source/include/linux/printk.h + +// with this redefinition we can easily prefix all log messages from pr_* logging macros +#ifdef pr_fmt +#undef pr_fmt +#endif +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +/* ########################################################################### */ + + +#define MSG "hello Monitoring!" + +static int count = 100; +module_param(count, int, 0644); + +struct instance_data { + int rx_count; +}; + + +// std::chrono::seconds timeoutPeriod = 5; +// auto timePoint = std::chrono::system_clock::now() + timeoutPeriod; + + +/** ----- NETLINK Driver defintion ------------------*/ + + +/** + * Generic Netlink will create a Netlink family with this name. Kernel will asign + * a numeric ID and afterwards we can talk to the family with its ID. To get + * the ID we use Generic Netlink in the userland and pass the family name. + * + * Short for: Generic Netlink VRPMDV Monitoring gnl_foobar_mcmd + */ +#define FAMILY_NAME "gnl-vrpmdv-mcmd" + +/** + * These are the attributes that we want to share in gnl_foobar_xmpl. + * You can understand an attribute as a semantic type. This is + * the payload of Netlink messages. + * GNl: Generic Netlink + */ +enum GNL_VRPMDV_XMPL_ATTRIBUTE { + /** + * 0 is never used (=> UNSPEC), you can also see this in other family definitions in Linux code. + * We do the same, although I'm not sure, if this is really enforced by code. + */ + GNL_VRPMDV_MCMD_A_UNSPEC, + /** We expect a MSG to be a null-terminated C-string. */ + GNL_VRPMDV_MCMD_A_MSG, + /** Unused marker field to get the length/count of enum entries. No real attribute. */ + __GNL_VRPMDV_MCMD_A_MAX, +}; +/** + * Number of elements in `enum GNL_VRPMDV_MCMD_COMMAND`. + */ +#define GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN (__GNL_VRPMDV_MCMD_A_MAX) +/** + * The number of actual usable attributes in `enum GNL_VRPMDV_MCMD_ATTRIBUTE`. + * This is `GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN` - 1 because "UNSPEC" is never used. + */ +#define GNL_VRPMDV_MCMD_ATTRIBUTE_COUNT (GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN - 1) + +/** + * Enumeration of all commands (functions) that our custom protocol on top + * of generic netlink supports. This can be understood as the action that + * we want to trigger on the receiving side. + */ +enum GNL_VRPMDV_MCMD_COMMAND { + /** + * 0 is never used (=> UNSPEC), you can also see this in other family definitions in Linux code. + * We do the same, although I'm not sure, if this is really enforced by code. + */ + GNL_VRPMDV_MCMD_C_UNSPEC, + + // first real command is "1" (>0) + /** + * When this command is received, we expect the attribute `GNL_VRPMDV_MCMD_ATTRIBUTE::GNL_VRPMDV_MCMD_A_MSG` to + * be present in the Generic Netlink request message. The kernel reads the message from the packet and + * sent it to the copro. THe result will be return by creating a new Generic Netlink response message + * with an corresponding attribute/payload. + * + * This command/signaling mechanism is independent of the Netlink flag `NLM_F_ECHO (0x08)`. We use it as + * "echo specific data" instead of return a 1:1 copy of the package, which you could do with + * `NLM_F_ECHO (0x08)` for example. + */ + GNL_VRPMDV_MCMD_C_MSG, + + /** + * Provokes a NLMSG_ERR answer to this request as described in netlink manpage + * (https://man7.org/linux/man-pages/man7/netlink.7.html). + */ + GNL_VRPMDV_MCMD_C_REPLY_WITH_NLMSG_ERR, + + /** Unused marker field to get the length/count of enum entries. No real attribute. */ + __GNL_VRPMDV_MCMD_C_MAX, +}; +/** + * Number of elements in `enum GNL_VRPMDV_MCMD_COMMAND`. + */ +#define GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN (__GNL_VRPMDV_MCMD_C_MAX) +/** + * The number of actual usable commands in `enum GNL_FOOBAR_XMPL_COMMAND`. + * This is `GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN` - 1 because "UNSPEC" is never used. + */ +#define GNL_VRPMDV_MCMD_COMMAND_COUNT (GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN - 1) + +#define NODATARECEIVED 0 +#define DATARECEIVED 1 + +/** + * Data structure required for our .doit callback handler to + * know about the progress of an ongoing cmd execution. + * See the cmd callback handler how it is used. + */ +// struct { +// // from +// /** +// * rpmsg wait for response from copro side. +// */ +// struct mutex sendMTX; +// /** +// * rpmsg wait for response from copro side. +// */ +// struct mutex receiveCV; +// /** +// * Wait Queue: if it is signaled we have received data from copro +// */ +// wait_queue_head_t receive_queue; +// /** +// * Waitflag: 0= no data received, 1 = data received +// */ +// int receive_queue_flag = NODATARECEIVED; +// /** +// * Condition vaiable signal we have received data from copro +// */ +// // std::condition_variable cv; +// // /** +// // * Number that describes how many packets we need to send until we are done +// // * during an ongoing dumpit process. 0 = done. +// // */ +// // int unsigned runs_to_go; +// // /** +// // * Number that describes how many packets per dump are sent in total. +// // * Constant per dump. +// // */ +// // int unsigned total_runs; + +// //the rpmsg device which sends the data to the copro +// struct rpmsg_device *rpdev; /* handle rpmsg device */ +// } cmd_cb_progress_data; + + +// struct rpmsg_vrpmdv_mon_t{ +// // from +// /** +// * rpmsg wait for response from copro side. +// */ +// struct mutex sendMTX; +// /** +// * rpmsg wait for response from copro side. +// */ +// struct mutex receiveCV; +// /** +// * Wait Queue: if it is signaled we have received data from copro +// */ +// wait_queue_head_t receive_queue; +// /** +// * Waitflag: 0= no data received, 1 = data received +// */ +// int receive_queue_flag; +// //the rpmsg device which sends the data to the copro +// struct rpmsg_device* rpdev; /* handle rpmsg device */ +// }; + +// struct rpmsg_vrpmdv_mon_t vrpmdv_mon; + + + /** + * rpmsg wait for response from copro side. + */ + struct mutex sendMTX; + /** + * rpmsg wait for response from copro side. + */ + struct mutex receiveCV; + /** + * Wait Queue: if it is signaled we have received data from copro + */ + wait_queue_head_t receive_queue; + /** + * Waitflag: 0= no data received, 1 = data received + */ + int receive_queue_flag = NODATARECEIVED; + + char* received_bytes = NULL; + int received_len = 0; + //the rpmsg device which sends the data to the copro + struct rpmsg_device* prpdev = NULL; /* handle rpmsg device */ + + + // struct mutex mutex; /* mutex to protect the ioctls */ + // struct miscdevice mdev; /* misc device ref */ + // struct rpmsg_device *rpdev; /* handle rpmsg device */ + // struct list_head buffer_list; /* buffer instances list */ + + + +// Documentation is on the implementation of this function. +int gnl_cb_vrpmdv_doit(struct sk_buff *sender_skb, struct genl_info *info); + +// Documentation is on the implementation of this function. +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info); + +/** + * The length of `struct genl_ops gnl_foobar_xmpl_ops[]`. Not necessarily + * the number of commands in `enum GNlFoobarXmplCommand`. It depends on your application logic. + * For example, you can use the same command multiple times and - dependent by flag - + * invoke a different callback handler. In our simple example we just use one .doit callback + * per operation/command. + */ +#define GNL_VRPMDV_OPS_LEN (GNL_VRPMDV_MCMD_COMMAND_COUNT) + +/** + * Array with all operations that our protocol on top of Generic Netlink + * supports. An operation is the glue between a command ("cmd" field in `struct genlmsghdr` of + * received Generic Netlink message) and the corresponding ".doit" callback function. + * See: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L148 + */ +struct genl_ops gnl_vrpmdv_mcmd_ops[GNL_VRPMDV_OPS_LEN] = { + { + /* The "cmd" field in `struct genlmsghdr` of received Generic Netlink message */ + .cmd = GNL_VRPMDV_MCMD_C_MSG, + /* TODO Use case ? */ + .flags = 0, + /* TODO Use case ? */ + .internal_flags = 0, + /* Callback handler when a request with the specified ".cmd" above is received. + * Always validates the payload except one set NO_STRICT_VALIDATION flag in ".validate" + * See: https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + * + * Quote from: https://lwn.net/Articles/208755 + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + * + */ + .doit = gnl_cb_vrpmdv_doit, + /* This callback is similar in use to the standard Netlink 'dumpit' callback. + * The 'dumpit' callback is invoked when a Generic Netlink message is received + * with the NLM_F_DUMP flag set. + * + * A dump can be understand as a "GET ALL DATA OF THE GIVEN ENTITY", i.e. + * the userland can receive as long as the .dumpit callback returns data. + * + * .dumpit is not mandatory, but either it or .doit must be provided, see + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L367 + * + * To be honest I don't know in what use case one should use .dumpit and why + * it is useful, because you can achieve the same also with .doit handlers. + * Anyway, this is just an example/tutorial. + * + * Quote from: https://lwn.net/Articles/208755 + * "The main difference between a 'dumpit' handler and a 'doit' handler is + * that a 'dumpit' handler does not allocate a message buffer for a response; + * a pre-allocated sk_buff is passed to the 'dumpit' handler as the first + * parameter. The 'dumpit' handler should fill the message buffer with the + * appropriate response message and return the size of the sk_buff, + * i.e. sk_buff->len, and the message buffer will automatically be sent to the + * Generic Netlink client that initiated the request. As long as the 'dumpit' + * handler returns a value greater than zero it will be called again with a + * newly allocated message buffer to fill, when the handler has no more data + * to send it should return zero; error conditions are indicated by returning + * a negative value. If necessary, state can be preserved in the + * netlink_callback parameter which is passed to the 'dumpit' handler; the + * netlink_callback parameter values will be preserved across handler calls + * for a single request." + * + * You can see the check for the NLM_F_DUMP-flag here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L780 + */ + .dumpit = NULL, //gnl_cb_echo_dumpit, + /* Start callback for dumps. Can be used to lock data structures. */ + .start = NULL, //gnl_cb_echo_dumpit_before, + /* Completion callback for dumps. Can be used for cleanup after a dump and releasing locks. */ + .done = NULL, //gnl_cb_echo_dumpit_before_after, + /* + 0 (= "validate strictly") or value `enum genl_validate_flags` + * see: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L108 + */ + .validate = 0, + }, + { + .cmd = GNL_VRPMDV_MCMD_C_REPLY_WITH_NLMSG_ERR, + .flags = 0, + .internal_flags = 0, + .doit = gnl_cb_doit_reply_with_nlmsg_err, + // .dumpit is not required, only optional; application specific/dependent on your use case + // in a real application you probably have different .dumpit handlers per operation/command + .dumpit = NULL, + // in a real application you probably have different .start handlers per operation/command + .start = NULL, + // in a real application you probably have different .done handlers per operation/command + .done = NULL, + .validate = 0, + } +}; + + +/** + * Attribute policy: defines which attribute has which type (e.g int, char * etc). + * This get validated for each received Generic Netlink message, if not deactivated + * in `gnl_foobar_xmpl_ops[].validate`. + * See https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + */ +static struct nla_policy gnl_vrpmdv_mcmd_policy[GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN] = { + // In case you are seeing this syntax for the first time (I also learned this just after a few years of + // coding with C myself): The following static array initiations are equivalent: + // `int a[2] = {1, 2}` <==> `int a[2] = {[0] => 1, [1] => 2}`. + + [GNL_VRPMDV_MCMD_A_UNSPEC] = {.type = NLA_UNSPEC}, + + // You can set this to NLA_U32 for testing and send an ECHO message from the userland + // It will fail in this case and you see a entry in the kernel log. + + // `enum GNL_FOOBAR_XMPL_ATTRIBUTE::GNL_FOOBAR_XMPL_A_MSG` is a null-terminated C-String + [GNL_VRPMDV_MCMD_A_MSG] = {.type = NLA_NUL_STRING}, +}; + + + +/** + * Definition of the Netlink family we want to register using Generic Netlink functionality + */ +static struct genl_family gnl_vrpmdv_mcmd_family = { + // automatically assign an id + .id = 0, + // we don't use custom additional header info / user specific header + .hdrsize = 0, + // The name of this family, used by userspace application to get the numeric ID + .name = FAMILY_NAME, + // family specific version number; can be used to evolve application over time (multiple versions) + .version = 1, + // delegates all incoming requests to callback functions + .ops = gnl_vrpmdv_mcmd_ops, + // length of array `gnl_foobar_xmpl_ops` + .n_ops = GNL_VRPMDV_OPS_LEN, + // attribute policy (for validation of messages). Enforced automatically, except ".validate" in + // corresponding ".ops"-field is set accordingly. + .policy = gnl_vrpmdv_mcmd_policy, + // Number of attributes / bounds check for policy (array length) + .maxattr = GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN, + // Owning Kernel module of the Netlink family we register. + .module = THIS_MODULE, + + // Actually not necessary because this memory region would be zeroed anyway during module load, + // but this way one sees all possible options. + + // if your application must handle multiple netlink calls in parallel (where one should not block the next + // from starting), set this to true! otherwise all netlink calls are mutually exclusive + .parallel_ops = 0, + // set to true if the family can handle network namespaces and should be presented in all of them + .netnsok = 0, + // called before an operation's doit callback, it may do additional, common, filtering and return an error + .pre_doit = NULL, + // called after an operation's doit callback, it may undo operations done by pre_doit, for example release locks + .post_doit = NULL, +}; + + + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_VRPMDV_MCMD_C_MSG` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_vrpmdv_mcmd_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_vrpmdv_doit(struct sk_buff *sender_skb, struct genl_info *info) { + struct nlattr *na; + struct sk_buff *reply_skb; + int rc; + void *msg_head; + char *recv_msg; + + pr_info("%s() invoked\n", __func__); + + if (info == NULL) { + // should never happen + pr_err("An error occurred in %s():\n", __func__); + return -EINVAL; + } + + /* + * For each attribute there is an index in info->attrs which points to a nlattr structure + * in this structure the data is stored. + */ + na = info->attrs[GNL_VRPMDV_MCMD_A_MSG]; + + if (!na) { + pr_err("no info->attrs[%i]\n", GNL_VRPMDV_MCMD_A_MSG); + return -EINVAL; // we return here because we expect to recv a msg + } + + recv_msg = (char *) nla_data(na); + if (recv_msg == NULL) { + pr_err("error while receiving data\n"); + } else { + pr_info("received: '%s'\n", recv_msg); + } + + //aquire lock for cmd repmsg channel + // std::lock sendlk(cmd_cb_progress_data.sendmtx); + + //send the message to the copro over RPMSG + if (prpdev) { + rc = rpmsg_send(prpdev->ept, recv_msg, strlen(recv_msg)); + if (rc) { + pr_err("rpmsg_send failed: %d\n", rc); + return rc; + } + + // struct rpmsg_vrpmdv_mon_t *drv = dev_get_drvdata(&rpdev->dev); + + // Send a message back after we receive the reply from rpmsg channel + // --------------------- + + // Allocate some memory, since the size is not yet known use NLMSG_GOODSIZE + reply_skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (reply_skb == NULL) { + pr_err("An error occurred in %s():\n", __func__); + return -ENOMEM; + } + + // Create the message headersprpdev + + // Add header to netlink message; + // afterwards the buffer looks like this: + // ---------------------------------- + // | netlink header | + // | generic netlink header | + // | | + // ---------------------------------- + // msg_head = genlmsg_put(reply_skb, // buffer for netlink message: struct sk_buff * + // // According to my findings: this is not used for routing + // // This can be used in an application specific way to target + // // different endpoints within the same user application + // // but general rule: just put sender port id here + // info->snd_portid, // sending port (not process) id: int + // info->snd_seq + 1, // sequence number: int (might be used by receiver, but not mandatory) + // &gnl_vrpmdv_mcmd_family, // struct genl_family * + // 0, // flags for Netlink header: int; application specific and not mandatory + // // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header + // GNL_VRPMDV_MCMD_C_MSG + // ); + + msg_head = genlmsg_put_reply(reply_skb, // buffer for netlink message: struct sk_buff * + info, // info + &gnl_vrpmdv_mcmd_family, // struct genl_family * + 0, // flags for Netlink header: int; application specific and not mandatory + // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header + info->genlhdr->cmd + ); + + if (msg_head == NULL) { + rc = ENOMEM; + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + { + pr_info("wait for response\n"); + // wait until receive_queue_flag=1 , that means we have received data from Copro + wait_event_interruptible(receive_queue, receive_queue_flag != 0 ); + + //Copy data + receive_queue_flag = NODATARECEIVED; + + + + // std::unique_lock lk(cmd_cb_progress_data.receivemtx); + // if (myCondVar.wait_until(uLock, timePoint) == std::cv_status::timeout) + // { + // dev_err(&cmd_cb_progress_data.rpdev, "rpmsg_send failed, timeout: \n"); + // return -1: + // } + + //pr_info("get response: '%s'\n", recv_msg); + if (received_len > 0) { + pr_info("received data from copro %s\n", received_bytes); + } + else { + pr_err("don't received data from Copro \n"); + } + + } + + // Add a GNL_VRPMDV_MCMD_A_MSG attribute (actual value/payload to be sent) + // echo the value we just received + rc = nla_put_string(reply_skb, GNL_VRPMDV_MCMD_A_MSG, received_bytes); + + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + //free the buffer + kfree(received_bytes); + received_bytes = NULL; + return -rc; + } + + // Finalize the message: + // Corrects the netlink message header (length) to include the appended + // attributes. Only necessary if attributes have been added to the message. + genlmsg_end(reply_skb, msg_head); + + // Send the message back + rc = genlmsg_reply(reply_skb, info); + // same as genlmsg_unicast(genl_info_net(info), reply_skb, info->snd_portid) + // see https://elixir.bootlin.com/linux/v5.8.9/source/include/net/genetlink.h#L326 + + kfree(received_bytes); + received_bytes = NULL; + + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + return 0; + + } + pr_info("Device not set in Probe. Should not happen"); + return -1; +} + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_FOOBAR_XMPL_C_REPLY_WITH_NLMSG_ERR` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info) { + pr_info("%s() invoked, a NLMSG_ERR response will be sent back\n", __func__); + + /* + * Generic Netlink is smart enough and sends a NLMSG_ERR reply automatically as reply + * Quote from https://lwn.net/Articles/208755/: + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + */ + return -EINVAL; +} + + + +/** ----- NETLINK Driver defintion ------------------*/ + + + + + +/** + * callback that is called after the copro send data + * we have to copy it in a buffer for the netlink and later send it back to the userland + * +*/ +static int vrpmdv_monitoring_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + int ret = 0; + // struct instance_data *idata = dev_get_drvdata(&rpdev->dev); + + // dev_info(&rpdev->dev, "incoming msg %d (src: 0x%x)\n", + // ++idata->rx_count, src); + + print_hex_dump_debug(__func__, DUMP_PREFIX_NONE, 16, 1, data, len, + true); + if (len == 0) { + pr_err("(%s) Empty lenght requested\n", __func__); + return -EINVAL; + } + else { + pr_info("received data from copro %s\n", (char*) data); + } + + // received_bytes = (char *)kmalloc(len+1, GFP_KERNEL); + // memcpy(received_bytes, data, len); + + // rpmsg_RxBuf[len] = 0; + + + + received_bytes = (char *)kmalloc(len, GFP_KERNEL); + memcpy(received_bytes, data, len); + received_len = len; + receive_queue_flag= DATARECEIVED; + wake_up_interruptible(&receive_queue); + + // /* samples should not live forever */ + // if (idata->rx_count >= count) { + // dev_info(&rpdev->dev, "goodbye!\n"); + // return 0; + // } + + /* send a new message now */ + // ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG)); + // if (ret) + // dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); + + return ret; +} + +static int vrpmdv_monitoring_probe(struct rpmsg_device *rpdev) +{ + int rc; + + // int ret; + // struct instance_data *idata; + + // dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n", + // rpdev->src, rpdev->dst); + + pr_info("RPMSG monitroing control device driver started.\n"); + + // idata = devm_kzalloc(&rpdev->dev, sizeof(*idata), GFP_KERNEL); + // if (!idata) + // return -ENOMEM; + + // dev_set_drvdata(&rpdev->dev, idata); + + // /* send a message to our remote processor to */ + // ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG)); + // if (ret) { + // dev_err(&rpdev->dev, "vrpmdv_monitoring_controler_send failed: %d\n", ret); + // return ret; + // } + + // return 0; + + // struct device *dev; + // dev = &rpdev->dev; + // struct rpmsg_vrpmdv_mon_t *rpmsg_vrpmdv_mon; + + // rpmsg_vrpmdv_mon = devm_kzalloc(dev, sizeof(*rpmsg_vrpmdv_mon), GFP_KERNEL); + // if (!rpmsg_vrpmdv_mon) + // return -ENOMEM; + + mutex_init(&sendMTX); + init_waitqueue_head (&receive_queue); + prpdev = rpdev; + + // dev_set_drvdata(&rpdev->dev, rpmsg_vrpmdv_mon); + pr_info("RPMSG CMD Device set.\n"); + + + /** NEU **/ + // if (cmd_cb_progress_data.rpdev == NULL) { + // cmd_cb_progress_data.rpdev = rpdev; + // pr_info("RPMSG CMD Device set.\n"); + // } + // else { + // pr_info("Error: RPMSG CMD Device already set. Don't set it twice\n"); + // } + + pr_info("Generic Netlink VRPMDV-Monitroring_Controler Module started.\n"); + + // Register family with its operations and policies + rc = genl_register_family(&gnl_vrpmdv_mcmd_family); + if (rc != 0) { + pr_err("FAILED: genl_register_family(): %i\n", rc); + pr_err("An error occurred while inserting the generic netlink example module\n"); + return -1; + } else { + pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + return 0; + + + +} + +static void vrpmdv_monitoring_remove(struct rpmsg_device *rpdev) +{ + int ret; + pr_info("Generic Netlink Example Module unloaded.\n"); + + // Unregister the family + ret = genl_unregister_family(&gnl_vrpmdv_mcmd_family); + if (ret != 0) { + pr_err("genl_unregister_family() failed: %i\n", ret); + return; + } else { + pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + mutex_destroy(&sendMTX); + wake_up_interruptible(&receive_queue); + + pr_info("vrpmdv-monitoring controler driver is removed\n"); + // dev_info(&rpdev->dev, "vrpmdv-monitoring controler driver is removed\n"); + +} + +static struct rpmsg_device_id vrpmdv_monitoring_controler_id_table[] = { + { .name = "vrpmdv-monitoring-controler" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, vrpmdv_monitoring_controler_id_table); + +static struct rpmsg_driver vrpmdv_monitoring_controler = { + .drv.name = KBUILD_MODNAME, + .id_table = vrpmdv_monitoring_controler_id_table, + .probe = vrpmdv_monitoring_probe, + .callback = vrpmdv_monitoring_cb, + .remove = vrpmdv_monitoring_remove, +}; +module_rpmsg_driver(vrpmdv_monitoring_controler); + + +// static struct rpmsg_driver vrpmdv_monitoring_data = { +// .drv.name = KBUILD_MODNAME, +// .id_table = vrpmdv_monitoring_controler_id_table, +// .probe = vrpmdv_monitoring_probe, +// .callback = vrpmdv_monitoring_cb, +// .remove = vrpmdv_monitoring_remove, +// }; + +// module_rpmsg_driver(vrpmdv_monitoring_data); + +MODULE_DESCRIPTION("Remote processor messaging vrpmdv monitoring controler"); +MODULE_LICENSE("GPL v2"); + + + +// /** +// * Module/driver initializer. Called on module load/insertion. +// * +// * @return success (0) or error code. +// */ +// static int __init gnl_foobar_xmpl_module_init(void) { +// int rc; +// pr_info("Generic Netlink Example Module inserted.\n"); + +// // Register family with its operations and policies +// rc = genl_register_family(&gnl_foobar_xmpl_family); +// if (rc != 0) { +// pr_err("FAILED: genl_register_family(): %i\n", rc); +// pr_err("An error occurred while inserting the generic netlink example module\n"); +// return -1; +// } else { +// pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); +// } + +// mutex_init(&dumpit_cb_progress_data.mtx); + +// return 0; +// } + +// /** +// * Module/driver uninitializer. Called on module unload/removal. +// * +// * @return success (0) or error code. +// */ +// static void __exit gnl_foobar_xmpl_module_exit(void) { +// int ret; +// pr_info("Generic Netlink Example Module unloaded.\n"); + +// // Unregister the family +// ret = genl_unregister_family(&gnl_foobar_xmpl_family); +// if (ret != 0) { +// pr_err("genl_unregister_family() failed: %i\n", ret); +// return; +// } else { +// pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); +// } + +// mutex_destroy(&dumpit_cb_progress_data.mtx); +// } + + +// ---- + +// static int __init rpmsg_sdb_drv_init(void) +// { +// int ret = 0; + +// /* Register rpmsg device */ +// ret = register_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + +// if (ret) { +// pr_err("rpmsg_sdb(ERROR): Failed to register device\n"); +// return ret; +// } + +// pr_info("rpmsg_sdb: Init done\n"); + +// return ret; +// } + +// static void __exit rpmsg_sdb_drv_exit(void) +// { +// unregister_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); +// pr_info("rpmsg_sdb: Exit\n"); +// } + +// module_init(rpmsg_sdb_drv_init); +// module_exit(rpmsg_sdb_drv_exit); diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/kernel-module-vrpmdv-monitoring-driver.bb b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/kernel-module-vrpmdv-monitoring-driver.bb new file mode 100644 index 0000000..9bd4cda --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/kernel-module-vrpmdv-monitoring-driver.bb @@ -0,0 +1,16 @@ +SUMMARY = "VRPMDV Monitoring Driver" +LICENSE = "GPL-2.0-only" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "file://Makefile \ + file://vrpmdv-monitoring.c \ + " + +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. + +RPROVIDES:${PN} += "kernel-module-vrpmdv-monitoring" diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/netlink-save.c.save b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/netlink-save.c.save new file mode 100644 index 0000000..8a32ba8 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/netlink-save.c.save @@ -0,0 +1,541 @@ +/* Copyright 2021 Philipp Schuster + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in the + * Software without restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// ################################################################################################## +/* + * This is a Linux kernel module/driver called "gnl_foobar_xmpl" which shows you the basics of using + * Generic Netlink in the kernel. It registers a Netlink family called "gnl_foobar_xmpl". See + * "gnl_foobar_xmpl_prop.h" for common properties of the family. "Generic Netlink" offers us a lot of + * convenient functions to register new/custom Netlink families on the fly during runtime. We use this + * functionality here. It implements simple IPC between Userland and Kernel (Kernel responds to userland). + * + * Check "gnl_foobar_xmpl_prop.h" for common properties of the family first, afterwards follow the code here. + * + * You can find some more interesting documentation about Generic Netlink here: + * "Generic Netlink HOW-TO based on Jamal's original doc" https://lwn.net/Articles/208755/ + */ + +// basic definitions for kernel module development +#include +// definitions for generic netlink families, policies etc; +// transitive dependencies for basic netlink, sockets etc +#include +// required for locking inside the .dumpit callback demonstration +#include + +// data/vars/enums/properties that describes our protocol that we implement +// on top of generic netlink (like functions we want to trigger on the receiving side) +#include "gnl_foobar_xmpl_prop.h" + +// Module/Driver description. +// You can see this for example when executing `$ modinfo ./gnl_foobar_xmpl.ko` (after build). +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Philipp Schuster "); +MODULE_DESCRIPTION( + "Linux driver that registers the custom Netlink family " + "\"" FAMILY_NAME "\" via Generic Netlink and responds to echo messages " + "according to the properties specified in \"gnl_foobar_xmpl_prop.h\"." +); + + +/* ######################## CONVENIENT LOGGING MACROS ######################## */ +// (Re)definition of some convenient logging macros from . You can see the logging +// messages when printing the kernel log, e.g. with `$ sudo dmesg`. +// See https://elixir.bootlin.com/linux/latest/source/include/linux/printk.h + +// with this redefinition we can easily prefix all log messages from pr_* logging macros +#ifdef pr_fmt +#undef pr_fmt +#endif +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +/* ########################################################################### */ + +/** + * Data structure required for our .dumpit callback handler to + * know about the progress of an ongoing dump. + * See the dumpit callback handler how it is used. + */ +struct { + // from + /** + * Only one process is allowed per dump process. We need a lock for that. + */ + struct mutex mtx; + /** + * Number that describes how many packets we need to send until we are done + * during an ongoing dumpit process. 0 = done. + */ + int unsigned runs_to_go; + /** + * Number that describes how many packets per dump are sent in total. + * Constant per dump. + */ + int unsigned total_runs; +} dumpit_cb_progress_data; + +// Documentation is on the implementation of this function. +int gnl_cb_echo_doit(struct sk_buff *sender_skb, struct genl_info *info); + +// Documentation is on the implementation of this function. +int gnl_cb_echo_dumpit(struct sk_buff *pre_allocated_skb, struct netlink_callback *cb); + +// Documentation is on the implementation of this function. +int gnl_cb_echo_dumpit_before(struct netlink_callback *cb); + +// Documentation is on the implementation of this function. +int gnl_cb_echo_dumpit_before_after(struct netlink_callback *cb); + +// Documentation is on the implementation of this function. +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info); + +/** + * The length of `struct genl_ops gnl_foobar_xmpl_ops[]`. Not necessarily + * the number of commands in `enum GNlFoobarXmplCommand`. It depends on your application logic. + * For example, you can use the same command multiple times and - dependent by flag - + * invoke a different callback handler. In our simple example we just use one .doit callback + * per operation/command. + */ +#define GNL_FOOBAR_OPS_LEN (GNL_FOOBAR_XMPL_COMMAND_COUNT) + +/** + * Array with all operations that our protocol on top of Generic Netlink + * supports. An operation is the glue between a command ("cmd" field in `struct genlmsghdr` of + * received Generic Netlink message) and the corresponding ".doit" callback function. + * See: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L148 + */ +struct genl_ops gnl_foobar_xmpl_ops[GNL_FOOBAR_OPS_LEN] = { + { + /* The "cmd" field in `struct genlmsghdr` of received Generic Netlink message */ + .cmd = GNL_FOOBAR_XMPL_C_ECHO_MSG, + /* TODO Use case ? */ + .flags = 0, + /* TODO Use case ? */ + .internal_flags = 0, + /* Callback handler when a request with the specified ".cmd" above is received. + * Always validates the payload except one set NO_STRICT_VALIDATION flag in ".validate" + * See: https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + * + * Quote from: https://lwn.net/Articles/208755 + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + * + */ + .doit = gnl_cb_echo_doit, + /* This callback is similar in use to the standard Netlink 'dumpit' callback. + * The 'dumpit' callback is invoked when a Generic Netlink message is received + * with the NLM_F_DUMP flag set. + * + * A dump can be understand as a "GET ALL DATA OF THE GIVEN ENTITY", i.e. + * the userland can receive as long as the .dumpit callback returns data. + * + * .dumpit is not mandatory, but either it or .doit must be provided, see + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L367 + * + * To be honest I don't know in what use case one should use .dumpit and why + * it is useful, because you can achieve the same also with .doit handlers. + * Anyway, this is just an example/tutorial. + * + * Quote from: https://lwn.net/Articles/208755 + * "The main difference between a 'dumpit' handler and a 'doit' handler is + * that a 'dumpit' handler does not allocate a message buffer for a response; + * a pre-allocated sk_buff is passed to the 'dumpit' handler as the first + * parameter. The 'dumpit' handler should fill the message buffer with the + * appropriate response message and return the size of the sk_buff, + * i.e. sk_buff->len, and the message buffer will automatically be sent to the + * Generic Netlink client that initiated the request. As long as the 'dumpit' + * handler returns a value greater than zero it will be called again with a + * newly allocated message buffer to fill, when the handler has no more data + * to send it should return zero; error conditions are indicated by returning + * a negative value. If necessary, state can be preserved in the + * netlink_callback parameter which is passed to the 'dumpit' handler; the + * netlink_callback parameter values will be preserved across handler calls + * for a single request." + * + * You can see the check for the NLM_F_DUMP-flag here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L780 + */ + .dumpit = gnl_cb_echo_dumpit, + /* Start callback for dumps. Can be used to lock data structures. */ + .start = gnl_cb_echo_dumpit_before, + /* Completion callback for dumps. Can be used for cleanup after a dump and releasing locks. */ + .done = gnl_cb_echo_dumpit_before_after, + /* + 0 (= "validate strictly") or value `enum genl_validate_flags` + * see: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L108 + */ + .validate = 0, + }, + { + .cmd = GNL_FOOBAR_XMPL_C_REPLY_WITH_NLMSG_ERR, + .flags = 0, + .internal_flags = 0, + .doit = gnl_cb_doit_reply_with_nlmsg_err, + // .dumpit is not required, only optional; application specific/dependent on your use case + // in a real application you probably have different .dumpit handlers per operation/command + .dumpit = NULL, + // in a real application you probably have different .start handlers per operation/command + .start = NULL, + // in a real application you probably have different .done handlers per operation/command + .done = NULL, + .validate = 0, + } +}; + +/** + * Attribute policy: defines which attribute has which type (e.g int, char * etc). + * This get validated for each received Generic Netlink message, if not deactivated + * in `gnl_foobar_xmpl_ops[].validate`. + * See https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + */ +static struct nla_policy gnl_foobar_xmpl_policy[GNL_FOOBAR_XMPL_ATTRIBUTE_ENUM_LEN] = { + // In case you are seeing this syntax for the first time (I also learned this just after a few years of + // coding with C myself): The following static array initiations are equivalent: + // `int a[2] = {1, 2}` <==> `int a[2] = {[0] => 1, [1] => 2}`. + + [GNL_FOOBAR_XMPL_A_UNSPEC] = {.type = NLA_UNSPEC}, + + // You can set this to NLA_U32 for testing and send an ECHO message from the userland + // It will fail in this case and you see a entry in the kernel log. + + // `enum GNL_FOOBAR_XMPL_ATTRIBUTE::GNL_FOOBAR_XMPL_A_MSG` is a null-terminated C-String + [GNL_FOOBAR_XMPL_A_MSG] = {.type = NLA_NUL_STRING}, +}; + +/** + * Definition of the Netlink family we want to register using Generic Netlink functionality + */ +static struct genl_family gnl_foobar_xmpl_family = { + // automatically assign an id + .id = 0, + // we don't use custom additional header info / user specific header + .hdrsize = 0, + // The name of this family, used by userspace application to get the numeric ID + .name = FAMILY_NAME, + // family specific version number; can be used to evolve application over time (multiple versions) + .version = 1, + // delegates all incoming requests to callback functions + .ops = gnl_foobar_xmpl_ops, + // length of array `gnl_foobar_xmpl_ops` + .n_ops = GNL_FOOBAR_OPS_LEN, + // attribute policy (for validation of messages). Enforced automatically, except ".validate" in + // corresponding ".ops"-field is set accordingly. + .policy = gnl_foobar_xmpl_policy, + // Number of attributes / bounds check for policy (array length) + .maxattr = GNL_FOOBAR_XMPL_ATTRIBUTE_ENUM_LEN, + // Owning Kernel module of the Netlink family we register. + .module = THIS_MODULE, + + // Actually not necessary because this memory region would be zeroed anyway during module load, + // but this way one sees all possible options. + + // if your application must handle multiple netlink calls in parallel (where one should not block the next + // from starting), set this to true! otherwise all netlink calls are mutually exclusive + .parallel_ops = 0, + // set to true if the family can handle network namespaces and should be presented in all of them + .netnsok = 0, + // called before an operation's doit callback, it may do additional, common, filtering and return an error + .pre_doit = NULL, + // called after an operation's doit callback, it may undo operations done by pre_doit, for example release locks + .post_doit = NULL, +}; + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_FOOBAR_XMPL_C_ECHO` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_echo_doit(struct sk_buff *sender_skb, struct genl_info *info) { + struct nlattr *na; + struct sk_buff *reply_skb; + int rc; + void *msg_head; + char *recv_msg; + + pr_info("%s() invoked\n", __func__); + + if (info == NULL) { + // should never happen + pr_err("An error occurred in %s():\n", __func__); + return -EINVAL; + } + + /* + * For each attribute there is an index in info->attrs which points to a nlattr structure + * in this structure the data is stored. + */ + na = info->attrs[GNL_FOOBAR_XMPL_A_MSG]; + + if (!na) { + pr_err("no info->attrs[%i]\n", GNL_FOOBAR_XMPL_A_MSG); + return -EINVAL; // we return here because we expect to recv a msg + } + + recv_msg = (char *) nla_data(na); + if (recv_msg == NULL) { + pr_err("error while receiving data\n"); + } else { + pr_info("received: '%s'\n", recv_msg); + } + + + // Send a message back + // --------------------- + + // Allocate some memory, since the size is not yet known use NLMSG_GOODSIZE + reply_skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (reply_skb == NULL) { + pr_err("An error occurred in %s():\n", __func__); + return -ENOMEM; + } + + // Create the message headers + + // Add header to netlink message; + // afterwards the buffer looks like this: + // ---------------------------------- + // | netlink header | + // | generic netlink header | + // | | + // ---------------------------------- + msg_head = genlmsg_put(reply_skb, // buffer for netlink message: struct sk_buff * + // According to my findings: this is not used for routing + // This can be used in an application specific way to target + // different endpoints within the same user application + // but general rule: just put sender port id here + info->snd_portid, // sending port (not process) id: int + info->snd_seq + 1, // sequence number: int (might be used by receiver, but not mandatory) + &gnl_foobar_xmpl_family, // struct genl_family * + 0, // flags for Netlink header: int; application specific and not mandatory + // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header + GNL_FOOBAR_XMPL_C_ECHO_MSG + ); + if (msg_head == NULL) { + rc = ENOMEM; + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + // Add a GNL_FOOBAR_XMPL_A_MSG attribute (actual value/payload to be sent) + // echo the value we just received + rc = nla_put_string(reply_skb, GNL_FOOBAR_XMPL_A_MSG, recv_msg); + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + // Finalize the message: + // Corrects the netlink message header (length) to include the appended + // attributes. Only necessary if attributes have been added to the message. + genlmsg_end(reply_skb, msg_head); + + // Send the message back + rc = genlmsg_reply(reply_skb, info); + // same as genlmsg_unicast(genl_info_net(info), reply_skb, info->snd_portid) + // see https://elixir.bootlin.com/linux/v5.8.9/source/include/net/genetlink.h#L326 + + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + return 0; +} + +/** + * ".dumpit"-callback function if a Generic Netlink with command ECHO_MSG and flag `NLM_F_DUMP` is received. + * Please look into the comments where this is used as ".dumpit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".dumpit" callbacks. + * + * A dump must be understand of "give me all data of a given entity" + * rather than a "dump of the netlink message itself" for debugging etc! + * + * This handler requires `gnl_cb_echo_dumpit_before` to run before a dump and `gnl_cb_echo_dumpit_after` after a dump. + * + * For the sake of simplicity, we use the ECHO_MSG command for the dump. In fact, we don't expect a + * MSG-Attribute here, unlike the regular ECHO_MSG handler. We reply with a dump of + * "all messages that we got" (application specific, hard coded in this example). +*/ +int gnl_cb_echo_dumpit(struct sk_buff *pre_allocated_skb, struct netlink_callback *cb) { + void *msg_head; + int ret; + static const char HELLO_FROM_DUMPIT_MSG[] = "You set the flag NLM_F_DUMP; this message is " + "brought to you by .dumpit callback :)"; + pr_info("Called %s()\n", __func__); + + if (dumpit_cb_progress_data.runs_to_go == 0) { + pr_info("no more data to send in dumpit cb\n"); + // mark that dump is done; + return 0; + } else { + dumpit_cb_progress_data.runs_to_go--; + pr_info("%s: %d more runs to do\n", __func__, dumpit_cb_progress_data.runs_to_go); + } + + msg_head = genlmsg_put(pre_allocated_skb, // buffer for netlink message: struct sk_buff * + // According to my findings: this is not used for routing + // This can be used in an application specific way to target + // different endpoints within the same user application + // but general rule: just put sender port id here + cb->nlh->nlmsg_pid, // sending port (not process) id: int + // sequence number: int (might be used by receiver, but not mandatory) + // sequence 0, 1, 2... + dumpit_cb_progress_data.total_runs - dumpit_cb_progress_data.runs_to_go - 1, + &gnl_foobar_xmpl_family, // struct genl_family * + 0, // flags: int (for netlink header); we don't check them in the userland; application specific + // this way we can trigger a specific command/callback on the receiving side or imply + // on which type of command we are currently answering; this is application specific + GNL_FOOBAR_XMPL_C_ECHO_MSG // cmd: u8 (for generic netlink header); + ); + if (msg_head == NULL) { + pr_info("An error occurred in %s(): genlmsg_put() failed\n", __func__); + return -ENOMEM; + } + ret = nla_put_string( + pre_allocated_skb, + GNL_FOOBAR_XMPL_A_MSG, + HELLO_FROM_DUMPIT_MSG + ); + if (ret < 0) { + pr_info("An error occurred in %s():\n", __func__); + return ret; + } + genlmsg_end(pre_allocated_skb, msg_head); + + // return the length of data we wrote into the pre-allocated buffer + return pre_allocated_skb->len; +} + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_FOOBAR_XMPL_C_REPLY_WITH_NLMSG_ERR` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info) { + pr_info("%s() invoked, a NLMSG_ERR response will be sent back\n", __func__); + + /* + * Generic Netlink is smart enough and sends a NLMSG_ERR reply automatically as reply + * Quote from https://lwn.net/Articles/208755/: + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + */ + return -EINVAL; +} + +/** + * Called before a dump with `gnl_cb_echo_dumpit()` starts. + * See where this is assigned in `struct genl_ops gnl_foobar_xmpl_ops[]` as + * `.start` callback for more comments. + * + * @return success (0) or error. + */ +int gnl_cb_echo_dumpit_before(struct netlink_callback *cb) { + int ret; + static int unsigned const dump_runs = 3; + pr_info("%s: dump started. acquire lock. initialize dump runs_to_go (number of receives userland can make) to %d runs\n", __func__, dump_runs); + // Lock the mutex like mutex_lock(), and return 0 if the mutex has been acquired or sleep until the mutex becomes available + // If a signal arrives while waiting for the lock then this function returns -EINTR. + ret = mutex_lock_interruptible(&dumpit_cb_progress_data.mtx); + if (ret != 0) { + pr_err("Failed to get lock!\n"); + return ret; + } + dumpit_cb_progress_data.total_runs = dump_runs; + dumpit_cb_progress_data.runs_to_go = dump_runs; + return 0; + +} + +/** + * Called before a dump with `gnl_cb_echo_dumpit()` starts. + * See where this is assigned in `struct genl_ops gnl_foobar_xmpl_ops[]` as + * `.start` callback for more comments. + * + * @return success (0) or error. + */ +int gnl_cb_echo_dumpit_before_after(struct netlink_callback *cb) { + pr_info("%s: dump done. release lock\n", __func__); + mutex_unlock(&dumpit_cb_progress_data.mtx); + return 0; +} + +/** + * Module/driver initializer. Called on module load/insertion. + * + * @return success (0) or error code. + */ +static int __init gnl_foobar_xmpl_module_init(void) { + int rc; + pr_info("Generic Netlink Example Module inserted.\n"); + + // Register family with its operations and policies + rc = genl_register_family(&gnl_foobar_xmpl_family); + if (rc != 0) { + pr_err("FAILED: genl_register_family(): %i\n", rc); + pr_err("An error occurred while inserting the generic netlink example module\n"); + return -1; + } else { + pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + mutex_init(&dumpit_cb_progress_data.mtx); + + return 0; +} + +/** + * Module/driver uninitializer. Called on module unload/removal. + * + * @return success (0) or error code. + */ +static void __exit gnl_foobar_xmpl_module_exit(void) { + int ret; + pr_info("Generic Netlink Example Module unloaded.\n"); + + // Unregister the family + ret = genl_unregister_family(&gnl_foobar_xmpl_family); + if (ret != 0) { + pr_err("genl_unregister_family() failed: %i\n", ret); + return; + } else { + pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + mutex_destroy(&dumpit_cb_progress_data.mtx); +} + +module_init(gnl_foobar_xmpl_module_init); +module_exit(gnl_foobar_xmpl_module_exit); \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/rpmsg_sdb.csva b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/rpmsg_sdb.csva new file mode 100644 index 0000000..857a42c --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/rpmsg_sdb.csva @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + * Author: Jean-Philippe Romain + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RPMSG_SDB_DRIVER_VERSION "1.0" + +/* + * Static global variables + */ +static const char rpmsg_sdb_driver_name[] = "stm32-rpmsg-sdb"; + +static int LastBufferId; + +struct rpmsg_sdb_ioctl_set_efd { + int bufferId, eventfd; +}; + +struct rpmsg_sdb_ioctl_get_data_size { + int bufferId; + uint32_t size; +}; + +/* ioctl numbers */ +/* _IOW means userland is writing and kernel is reading */ +/* _IOR means userland is reading and kernel is writing */ +/* _IOWR means userland and kernel can both read and write */ +#define RPMSG_SDB_IOCTL_SET_EFD _IOW('R', 0x00, struct rpmsg_sdb_ioctl_set_efd *) +#define RPMSG_SDB_IOCTL_GET_DATA_SIZE _IOWR('R', 0x01, struct rpmsg_sdb_ioctl_get_data_size *) + +struct sdb_buf_t { + int index; /* index of buffer */ + size_t size; /* buffer size */ + size_t writing_size; /* size of data written by copro */ + dma_addr_t paddr; /* physical address*/ + void *vaddr; /* virtual address */ + void *uaddr; /* mapped address for userland */ + struct eventfd_ctx *efd_ctx; /* eventfd context */ + struct list_head buflist; /* reference in the buffers list */ +}; + +struct rpmsg_sdb_t { + struct mutex mutex; /* mutex to protect the ioctls */ + struct miscdevice mdev; /* misc device ref */ + struct rpmsg_device *rpdev; /* handle rpmsg device */ + struct list_head buffer_list; /* buffer instances list */ +}; + +struct device *rpmsg_sdb_dev; + +static int rpmsg_sdb_format_txbuf_string(struct sdb_buf_t *buffer, char *bufinfo_str, size_t bufinfo_str_size) +{ + pr_debug("rpmsg_sdb(%s): Buffer index:%d, addr:%08x, size:%08x\n", + __func__, + buffer->index, + buffer->paddr, + buffer->size); + return snprintf(bufinfo_str, bufinfo_str_size, "B%dA%08xL%08x", buffer->index, buffer->paddr, buffer->size); +} + +static long rpmsg_sdb_decode_rxbuf_string(char *rxbuf_str, int *buffer_id, size_t *size) +{ + int ret = 0; + char *sub_str; + long bsize; + long bufid; + const char delimiter[2] = {'L','\0'}; + + pr_debug("rpmsg_sdb(%s): rxbuf_str:%s\n", __func__, rxbuf_str); + + /* Get first part containing the buffer id */ + sub_str = strsep(&rxbuf_str, delimiter); + + pr_debug("rpmsg_sdb(%s): sub_str:%s\n", __func__, sub_str); + + /* Save Buffer id and size: template BxLyyyyyyyy*/ + ret = kstrtol(&sub_str[1], 10, &bufid); + if (ret < 0) { + pr_err("rpmsg_sdb(ERROR): Extract of buffer id failed(%d)", ret); + goto out; + } + + ret = kstrtol(&rxbuf_str[2], 16, &bsize); + if (ret < 0) { + pr_err("rpmsg_sdb(ERROR): Extract of buffer size failed(%d)", ret); + goto out; + } + + *size = (size_t)bsize; + *buffer_id = (int)bufid; + +out: + return ret; +} + +static int rpmsg_sdb_send_buf_info(struct rpmsg_sdb_t *rpmsg_sdb, struct sdb_buf_t *buffer) +{ + int count = 0, ret = 0; + const unsigned char *tbuf; + char mybuf[32]; + int msg_size; + struct rpmsg_device *_rpdev; + + _rpdev = rpmsg_sdb->rpdev; + msg_size = rpmsg_get_buffer_size(_rpdev->ept); + + if (msg_size < 0) + return msg_size; + + count = rpmsg_sdb_format_txbuf_string(buffer, mybuf, 32); + tbuf = &mybuf[0]; + + do { + /* send a message to our remote processor */ + ret = rpmsg_send(_rpdev->ept, (void *)tbuf, + count > msg_size ? msg_size : count); + if (ret) { + dev_err(&_rpdev->dev, "rpmsg_send failed: %d\n", ret); + return ret; + } + + if (count > msg_size) { + count -= msg_size; + tbuf += msg_size; + } else { + count = 0; + } + } while (count > 0); + + return count; +} + +static int rpmsg_sdb_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long vsize = vma->vm_end - vma->vm_start; + unsigned long size = PAGE_ALIGN(vsize); + unsigned long NumPages = size >> PAGE_SHIFT; + unsigned long align = get_order(size); + pgprot_t prot = pgprot_noncached(vma->vm_page_prot); + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *_buffer; + + if (align > CONFIG_CMA_ALIGNMENT) + align = CONFIG_CMA_ALIGNMENT; + + if (rpmsg_sdb_dev == NULL) + return -ENOMEM; + + rpmsg_sdb_dev->coherent_dma_mask = DMA_BIT_MASK(32); + rpmsg_sdb_dev->dma_mask = &rpmsg_sdb_dev->coherent_dma_mask; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Field the last buffer entry which is the last one created */ + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + _buffer = list_last_entry(&_rpmsg_sdb->buffer_list, + struct sdb_buf_t, buflist); + + _buffer->uaddr = NULL; + _buffer->size = NumPages * PAGE_SIZE; + _buffer->writing_size = -1; + _buffer->vaddr = dma_alloc_coherent(rpmsg_sdb_dev, + _buffer->size, + &_buffer->paddr, + GFP_KERNEL); + + if (!_buffer->vaddr) { + pr_err("rpmsg_sdb(ERROR): Memory allocation issue\n"); + return -ENOMEM; + } + + pr_debug("rpmsg_sdb(%s): dma_alloc_coherent done - paddr[%d]:%x - vaddr[%d]:%p\n", + __func__, + _buffer->index, + _buffer->paddr, + _buffer->index, + _buffer->vaddr); + + /* Get address for userland */ + if (remap_pfn_range(vma, vma->vm_start, + (_buffer->paddr >> PAGE_SHIFT) + vma->vm_pgoff, + size, prot)) + return -EAGAIN; + + _buffer->uaddr = (void *)vma->vm_start; + + /* Send information to remote proc */ + rpmsg_sdb_send_buf_info(_rpmsg_sdb, _buffer); + } else { + dev_err(rpmsg_sdb_dev, "No existing buffer entry exist in the list !!!"); + return -EINVAL; + } + + /* Increment for number of requested buffer */ + LastBufferId++; + + return 0; +} + +/** + * rpmsg_sdb_open - Open Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_open(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + /* Initialize the buffer list*/ + INIT_LIST_HEAD(&_rpmsg_sdb->buffer_list); + + mutex_init(&_rpmsg_sdb->mutex); + + return 0; +} + +/** + * rpmsg_sdb_close - Close Session + * + * @inode: inode struct + * @file: file struct + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static int rpmsg_sdb_close(struct inode *inode, struct file *file) +{ + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *pos, *next; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + list_for_each_entry_safe(pos, next, &_rpmsg_sdb->buffer_list, buflist) { + /* Free the CMA allocation */ + pr_debug("rpmsg_sdb(%s): Free the CMA allocation: pos->size:%08x, pos->vaddr:%08x, pos->paddr:%08x\n", + __func__, + pos->size, + pos->vaddr, + pos->paddr); + + dma_free_coherent(rpmsg_sdb_dev, pos->size, pos->vaddr, + pos->paddr); + /* Remove the buffer from the list */ + list_del(&pos->buflist); + /* Free the buffer */ + kfree(pos); + } + + /* Reset LastBufferId */ + LastBufferId = 0; + + return 0; +} + +/** + * rpmsg_sdb_ioctl - IOCTL + * + * @session: ibmvmc_file_session struct + * @cmd: cmd field + * @arg: Argument field + * + * Return: + * 0 - Success + * Non-zero - Failure + */ +static long rpmsg_sdb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int idx = 0; + + struct rpmsg_sdb_t *_rpmsg_sdb; + struct sdb_buf_t *buffer, *lastbuffer; + + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + struct rpmsg_sdb_ioctl_set_efd q_set_efd; + struct rpmsg_sdb_ioctl_get_data_size q_get_dat_size; + + void __user *argp = (void __user *)arg; + + _rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t, + mdev); + + switch (cmd) { + case RPMSG_SDB_IOCTL_SET_EFD: + mutex_lock(&_rpmsg_sdb->mutex); + + /* Get index from the last buffer in the list */ + if (!list_empty(&_rpmsg_sdb->buffer_list)) { + lastbuffer = list_last_entry(&_rpmsg_sdb->buffer_list, struct sdb_buf_t, buflist); + idx = lastbuffer->index; + + /* Check last index was properly initiated*/ + if (lastbuffer->vaddr == NULL) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - previous buffer was not allocated\n"); + mutex_unlock(&_rpmsg_sdb->mutex); + return -EBADE; + } + + /* increment this index for the next buffer creation*/ + idx++; + } + + if (copy_from_user(&q_set_efd, (struct rpmsg_sdb_ioctl_set_efd *)argp, + sizeof(struct rpmsg_sdb_ioctl_set_efd))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_SET_EFD - copy from user failed\n"); + mutex_unlock(&_rpmsg_sdb->mutex); + return -EFAULT; + } + + /* create a new buffer which will be added in the buffer list */ + buffer = kmalloc(sizeof(struct sdb_buf_t), GFP_KERNEL); + + buffer->index = idx; + buffer->vaddr = NULL; + buffer->efd_ctx = eventfd_ctx_fdget(q_set_efd.eventfd); + list_add_tail(&buffer->buflist, &_rpmsg_sdb->buffer_list); + + mutex_unlock(&_rpmsg_sdb->mutex); + break; + + case RPMSG_SDB_IOCTL_GET_DATA_SIZE: + if (copy_from_user(&q_get_dat_size, (struct rpmsg_sdb_ioctl_get_data_size *)argp, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy from user failed\n"); + return -EFAULT; + } + + /* Get the index of the requested buffer and then look-up in the buffer list*/ + idx = q_get_dat_size.bufferId; + + list_for_each(pos, &_rpmsg_sdb->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == idx) { + /* Get the writing size*/ + q_get_dat_size.size = datastructureptr->writing_size; + break; + } + } + + if (copy_to_user((struct rpmsg_sdb_ioctl_get_data_size *)argp, &q_get_dat_size, + sizeof(struct rpmsg_sdb_ioctl_get_data_size))) { + pr_err("rpmsg_sdb(ERROR): RPMSG_SDB_IOCTL_GET_DATA_SIZE - copy to user failed\n"); + return -EFAULT; + } + + /* Reset the writing size*/ + datastructureptr->writing_size = -1; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct file_operations rpmsg_sdb_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = rpmsg_sdb_ioctl, + .mmap = rpmsg_sdb_mmap, + .open = rpmsg_sdb_open, + .release = rpmsg_sdb_close, +}; + +static int rpmsg_sdb_drv_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + int ret = 0; + int buffer_id = 0; + size_t buffer_size; + char *rpmsg_RxBuf; + struct list_head *pos; + struct sdb_buf_t *datastructureptr = NULL; + + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpdev->dev); + + if (len == 0) { + dev_err(rpmsg_sdb_dev, "(%s) Empty lenght requested\n", __func__); + return -EINVAL; + } + + //dev_err(rpmsg_sdb_dev, "(%s) lenght: %d\n", __func__,len); + + rpmsg_RxBuf = (char *)kmalloc(len+1, GFP_KERNEL); + memcpy(rpmsg_RxBuf, data, len); + + rpmsg_RxBuf[len] = 0; + + ret = rpmsg_sdb_decode_rxbuf_string(rpmsg_RxBuf, &buffer_id, &buffer_size); + kfree(rpmsg_RxBuf); + if (ret < 0) + goto out; + + if (buffer_id > LastBufferId) { + ret = -EINVAL; + goto out; + } + + /* Signal to User space application */ + list_for_each(pos, &drv->buffer_list) + { + datastructureptr = list_entry(pos, struct sdb_buf_t, buflist); + if (datastructureptr->index == buffer_id) { + datastructureptr->writing_size = buffer_size; + + if (datastructureptr->writing_size > datastructureptr->size) { + dev_err(rpmsg_sdb_dev, "(%s) Writing size is bigger than buffer size\n", __func__); + ret = -EINVAL; + goto out; + } + + eventfd_signal(datastructureptr->efd_ctx, 1); + break; + } + /* TODO: quid if nothing find during the loop ? */ + } + +out: + return ret; +} + +static int rpmsg_sdb_drv_probe(struct rpmsg_device *rpdev) +{ + int ret = 0; + struct device *dev = &rpdev->dev; + struct rpmsg_sdb_t *rpmsg_sdb; + + rpmsg_sdb = devm_kzalloc(dev, sizeof(*rpmsg_sdb), GFP_KERNEL); + if (!rpmsg_sdb) + return -ENOMEM; + + mutex_init(&rpmsg_sdb->mutex); + + rpmsg_sdb->rpdev = rpdev; + + rpmsg_sdb->mdev.name = "rpmsg-sdb"; + rpmsg_sdb->mdev.minor = MISC_DYNAMIC_MINOR; + rpmsg_sdb->mdev.fops = &rpmsg_sdb_fops; + + dev_set_drvdata(&rpdev->dev, rpmsg_sdb); + + /* Register misc device */ + ret = misc_register(&rpmsg_sdb->mdev); + + if (ret) { + dev_err(dev, "Failed to register device\n"); + goto err_out; + } + + rpmsg_sdb_dev = rpmsg_sdb->mdev.this_device; + + dev_info(dev, "%s probed\n", rpmsg_sdb_driver_name); + +err_out: + return ret; +} + +static void rpmsg_sdb_drv_remove(struct rpmsg_device *rpmsgdev) +{ + struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpmsgdev->dev); + + misc_deregister(&drv->mdev); +} + +static struct rpmsg_device_id rpmsg_driver_sdb_id_table[] = { + { .name = "rpmsg-sdb-channel" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sdb_id_table); + +static struct rpmsg_driver rpmsg_sdb_rmpsg_drv = { + .drv.name = KBUILD_MODNAME, + .drv.owner = THIS_MODULE, + .id_table = rpmsg_driver_sdb_id_table, + .probe = rpmsg_sdb_drv_probe, + .callback = rpmsg_sdb_drv_cb, + .remove = rpmsg_sdb_drv_remove, +}; + +static int __init rpmsg_sdb_drv_init(void) +{ + int ret = 0; + + /* Register rpmsg device */ + ret = register_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + + if (ret) { + pr_err("rpmsg_sdb(ERROR): Failed to register device\n"); + return ret; + } + + pr_info("rpmsg_sdb: Init done\n"); + + return ret; +} + +static void __exit rpmsg_sdb_drv_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + pr_info("rpmsg_sdb: Exit\n"); +} + +module_init(rpmsg_sdb_drv_init); +module_exit(rpmsg_sdb_drv_exit); + + +MODULE_AUTHOR("Jean-Philippe Romain "); +MODULE_DESCRIPTION("shared data buffer over RPMSG"); +MODULE_VERSION(RPMSG_SDB_DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/ttyrpmsg.csave b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/ttyrpmsg.csave new file mode 100644 index 0000000..ec89035 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/ttyrpmsg.csave @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 STMicroelectronics - All Rights Reserved + * + * The rpmsg tty driver implements serial communication on the RPMsg bus to makes + * possible for user-space programs to send and receive rpmsg messages as a standard + * tty protocol. + * + * The remote processor can instantiate a new tty by requesting a "rpmsg-tty" RPMsg service. + * The "rpmsg-tty" service is directly used for data exchange. No flow control is implemented yet. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#define RPMSG_TTY_NAME "ttyRPMSG" +#define MAX_TTY_RPMSG 32 + +static DEFINE_IDR(tty_idr); /* tty instance id */ +static DEFINE_MUTEX(idr_lock); /* protects tty_idr */ + +static struct tty_driver *rpmsg_tty_driver; + +struct rpmsg_tty_port { + struct tty_port port; /* TTY port data */ + int id; /* TTY rpmsg index */ + struct rpmsg_device *rpdev; /* rpmsg device */ +}; + +static int rpmsg_tty_cb(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 src) +{ + struct rpmsg_tty_port *cport = dev_get_drvdata(&rpdev->dev); + int copied; + + if (!len) + return -EINVAL; + copied = tty_insert_flip_string(&cport->port, data, len); + if (copied != len) + dev_err_ratelimited(&rpdev->dev, "Trunc buffer: available space is %d\n", copied); + tty_flip_buffer_push(&cport->port); + + return 0; +} + +static int rpmsg_tty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct rpmsg_tty_port *cport = idr_find(&tty_idr, tty->index); + struct tty_port *port; + + tty->driver_data = cport; + + port = tty_port_get(&cport->port); + return tty_port_install(port, driver, tty); +} + +static void rpmsg_tty_cleanup(struct tty_struct *tty) +{ + tty_port_put(tty->port); +} + +static int rpmsg_tty_open(struct tty_struct *tty, struct file *filp) +{ + return tty_port_open(tty->port, tty, filp); +} + +static void rpmsg_tty_close(struct tty_struct *tty, struct file *filp) +{ + return tty_port_close(tty->port, tty, filp); +} + +static int rpmsg_tty_write(struct tty_struct *tty, const u8 *buf, int len) +{ + struct rpmsg_tty_port *cport = tty->driver_data; + struct rpmsg_device *rpdev; + int msg_max_size, msg_size; + int ret; + + rpdev = cport->rpdev; + + msg_max_size = rpmsg_get_mtu(rpdev->ept); + if (msg_max_size < 0) + return msg_max_size; + + msg_size = min(len, msg_max_size); + + /* + * Use rpmsg_trysend instead of rpmsg_send to send the message so the caller is not + * hung until a rpmsg buffer is available. In such case rpmsg_trysend returns -ENOMEM. + */ + ret = rpmsg_trysend(rpdev->ept, (void *)buf, msg_size); + if (ret) { + dev_dbg_ratelimited(&rpdev->dev, "rpmsg_send failed: %d\n", ret); + return ret; + } + + return msg_size; +} + +static unsigned int rpmsg_tty_write_room(struct tty_struct *tty) +{ + struct rpmsg_tty_port *cport = tty->driver_data; + int size; + + size = rpmsg_get_mtu(cport->rpdev->ept); + if (size < 0) + return 0; + + return size; +} + +static void rpmsg_tty_hangup(struct tty_struct *tty) +{ + tty_port_hangup(tty->port); +} + +static const struct tty_operations rpmsg_tty_ops = { + .install = rpmsg_tty_install, + .open = rpmsg_tty_open, + .close = rpmsg_tty_close, + .write = rpmsg_tty_write, + .write_room = rpmsg_tty_write_room, + .hangup = rpmsg_tty_hangup, + .cleanup = rpmsg_tty_cleanup, +}; + +static struct rpmsg_tty_port *rpmsg_tty_alloc_cport(void) +{ + struct rpmsg_tty_port *cport; + int ret; + + cport = kzalloc(sizeof(*cport), GFP_KERNEL); + if (!cport) + return ERR_PTR(-ENOMEM); + + mutex_lock(&idr_lock); + ret = idr_alloc(&tty_idr, cport, 0, MAX_TTY_RPMSG, GFP_KERNEL); + mutex_unlock(&idr_lock); + + if (ret < 0) { + kfree(cport); + return ERR_PTR(ret); + } + + cport->id = ret; + + return cport; +} + +static void rpmsg_tty_destruct_port(struct tty_port *port) +{ + struct rpmsg_tty_port *cport = container_of(port, struct rpmsg_tty_port, port); + + mutex_lock(&idr_lock); + idr_remove(&tty_idr, cport->id); + mutex_unlock(&idr_lock); + + kfree(cport); +} + +static const struct tty_port_operations rpmsg_tty_port_ops = { + .destruct = rpmsg_tty_destruct_port, +}; + + +static int rpmsg_tty_probe(struct rpmsg_device *rpdev) +{ + struct rpmsg_tty_port *cport; + struct device *dev = &rpdev->dev; + struct device *tty_dev; + int ret; + + cport = rpmsg_tty_alloc_cport(); + if (IS_ERR(cport)) + return dev_err_probe(dev, PTR_ERR(cport), "Failed to alloc tty port\n"); + + tty_port_init(&cport->port); + cport->port.ops = &rpmsg_tty_port_ops; + + tty_dev = tty_port_register_device(&cport->port, rpmsg_tty_driver, + cport->id, dev); + if (IS_ERR(tty_dev)) { + ret = dev_err_probe(dev, PTR_ERR(tty_dev), "Failed to register tty port\n"); + tty_port_put(&cport->port); + return ret; + } + + cport->rpdev = rpdev; + + dev_set_drvdata(dev, cport); + + dev_dbg(dev, "New channel: 0x%x -> 0x%x: " RPMSG_TTY_NAME "%d\n", + rpdev->src, rpdev->dst, cport->id); + + return 0; +} + +static void rpmsg_tty_remove(struct rpmsg_device *rpdev) +{ + struct rpmsg_tty_port *cport = dev_get_drvdata(&rpdev->dev); + + dev_dbg(&rpdev->dev, "Removing rpmsg tty device %d\n", cport->id); + + /* User hang up to release the tty */ + tty_port_tty_hangup(&cport->port, false); + + tty_unregister_device(rpmsg_tty_driver, cport->id); + + tty_port_put(&cport->port); +} + +static struct rpmsg_device_id rpmsg_driver_tty_id_table[] = { + { .name = "rpmsg-tty" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_tty_id_table); + +static struct rpmsg_driver rpmsg_tty_rpmsg_drv = { + .drv.name = KBUILD_MODNAME, + .id_table = rpmsg_driver_tty_id_table, + .probe = rpmsg_tty_probe, + .callback = rpmsg_tty_cb, + .remove = rpmsg_tty_remove, +}; + +static int __init rpmsg_tty_init(void) +{ + int ret; + + rpmsg_tty_driver = tty_alloc_driver(MAX_TTY_RPMSG, TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(rpmsg_tty_driver)) + return PTR_ERR(rpmsg_tty_driver); + + rpmsg_tty_driver->driver_name = "rpmsg_tty"; + rpmsg_tty_driver->name = RPMSG_TTY_NAME; + rpmsg_tty_driver->major = 0; + rpmsg_tty_driver->type = TTY_DRIVER_TYPE_CONSOLE; + + /* Disable unused mode by default */ + rpmsg_tty_driver->init_termios = tty_std_termios; + rpmsg_tty_driver->init_termios.c_lflag &= ~(ECHO | ICANON); + rpmsg_tty_driver->init_termios.c_oflag &= ~(OPOST | ONLCR); + + tty_set_operations(rpmsg_tty_driver, &rpmsg_tty_ops); + + ret = tty_register_driver(rpmsg_tty_driver); + if (ret < 0) { + pr_err("Couldn't install driver: %d\n", ret); + goto error_put; + } + + ret = register_rpmsg_driver(&rpmsg_tty_rpmsg_drv); + if (ret < 0) { + pr_err("Couldn't register driver: %d\n", ret); + goto error_unregister; + } + + return 0; + +error_unregister: + tty_unregister_driver(rpmsg_tty_driver); + +error_put: + tty_driver_kref_put(rpmsg_tty_driver); + + return ret; +} + +static void __exit rpmsg_tty_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_tty_rpmsg_drv); + tty_unregister_driver(rpmsg_tty_driver); + tty_driver_kref_put(rpmsg_tty_driver); + idr_destroy(&tty_idr); +} + +module_init(rpmsg_tty_init); +module_exit(rpmsg_tty_exit); + +MODULE_AUTHOR("Arnaud Pouliquen "); +MODULE_DESCRIPTION("remote processor messaging tty driver"); +MODULE_LICENSE("GPL v2"); \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/vrpmdv-monitoring-controler.cold b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/vrpmdv-monitoring-controler.cold new file mode 100644 index 0000000..12438fe --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/vrpmdv-monitoring-controler.cold @@ -0,0 +1,804 @@ +/* Copyright 2024 Markus Lehr + * + * + * This Software is owned by Markus Lehr. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// ################################################################################################## +/* + * This is a Linux kernel module/driver called "vrpmdv-monitoring-cmd " which holds the family ID + * and functions to handle the monitoring in a Generic Netlink in the kernel. + * "It registers a Netlink family called "vrpmdv-monitoring_cmd". + * + * + * You can find some more interesting documentation about Generic Netlink here: + * "Generic Netlink HOW-TO based on Jamal's original doc" https://lwn.net/Articles/208755/ + */ + + +#include +#include + +// basic definitions for kernel module development +#include +// definitions for generic netlink families, policies etc; +// transitive dependencies for basic netlink, sockets etc +#include +// required for locking inside the .dumpit callback demonstration +#include +#include + + +// data/vars/enums/properties that describes our protocol that we implement +// on top of generic netlink (like functions we want to trigger on the receiving side) +//#include "vrpmdv-monitoring-cmd.h" + + +/* ######################## CONVENIENT LOGGING MACROS ######################## */ +// (Re)definition of some convenient logging macros from . You can see the logging +// messages when printing the kernel log, e.g. with `$ sudo dmesg`. +// See https://elixir.bootlin.com/linux/latest/source/include/linux/printk.h + +// with this redefinition we can easily prefix all log messages from pr_* logging macros +#ifdef pr_fmt +#undef pr_fmt +#endif +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +/* ########################################################################### */ + + +#define MSG "hello Monitoring!" + +static int count = 100; +module_param(count, int, 0644); + +struct instance_data { + int rx_count; +}; + + +// std::chrono::seconds timeoutPeriod = 5; +// auto timePoint = std::chrono::system_clock::now() + timeoutPeriod; + + +/** ----- NETLINK Driver defintion ------------------*/ + + +/** + * Generic Netlink will create a Netlink family with this name. Kernel will asign + * a numeric ID and afterwards we can talk to the family with its ID. To get + * the ID we use Generic Netlink in the userland and pass the family name. + * + * Short for: Generic Netlink VRPMDV Monitoring gnl_foobar_mcmd + */ +#define FAMILY_NAME "gnl-vrpmdv-mcmd" + +/** + * These are the attributes that we want to share in gnl_foobar_xmpl. + * You can understand an attribute as a semantic type. This is + * the payload of Netlink messages. + * GNl: Generic Netlink + */ +enum GNL_VRPMDV_XMPL_ATTRIBUTE { + /** + * 0 is never used (=> UNSPEC), you can also see this in other family definitions in Linux code. + * We do the same, although I'm not sure, if this is really enforced by code. + */ + GNL_VRPMDV_MCMD_A_UNSPEC, + /** We expect a MSG to be a null-terminated C-string. */ + GNL_VRPMDV_MCMD_A_MSG, + /** Unused marker field to get the length/count of enum entries. No real attribute. */ + __GNL_VRPMDV_MCMD_A_MAX, +}; +/** + * Number of elements in `enum GNL_VRPMDV_MCMD_COMMAND`. + */ +#define GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN (__GNL_VRPMDV_MCMD_A_MAX) +/** + * The number of actual usable attributes in `enum GNL_VRPMDV_MCMD_ATTRIBUTE`. + * This is `GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN` - 1 because "UNSPEC" is never used. + */ +#define GNL_VRPMDV_MCMD_ATTRIBUTE_COUNT (GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN - 1) + +/** + * Enumeration of all commands (functions) that our custom protocol on top + * of generic netlink supports. This can be understood as the action that + * we want to trigger on the receiving side. + */ +enum GNL_VRPMDV_MCMD_COMMAND { + /** + * 0 is never used (=> UNSPEC), you can also see this in other family definitions in Linux code. + * We do the same, although I'm not sure, if this is really enforced by code. + */ + GNL_VRPMDV_MCMD_C_UNSPEC, + + // first real command is "1" (>0) + /** + * When this command is received, we expect the attribute `GNL_VRPMDV_MCMD_ATTRIBUTE::GNL_VRPMDV_MCMD_A_MSG` to + * be present in the Generic Netlink request message. The kernel reads the message from the packet and + * sent it to the copro. THe result will be return by creating a new Generic Netlink response message + * with an corresponding attribute/payload. + * + * This command/signaling mechanism is independent of the Netlink flag `NLM_F_ECHO (0x08)`. We use it as + * "echo specific data" instead of return a 1:1 copy of the package, which you could do with + * `NLM_F_ECHO (0x08)` for example. + */ + GNL_VRPMDV_MCMD_C_MSG, + + /** + * Provokes a NLMSG_ERR answer to this request as described in netlink manpage + * (https://man7.org/linux/man-pages/man7/netlink.7.html). + */ + GNL_VRPMDV_MCMD_C_REPLY_WITH_NLMSG_ERR, + + /** Unused marker field to get the length/count of enum entries. No real attribute. */ + __GNL_VRPMDV_MCMD_C_MAX, +}; +/** + * Number of elements in `enum GNL_VRPMDV_MCMD_COMMAND`. + */ +#define GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN (__GNL_VRPMDV_MCMD_C_MAX) +/** + * The number of actual usable commands in `enum GNL_FOOBAR_XMPL_COMMAND`. + * This is `GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN` - 1 because "UNSPEC" is never used. + */ +#define GNL_VRPMDV_MCMD_COMMAND_COUNT (GNL_VRPMDV_MCMD_COMMAND_ENUM_LEN - 1) + +#define NODATARECEIVED 0 +#define DATARECEIVED 1 + +/** + * Data structure required for our .doit callback handler to + * know about the progress of an ongoing cmd execution. + * See the cmd callback handler how it is used. + */ +// struct { +// // from +// /** +// * rpmsg wait for response from copro side. +// */ +// struct mutex sendMTX; +// /** +// * rpmsg wait for response from copro side. +// */ +// struct mutex receiveCV; +// /** +// * Wait Queue: if it is signaled we have received data from copro +// */ +// wait_queue_head_t receive_queue; +// /** +// * Waitflag: 0= no data received, 1 = data received +// */ +// int receive_queue_flag = NODATARECEIVED; +// /** +// * Condition vaiable signal we have received data from copro +// */ +// // std::condition_variable cv; +// // /** +// // * Number that describes how many packets we need to send until we are done +// // * during an ongoing dumpit process. 0 = done. +// // */ +// // int unsigned runs_to_go; +// // /** +// // * Number that describes how many packets per dump are sent in total. +// // * Constant per dump. +// // */ +// // int unsigned total_runs; + +// //the rpmsg device which sends the data to the copro +// struct rpmsg_device *rpdev; /* handle rpmsg device */ +// } cmd_cb_progress_data; + + +struct rpmsg_vrpmdv_mon_t{ + // from + /** + * rpmsg wait for response from copro side. + */ + struct mutex sendMTX; + /** + * rpmsg wait for response from copro side. + */ + struct mutex receiveCV; + /** + * Wait Queue: if it is signaled we have received data from copro + */ + wait_queue_head_t receive_queue; + /** + * Waitflag: 0= no data received, 1 = data received + */ + int receive_queue_flag; + //the rpmsg device which sends the data to the copro + struct rpmsg_device* rpdev; /* handle rpmsg device */ +}; + +struct rpmsg_vrpmdv_mon_t vrpmdv_mon; + + // struct mutex mutex; /* mutex to protect the ioctls */ + // struct miscdevice mdev; /* misc device ref */ + // struct rpmsg_device *rpdev; /* handle rpmsg device */ + // struct list_head buffer_list; /* buffer instances list */ + + + +// Documentation is on the implementation of this function. +int gnl_cb_vrpmdv_doit(struct sk_buff *sender_skb, struct genl_info *info); + +// Documentation is on the implementation of this function. +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info); + +/** + * The length of `struct genl_ops gnl_foobar_xmpl_ops[]`. Not necessarily + * the number of commands in `enum GNlFoobarXmplCommand`. It depends on your application logic. + * For example, you can use the same command multiple times and - dependent by flag - + * invoke a different callback handler. In our simple example we just use one .doit callback + * per operation/command. + */ +#define GNL_VRPMDV_OPS_LEN (GNL_VRPMDV_MCMD_COMMAND_COUNT) + +/** + * Array with all operations that our protocol on top of Generic Netlink + * supports. An operation is the glue between a command ("cmd" field in `struct genlmsghdr` of + * received Generic Netlink message) and the corresponding ".doit" callback function. + * See: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L148 + */ +struct genl_ops gnl_vrpmdv_mcmd_ops[GNL_VRPMDV_OPS_LEN] = { + { + /* The "cmd" field in `struct genlmsghdr` of received Generic Netlink message */ + .cmd = GNL_VRPMDV_MCMD_C_MSG, + /* TODO Use case ? */ + .flags = 0, + /* TODO Use case ? */ + .internal_flags = 0, + /* Callback handler when a request with the specified ".cmd" above is received. + * Always validates the payload except one set NO_STRICT_VALIDATION flag in ".validate" + * See: https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + * + * Quote from: https://lwn.net/Articles/208755 + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + * + */ + .doit = gnl_cb_vrpmdv_doit, + /* This callback is similar in use to the standard Netlink 'dumpit' callback. + * The 'dumpit' callback is invoked when a Generic Netlink message is received + * with the NLM_F_DUMP flag set. + * + * A dump can be understand as a "GET ALL DATA OF THE GIVEN ENTITY", i.e. + * the userland can receive as long as the .dumpit callback returns data. + * + * .dumpit is not mandatory, but either it or .doit must be provided, see + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L367 + * + * To be honest I don't know in what use case one should use .dumpit and why + * it is useful, because you can achieve the same also with .doit handlers. + * Anyway, this is just an example/tutorial. + * + * Quote from: https://lwn.net/Articles/208755 + * "The main difference between a 'dumpit' handler and a 'doit' handler is + * that a 'dumpit' handler does not allocate a message buffer for a response; + * a pre-allocated sk_buff is passed to the 'dumpit' handler as the first + * parameter. The 'dumpit' handler should fill the message buffer with the + * appropriate response message and return the size of the sk_buff, + * i.e. sk_buff->len, and the message buffer will automatically be sent to the + * Generic Netlink client that initiated the request. As long as the 'dumpit' + * handler returns a value greater than zero it will be called again with a + * newly allocated message buffer to fill, when the handler has no more data + * to send it should return zero; error conditions are indicated by returning + * a negative value. If necessary, state can be preserved in the + * netlink_callback parameter which is passed to the 'dumpit' handler; the + * netlink_callback parameter values will be preserved across handler calls + * for a single request." + * + * You can see the check for the NLM_F_DUMP-flag here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L780 + */ + .dumpit = NULL, //gnl_cb_echo_dumpit, + /* Start callback for dumps. Can be used to lock data structures. */ + .start = NULL, //gnl_cb_echo_dumpit_before, + /* Completion callback for dumps. Can be used for cleanup after a dump and releasing locks. */ + .done = NULL, //gnl_cb_echo_dumpit_before_after, + /* + 0 (= "validate strictly") or value `enum genl_validate_flags` + * see: https://elixir.bootlin.com/linux/v5.11/source/include/net/genetlink.h#L108 + */ + .validate = 0, + }, + { + .cmd = GNL_VRPMDV_MCMD_C_REPLY_WITH_NLMSG_ERR, + .flags = 0, + .internal_flags = 0, + .doit = gnl_cb_doit_reply_with_nlmsg_err, + // .dumpit is not required, only optional; application specific/dependent on your use case + // in a real application you probably have different .dumpit handlers per operation/command + .dumpit = NULL, + // in a real application you probably have different .start handlers per operation/command + .start = NULL, + // in a real application you probably have different .done handlers per operation/command + .done = NULL, + .validate = 0, + } +}; + + +/** + * Attribute policy: defines which attribute has which type (e.g int, char * etc). + * This get validated for each received Generic Netlink message, if not deactivated + * in `gnl_foobar_xmpl_ops[].validate`. + * See https://elixir.bootlin.com/linux/v5.11/source/net/netlink/genetlink.c#L717 + */ +static struct nla_policy gnl_vrpmdv_mcmd_policy[GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN] = { + // In case you are seeing this syntax for the first time (I also learned this just after a few years of + // coding with C myself): The following static array initiations are equivalent: + // `int a[2] = {1, 2}` <==> `int a[2] = {[0] => 1, [1] => 2}`. + + [GNL_VRPMDV_MCMD_A_UNSPEC] = {.type = NLA_UNSPEC}, + + // You can set this to NLA_U32 for testing and send an ECHO message from the userland + // It will fail in this case and you see a entry in the kernel log. + + // `enum GNL_FOOBAR_XMPL_ATTRIBUTE::GNL_FOOBAR_XMPL_A_MSG` is a null-terminated C-String + [GNL_VRPMDV_MCMD_A_MSG] = {.type = NLA_NUL_STRING}, +}; + + + +/** + * Definition of the Netlink family we want to register using Generic Netlink functionality + */ +static struct genl_family gnl_vrpmdv_mcmd_family = { + // automatically assign an id + .id = 0, + // we don't use custom additional header info / user specific header + .hdrsize = 0, + // The name of this family, used by userspace application to get the numeric ID + .name = FAMILY_NAME, + // family specific version number; can be used to evolve application over time (multiple versions) + .version = 1, + // delegates all incoming requests to callback functions + .ops = gnl_vrpmdv_mcmd_ops, + // length of array `gnl_foobar_xmpl_ops` + .n_ops = GNL_VRPMDV_OPS_LEN, + // attribute policy (for validation of messages). Enforced automatically, except ".validate" in + // corresponding ".ops"-field is set accordingly. + .policy = gnl_vrpmdv_mcmd_policy, + // Number of attributes / bounds check for policy (array length) + .maxattr = GNL_VRPMDV_MCMD_ATTRIBUTE_ENUM_LEN, + // Owning Kernel module of the Netlink family we register. + .module = THIS_MODULE, + + // Actually not necessary because this memory region would be zeroed anyway during module load, + // but this way one sees all possible options. + + // if your application must handle multiple netlink calls in parallel (where one should not block the next + // from starting), set this to true! otherwise all netlink calls are mutually exclusive + .parallel_ops = 0, + // set to true if the family can handle network namespaces and should be presented in all of them + .netnsok = 0, + // called before an operation's doit callback, it may do additional, common, filtering and return an error + .pre_doit = NULL, + // called after an operation's doit callback, it may undo operations done by pre_doit, for example release locks + .post_doit = NULL, +}; + + + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_VRPMDV_MCMD_C_MSG` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_vrpmdv_mcmd_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_vrpmdv_doit(struct sk_buff *sender_skb, struct genl_info *info) { + struct nlattr *na; + struct sk_buff *reply_skb; + int rc; + void *msg_head; + char *recv_msg; + + pr_info("%s() invoked\n", __func__); + + if (info == NULL) { + // should never happen + pr_err("An error occurred in %s():\n", __func__); + return -EINVAL; + } + + /* + * For each attribute there is an index in info->attrs which points to a nlattr structure + * in this structure the data is stored. + */ + na = info->attrs[GNL_VRPMDV_MCMD_A_MSG]; + + if (!na) { + pr_err("no info->attrs[%i]\n", GNL_VRPMDV_MCMD_A_MSG); + return -EINVAL; // we return here because we expect to recv a msg + } + + recv_msg = (char *) nla_data(na); + if (recv_msg == NULL) { + pr_err("error while receiving data\n"); + } else { + pr_info("received: '%s'\n", recv_msg); + } + + //aquire lock for cmd repmsg channel + // std::lock sendlk(cmd_cb_progress_data.sendmtx); + + //send the message to the copro over RPMSG + rc = rpmsg_send(vrpmdv_mon.rpdev->ept, recv_msg, strlen(recv_msg)); + if (rc) { + pr_err("rpmsg_send failed: %d\n", rc); + return rc; + } + + // struct rpmsg_vrpmdv_mon_t *drv = dev_get_drvdata(&rpdev->dev); + + { + pr_info("wait for response\n"); + // wait until receive_queue_flag=1 , that means we have received data from Copro + wait_event_interruptible(vrpmdv_mon.receive_queue, vrpmdv_mon.receive_queue_flag != 0 ); + pr_info("received data \n"); + //Copy data + vrpmdv_mon.receive_queue_flag = NODATARECEIVED; + + + + // std::unique_lock lk(cmd_cb_progress_data.receivemtx); + // if (myCondVar.wait_until(uLock, timePoint) == std::cv_status::timeout) + // { + // dev_err(&cmd_cb_progress_data.rpdev, "rpmsg_send failed, timeout: \n"); + // return -1: + // } + + pr_info("get response: '%s'\n", recv_msg); + } + + + + // Send a message back after we receive the reply from rpmsg channel + // --------------------- + + // Allocate some memory, since the size is not yet known use NLMSG_GOODSIZE + reply_skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (reply_skb == NULL) { + pr_err("An error occurred in %s():\n", __func__); + return -ENOMEM; + } + + // Create the message headers + + // Add header to netlink message; + // afterwards the buffer looks like this: + // ---------------------------------- + // | netlink header | + // | generic netlink header | + // | | + // ---------------------------------- + // msg_head = genlmsg_put(reply_skb, // buffer for netlink message: struct sk_buff * + // // According to my findings: this is not used for routing + // // This can be used in an application specific way to target + // // different endpoints within the same user application + // // but general rule: just put sender port id here + // info->snd_portid, // sending port (not process) id: int + // info->snd_seq + 1, // sequence number: int (might be used by receiver, but not mandatory) + // &gnl_vrpmdv_mcmd_family, // struct genl_family * + // 0, // flags for Netlink header: int; application specific and not mandatory + // // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header + // GNL_VRPMDV_MCMD_C_MSG + // ); + + msg_head = genlmsg_put_reply(reply_skb, // buffer for netlink message: struct sk_buff * + info, // info + &gnl_vrpmdv_mcmd_family, // struct genl_family * + 0, // flags for Netlink header: int; application specific and not mandatory + // The command/operation (u8) from `enum GNL_FOOBAR_XMPL_COMMAND` for Generic Netlink header + info->genlhdr->cmd + ); + + if (msg_head == NULL) { + rc = ENOMEM; + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + // Add a GNL_VRPMDV_MCMD_A_MSG attribute (actual value/payload to be sent) + // echo the value we just received + rc = nla_put_string(reply_skb, GNL_VRPMDV_MCMD_A_MSG, recv_msg); + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + + // Finalize the message: + // Corrects the netlink message header (length) to include the appended + // attributes. Only necessary if attributes have been added to the message. + genlmsg_end(reply_skb, msg_head); + + // Send the message back + rc = genlmsg_reply(reply_skb, info); + // same as genlmsg_unicast(genl_info_net(info), reply_skb, info->snd_portid) + // see https://elixir.bootlin.com/linux/v5.8.9/source/include/net/genetlink.h#L326 + + if (rc != 0) { + pr_err("An error occurred in %s():\n", __func__); + return -rc; + } + return 0; +} + +/** + * Regular ".doit"-callback function if a Generic Netlink with command `GNL_FOOBAR_XMPL_C_REPLY_WITH_NLMSG_ERR` is received. + * Please look into the comments where this is used as ".doit" callback above in + * `struct genl_ops gnl_foobar_xmpl_ops[]` for more information about ".doit" callbacks. +*/ +int gnl_cb_doit_reply_with_nlmsg_err(struct sk_buff *sender_skb, struct genl_info *info) { + pr_info("%s() invoked, a NLMSG_ERR response will be sent back\n", __func__); + + /* + * Generic Netlink is smart enough and sends a NLMSG_ERR reply automatically as reply + * Quote from https://lwn.net/Articles/208755/: + * "The 'doit' handler should do whatever processing is necessary and return + * zero on success, or a negative value on failure. Negative return values + * will cause a NLMSG_ERROR message to be sent while a zero return value will + * only cause a NLMSG_ERROR message to be sent if the request is received with + * the NLM_F_ACK flag set." + * + * You can find this in Linux code here: + * https://elixir.bootlin.com/linux/v5.11/source/net/netlink/af_netlink.c#L2499 + * + * One can find more information about NLMSG_ERROR responses and how to handle them + * in userland in the manpage: https://man7.org/linux/man-pages/man7/netlink.7.html + */ + return -EINVAL; +} + + + +/** ----- NETLINK Driver defintion ------------------*/ + + + + + +/** + * callback that is called after the copro send data + * we have to copy it in a buffer for the netlink and later send it back to the userland + * +*/ +static int vrpmdv_monitoring_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + int ret = 0; + struct instance_data *idata = dev_get_drvdata(&rpdev->dev); + + dev_info(&rpdev->dev, "incoming msg %d (src: 0x%x)\n", + ++idata->rx_count, src); + + print_hex_dump_debug(__func__, DUMP_PREFIX_NONE, 16, 1, data, len, + true); + + vrpmdv_mon.receive_queue_flag= DATARECEIVED; + wake_up_interruptible(&(vrpmdv_mon.receive_queue)); + + // /* samples should not live forever */ + // if (idata->rx_count >= count) { + // dev_info(&rpdev->dev, "goodbye!\n"); + // return 0; + // } + + /* send a new message now */ + // ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG)); + // if (ret) + // dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); + + return ret; +} + +static int vrpmdv_monitoring_probe(struct rpmsg_device *rpdev) +{ + int rc; + + // int ret; + // struct instance_data *idata; + + dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n", + rpdev->src, rpdev->dst); + + // idata = devm_kzalloc(&rpdev->dev, sizeof(*idata), GFP_KERNEL); + // if (!idata) + // return -ENOMEM; + + // dev_set_drvdata(&rpdev->dev, idata); + + // /* send a message to our remote processor to */ + // ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG)); + // if (ret) { + // dev_err(&rpdev->dev, "vrpmdv_monitoring_controler_send failed: %d\n", ret); + // return ret; + // } + + // return 0; + + // struct device *dev; + // dev = &rpdev->dev; + // struct rpmsg_vrpmdv_mon_t *rpmsg_vrpmdv_mon; + + // rpmsg_vrpmdv_mon = devm_kzalloc(dev, sizeof(*rpmsg_vrpmdv_mon), GFP_KERNEL); + // if (!rpmsg_vrpmdv_mon) + // return -ENOMEM; + + mutex_init(&(vrpmdv_mon.sendMTX)); + init_waitqueue_head (&(vrpmdv_mon.receive_queue)); + vrpmdv_mon.rpdev = rpdev; + + // dev_set_drvdata(&rpdev->dev, rpmsg_vrpmdv_mon); + pr_info("RPMSG CMD Device set.\n"); + + + /** NEU **/ + // if (cmd_cb_progress_data.rpdev == NULL) { + // cmd_cb_progress_data.rpdev = rpdev; + // pr_info("RPMSG CMD Device set.\n"); + // } + // else { + // pr_info("Error: RPMSG CMD Device already set. Don't set it twice\n"); + // } + + pr_info("Generic Netlink VRPMDV-Monitroring_Controler Module started.\n"); + + // Register family with its operations and policies + rc = genl_register_family(&gnl_vrpmdv_mcmd_family); + if (rc != 0) { + pr_err("FAILED: genl_register_family(): %i\n", rc); + pr_err("An error occurred while inserting the generic netlink example module\n"); + return -1; + } else { + pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + return 0; + + + +} + +static void vrpmdv_monitoring_remove(struct rpmsg_device *rpdev) +{ + int ret; + pr_info("Generic Netlink Example Module unloaded.\n"); + + // Unregister the family + ret = genl_unregister_family(&gnl_vrpmdv_mcmd_family); + if (ret != 0) { + pr_err("genl_unregister_family() failed: %i\n", ret); + return; + } else { + pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); + } + + mutex_destroy(&(vrpmdv_mon.sendMTX)); + wake_up_interruptible(&(vrpmdv_mon.receive_queue)); + + dev_info(&rpdev->dev, "vrpmdv-monitoring controler driver is removed\n"); + +} + +static struct rpmsg_device_id vrpmdv_monitoring_controler_id_table[] = { + { .name = "vrpmdv-monitoring-controler" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, vrpmdv_monitoring_controler_id_table); + +static struct rpmsg_driver vrpmdv_monitoring_controler = { + .drv.name = KBUILD_MODNAME, + .id_table = vrpmdv_monitoring_controler_id_table, + .probe = vrpmdv_monitoring_probe, + .callback = vrpmdv_monitoring_cb, + .remove = vrpmdv_monitoring_remove, +}; +module_rpmsg_driver(vrpmdv_monitoring_controler); + + +// static struct rpmsg_driver vrpmdv_monitoring_data = { +// .drv.name = KBUILD_MODNAME, +// .id_table = vrpmdv_monitoring_controler_id_table, +// .probe = vrpmdv_monitoring_probe, +// .callback = vrpmdv_monitoring_cb, +// .remove = vrpmdv_monitoring_remove, +// }; + +// module_rpmsg_driver(vrpmdv_monitoring_data); + +MODULE_DESCRIPTION("Remote processor messaging vrpmdv monitoring controler"); +MODULE_LICENSE("GPL v2"); + + + +// /** +// * Module/driver initializer. Called on module load/insertion. +// * +// * @return success (0) or error code. +// */ +// static int __init gnl_foobar_xmpl_module_init(void) { +// int rc; +// pr_info("Generic Netlink Example Module inserted.\n"); + +// // Register family with its operations and policies +// rc = genl_register_family(&gnl_foobar_xmpl_family); +// if (rc != 0) { +// pr_err("FAILED: genl_register_family(): %i\n", rc); +// pr_err("An error occurred while inserting the generic netlink example module\n"); +// return -1; +// } else { +// pr_info("successfully registered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); +// } + +// mutex_init(&dumpit_cb_progress_data.mtx); + +// return 0; +// } + +// /** +// * Module/driver uninitializer. Called on module unload/removal. +// * +// * @return success (0) or error code. +// */ +// static void __exit gnl_foobar_xmpl_module_exit(void) { +// int ret; +// pr_info("Generic Netlink Example Module unloaded.\n"); + +// // Unregister the family +// ret = genl_unregister_family(&gnl_foobar_xmpl_family); +// if (ret != 0) { +// pr_err("genl_unregister_family() failed: %i\n", ret); +// return; +// } else { +// pr_info("successfully unregistered custom Netlink family '" FAMILY_NAME "' using Generic Netlink.\n"); +// } + +// mutex_destroy(&dumpit_cb_progress_data.mtx); +// } + + +// ---- + +// static int __init rpmsg_sdb_drv_init(void) +// { +// int ret = 0; + +// /* Register rpmsg device */ +// ret = register_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); + +// if (ret) { +// pr_err("rpmsg_sdb(ERROR): Failed to register device\n"); +// return ret; +// } + +// pr_info("rpmsg_sdb: Init done\n"); + +// return ret; +// } + +// static void __exit rpmsg_sdb_drv_exit(void) +// { +// unregister_rpmsg_driver(&rpmsg_sdb_rmpsg_drv); +// pr_info("rpmsg_sdb: Exit\n"); +// } + +// module_init(rpmsg_sdb_drv_init); +// module_exit(rpmsg_sdb_drv_exit); diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/vrpmdv-monitoring-driver copy.bb.old b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/vrpmdv-monitoring-driver copy.bb.old new file mode 100644 index 0000000..6aeebb9 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/vrpmdv-monitoring-driver copy.bb.old @@ -0,0 +1,24 @@ +SUMMARY = "VRPMDV Monitoring Driver" +LICENSE = "CLOSED" +# LICENSE = "GPL-2.0-only" +# LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "file://Makefile \ + file://stm32_rpmsg_sdb.c \ + " +# file://75-rpmsg-sdb.rules \ +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. + +# do_install:append() { + # udev rules for rpmsg-sdb + # install -d ${D}${sysconfdir}/udev/rules.d/ + # install -m 0644 ${WORKDIR}/75-rpmsg-sdb.rules ${D}${sysconfdir}/udev/rules.d/75-rpmsg-sdb.rules +# } +#FILES:${PN} += "${sysconfdir}/udev/rules.d/" + +RPROVIDES:${PN} += "kernel-module-vrpmdv-monitoring-driver" \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/vrpmdv-monitoring-driver copy1.bb.old b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/vrpmdv-monitoring-driver copy1.bb.old new file mode 100644 index 0000000..2f245db --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/vrpmdv-monitoring-driver copy1.bb.old @@ -0,0 +1,26 @@ +SUMMARY = "VRPMDV Monitoring Driver" +LICENSE = "GPL-2.0-only" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "file://Makefile \ + file://vrpmdv-monitoring.c \ + file://75-vrpmdv-monitoring.rules \ + " + +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. + +do_install:append() { + # udev rules for vrpmdv-monitoring + install -d ${D}${sysconfdir}/udev/rules.d/ + install -m 0644 ${WORKDIR}/75-vrpmdv-monitoring.rules ${D}${sysconfdir}/udev/rules.d/75-vrpmdv-monitoring.rules +} + +FILES:${PN} += "${sysconfdir}/udev/rules.d/" + + +RPROVIDES:${PN} += "kernel-module-vrpmdv-monitoring" \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/vrpmdv-monitoring-driverv2.bb.old b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/vrpmdv-monitoring-driverv2.bb.old new file mode 100644 index 0000000..03bdb32 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/save/vrpmdv-monitoring-driverv2.bb.old @@ -0,0 +1,26 @@ +SUMMARY = "VRPMDV Monitoring Driver" +LICENSE = "GPL-2.0-only" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "file://Makefile \ + file://vrpmdv-monitoring.c \ + file://75-vrpmdv-monitoring.rules \ + " + +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. + +do_install:append() { + # udev rules for vrpmdv-monitoring + install -d ${D}${sysconfdir}/udev/rules.d/ + install -m 0644 ${WORKDIR}/75-vrpmdv-monitoring.rules ${D}${sysconfdir}/udev/rules.d/75-vrpmdv-monitoring.rules +} + +FILES:${PN} += "${sysconfdir}/udev/rules.d/" +FILES:${PN} += "${base_libdir}/modules/" + +RPROVIDES:${PN} += "vrpmdv-monitoring" diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/vrpmdv-monitoring-controler.bb b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/vrpmdv-monitoring-controler.bb new file mode 100644 index 0000000..adcf05e --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-monitoring-controler/vrpmdv-monitoring-controler.bb @@ -0,0 +1,17 @@ +SUMMARY = "VRPMDV Monitoring Controler" +LICENSE = "GPL-2.0-only" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" + +inherit module + +SRC_URI = "file://Makefile \ + file://vrpmdv-monitoring-controler.c \ + " + +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. + + +RPROVIDES:${PN} += "kernel-module-vrpmdv-monitoring-controler" diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/CMakeLists.txt b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/CMakeLists.txt new file mode 100644 index 0000000..f471735 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/CMakeLists.txt @@ -0,0 +1,59 @@ + +cmake_minimum_required(VERSION 3.5) +#set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +#SET(CMAKE_SYSTEM_PROCESSOR arm) +#SET(CMAKE_CROSSCOMPILING 1) + +project(RTService) + +# set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") +# set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +# set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") +# set(CMAKE_C_COMPILER_WORKS 1) + + +# set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) + +# SET(CMAKE_SYSTEM_PROCESSOR arm) +# SET(CMAKE_CROSSCOMPILING 1) + + +# set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +# set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +# set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + + + + set(GCC_COMPILE_FLAGS "-Wall -Wextra -Werror -Wsuggest-override -Wno-register -Wno-missing-braces -Wno-unknown-pragmas -fdiagnostics-show-option") + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COMPILE_FLAGS}") + +# Find python and Boost - both are required dependencies +find_package(Boost) + +set(source_files + + ${CMAKE_CURRENT_SOURCE_DIR}/src/RTService.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/monitoringTask/RTSMonitoringTask.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/utilities/RTSCoproHelper.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/RTSMonFrame.h + +) + +add_executable(${PROJECT_NAME} ${source_files}) + + +#file( GLOB LIB_SOURCES .src/*.cpp ) +#file( GLOB LIB_HEADERS lib/*.h ) + +# target_link_libraries(RTService +# PRIVATE +# Boost +#)cd +#target_include_directories(${PROJECT_NAME} PRIVATE ${RTSERVICEINCLUDE}) + +install(TARGETS RTService DESTINATION bin) \ No newline at end of file diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/.gitignore b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/.gitignore new file mode 100644 index 0000000..9595c5e --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/.gitignore @@ -0,0 +1,4 @@ +/CMakeCache.txt +/Makefile +/RTService +/cmake_install.cmake diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/.gitignore b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/.gitignore new file mode 100644 index 0000000..2f794c2 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/.gitignore @@ -0,0 +1,7 @@ +/CMakeDirectoryInformation.cmake +/CMakeOutput.log +/Makefile.cmake +/Makefile2 +/TargetDirectories.txt +/cmake.check_cache +/progress.marks diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/3.25.2/.gitignore b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/3.25.2/.gitignore new file mode 100644 index 0000000..1675f1a --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/3.25.2/.gitignore @@ -0,0 +1,5 @@ +/CMakeCCompiler.cmake +/CMakeCXXCompiler.cmake +/CMakeDetermineCompilerABI_C.bin +/CMakeDetermineCompilerABI_CXX.bin +/CMakeSystem.cmake diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/3.25.2/CompilerIdC/.gitignore b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/3.25.2/CompilerIdC/.gitignore new file mode 100644 index 0000000..4377608 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/3.25.2/CompilerIdC/.gitignore @@ -0,0 +1,2 @@ +/CMakeCCompilerId.c +/a.out diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/3.25.2/CompilerIdCXX/.gitignore b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/3.25.2/CompilerIdCXX/.gitignore new file mode 100644 index 0000000..44b2e04 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/3.25.2/CompilerIdCXX/.gitignore @@ -0,0 +1,2 @@ +/CMakeCXXCompilerId.cpp +/a.out diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/RTService.dir/.gitignore b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/RTService.dir/.gitignore new file mode 100644 index 0000000..9db10cb --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/RTService.dir/.gitignore @@ -0,0 +1,10 @@ +/DependInfo.cmake +/build.make +/cmake_clean.cmake +/compiler_depend.internal +/compiler_depend.make +/compiler_depend.ts +/depend.make +/flags.make +/link.txt +/progress.make diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/RTService.dir/src/.gitignore b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/RTService.dir/src/.gitignore new file mode 100644 index 0000000..b1cb1c6 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/RTService.dir/src/.gitignore @@ -0,0 +1,2 @@ +/RTService.cpp.o +/RTService.cpp.o.d diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/RTService.dir/src/monitoringTask/.gitignore b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/RTService.dir/src/monitoringTask/.gitignore new file mode 100644 index 0000000..70f3e6c --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/RTService.dir/src/monitoringTask/.gitignore @@ -0,0 +1,2 @@ +/RTSMonitoringTask.cpp.o +/RTSMonitoringTask.cpp.o.d diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/RTService.dir/src/utilities/.gitignore b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/RTService.dir/src/utilities/.gitignore new file mode 100644 index 0000000..236a537 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/_build/CMakeFiles/RTService.dir/src/utilities/.gitignore @@ -0,0 +1,2 @@ +/RTSCoproHelper.cpp.o +/RTSCoproHelper.cpp.o.d diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/include/RTSCoproHelper.h b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/include/RTSCoproHelper.h new file mode 100644 index 0000000..4b1de71 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/include/RTSCoproHelper.h @@ -0,0 +1,37 @@ +/* +*/ + +#include + + +class RTSCoproHelper +{ +private: + /* data */ + //lock Element + int mFdRpmsg[2] = {-1, -1}; + +public: + + RTSCoproHelper(/* args */); + ~RTSCoproHelper(); + + int Init(std::string fwPath, std::string fwName); + + int Copro_isFwRunning(void); + + int Copro_openTtyRpmsg(int ttyNb, int modeRaw); + int Copro_closeTtyRpmsg(int ttyNb); + int Copro_writeTtyRpmsg(int ttyNb, int len, char* pData); + int Copro_readTtyRpmsg(int ttyNb, int len, char* pData); + int Copro_stopFw(void); + int Copro_startFw(void); + +private: + + int Copro_getFwPath(char* pathStr); + int Copro_setFwPath(const char* pathStr); + int Copro_getFwName(char* pathStr); + int Copro_setFwName(const char* nameStr); + +}; diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/include/RTSMonFrame.h b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/include/RTSMonFrame.h new file mode 100644 index 0000000..6862920 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/include/RTSMonFrame.h @@ -0,0 +1,72 @@ +/* +*/ + +#include +#include + + +class RTSMonFrame +{ +private: + /* data */ + int id; + std::string name; + int samplerate; + int sampleperiod; + int downtime; + std::string status; + std::list items; + +public: + + RTSMonFrame(int id, std::string name, int samplerate, int sampleperiod, int downtime, std::string status) { + this->id = id; + this->name = name; + this->samplerate = samplerate; + this->sampleperiod = sampleperiod; + this->downtime = downtime; + this->status = status; + } + + RTSMonFrame(int id) { + this->id = id; + } + + RTSMonFrame(RTSMonFrame& rtsMonFrameCopy) { + this->id = rtsMonFrameCopy.GetId(); + } + + ~RTSMonFrame(); + + void Add(std::string item){ + this->items.push_back(item); + } + int GetId() { + return this->id; + } + + std::string GetName() { + return this->name; + } + + int GetSampleRate() { + return this->samplerate; + } + + int GetSamplePeriod(){ + return this->sampleperiod; + } + + int GetDownTime(){ + return this->downtime; + } + + std::string Gettatus() { + return this->status; + } + + std::list& GetItems() { + return this->items; + } + +}; diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/include/RTSMonitoringTask.h b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/include/RTSMonitoringTask.h new file mode 100644 index 0000000..0eb8a67 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/include/RTSMonitoringTask.h @@ -0,0 +1,42 @@ +/* +*/ +#include "RTSCoproHelper.h" +#include "RTSMonFrame.h" +#include +#include +#include + + + + +class RTSMonitoringTask +{ +private: + /* data */ + //lock Element + + /* The file descriptor used to manage our TTY over RPMSG */ + int mFdRpmsg[2] = {-1, -1}; + RTSCoproHelper coproHelper; + + std::map rtsMonFrames; + + pthread_t monThread; + +public: + + RTSMonitoringTask(/* args */); + ~RTSMonitoringTask(); + + //static RTSMonitoringTask& Instance(); + bool Init(); + bool CreateMonitoring(int id, std::string name, int samplerate, int sampleperiod, int downtime, std::string status); + bool LoadFW(); + bool Start(); + bool Stop(); + +private: + static void* Run(void *obj); + bool Load(); + +}; diff --git a/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/include/json.hpp b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/include/json.hpp new file mode 100644 index 0000000..a858728 --- /dev/null +++ b/recipes-vrpmdv/recipes-base/vrpmdv-rtservice/files/include/json.hpp @@ -0,0 +1,24766 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + +/****************************************************************************\ + * Note on documentation: The source files contain links to the online * + * documentation of the public API at https://json.nlohmann.me. This URL * + * contains the most recent documentation and should also be applicable to * + * previous versions; documentation for deprecated functions is not * + * removed, but marked deprecated. See "Generate documentation" section in * + * file docs/README.md. * +\****************************************************************************/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#ifndef JSON_NO_IO + #include // istream, ostream +#endif // JSON_NO_IO +#include // random_access_iterator_tag +#include // unique_ptr +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// This file contains all macro definitions affecting or depending on the ABI + +#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK + #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3 + #warning "Already included a different version of the library!" + #endif + #endif +#endif + +#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum) + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + +#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 +#endif + +#if JSON_DIAGNOSTICS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS +#endif + +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp +#else + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION + #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 +#endif + +// Construct the namespace ABI tags component +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) + +#define NLOHMANN_JSON_ABI_TAGS \ + NLOHMANN_JSON_ABI_TAGS_CONCAT( \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) + +// Construct the namespace version component +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ + _v ## major ## _ ## minor ## _ ## patch +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) + +#if NLOHMANN_JSON_NAMESPACE_NO_VERSION +#define NLOHMANN_JSON_NAMESPACE_VERSION +#else +#define NLOHMANN_JSON_NAMESPACE_VERSION \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ + NLOHMANN_JSON_VERSION_MINOR, \ + NLOHMANN_JSON_VERSION_PATCH) +#endif + +// Combine namespace components +#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b +#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ + NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) + +#ifndef NLOHMANN_JSON_NAMESPACE +#define NLOHMANN_JSON_NAMESPACE \ + nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN +#define NLOHMANN_JSON_NAMESPACE_BEGIN \ + namespace nlohmann \ + { \ + inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) \ + { +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_END +#define NLOHMANN_JSON_NAMESPACE_END \ + } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ + } // namespace nlohmann +#endif + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // nullptr_t +#include // exception +#if JSON_DIAGNOSTICS + #include // accumulate +#endif +#include // runtime_error +#include // to_string +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // uint8_t +#include // string + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // declval, pair +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +// https://en.cppreference.com/w/cpp/experimental/is_detected +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + + +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson +// SPDX-License-Identifier: MIT + +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 15 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(JSON_HEDLEY_MSVC_VERSION) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #undef JSON_HEDLEY_INTEL_CL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) + #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #undef JSON_HEDLEY_MCST_LCC_VERSION +#endif +#if defined(__LCC__) && defined(__LCC_MINOR__) + #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) + #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_CRAY_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) && \ + !defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if \ + defined(__has_attribute) && \ + ( \ + (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ + ) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#else + #define JSON_HEDLEY_FLAGS +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if \ + (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + + +// This file contains all internal macro definitions (except those affecting ABI) +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// #include + + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif + +#ifdef __has_include + #if __has_include() + #include + #endif +#endif + +#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) + #ifdef JSON_HAS_CPP_17 + #if defined(__cpp_lib_filesystem) + #define JSON_HAS_FILESYSTEM 1 + #elif defined(__cpp_lib_experimental_filesystem) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif !defined(__has_include) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #endif + + // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ + #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__clang_major__) && __clang_major__ < 7 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support + #if defined(_MSC_VER) && _MSC_VER < 1914 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before iOS 13 + #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before macOS Catalina + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + #endif +#endif + +#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_FILESYSTEM + #define JSON_HAS_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_THREE_WAY_COMPARISON + #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ + && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L + #define JSON_HAS_THREE_WAY_COMPARISON 1 + #else + #define JSON_HAS_THREE_WAY_COMPARISON 0 + #endif +#endif + +#ifndef JSON_HAS_RANGES + // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error + #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 + #define JSON_HAS_RANGES 0 + #elif defined(__cpp_lib_ranges) + #define JSON_HAS_RANGES 1 + #else + #define JSON_HAS_RANGES 0 + #endif +#endif + +#ifndef JSON_HAS_STATIC_RTTI + #if !defined(_HAS_STATIC_RTTI) || _HAS_STATIC_RTTI != 0 + #define JSON_HAS_STATIC_RTTI 1 + #else + #define JSON_HAS_STATIC_RTTI 0 + #endif +#endif + +#ifdef JSON_HAS_CPP_17 + #define JSON_INLINE_VARIABLE inline +#else + #define JSON_INLINE_VARIABLE +#endif + +#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) + #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else + #define JSON_NO_UNIQUE_ADDRESS +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +// allow disabling exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow overriding assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +// allow to access some private functions (needed by the test suite) +#if defined(JSON_TESTS_PRIVATE) + #define JSON_PRIVATE_UNLESS_TESTED public +#else + #define JSON_PRIVATE_UNLESS_TESTED private +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer, \ + class BinaryType, \ + class CustomBaseClass> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +// inspired from https://stackoverflow.com/a/26745591 +// allows to call any std function as if (e.g. with begin): +// using std::begin; begin(x); +// +// it allows using the detected idiom to retrieve the return type +// of such an expression +#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } /* namespace detail2 */ \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + +#ifndef JSON_DISABLE_ENUM_SERIALIZATION + #define JSON_DISABLE_ENUM_SERIALIZATION 0 +#endif + +#ifndef JSON_USE_GLOBAL_UDLS + #define JSON_USE_GLOBAL_UDLS 1 +#endif + +#if JSON_HAS_THREE_WAY_COMPARISON + #include // partial_ordering +#endif + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +#if JSON_HAS_THREE_WAY_COMPARISON + inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD* +#else + inline bool operator<(const value_t lhs, const value_t rhs) noexcept +#endif +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); +#if JSON_HAS_THREE_WAY_COMPARISON + if (l_index < order.size() && r_index < order.size()) + { + return order[l_index] <=> order[r_index]; // *NOPAD* + } + return std::partial_ordering::unordered; +#else + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +#endif +} + +// GCC selects the built-in operator< over an operator rewritten from +// a user-defined spaceship operator +// Clang, MSVC, and ICC select the rewritten candidate +// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200) +#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__) +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + return std::is_lt(lhs <=> rhs); // *NOPAD* +} +#endif + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +template +inline void replace_substring(StringType& s, const StringType& f, + const StringType& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != StringType::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +template +inline StringType escape(StringType s) +{ + replace_substring(s, StringType{"~"}, StringType{"~0"}); + replace_substring(s, StringType{"/"}, StringType{"~1"}); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +template +static void unescape(StringType& s) +{ + replace_substring(s, StringType{"~1"}, StringType{"/"}); + replace_substring(s, StringType{"~0"}, StringType{"~"}); +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // size_t + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2018 The Abseil Authors +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +using uncvref_t = typename std::remove_cv::type>::type; + +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal +{ + +template +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template +struct Extend, SeqSize, 0> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; +}; + +template +struct Extend, SeqSize, 1> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; +}; + +// Recursion helper for 'make_integer_sequence'. +// 'Gen::type' is an alias for 'integer_sequence'. +template +struct Gen +{ + using type = + typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; +}; + +template +struct Gen +{ + using type = integer_sequence; +}; + +} // namespace utility_internal + +// Compile-time sequences of integers + +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template +using make_integer_sequence = typename utility_internal::Gen::type; + +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template +using make_index_sequence = make_integer_sequence; + +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template +using index_sequence_for = make_index_sequence; + +//// END OF CODE FROM GOOGLE ABSEIL + +#endif + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static JSON_INLINE_VARIABLE constexpr T value{}; +}; + +#ifndef JSON_HAS_CPP_17 + template + constexpr T static_const::value; +#endif + +template +inline constexpr std::array make_array(Args&& ... args) +{ + return std::array {{static_cast(std::forward(args))...}}; +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval +#include // tuple +#include // char_traits + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // random_access_iterator_tag + +// #include + +// #include + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); + +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); + +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ + #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + #include // int64_t, uint64_t + #include // map + #include // allocator + #include // string + #include // vector + + // #include + + + /*! + @brief namespace for Niels Lohmann + @see https://github.com/nlohmann + @since version 1.0.0 + */ + NLOHMANN_JSON_NAMESPACE_BEGIN + + /*! + @brief default JSONSerializer template argument + + This serializer ignores the template arguments and uses ADL + ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) + for serialization. + */ + template + struct adl_serializer; + + /// a class to store JSON values + /// @sa https://json.nlohmann.me/api/basic_json/ + template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector, // cppcheck-suppress syntaxError + class CustomBaseClass = void> + class basic_json; + + /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document + /// @sa https://json.nlohmann.me/api/json_pointer/ + template + class json_pointer; + + /*! + @brief default specialization + @sa https://json.nlohmann.me/api/json/ + */ + using json = basic_json<>; + + /// @brief a minimal map-like container that preserves insertion order + /// @sa https://json.nlohmann.me/api/ordered_map/ + template + struct ordered_map; + + /// @brief specialization that maintains the insertion order of object keys + /// @sa https://json.nlohmann.me/api/ordered_json/ + using ordered_json = basic_json; + + NLOHMANN_JSON_NAMESPACE_END + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + +NLOHMANN_JSON_NAMESPACE_BEGIN +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ + +///////////// +// helpers // +///////////// + +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +// used by exceptions create() member functions +// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t +// false_type otherwise +template +struct is_basic_json_context : + std::integral_constant < bool, + is_basic_json::type>::type>::value + || std::is_same::value > +{}; + +////////////////////// +// json_ref helpers // +////////////////////// + +template +class json_ref; + +template +struct is_json_ref : std::false_type {}; + +template +struct is_json_ref> : std::true_type {}; + +////////////////////////// +// aliases for detected // +////////////////////////// + +template +using mapped_type_t = typename T::mapped_type; + +template +using key_type_t = typename T::key_type; + +template +using value_type_t = typename T::value_type; + +template +using difference_type_t = typename T::difference_type; + +template +using pointer_t = typename T::pointer; + +template +using reference_t = typename T::reference; + +template +using iterator_category_t = typename T::iterator_category; + +template +using to_json_function = decltype(T::to_json(std::declval()...)); + +template +using from_json_function = decltype(T::from_json(std::declval()...)); + +template +using get_template_function = decltype(std::declval().template get()); + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json : std::false_type {}; + +// trait checking if j.get is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template +struct is_getable +{ + static constexpr bool value = is_detected::value; +}; + +template +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + +template +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +template +using detect_key_compare = typename T::key_compare; + +template +struct has_key_compare : std::integral_constant::value> {}; + +// obtains the actual object key comparator +template +struct actual_object_comparator +{ + using object_t = typename BasicJsonType::object_t; + using object_comparator_t = typename BasicJsonType::default_object_comparator_t; + using type = typename std::conditional < has_key_compare::value, + typename object_t::key_compare, object_comparator_t>::type; +}; + +template +using actual_object_comparator_t = typename actual_object_comparator::type; + +///////////////// +// char_traits // +///////////////// + +// Primary template of char_traits calls std char_traits +template +struct char_traits : std::char_traits +{}; + +// Explicitly define char traits for unsigned char since it is not standard +template<> +struct char_traits : std::char_traits +{ + using char_type = unsigned char; + using int_type = uint64_t; + + // Redefine to_int_type function + static int_type to_int_type(char_type c) noexcept + { + return static_cast(c); + } + + static char_type to_char_type(int_type i) noexcept + { + return static_cast(i); + } + + static constexpr int_type eof() noexcept + { + return static_cast(EOF); + } +}; + +// Explicitly define char traits for signed char since it is not standard +template<> +struct char_traits : std::char_traits +{ + using char_type = signed char; + using int_type = uint64_t; + + // Redefine to_int_type function + static int_type to_int_type(char_type c) noexcept + { + return static_cast(c); + } + + static char_type to_char_type(int_type i) noexcept + { + return static_cast(i); + } + + static constexpr int_type eof() noexcept + { + return static_cast(EOF); + } +}; + +/////////////////// +// is_ functions // +/////////////////// + +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B { }; +template +struct conjunction +: std::conditional(B::value), conjunction, B>::type {}; + +// https://en.cppreference.com/w/cpp/types/negation +template struct negation : std::integral_constant < bool, !B::value > { }; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +template +struct is_default_constructible : std::is_default_constructible {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_constructible : std::is_constructible {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_iterator_traits : std::false_type {}; + +template +struct is_iterator_traits> +{ + private: + using traits = iterator_traits; + + public: + static constexpr auto value = + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value; +}; + +template +struct is_range +{ + private: + using t_ref = typename std::add_lvalue_reference::type; + + using iterator = detected_t; + using sentinel = detected_t; + + // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator + // and https://en.cppreference.com/w/cpp/iterator/sentinel_for + // but reimplementing these would be too much work, as a lot of other concepts are used underneath + static constexpr auto is_iterator_begin = + is_iterator_traits>::value; + + public: + static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; +}; + +template +using iterator_t = enable_if_t::value, result_of_begin())>>; + +template +using range_value_t = value_type_t>>; + +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + is_constructible::value && + is_constructible::value; +}; + +template +struct is_compatible_object_type + : is_compatible_object_type_impl {}; + +template +struct is_constructible_object_type_impl : std::false_type {}; + +template +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (is_default_constructible::value && + (std::is_move_assignable::value || + std::is_copy_assignable::value) && + (is_constructible::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template +struct is_constructible_object_type + : is_constructible_object_type_impl {}; + +template +struct is_compatible_string_type +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_constructible_string_type +{ + // launder type through decltype() to fix compilation failure on ICPC +#ifdef __INTEL_COMPILER + using laundered_type = decltype(std::declval()); +#else + using laundered_type = ConstructibleStringType; +#endif + + static constexpr auto value = + conjunction < + is_constructible, + is_detected_exact>::value; +}; + +template +struct is_compatible_array_type_impl : std::false_type {}; + +template +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < + is_detected::value&& + is_iterator_traits>>::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 + !std::is_same>::value >> +{ + static constexpr bool value = + is_constructible>::value; +}; + +template +struct is_compatible_array_type + : is_compatible_array_type_impl {}; + +template +struct is_constructible_array_type_impl : std::false_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t::value >> + : std::true_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same::value&& + !is_compatible_string_type::value&& + is_default_constructible::value&& +(std::is_move_assignable::value || + std::is_copy_assignable::value)&& +is_detected::value&& +is_iterator_traits>>::value&& +is_detected::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 +!std::is_same>::value&& + is_complete_type < + detected_t>::value >> +{ + using value_type = range_value_t; + + static constexpr bool value = + std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; +}; + +template +struct is_constructible_array_type + : is_constructible_array_type_impl {}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral::value&& + std::is_integral::value&& + !std::is_same::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + is_constructible::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type + : is_compatible_integer_type_impl {}; + +template +struct is_compatible_type_impl: std::false_type {}; + +template +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t::value >> +{ + static constexpr bool value = + has_to_json::value; +}; + +template +struct is_compatible_type + : is_compatible_type_impl {}; + +template +struct is_constructible_tuple : std::false_type {}; + +template +struct is_constructible_tuple> : conjunction...> {}; + +template +struct is_json_iterator_of : std::false_type {}; + +template +struct is_json_iterator_of : std::true_type {}; + +template +struct is_json_iterator_of : std::true_type +{}; + +// checks if a given type T is a template specialization of Primary +template