summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/staging/greybus/operation.c35
-rw-r--r--drivers/staging/greybus/operation.h28
2 files changed, 49 insertions, 14 deletions
diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c
index 9de548d1a05c..5818d7c92c4e 100644
--- a/drivers/staging/greybus/operation.c
+++ b/drivers/staging/greybus/operation.c
@@ -530,20 +530,25 @@ err_cache:
* 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 c3f7ce71b089..38e5303a511f 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);