static int gb_bootrom_firmware_size_request(struct gb_operation *op) { struct gb_bootrom *bootrom = gb_connection_get_data(op->connection); struct gb_bootrom_firmware_size_request *size_request = op->request->payload; struct gb_bootrom_firmware_size_response *size_response; struct device *dev = &op->connection->bundle->dev; int ret; /* Disable timeouts */ gb_bootrom_cancel_timeout(bootrom); if (op->request->payload_size != sizeof(*size_request)) { dev_err(dev, "%s: illegal size of firmware size request (%zu != %zu)\n", __func__, op->request->payload_size, sizeof(*size_request)); ret = -EINVAL; goto queue_work; } mutex_lock(&bootrom->mutex); ret = find_firmware(bootrom, size_request->stage); if (ret) goto unlock; if (!gb_operation_response_alloc(op, sizeof(*size_response), GFP_KERNEL)) { dev_err(dev, "%s: error allocating response\n", __func__); free_firmware(bootrom); ret = -ENOMEM; goto unlock; } size_response = op->response->payload; size_response->size = cpu_to_le32(bootrom->fw->size); dev_dbg(dev, "%s: firmware size %d bytes\n", __func__, size_response->size); unlock: mutex_unlock(&bootrom->mutex); queue_work: if (!ret) { /* Refresh timeout */ gb_bootrom_set_timeout(bootrom, NEXT_REQ_GET_FIRMWARE, NEXT_REQ_TIMEOUT_MS); } return ret; }
static int gb_firmware_get_firmware(struct gb_operation *op) { struct gb_firmware *firmware = gb_connection_get_data(op->connection); const struct firmware *fw = firmware->fw; struct gb_firmware_get_firmware_request *firmware_request; struct gb_firmware_get_firmware_response *firmware_response; struct device *dev = &op->connection->bundle->dev; unsigned int offset, size; if (op->request->payload_size != sizeof(*firmware_request)) { dev_err(dev, "%s: Illegal size of get firmware request (%zu %zu)\n", __func__, op->request->payload_size, sizeof(*firmware_request)); return -EINVAL; } if (!fw) { dev_err(dev, "%s: firmware not available\n", __func__); return -EINVAL; } firmware_request = op->request->payload; offset = le32_to_cpu(firmware_request->offset); size = le32_to_cpu(firmware_request->size); if (offset >= fw->size || size > fw->size - offset) { dev_warn(dev, "bad firmware request (offs = %u, size = %u)\n", offset, size); return -EINVAL; } if (!gb_operation_response_alloc(op, sizeof(*firmware_response) + size, GFP_KERNEL)) { dev_err(dev, "%s: error allocating response\n", __func__); return -ENOMEM; } firmware_response = op->response->payload; memcpy(firmware_response->data, fw->data + offset, size); dev_dbg(dev, "responding with firmware (offs = %u, size = %u)\n", offset, size); return 0; }
/* * Create a Greybus operation to be sent over the given connection. * The request buffer will be big enough for a payload of the given * size. * * For outgoing requests, the request message's header will be * initialized with the type of the request and the message size. * Outgoing operations must also specify the response buffer size, * which must be sufficient to hold all expected response data. The * response message header will eventually be overwritten, so there's * no need to initialize it here. * * Request messages for incoming operations can arrive in interrupt * context, so they must be allocated with GFP_ATOMIC. In this case * the request buffer will be immediately overwritten, so there is * no need to initialize the message header. Responsibility for * allocating a response buffer lies with the incoming request * handler for a protocol. So we don't allocate that here. * * Returns a pointer to the new operation or a null pointer if an * error occurs. */ static struct gb_operation * gb_operation_create_common(struct gb_connection *connection, u8 type, size_t request_size, size_t response_size, unsigned long op_flags, gfp_t gfp_flags) { struct gb_host_device *hd = connection->hd; struct gb_operation *operation; operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags); if (!operation) return NULL; operation->connection = connection; operation->request = gb_operation_message_alloc(hd, type, request_size, gfp_flags); if (!operation->request) goto err_cache; operation->request->operation = operation; /* Allocate the response buffer for outgoing operations */ if (!(op_flags & GB_OPERATION_FLAG_INCOMING)) { if (!gb_operation_response_alloc(operation, response_size, gfp_flags)) { goto err_request; } } operation->flags = op_flags; operation->type = type; operation->errno = -EBADR; /* Initial value--means "never set" */ INIT_WORK(&operation->work, gb_operation_work); init_completion(&operation->completion); kref_init(&operation->kref); atomic_set(&operation->waiters, 0); return operation; err_request: gb_operation_message_free(operation->request); err_cache: kmem_cache_free(gb_operation_cache, operation); return NULL; }
/* * Send a response for an incoming operation request. A non-zero * errno indicates a failed operation. * * If there is any response payload, the incoming request handler is * responsible for allocating the response message. Otherwise the * it can simply supply the result errno; this function will * allocate the response message if necessary. */ static int gb_operation_response_send(struct gb_operation *operation, int errno) { struct gb_connection *connection = operation->connection; int ret; if (!operation->response && !gb_operation_is_unidirectional(operation)) { if (!gb_operation_response_alloc(operation, 0, GFP_KERNEL)) return -ENOMEM; } /* Record the result */ if (!gb_operation_result_set(operation, errno)) { dev_err(&connection->hd->dev, "request result already set\n"); return -EIO; /* Shouldn't happen */ } /* Sender of request does not care about response. */ if (gb_operation_is_unidirectional(operation)) return 0; /* Reference will be dropped when message has been sent. */ gb_operation_get(operation); ret = gb_operation_get_active(operation); if (ret) goto err_put; /* Fill in the response header and send it */ operation->response->header->result = gb_operation_errno_map(errno); ret = gb_message_send(operation->response, GFP_KERNEL); if (ret) goto err_put_active; return 0; err_put_active: gb_operation_put_active(operation); err_put: gb_operation_put(operation); return ret; }
static int gb_firmware_size_request(struct gb_operation *op) { struct gb_firmware *firmware = gb_connection_get_data(op->connection); struct gb_firmware_size_request *size_request = op->request->payload; struct gb_firmware_size_response *size_response; struct device *dev = &op->connection->bundle->dev; int ret; if (op->request->payload_size != sizeof(*size_request)) { dev_err(dev, "%s: illegal size of firmware size request (%zu != %zu)\n", __func__, op->request->payload_size, sizeof(*size_request)); return -EINVAL; } ret = download_firmware(firmware, size_request->stage); if (ret) { dev_err(dev, "%s: failed to download firmware (%d)\n", __func__, ret); return ret; } if (!gb_operation_response_alloc(op, sizeof(*size_response), GFP_KERNEL)) { dev_err(dev, "%s: error allocating response\n", __func__); free_firmware(firmware); return -ENOMEM; } size_response = op->response->payload; size_response->size = cpu_to_le32(firmware->fw->size); dev_dbg(dev, "%s: firmware size %d bytes\n", __func__, size_response->size); return 0; }
static int gb_bootrom_get_firmware(struct gb_operation *op) { struct gb_bootrom *bootrom = gb_connection_get_data(op->connection); const struct firmware *fw; struct gb_bootrom_get_firmware_request *firmware_request; struct gb_bootrom_get_firmware_response *firmware_response; struct device *dev = &op->connection->bundle->dev; unsigned int offset, size; enum next_request_type next_request; int ret = 0; /* Disable timeouts */ gb_bootrom_cancel_timeout(bootrom); if (op->request->payload_size != sizeof(*firmware_request)) { dev_err(dev, "%s: Illegal size of get firmware request (%zu %zu)\n", __func__, op->request->payload_size, sizeof(*firmware_request)); ret = -EINVAL; goto queue_work; } mutex_lock(&bootrom->mutex); fw = bootrom->fw; if (!fw) { dev_err(dev, "%s: firmware not available\n", __func__); ret = -EINVAL; goto unlock; } firmware_request = op->request->payload; offset = le32_to_cpu(firmware_request->offset); size = le32_to_cpu(firmware_request->size); if (offset >= fw->size || size > fw->size - offset) { dev_warn(dev, "bad firmware request (offs = %u, size = %u)\n", offset, size); ret = -EINVAL; goto unlock; } if (!gb_operation_response_alloc(op, sizeof(*firmware_response) + size, GFP_KERNEL)) { dev_err(dev, "%s: error allocating response\n", __func__); ret = -ENOMEM; goto unlock; } firmware_response = op->response->payload; memcpy(firmware_response->data, fw->data + offset, size); dev_dbg(dev, "responding with firmware (offs = %u, size = %u)\n", offset, size); unlock: mutex_unlock(&bootrom->mutex); queue_work: /* Refresh timeout */ if (!ret && (offset + size == fw->size)) next_request = NEXT_REQ_READY_TO_BOOT; else next_request = NEXT_REQ_GET_FIRMWARE; gb_bootrom_set_timeout(bootrom, next_request, NEXT_REQ_TIMEOUT_MS); return ret; }