/* * We've received data on a connection, and it doesn't look like a * response, so we assume it's a request. * * This is called in interrupt context, so just copy the incoming * data into the request buffer and handle the rest via workqueue. */ static void gb_connection_recv_request(struct gb_connection *connection, u16 operation_id, u8 type, void *data, size_t size) { struct gb_operation *operation; int ret; operation = gb_operation_create_incoming(connection, operation_id, type, data, size); if (!operation) { dev_err(&connection->hd->dev, "%s: can't create incoming operation\n", connection->name); return; } ret = gb_operation_get_active(operation); if (ret) { gb_operation_put(operation); return; } trace_gb_message_recv_request(operation->request); /* * The initial reference to the operation will be dropped when the * request handler returns. */ if (gb_operation_result_set(operation, -EINPROGRESS)) queue_work(connection->wq, &operation->work); }
/** * gb_operation_unidirectional_timeout() - initiate a unidirectional operation * @connection: connection to use * @type: type of operation to send * @request: memory buffer to copy the request from * @request_size: size of @request * @timeout: send timeout in milliseconds * * Initiate a unidirectional operation by sending a request message and * waiting for it to be acknowledged as sent by the host device. * * Note that successful send of a unidirectional operation does not imply that * the request as actually reached the remote end of the connection. */ int gb_operation_unidirectional_timeout(struct gb_connection *connection, int type, void *request, int request_size, unsigned int timeout) { struct gb_operation *operation; int ret; if (request_size && !request) return -EINVAL; operation = gb_operation_create_flags(connection, type, request_size, 0, GB_OPERATION_FLAG_UNIDIRECTIONAL, GFP_KERNEL); if (!operation) return -ENOMEM; if (request_size) memcpy(operation->request->payload, request, request_size); ret = gb_operation_request_send_sync_timeout(operation, timeout); if (ret) { dev_err(&connection->hd->dev, "%s: unidirectional operation of type 0x%02x failed: %d\n", connection->name, type, ret); } gb_operation_put(operation); return ret; }
/* * This function is called when a message send request has completed. */ void greybus_message_sent(struct gb_host_device *hd, struct gb_message *message, int status) { struct gb_operation *operation = message->operation; struct gb_connection *connection = operation->connection; /* * If the message was a response, we just need to drop our * reference to the operation. If an error occurred, report * it. * * For requests, if there's no error and the operation in not * unidirectional, there's nothing more to do until the response * arrives. If an error occurred attempting to send it, or if the * operation is unidrectional, record the result of the operation and * schedule its completion. */ if (message == operation->response) { if (status) { dev_err(&connection->hd->dev, "%s: error sending response 0x%02x: %d\n", connection->name, operation->type, status); } gb_operation_put_active(operation); gb_operation_put(operation); } else if (status || gb_operation_is_unidirectional(operation)) { if (gb_operation_result_set(operation, status)) { queue_work(gb_operation_completion_wq, &operation->work); } } }
/* * We've received data that appears to be an operation response * message. Look up the operation, and record that we've received * its response. * * This is called in interrupt context, so just copy the incoming * data into the response buffer and handle the rest via workqueue. */ static void gb_connection_recv_response(struct gb_connection *connection, u16 operation_id, u8 result, void *data, size_t size) { struct gb_operation_msg_hdr *header; struct gb_operation *operation; struct gb_message *message; int errno = gb_operation_status_map(result); size_t message_size; if (!operation_id) { dev_err(&connection->hd->dev, "%s: invalid response id 0 received\n", connection->name); return; } operation = gb_operation_find_outgoing(connection, operation_id); if (!operation) { dev_err(&connection->hd->dev, "%s: unexpected response id 0x%04x received\n", connection->name, operation_id); return; } message = operation->response; header = message->header; message_size = sizeof(*header) + message->payload_size; if (!errno && size > message_size) { dev_err(&connection->hd->dev, "%s: malformed response 0x%02x received (%zu > %zu)\n", connection->name, header->type, size, message_size); errno = -EMSGSIZE; } else if (!errno && size < message_size) { if (gb_operation_short_response_allowed(operation)) { message->payload_size = size - sizeof(*header); } else { dev_err(&connection->hd->dev, "%s: short response 0x%02x received (%zu < %zu)\n", connection->name, header->type, size, message_size); errno = -EMSGSIZE; } } trace_gb_message_recv_response(operation->response); /* We must ignore the payload if a bad status is returned */ if (errno) size = sizeof(*header); /* The rest will be handled in work queue context */ if (gb_operation_result_set(operation, errno)) { memcpy(header, data, size); queue_work(gb_operation_completion_wq, &operation->work); } gb_operation_put(operation); }
/** * gb_operation_request_send() - send an operation request message * @operation: the operation to initiate * @callback: the operation completion callback * @gfp: the memory flags to use for any allocations * * The caller has filled in any payload so the request message is ready to go. * The callback function supplied will be called when the response message has * arrived, a unidirectional request has been sent, or the operation is * cancelled, indicating that the operation is complete. The callback function * can fetch the result of the operation using gb_operation_result() if * desired. * * Return: 0 if the request was successfully queued in the host-driver queues, * or a negative errno. */ int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback, gfp_t gfp) { struct gb_connection *connection = operation->connection; struct gb_operation_msg_hdr *header; unsigned int cycle; int ret; if (!callback) return -EINVAL; /* * Record the callback function, which is executed in * non-atomic (workqueue) context when the final result * of an operation has been set. */ operation->callback = callback; /* * Assign the operation's id, and store it in the request header. * Zero is a reserved operation id for unidirectional operations. */ if (gb_operation_is_unidirectional(operation)) { operation->id = 0; } else { cycle = (unsigned int)atomic_inc_return(&connection->op_cycle); operation->id = (u16)(cycle % U16_MAX + 1); } header = operation->request->header; header->operation_id = cpu_to_le16(operation->id); gb_operation_result_set(operation, -EINPROGRESS); /* * Get an extra reference on the operation. It'll be dropped when the * operation completes. */ gb_operation_get(operation); ret = gb_operation_get_active(operation); if (ret) goto err_put; ret = gb_message_send(operation->request, gfp); if (ret) goto err_put_active; return 0; err_put_active: gb_operation_put_active(operation); err_put: gb_operation_put(operation); return ret; }
/* * Process operation work. * * For incoming requests, call the protocol request handler. The operation * result should be -EINPROGRESS at this point. * * For outgoing requests, the operation result value should have * been set before queueing this. The operation callback function * allows the original requester to know the request has completed * and its result is available. */ static void gb_operation_work(struct work_struct *work) { struct gb_operation *operation; operation = container_of(work, struct gb_operation, work); if (gb_operation_is_incoming(operation)) gb_operation_request_handle(operation); else operation->callback(operation); gb_operation_put_active(operation); gb_operation_put(operation); }
/* * 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; }
/* * Cancel all active operations on a connection. * * Should only be called during connection tear down. */ static void gb_connection_cancel_operations(struct gb_connection *connection, int errno) { struct gb_operation *operation; spin_lock_irq(&connection->lock); while (!list_empty(&connection->operations)) { operation = list_last_entry(&connection->operations, struct gb_operation, links); gb_operation_get(operation); spin_unlock_irq(&connection->lock); if (gb_operation_is_incoming(operation)) gb_operation_cancel_incoming(operation, errno); else gb_operation_cancel(operation, errno); gb_operation_put(operation); spin_lock_irq(&connection->lock); } spin_unlock_irq(&connection->lock); }
/** * gb_operation_sync_timeout() - implement a "simple" synchronous operation * @connection: the Greybus connection to send this to * @type: the type of operation to send * @request: pointer to a memory buffer to copy the request from * @request_size: size of @request * @response: pointer to a memory buffer to copy the response to * @response_size: the size of @response. * @timeout: operation timeout in milliseconds * * This function implements a simple synchronous Greybus operation. It sends * the provided operation request and waits (sleeps) until the corresponding * operation response message has been successfully received, or an error * occurs. @request and @response are buffers to hold the request and response * data respectively, and if they are not NULL, their size must be specified in * @request_size and @response_size. * * If a response payload is to come back, and @response is not NULL, * @response_size number of bytes will be copied into @response if the operation * is successful. * * If there is an error, the response buffer is left alone. */ int gb_operation_sync_timeout(struct gb_connection *connection, int type, void *request, int request_size, void *response, int response_size, unsigned int timeout) { struct gb_operation *operation; int ret; if ((response_size && !response) || (request_size && !request)) return -EINVAL; operation = gb_operation_create(connection, type, request_size, response_size, GFP_KERNEL); if (!operation) return -ENOMEM; if (request_size) memcpy(operation->request->payload, request, request_size); ret = gb_operation_request_send_sync_timeout(operation, timeout); if (ret) { dev_err(&connection->hd->dev, "%s: synchronous operation of type 0x%02x failed: %d\n", connection->name, type, ret); } else { if (response_size) { memcpy(response, operation->response->payload, response_size); } } gb_operation_put(operation); return ret; }