/**************************************************************************** * drivers/misc/rpmsgblk.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rpmsgblk.h" /**************************************************************************** * Private Types ****************************************************************************/ struct rpmsgblk_s { struct block_operations blk; /* Rpmsg-Block device operation */ struct rpmsg_endpoint ept; /* Rpmsg endpoint */ FAR const char *remotecpu; /* The server cpu name */ FAR const char *remotepath; /* The device path in the server cpu */ sem_t wait; /* Wait sem, used for preventing any * opreation until the connection * between two cpu established. */ mutex_t lock; /* Lock for thread-safe */ struct geometry geo; /* block geomerty */ int refs; /* refence count */ }; /* Rpmsg device cookie used to handle the response from the remote cpu */ struct rpmsgblk_cookie_s { sem_t sem; /* Semaphore used fo rpmsg */ int result; /* The return value of the remote call */ FAR void *data; /* The return data buffer of the remote call */ }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* The block operation functions */ static int rpmsgblk_open(FAR struct inode *inode); static int rpmsgblk_close(FAR struct inode *inode); static ssize_t rpmsgblk_read(FAR struct inode *inode, FAR unsigned char *buffer, blkcnt_t start_sector, unsigned int nsectors); static ssize_t rpmsgblk_write(FAR struct inode *inode, FAR const unsigned char *buffer, blkcnt_t start_sector, unsigned int nsectors); static int rpmsgblk_geometry(FAR struct inode *inode, FAR struct geometry *geometry); static ssize_t rpmsgblk_ioctl_arglen(int cmd); static int rpmsgblk_ioctl(FAR struct inode *inode, int cmd, unsigned long arg); #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS static int rpmsgblk_unlink(FAR struct inode *inode); #endif /* Functions for sending data to the remote cpu */ static int rpmsgblk_send_recv(FAR struct rpmsgblk_s *priv, uint32_t command, bool copy, FAR struct rpmsgblk_header_s *msg, int len, FAR void *data); static FAR void *rpmsgblk_get_tx_payload_buffer(FAR struct rpmsgblk_s *priv, FAR uint32_t *len); /* Functions handle the responses from the remote cpu */ static int rpmsgblk_default_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgblk_read_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgblk_geometry_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgblk_ioctl_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); /* Functions for creating communication with remote cpu */ static void rpmsgblk_device_created(struct rpmsg_device *rdev, FAR void *priv_); static void rpmsgblk_device_destroy(struct rpmsg_device *rdev, FAR void *priv_); static int rpmsgblk_ept_cb(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static void rpmsgblk_ns_bound(struct rpmsg_endpoint *ept); /**************************************************************************** * Private Data ****************************************************************************/ /* Rpmsg device response handler table */ static const rpmsg_ept_cb g_rpmsgblk_handler[] = { [RPMSGBLK_OPEN] = rpmsgblk_default_handler, [RPMSGBLK_CLOSE] = rpmsgblk_default_handler, [RPMSGBLK_READ] = rpmsgblk_read_handler, [RPMSGBLK_WRITE] = rpmsgblk_default_handler, [RPMSGBLK_GEOMETRY] = rpmsgblk_geometry_handler, [RPMSGBLK_IOCTL] = rpmsgblk_ioctl_handler, }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: rpmsgblk_open * * Description: * Rpmsg-blk open operation * * Parameters: * inode * * Returned Values: * OK on success; A negated errno value is returned on any failure. * ****************************************************************************/ static int rpmsgblk_open(FAR struct inode *inode) { FAR struct rpmsgblk_s *priv = inode->i_private; struct rpmsgblk_open_s msg; int ret; /* Sanity checks */ DEBUGASSERT(priv != NULL); ret = nxmutex_lock(&priv->lock); if (ret < 0) { ferr("lock failed, ret=%d\n", ret); return ret; } /* Check if this is the first time that the driver has been opened. */ if (priv->refs++ == 0) { /* Try to open the block device in the remote cpu */ ret = rpmsgblk_send_recv(priv, RPMSGBLK_OPEN, true, &msg.header, sizeof(msg), NULL); if (ret < 0) { ferr("open failed, ret=%d\n", ret); priv->refs--; } } nxmutex_unlock(&priv->lock); return ret; } /**************************************************************************** * Name: rpmsgblk_close * * Description: * Rpmsg-blk close operation * * Parameters: * inode * * Returned Values: * OK on success; A negated errno value is returned on any failure. * ****************************************************************************/ static int rpmsgblk_close(FAR struct inode *inode) { FAR struct rpmsgblk_s *priv = inode->i_private; struct rpmsgblk_close_s msg; int ret; ret = nxmutex_lock(&priv->lock); if (ret < 0) { ferr("lock failed, ret=%d\n", ret); return ret; } /* There are no more references to the port */ if (--priv->refs == 0) { ret = rpmsgblk_send_recv(priv, RPMSGBLK_CLOSE, true, &msg.header, sizeof(msg), NULL); if (ret < 0) { ferr("close failed, ret=%d\n", ret); priv->refs++; } } nxmutex_unlock(&priv->lock); return ret; } /**************************************************************************** * Name: rpmsgblk_read * * Description: * Rpmsg-blk read operation * * Parameters: * dev - the blk device * offset - read offset address in the blk device * nbytes - read number in bytes * buffer - read buffer * * Returned Values: * The positive non-zero number of bytes read on success, 0 on if an * end-of-file condition, or a negated errno value on any failure. * ****************************************************************************/ static ssize_t rpmsgblk_read(FAR struct inode *inode, FAR unsigned char *buffer, blkcnt_t start_sector, unsigned int nsectors) { FAR struct rpmsgblk_s *priv = inode->i_private; struct rpmsgblk_read_s msg; struct iovec iov; int ret; if (buffer == NULL) { return -EINVAL; } /* Sanity checks */ DEBUGASSERT(priv != NULL); ret = rpmsgblk_geometry(inode, &priv->geo); if (ret < 0) { ferr("Get geometry failed, ret=%d\n", ret); return ret; } /* In block read, iov_len represent the received block number */ iov.iov_base = buffer; iov.iov_len = 0; msg.startsector = start_sector; msg.nsectors = nsectors; msg.sectorsize = priv->geo.geo_sectorsize; ret = rpmsgblk_send_recv(priv, RPMSGBLK_READ, true, &msg.header, sizeof(msg) - 1, &iov); return ret < 0 ? ret : iov.iov_len; } /**************************************************************************** * Name: rpmsgblk_write * * Description: * Rpmsg-blk write operation * * Parameters: * dev - the blk device * offset - write offset address in the blk device * nbytes - write number in bytes * buffer - write buffer * * Returned Values: * On success, the number of bytes written are returned (zero indicates * nothing was written). On any failure, a negated errno value is returned * (see comments withwrite() for a description of the appropriate errno * values). * ****************************************************************************/ static ssize_t rpmsgblk_write(FAR struct inode *inode, FAR const unsigned char *buffer, blkcnt_t start_sector, unsigned int nsectors) { FAR struct rpmsgblk_s *priv = inode->i_private; FAR struct rpmsgblk_write_s *msg; struct rpmsgblk_cookie_s cookie; uint32_t sectorsize; uint32_t space; size_t written = 0; int ret; if (buffer == NULL) { return -EINVAL; } /* Sanity checks */ DEBUGASSERT(priv != NULL); ret = rpmsgblk_geometry(inode, &priv->geo); if (ret < 0) { ferr("Get geometry failed, ret=%d\n", ret); return ret; } /* Perform the rpmsg write */ memset(&cookie, 0, sizeof(cookie)); nxsem_init(&cookie.sem, 0, 0); sectorsize = priv->geo.geo_sectorsize; while (written < nsectors) { msg = rpmsgblk_get_tx_payload_buffer(priv, &space); if (msg == NULL) { ret = -ENOMEM; goto out; } DEBUGASSERT(sizeof(*msg) - 1 + sectorsize <= space); msg->nsectors = (space - sizeof(*msg) + 1) / sectorsize; if (msg->nsectors >= nsectors - written) { /* Send complete, set cookie is valid, need ack */ msg->nsectors = nsectors - written; msg->header.cookie = (uintptr_t)&cookie; } else { /* Not send complete, set cookie invalid, do not need ack */ msg->header.cookie = 0; } msg->header.command = RPMSGBLK_WRITE; msg->header.result = -ENXIO; msg->startsector = start_sector; msg->sectorsize = sectorsize; memcpy(msg->buf, buffer, msg->nsectors * sectorsize); buffer += msg->nsectors * sectorsize; start_sector += msg->nsectors; written += msg->nsectors; ret = rpmsg_send_nocopy(&priv->ept, msg, sizeof(*msg) - 1 + msg->nsectors * sectorsize); if (ret < 0) { goto out; } } ret = rpmsg_wait(&priv->ept, &cookie.sem); if (ret < 0) { goto out; } ret = cookie.result; out: nxsem_destroy(&cookie.sem); return ret < 0 ? ret : nsectors; } /**************************************************************************** * Name: rpmsgblk_geometry * * Description: * Rpmsg-blk geometry operation * * Parameters: * inode - the blk device inode * geometry - pointer to the application geomoetry struct * * Returned Values: * On success, the number of bytes written are returned (zero indicates * nothing was written). On any failure, a negated errno value is returned * (see comments withwrite() for a description of the appropriate errno * values). * ****************************************************************************/ static int rpmsgblk_geometry(FAR struct inode *inode, FAR struct geometry *geometry) { FAR struct rpmsgblk_s *priv = inode->i_private; struct rpmsgblk_geometry_s msg; int ret; /* Sanity checks */ DEBUGASSERT(priv != NULL); ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* Return the perviously got geometry */ if (priv->geo.geo_sectorsize != 0) { memcpy(geometry, &priv->geo, sizeof(*geometry)); goto out; } ret = rpmsgblk_send_recv(priv, RPMSGBLK_GEOMETRY, true, &msg.header, sizeof(msg), NULL); if (ret >= 0) { DEBUGASSERT(msg.nsectors == (blkcnt_t)msg.nsectors); DEBUGASSERT(strlen(msg.model) <= RPMSGBLK_NAME_MAX); geometry->geo_available = msg.available; geometry->geo_mediachanged = msg.mediachanged; geometry->geo_writeenabled = msg.writeenabled; geometry->geo_nsectors = msg.nsectors; geometry->geo_sectorsize = msg.sectorsize; strlcpy(geometry->geo_model, msg.model, sizeof(geometry->geo_model)); memcpy(&priv->geo, geometry, sizeof(priv->geo)); } out: nxmutex_unlock(&priv->lock); return ret; } /**************************************************************************** * Name: rpmsgblk_ioctl_arglen * * Description: * Get rpmsg blk ioctl argument length according to the command * * Parameters: * cmd - the ioctl command * * Returned Values: * 0 - ioctl command not support * positive - the argument length * ****************************************************************************/ static ssize_t rpmsgblk_ioctl_arglen(int cmd) { switch (cmd) { case BIOC_XIPBASE: return sizeof(uintptr_t); case BIOC_PROBE: case BIOC_EJECT: case BIOC_LLFORMAT: case BIOC_GETFORMAT: case BIOC_ALLOCSECT: case BIOC_FREESECT: case BIOC_FLUSH: return 0; case BIOC_READSECT: case BIOC_WRITESECT: return sizeof(struct smart_read_write_s); case BIOC_GETPROCFSD: return sizeof(struct mtd_smart_procfs_data_s); case BIOC_DEBUGCMD: return sizeof(struct mtd_smart_debug_data_s); case BIOC_PARTINFO: return sizeof(struct partition_info_s); case BIOC_BLKSSZGET: return sizeof(blksize_t); default: return -ENOTTY; } } /**************************************************************************** * Name: rpmsgblk_mmc_cmd_ioctl * * Description: * If args include a usrspace buffer pointer, then the content of this * pointer should also be copied into share memory and set after args. * ****************************************************************************/ static int rpmsgblk_mmc_cmd_ioctl(FAR struct inode *inode, unsigned long arg) { FAR struct rpmsgblk_s *priv = inode->i_private; FAR struct mmc_ioc_cmd *ioc = (FAR struct mmc_ioc_cmd *)(uintptr_t)arg; FAR struct rpmsgblk_ioctl_s *msg; uint32_t space; size_t arglen; size_t msglen; arglen = sizeof(struct mmc_ioc_cmd); if (ioc->write_flag) { arglen += ioc->blksz * ioc->blocks; } msglen = sizeof(*msg) + arglen - 1; msg = rpmsgblk_get_tx_payload_buffer(priv, &space); if (msg == NULL) { return -ENOMEM; } DEBUGASSERT(space >= msglen); msg->request = MMC_IOC_CMD; msg->arg = arg; msg->arglen = arglen; memcpy(msg->buf, ioc, sizeof(*ioc)); if (ioc->data_ptr && ioc->write_flag) { memcpy(msg->buf + sizeof(*ioc), (FAR void *)(uintptr_t)ioc->data_ptr, ioc->blksz * ioc->blocks); } return rpmsgblk_send_recv(priv, RPMSGBLK_IOCTL, false, &msg->header, msglen, ioc); } /**************************************************************************** * Name: rpmsgblk_mmc_multi_cmd_ioctl * * Description: * The size of MMC_IOC_MILTI_CMD's memory occupation is likely to be * larger than that allocated by rpmsgblk_get_tx_payload_buffer, which * is not allowed by current implement. So split the mmc_ioc_multi_cmd * into mmc_ioc_cmds to transfer. * ****************************************************************************/ static int rpmsgblk_mmc_multi_cmd_ioctl(FAR struct inode *inode, unsigned long arg) { FAR struct rpmsgblk_s *priv = inode->i_private; FAR struct mmc_ioc_multi_cmd *mioc = (FAR struct mmc_ioc_multi_cmd *)(uintptr_t)arg; FAR struct rpmsgblk_ioctl_s *msg; size_t arglen; size_t msglen; size_t rsplen; uint64_t i; arglen = sizeof(struct mmc_ioc_multi_cmd) + mioc->num_of_cmds * sizeof(struct mmc_ioc_cmd); rsplen = arglen; for (i = 0; i < mioc->num_of_cmds; i++) { if (mioc->cmds[i].data_ptr && mioc->cmds[i].write_flag) { arglen += mioc->cmds[i].blksz * mioc->cmds[i].blocks; } else { rsplen += mioc->cmds[i].blksz * mioc->cmds[i].blocks; } } /* When multi cmds are read cmd, it also needs to be split if the rsp * msg is too large. */ msglen = sizeof(*msg) + arglen - 1; rsplen += sizeof(*msg) - 1; if (MAX(msglen, rsplen) > rpmsg_virtio_get_buffer_size(priv->ept.rdev)) { int ret = 0; for (i = 0; i < mioc->num_of_cmds; i++) { ret = rpmsgblk_mmc_cmd_ioctl(inode, (unsigned long)(uintptr_t)&mioc->cmds[i]); if (ret < 0) { return ret; } } return ret; } else { size_t off = sizeof(struct mmc_ioc_multi_cmd) + mioc->num_of_cmds * sizeof(struct mmc_ioc_cmd); uint32_t space; msg = rpmsgblk_get_tx_payload_buffer(priv, &space); if (msg == NULL) { return -ENOMEM; } DEBUGASSERT(space >= msglen); msg->request = MMC_IOC_MULTI_CMD; msg->arg = arg; msg->arglen = arglen; memcpy(msg->buf, mioc, off); for (i = 0; i < mioc->num_of_cmds; i++) { if (mioc->cmds[i].data_ptr && mioc->cmds[i].write_flag) { memcpy(msg->buf + off, (FAR void *)(uintptr_t)mioc->cmds[i].data_ptr, mioc->cmds[i].blksz * mioc->cmds[i].blocks); off += mioc->cmds[i].blksz * mioc->cmds[i].blocks; } } return rpmsgblk_send_recv(priv, RPMSGBLK_IOCTL, false, &msg->header, msglen, (FAR void *)arg); } } /**************************************************************************** * Name: rpmsgblk_default_ioctl ****************************************************************************/ static int rpmsgblk_default_ioctl(FAR struct inode *inode, int cmd, unsigned long arg) { FAR struct rpmsgblk_s *priv = inode->i_private; FAR struct rpmsgblk_ioctl_s *msg; uint32_t space; ssize_t arglen; size_t msglen; /* Call our internal routine to perform the ioctl */ arglen = rpmsgblk_ioctl_arglen(cmd); if (arglen < 0) { return arglen; } msglen = sizeof(*msg) + arglen - 1; msg = rpmsgblk_get_tx_payload_buffer(priv, &space); if (msg == NULL) { return -ENOMEM; } DEBUGASSERT(space >= msglen); msg->request = cmd; msg->arg = arg; msg->arglen = arglen; if (arglen > 0) { memcpy(msg->buf, (FAR void *)(uintptr_t)arg, arglen); } return rpmsgblk_send_recv(priv, RPMSGBLK_IOCTL, false, &msg->header, msglen, arglen > 0 ? (FAR void *)arg : NULL); } /**************************************************************************** * Name: rpmsgblk_ioctl * * Description: * Rpmsg-blk ioctl operation * * Parameters: * inode - the blk device inode * cmd - the ioctl command * arg - the ioctl arguments * * Returned Values: * OK on success; A negated errno value is returned on any failure. * ****************************************************************************/ static int rpmsgblk_ioctl(FAR struct inode *inode, int cmd, unsigned long arg) { FAR struct rpmsgblk_s *priv = inode->i_private; /* Sanity checks */ DEBUGASSERT(priv != NULL); switch (cmd) { case BIOC_GEOMETRY: return rpmsgblk_geometry(inode, (FAR struct geometry *)arg); case MMC_IOC_CMD: return rpmsgblk_mmc_cmd_ioctl(inode, arg); case MMC_IOC_MULTI_CMD: return rpmsgblk_mmc_multi_cmd_ioctl(inode, arg); default: return rpmsgblk_default_ioctl(inode, cmd, arg); } } /**************************************************************************** * Name: rpmsgblk_unlink * * Description: * Rpmsg-blk unlink operation. Client can not delete the actual block * device in server's side, so return success directly. * * Parameters: * inode - the blk device inode * * Returned Values: * OK on success; A negated errno value is returned on any failure. * ****************************************************************************/ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS static int rpmsgblk_unlink(FAR struct inode *inode) { return OK; } #endif /**************************************************************************** * Name: rpmsgblk_get_tx_payload_buffer * * Description: * Get the rpmsg blk tx payload, the buffer is from the rpmsg share memory * that can be accessed by local and remote cpu. * * Parameters: * priv - The rpmsg-blk handle * len - The got memroy size * * Returned Values: * NULL - failure * not NULL - success * ****************************************************************************/ static FAR void *rpmsgblk_get_tx_payload_buffer(FAR struct rpmsgblk_s *priv, FAR uint32_t *len) { int sval; nxsem_get_value(&priv->wait, &sval); if (sval <= 0) { rpmsg_wait(&priv->ept, &priv->wait); rpmsg_post(&priv->ept, &priv->wait); } return rpmsg_get_tx_payload_buffer(&priv->ept, len, true); } /**************************************************************************** * Name: rpmsgblk_send_recv * * Description: * Send and receive the rpmsg data. * * Parameters: * priv - rpmsg blk handle * command - the command, RPMSGBLK_OPEN, RPMSGBLK_CLOSE, RPMSGBLK_READ, * RPMSGBLK_WRITE, RPMSGBLK_IOCTL * copy - true, send a message across to the remote processor, and the * tx buffer will be alloced inside function rpmsg_send() * false, send a message in tx buffer reserved by * rpmsg_get_tx_payload_buffer() across to the remote * processor. * msg - the message header * len - length of the payload * data - the data * * Returned Values: * OK on success; A negated errno value is returned on any failure. * ****************************************************************************/ static int rpmsgblk_send_recv(FAR struct rpmsgblk_s *priv, uint32_t command, bool copy, FAR struct rpmsgblk_header_s *msg, int len, FAR void *data) { struct rpmsgblk_cookie_s cookie; int ret; memset(&cookie, 0, sizeof(cookie)); nxsem_init(&cookie.sem, 0, 0); if (data != NULL) { cookie.data = data; } else if (copy) { cookie.data = msg; } msg->command = command; msg->result = -ENXIO; msg->cookie = (uintptr_t)&cookie; if (copy) { ret = rpmsg_send(&priv->ept, msg, len); } else { ret = rpmsg_send_nocopy(&priv->ept, msg, len); } if (ret < 0) { goto fail; } ret = rpmsg_wait(&priv->ept, &cookie.sem); if (ret >= 0) { ret = cookie.result; } fail: nxsem_destroy(&cookie.sem); return ret; } /**************************************************************************** * Name: rpmsgblk_default_handler * * Description: * Default rpmsg-blk response handler, this function will be called to * process the return message of rpmsgblk_open(), rpmsgblk_close() and * rpmsgblk_write(). * * Parameters: * ept - The rpmsg endpoint * data - The return message * len - The return message length * src - unknow * priv - unknow * * Returned Values: * Always OK * ****************************************************************************/ static int rpmsgblk_default_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgblk_header_s *header = data; FAR struct rpmsgblk_cookie_s *cookie = (FAR struct rpmsgblk_cookie_s *)(uintptr_t)header->cookie; cookie->result = header->result; if (cookie->result >= 0 && cookie->data) { memcpy(cookie->data, data, len); } return rpmsg_post(ept, &cookie->sem); } /**************************************************************************** * Name: rpmsgblk_read_handler * * Description: * Rpmsg-blk block read response handler, this function will be called to * process the return message of rpmsgblk_bread(). * * Parameters: * ept - The rpmsg endpoint * data - The return message * len - The return message length * src - unknow * priv - unknow * * Returned Values: * Always OK * ****************************************************************************/ static int rpmsgblk_read_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgblk_header_s *header = data; FAR struct rpmsgblk_cookie_s *cookie = (FAR struct rpmsgblk_cookie_s *)(uintptr_t)header->cookie; FAR struct rpmsgblk_read_s *rsp = data; FAR struct iovec *iov = cookie->data; size_t read; cookie->result = header->result; if (cookie->result > 0) { read = cookie->result * rsp->sectorsize; memcpy(iov->iov_base, rsp->buf, read); iov->iov_base += read; iov->iov_len += cookie->result; } if (cookie->result <= 0 || iov->iov_len >= rsp->nsectors) { return rpmsg_post(ept, &cookie->sem); } return 0; } /**************************************************************************** * Name: rpmsgblk_geometry_handler * * Description: * Rpmsg-blk geometry response handler, this function will be called to * process the return message of rpmsgblk_geometry(). * * Parameters: * ept - The rpmsg endpoint * data - The return message * len - The return message length * src - unknow * priv - unknow * * Returned Values: * Always OK * ****************************************************************************/ static int rpmsgblk_geometry_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgblk_header_s *header = data; FAR struct rpmsgblk_cookie_s *cookie = (FAR struct rpmsgblk_cookie_s *)(uintptr_t)header->cookie; cookie->result = header->result; if (cookie->result >= 0) { memcpy(cookie->data, data, len); } return rpmsg_post(ept, &cookie->sem); } /**************************************************************************** * Name: rpmsgblk_mmc_cmd_handler ****************************************************************************/ static void rpmsgblk_mmc_cmd_handler(FAR struct rpmsgblk_cookie_s *cookie, FAR struct rpmsgblk_ioctl_s *rsp) { FAR struct mmc_ioc_cmd *ioc = (FAR struct mmc_ioc_cmd *)(uintptr_t)cookie->data; /* Copy struct mmc_ioc_cmd back to the usrspace buffer * except data_ptr which is another buffer pointer */ memcpy(ioc, rsp->buf, sizeof(*ioc) - sizeof(ioc->data_ptr)); if (ioc->data_ptr && !ioc->write_flag) { memcpy((FAR void *)(uintptr_t)ioc->data_ptr, rsp->buf + sizeof(*ioc), ioc->blksz * ioc->blocks); } } /**************************************************************************** * Name: rpmsgblk_mmc_multi_cmd_handler ****************************************************************************/ static void rpmsgblk_mmc_multi_cmd_handler(FAR struct rpmsgblk_cookie_s *cookie, FAR struct rpmsgblk_ioctl_s *rsp) { FAR struct mmc_ioc_multi_cmd *mioc = (FAR struct mmc_ioc_multi_cmd *)(uintptr_t)cookie->data; FAR struct mmc_ioc_multi_cmd *mioc_rsp = (FAR struct mmc_ioc_multi_cmd *)(uintptr_t)rsp->buf; size_t off = sizeof(struct mmc_ioc_multi_cmd) + mioc->num_of_cmds * sizeof(struct mmc_ioc_cmd); uint64_t i; for (i = 0; i < mioc->num_of_cmds; i++) { memcpy(&mioc->cmds[i], &mioc_rsp->cmds[i], sizeof(struct mmc_ioc_cmd) - sizeof(mioc->cmds[i].data_ptr)); if (mioc->cmds[i].data_ptr && !mioc->cmds[i].write_flag) { memcpy((FAR void *)(uintptr_t)mioc->cmds[i].data_ptr, rsp->buf + off, mioc->cmds[i].blksz * mioc->cmds[i].blocks); off += mioc->cmds[i].blksz * mioc->cmds[i].blocks; } } } /**************************************************************************** * Name: rpmsgblk_ioctl_handler * * Description: * Rpmsg-blk ioctl response handler, this function will be called to * process the return message of rpmsgblk_ioctl(). * * Parameters: * ept - The rpmsg endpoint * data - The return message * len - The return message length * src - unknow * priv - unknow * * Returned Values: * Always OK * ****************************************************************************/ static int rpmsgblk_ioctl_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgblk_header_s *header = data; FAR struct rpmsgblk_cookie_s *cookie = (FAR struct rpmsgblk_cookie_s *)(uintptr_t)header->cookie; FAR struct rpmsgblk_ioctl_s *rsp = data; cookie->result = header->result; if (cookie->result >= 0 && rsp->arglen > 0) { switch (rsp->request) { case MMC_IOC_CMD: rpmsgblk_mmc_cmd_handler(cookie, rsp); break; case MMC_IOC_MULTI_CMD: rpmsgblk_mmc_multi_cmd_handler(cookie, rsp); break; default: memcpy(cookie->data, rsp->buf, rsp->arglen); break; } } return rpmsg_post(ept, &cookie->sem); } /**************************************************************************** * Name: rpmsgblk_ns_bound * * Description: * Rpmsg blk end point service bound callback function , called when * remote end point address is received. * * Parameters: * ept - The rpmsg-blk end point * * Returned Values: * None * ****************************************************************************/ static void rpmsgblk_ns_bound(FAR struct rpmsg_endpoint *ept) { FAR struct rpmsgblk_s *priv = ept->priv; rpmsg_post(&priv->ept, &priv->wait); } /**************************************************************************** * Name: rpmsgblk_device_created * * Description: * Rpmsg blk create function, this function will be called by rptun to * create a rpmsg-blk end point. * * Parameters: * rdev - The rpmsg-blk end point * priv_ - Rpmsg-blk handle * * Returned Values: * None * ****************************************************************************/ static void rpmsgblk_device_created(FAR struct rpmsg_device *rdev, FAR void *priv_) { FAR struct rpmsgblk_s *priv = priv_; char buf[RPMSG_NAME_SIZE]; if (strcmp(priv->remotecpu, rpmsg_get_cpuname(rdev)) == 0) { priv->ept.priv = priv; priv->ept.ns_bound_cb = rpmsgblk_ns_bound; snprintf(buf, sizeof(buf), "%s%s", RPMSGBLK_NAME_PREFIX, priv->remotepath); rpmsg_create_ept(&priv->ept, rdev, buf, RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, rpmsgblk_ept_cb, NULL); } } /**************************************************************************** * Name: rpmsgblk_device_destroy * * Description: * Rpmsg blk destroy function, this function will be called by rptun to * destroy rpmsg-blk end point. * * Parameters: * rdev - The rpmsg-blk end point * priv_ - Rpmsg-blk handle * * Returned Values: * None * ****************************************************************************/ static void rpmsgblk_device_destroy(FAR struct rpmsg_device *rdev, FAR void *priv_) { FAR struct rpmsgblk_s *priv = priv_; if (strcmp(priv->remotecpu, rpmsg_get_cpuname(rdev)) == 0) { rpmsg_destroy_ept(&priv->ept); } } /**************************************************************************** * Name: rpmsgblk_ept_cb * * Description: * Rpmsg blk end point callback function, this function will be called * when receive the remote cpu message. * * Parameters: * ept - The rpmsg-blk end point * data - The received data * len - The received data length * src - unknow * priv - unknow * * Returned Values: * OK on success; A negated errno value is returned on any failure. * ****************************************************************************/ static int rpmsgblk_ept_cb(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgblk_header_s *header = data; uint32_t command = header->command; if (command < nitems(g_rpmsgblk_handler)) { return g_rpmsgblk_handler[command](ept, data, len, src, priv); } return -EINVAL; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: rpmsgblk_register * * Description: * Rpmsg-blk client initialize function, the client cpu should call * this function in the board initialize process. * * Parameters: * remotecpu - the server cpu name * remotepath - the device you want to access in the remote cpu * localpath - the device path in local cpu, if NULL, the localpath is * same as the remotepath, provide this argument to supoort * custom device path * * Returned Values: * OK on success; A negated errno value is returned on any failure. * ****************************************************************************/ int rpmsgblk_register(FAR const char *remotecpu, FAR const char *remotepath, FAR const char *localpath) { FAR struct rpmsgblk_s *dev; int ret; /* Arguments check */ if (remotecpu == NULL || remotepath == NULL) { ferr("ERROR: Input arguments null\n"); return -EINVAL; } /* Create an instance of the RPMSG MTD device */ dev = kmm_zalloc(sizeof(*dev)); if (dev == NULL) { ferr("ERROR: Failed to allocate the RPMSG MTD degice\n"); return -ENOMEM; } /* Perform initialization as necessary. (unsupported methods were * nullified by kmm_zalloc). */ dev->blk.open = rpmsgblk_open; dev->blk.close = rpmsgblk_close; dev->blk.read = rpmsgblk_read; dev->blk.write = rpmsgblk_write; dev->blk.geometry = rpmsgblk_geometry; dev->blk.ioctl = rpmsgblk_ioctl; #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS dev->blk.unlink = rpmsgblk_unlink; #endif /* Initialize the rpmsg device */ dev->remotecpu = remotecpu; dev->remotepath = remotepath; nxsem_init(&dev->wait, 0, 0); nxmutex_init(&dev->lock); /* Register the rpmsg callback */ ret = rpmsg_register_callback(dev, rpmsgblk_device_created, rpmsgblk_device_destroy, NULL, NULL); if (ret < 0) { ferr("ERROR: register callback failed, ret=%d\n", ret); goto fail; } /* Register driver, using the remotepath if localpath is NULL */ if (localpath == NULL) { localpath = remotepath; } ret = register_blockdriver(localpath, &dev->blk, 0755, dev); if (ret < 0) { ferr("ERROR: register driver failed, ret=%d\n", ret); goto fail_with_rpmsg; } return OK; fail_with_rpmsg: rpmsg_unregister_callback(dev, rpmsgblk_device_created, rpmsgblk_device_destroy, NULL, NULL); fail: nxsem_destroy(&dev->wait); nxmutex_destroy(&dev->lock); kmm_free(dev); return ret; }