diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 9de548d1a05cd8ba4353c70d9fcef61ef5140a73..5818d7c92c4e8d928fb81a01f9eea0c8afddd1e1 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -530,20 +530,25 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, * invalid operation type for all protocols, and this is enforced * here. */ -struct gb_operation *gb_operation_create(struct gb_connection *connection, - u8 type, size_t request_size, - size_t response_size, - gfp_t gfp) +struct gb_operation * +gb_operation_create_flags(struct gb_connection *connection, + u8 type, size_t request_size, + size_t response_size, unsigned long flags, + gfp_t gfp) { if (WARN_ON_ONCE(type == GB_OPERATION_TYPE_INVALID)) return NULL; if (WARN_ON_ONCE(type & GB_MESSAGE_TYPE_RESPONSE)) type &= ~GB_MESSAGE_TYPE_RESPONSE; + if (WARN_ON_ONCE(flags & ~GB_OPERATION_FLAG_USER_MASK)) + flags &= GB_OPERATION_FLAG_USER_MASK; + return gb_operation_create_common(connection, type, - request_size, response_size, 0, gfp); + request_size, response_size, + flags, gfp); } -EXPORT_SYMBOL_GPL(gb_operation_create); +EXPORT_SYMBOL_GPL(gb_operation_create_flags); size_t gb_operation_get_payload_size_max(struct gb_connection *connection) { @@ -875,12 +880,22 @@ static void gb_connection_recv_response(struct gb_connection *connection, message = operation->response; header = message->header; message_size = sizeof(*header) + message->payload_size; - if (!errno && size != message_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); + "%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); diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index c3f7ce71b0897b96805f7c0808bee5d220d5759c..38e5303a511fc19d458287f7e6da0e93c5767aa2 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -65,6 +65,9 @@ struct gb_message { #define GB_OPERATION_FLAG_INCOMING BIT(0) #define GB_OPERATION_FLAG_UNIDIRECTIONAL BIT(1) +#define GB_OPERATION_FLAG_SHORT_RESPONSE BIT(2) + +#define GB_OPERATION_FLAG_USER_MASK GB_OPERATION_FLAG_SHORT_RESPONSE /* * A Greybus operation is a remote procedure call performed over a @@ -119,16 +122,33 @@ gb_operation_is_unidirectional(struct gb_operation *operation) return operation->flags & GB_OPERATION_FLAG_UNIDIRECTIONAL; } +static inline bool +gb_operation_short_response_allowed(struct gb_operation *operation) +{ + return operation->flags & GB_OPERATION_FLAG_SHORT_RESPONSE; +} + void gb_connection_recv(struct gb_connection *connection, void *data, size_t size); int gb_operation_result(struct gb_operation *operation); size_t gb_operation_get_payload_size_max(struct gb_connection *connection); -struct gb_operation *gb_operation_create(struct gb_connection *connection, - u8 type, size_t request_size, - size_t response_size, - gfp_t gfp); +struct gb_operation * +gb_operation_create_flags(struct gb_connection *connection, + u8 type, size_t request_size, + size_t response_size, unsigned long flags, + gfp_t gfp); + +static inline struct gb_operation * +gb_operation_create(struct gb_connection *connection, + u8 type, size_t request_size, + size_t response_size, gfp_t gfp) +{ + return gb_operation_create_flags(connection, type, request_size, + response_size, 0, gfp); +} + void gb_operation_get(struct gb_operation *operation); void gb_operation_put(struct gb_operation *operation);