1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Greybus driver for the log protocol |
4 | * |
5 | * Copyright 2016 Google Inc. |
6 | */ |
7 | #include <linux/kernel.h> |
8 | #include <linux/module.h> |
9 | #include <linux/slab.h> |
10 | #include <linux/sizes.h> |
11 | #include <linux/uaccess.h> |
12 | #include <linux/greybus.h> |
13 | |
14 | struct gb_log { |
15 | struct gb_connection *connection; |
16 | }; |
17 | |
18 | static int gb_log_request_handler(struct gb_operation *op) |
19 | { |
20 | struct gb_connection *connection = op->connection; |
21 | struct device *dev = &connection->bundle->dev; |
22 | struct gb_log_send_log_request *receive; |
23 | u16 len; |
24 | |
25 | if (op->type != GB_LOG_TYPE_SEND_LOG) { |
26 | dev_err(dev, "unknown request type 0x%02x\n" , op->type); |
27 | return -EINVAL; |
28 | } |
29 | |
30 | /* Verify size of payload */ |
31 | if (op->request->payload_size < sizeof(*receive)) { |
32 | dev_err(dev, "log request too small (%zu < %zu)\n" , |
33 | op->request->payload_size, sizeof(*receive)); |
34 | return -EINVAL; |
35 | } |
36 | receive = op->request->payload; |
37 | len = le16_to_cpu(receive->len); |
38 | if (len != (op->request->payload_size - sizeof(*receive))) { |
39 | dev_err(dev, "log request wrong size %d vs %zu\n" , len, |
40 | (op->request->payload_size - sizeof(*receive))); |
41 | return -EINVAL; |
42 | } |
43 | if (len == 0) { |
44 | dev_err(dev, "log request of 0 bytes?\n" ); |
45 | return -EINVAL; |
46 | } |
47 | |
48 | if (len > GB_LOG_MAX_LEN) { |
49 | dev_err(dev, "log request too big: %d\n" , len); |
50 | return -EINVAL; |
51 | } |
52 | |
53 | /* Ensure the buffer is 0 terminated */ |
54 | receive->msg[len - 1] = '\0'; |
55 | |
56 | /* |
57 | * Print with dev_dbg() so that it can be easily turned off using |
58 | * dynamic debugging (and prevent any DoS) |
59 | */ |
60 | dev_dbg(dev, "%s" , receive->msg); |
61 | |
62 | return 0; |
63 | } |
64 | |
65 | static int gb_log_probe(struct gb_bundle *bundle, |
66 | const struct greybus_bundle_id *id) |
67 | { |
68 | struct greybus_descriptor_cport *cport_desc; |
69 | struct gb_connection *connection; |
70 | struct gb_log *log; |
71 | int retval; |
72 | |
73 | if (bundle->num_cports != 1) |
74 | return -ENODEV; |
75 | |
76 | cport_desc = &bundle->cport_desc[0]; |
77 | if (cport_desc->protocol_id != GREYBUS_PROTOCOL_LOG) |
78 | return -ENODEV; |
79 | |
80 | log = kzalloc(size: sizeof(*log), GFP_KERNEL); |
81 | if (!log) |
82 | return -ENOMEM; |
83 | |
84 | connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), |
85 | handler: gb_log_request_handler); |
86 | if (IS_ERR(ptr: connection)) { |
87 | retval = PTR_ERR(ptr: connection); |
88 | goto error_free; |
89 | } |
90 | |
91 | log->connection = connection; |
92 | greybus_set_drvdata(bundle, data: log); |
93 | |
94 | retval = gb_connection_enable(connection); |
95 | if (retval) |
96 | goto error_connection_destroy; |
97 | |
98 | return 0; |
99 | |
100 | error_connection_destroy: |
101 | gb_connection_destroy(connection); |
102 | error_free: |
103 | kfree(objp: log); |
104 | return retval; |
105 | } |
106 | |
107 | static void gb_log_disconnect(struct gb_bundle *bundle) |
108 | { |
109 | struct gb_log *log = greybus_get_drvdata(bundle); |
110 | struct gb_connection *connection = log->connection; |
111 | |
112 | gb_connection_disable(connection); |
113 | gb_connection_destroy(connection); |
114 | |
115 | kfree(objp: log); |
116 | } |
117 | |
118 | static const struct greybus_bundle_id gb_log_id_table[] = { |
119 | { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOG) }, |
120 | { } |
121 | }; |
122 | MODULE_DEVICE_TABLE(greybus, gb_log_id_table); |
123 | |
124 | static struct greybus_driver gb_log_driver = { |
125 | .name = "log" , |
126 | .probe = gb_log_probe, |
127 | .disconnect = gb_log_disconnect, |
128 | .id_table = gb_log_id_table, |
129 | }; |
130 | module_greybus_driver(gb_log_driver); |
131 | |
132 | MODULE_LICENSE("GPL v2" ); |
133 | |