blob: 852b6beaecac1a0c3e80390b6a14fbb00b0b23a3 [file] [log] [blame]
Alexandre Bailon355a7052015-03-31 09:51:59 +02001/*
2 * Loopback bridge driver for the Greybus loopback module.
3 *
4 * Copyright 2014 Google Inc.
5 * Copyright 2014 Linaro Ltd.
6 *
7 * Released under the GPLv2 only.
8 */
9#include <linux/kernel.h>
10#include <linux/module.h>
Bryan O'Donoghue85d678c2015-07-28 18:34:36 +010011#include <linux/mutex.h>
Alexandre Bailon355a7052015-03-31 09:51:59 +020012#include <linux/slab.h>
13#include <linux/kthread.h>
14#include <linux/delay.h>
15#include <linux/random.h>
Greg Kroah-Hartman5679f782015-03-31 23:01:45 +020016#include <linux/sizes.h>
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +010017#include <linux/cdev.h>
18#include <linux/fs.h>
19#include <linux/kfifo.h>
20
Johan Hovold1ffc12b2015-04-16 09:53:59 +020021#include <asm/div64.h>
22
Alexandre Bailon355a7052015-03-31 09:51:59 +020023#include "greybus.h"
24
Bryan O'Donoghuefd489e12015-08-11 13:50:54 +010025#define NSEC_PER_DAY 86400000000000ULL
26
Alexandre Bailon355a7052015-03-31 09:51:59 +020027struct gb_loopback_stats {
Alex Eldere051c822015-08-03 12:57:14 -050028 u32 min;
29 u32 max;
Bryan O'Donoghue00af6582015-07-21 23:50:08 +010030 u64 sum;
Alex Eldere13fc282015-08-03 12:57:15 -050031 u32 count;
Alexandre Bailon355a7052015-03-31 09:51:59 +020032};
33
34struct gb_loopback {
35 struct gb_connection *connection;
Alexandre Bailon355a7052015-03-31 09:51:59 +020036
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +010037 struct kfifo kfifo;
Bryan O'Donoghue85d678c2015-07-28 18:34:36 +010038 struct mutex mutex;
Alexandre Bailon355a7052015-03-31 09:51:59 +020039 struct task_struct *task;
Bryan O'Donoghue3dfe8aa2015-07-24 10:02:56 +010040 wait_queue_head_t wq;
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +010041 dev_t dev;
42 struct cdev cdev;
43 struct device *device;
Alexandre Bailon355a7052015-03-31 09:51:59 +020044
45 int type;
46 u32 size;
Bryan O'Donoghue00af6582015-07-21 23:50:08 +010047 u32 iteration_max;
48 u32 iteration_count;
Bryan O'Donoghuee51eafe2015-07-14 00:53:13 +010049 size_t size_max;
Alexandre Bailon355a7052015-03-31 09:51:59 +020050 int ms_wait;
51
52 struct gb_loopback_stats latency;
53 struct gb_loopback_stats throughput;
Bryan O'Donoghue583cbf52015-07-21 23:50:10 +010054 struct gb_loopback_stats requests_per_second;
Alexandre Bailon355a7052015-03-31 09:51:59 +020055 u64 elapsed_nsecs;
56 u32 error;
57};
58
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +010059#define GB_LOOPBACK_FIFO_DEFAULT 8192
60
61static struct class *loopback_class;
62static int loopback_major;
63static const struct file_operations loopback_fops;
64static DEFINE_IDA(minors);
65static unsigned kfifo_depth = GB_LOOPBACK_FIFO_DEFAULT;
66module_param(kfifo_depth, uint, 0444);
67
68/* Number of minor devices this driver supports */
69#define NUM_MINORS 256
70
71/* Maximum size of any one send data buffer we support */
72#define MAX_PACKET_SIZE (PAGE_SIZE * 2)
73
Alex Elder91262c32015-05-11 21:16:37 -050074#define GB_LOOPBACK_MS_WAIT_MAX 1000
Alexandre Bailon355a7052015-03-31 09:51:59 +020075
Alexandre Bailon355a7052015-03-31 09:51:59 +020076/* interface sysfs attributes */
Alex Elder19c2a442015-08-03 12:57:17 -050077#define gb_loopback_ro_attr(field) \
Alexandre Bailon355a7052015-03-31 09:51:59 +020078static ssize_t field##_show(struct device *dev, \
79 struct device_attribute *attr, \
80 char *buf) \
81{ \
82 struct gb_connection *connection = to_gb_connection(dev); \
Alex Elder3f12c3e2015-08-03 12:57:11 -050083 struct gb_loopback *gb = connection->private; \
Alex Elder19c2a442015-08-03 12:57:17 -050084 return sprintf(buf, "%u\n", gb->field); \
Alexandre Bailon355a7052015-03-31 09:51:59 +020085} \
86static DEVICE_ATTR_RO(field)
87
88#define gb_loopback_ro_stats_attr(name, field, type) \
89static ssize_t name##_##field##_show(struct device *dev, \
90 struct device_attribute *attr, \
91 char *buf) \
92{ \
93 struct gb_connection *connection = to_gb_connection(dev); \
Alex Elder3f12c3e2015-08-03 12:57:11 -050094 struct gb_loopback *gb = connection->private; \
Alexandre Bailon355a7052015-03-31 09:51:59 +020095 return sprintf(buf, "%"#type"\n", gb->name.field); \
96} \
97static DEVICE_ATTR_RO(name##_##field)
98
Alex Elder7a135a92015-08-03 12:57:18 -050099#define gb_loopback_ro_avg_attr(name) \
100static ssize_t name##_avg_show(struct device *dev, \
101 struct device_attribute *attr, \
102 char *buf) \
103{ \
104 struct gb_connection *connection = to_gb_connection(dev); \
105 struct gb_loopback *gb = connection->private; \
Alex Elderff71d392015-08-03 12:57:19 -0500106 struct gb_loopback_stats *stats = &gb->name; \
107 u32 count = stats->count ? stats->count : 1; \
108 u64 avg = stats->sum + count / 2; /* round closest */ \
109 u32 rem = do_div(avg, count); \
110 return sprintf(buf, "%llu.%06u\n", avg, 1000000 * rem / count); \
Alex Elder7a135a92015-08-03 12:57:18 -0500111} \
112static DEVICE_ATTR_RO(name##_avg)
113
Alexandre Bailon355a7052015-03-31 09:51:59 +0200114#define gb_loopback_stats_attrs(field) \
Alex Eldere051c822015-08-03 12:57:14 -0500115 gb_loopback_ro_stats_attr(field, min, u); \
116 gb_loopback_ro_stats_attr(field, max, u); \
Alex Elder7a135a92015-08-03 12:57:18 -0500117 gb_loopback_ro_avg_attr(field);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200118
119#define gb_loopback_attr(field, type) \
120static ssize_t field##_show(struct device *dev, \
121 struct device_attribute *attr, \
122 char *buf) \
123{ \
124 struct gb_connection *connection = to_gb_connection(dev); \
Alex Elder3f12c3e2015-08-03 12:57:11 -0500125 struct gb_loopback *gb = connection->private; \
Alexandre Bailon355a7052015-03-31 09:51:59 +0200126 return sprintf(buf, "%"#type"\n", gb->field); \
127} \
128static ssize_t field##_store(struct device *dev, \
129 struct device_attribute *attr, \
130 const char *buf, \
131 size_t len) \
132{ \
133 int ret; \
134 struct gb_connection *connection = to_gb_connection(dev); \
Alex Elder3f12c3e2015-08-03 12:57:11 -0500135 struct gb_loopback *gb = connection->private; \
Bryan O'Donoghue85d678c2015-07-28 18:34:36 +0100136 mutex_lock(&gb->mutex); \
Alexandre Bailon355a7052015-03-31 09:51:59 +0200137 ret = sscanf(buf, "%"#type, &gb->field); \
Alexandre Bailon355a7052015-03-31 09:51:59 +0200138 if (ret != 1) \
Bryan O'Donoghue85d678c2015-07-28 18:34:36 +0100139 len = -EINVAL; \
140 else \
Bryan O'Donoghuecb60f492015-07-28 18:34:39 +0100141 gb_loopback_check_attr(connection, gb); \
Bryan O'Donoghue85d678c2015-07-28 18:34:36 +0100142 mutex_unlock(&gb->mutex); \
Alexandre Bailon355a7052015-03-31 09:51:59 +0200143 return len; \
144} \
145static DEVICE_ATTR_RW(field)
146
147static void gb_loopback_reset_stats(struct gb_loopback *gb);
Bryan O'Donoghuecb60f492015-07-28 18:34:39 +0100148static void gb_loopback_check_attr(struct gb_connection *connection,
149 struct gb_loopback *gb)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200150{
Alex Elder91262c32015-05-11 21:16:37 -0500151 if (gb->ms_wait > GB_LOOPBACK_MS_WAIT_MAX)
152 gb->ms_wait = GB_LOOPBACK_MS_WAIT_MAX;
Bryan O'Donoghuec3bba872015-07-13 20:20:50 +0100153 if (gb->size > gb->size_max)
154 gb->size = gb->size_max;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200155 gb->error = 0;
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100156 gb->iteration_count = 0;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200157 gb_loopback_reset_stats(gb);
Bryan O'Donoghue3dfe8aa2015-07-24 10:02:56 +0100158
Bryan O'Donoghuecb60f492015-07-28 18:34:39 +0100159 if (kfifo_depth < gb->iteration_max) {
160 dev_warn(&connection->dev,
161 "iteration_max %u kfifo_depth %u cannot log all data\n",
162 gb->iteration_max, kfifo_depth);
163 }
164
Bryan O'Donoghue3dfe8aa2015-07-24 10:02:56 +0100165 switch (gb->type) {
166 case GB_LOOPBACK_TYPE_PING:
167 case GB_LOOPBACK_TYPE_TRANSFER:
168 case GB_LOOPBACK_TYPE_SINK:
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100169 kfifo_reset_out(&gb->kfifo);
Bryan O'Donoghue3dfe8aa2015-07-24 10:02:56 +0100170 wake_up(&gb->wq);
171 break;
172 default:
173 gb->type = 0;
174 break;
175 }
Alexandre Bailon355a7052015-03-31 09:51:59 +0200176}
177
178/* Time to send and receive one message */
179gb_loopback_stats_attrs(latency);
Bryan O'Donoghue583cbf52015-07-21 23:50:10 +0100180/* Number of requests sent per second on this cport */
181gb_loopback_stats_attrs(requests_per_second);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200182/* Quantity of data sent and received on this cport */
183gb_loopback_stats_attrs(throughput);
Bryan O'Donoghuee140c752015-07-21 23:50:09 +0100184/* Number of errors encountered during loop */
Alex Elder19c2a442015-08-03 12:57:17 -0500185gb_loopback_ro_attr(error);
Bryan O'Donoghuee140c752015-07-21 23:50:09 +0100186/* The current index of the for (i = 0; i < iteration_max; i++) loop */
Alex Elder19c2a442015-08-03 12:57:17 -0500187gb_loopback_ro_attr(iteration_count);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200188
189/*
Bryan O'Donoghue799a3f02015-07-21 23:50:07 +0100190 * Type of loopback message to send based on protocol type definitions
Alexandre Bailon355a7052015-03-31 09:51:59 +0200191 * 0 => Don't send message
Bryan O'Donoghue799a3f02015-07-21 23:50:07 +0100192 * 2 => Send ping message continuously (message without payload)
Alex Elder006335a2015-08-03 12:57:10 -0500193 * 3 => Send transfer message continuously (message with payload,
Bryan O'Donoghue799a3f02015-07-21 23:50:07 +0100194 * payload returned in response)
195 * 4 => Send a sink message (message with payload, no payload in response)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200196 */
197gb_loopback_attr(type, d);
198/* Size of transfer message payload: 0-4096 bytes */
199gb_loopback_attr(size, u);
Alex Elder48f19ee2015-05-11 21:16:36 -0500200/* Time to wait between two messages: 0-1000 ms */
Alexandre Bailon355a7052015-03-31 09:51:59 +0200201gb_loopback_attr(ms_wait, d);
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100202/* Maximum iterations for a given operation: 1-(2^32-1), 0 implies infinite */
203gb_loopback_attr(iteration_max, u);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200204
205#define dev_stats_attrs(name) \
206 &dev_attr_##name##_min.attr, \
207 &dev_attr_##name##_max.attr, \
208 &dev_attr_##name##_avg.attr
209
210static struct attribute *loopback_attrs[] = {
211 dev_stats_attrs(latency),
Bryan O'Donoghue583cbf52015-07-21 23:50:10 +0100212 dev_stats_attrs(requests_per_second),
Alexandre Bailon355a7052015-03-31 09:51:59 +0200213 dev_stats_attrs(throughput),
214 &dev_attr_type.attr,
215 &dev_attr_size.attr,
216 &dev_attr_ms_wait.attr,
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100217 &dev_attr_iteration_count.attr,
218 &dev_attr_iteration_max.attr,
Alexandre Bailon355a7052015-03-31 09:51:59 +0200219 &dev_attr_error.attr,
220 NULL,
221};
222ATTRIBUTE_GROUPS(loopback);
223
Bryan O'Donoghue4c192662015-08-11 13:50:53 +0100224static void gb_loopback_calc_latency(struct gb_loopback *gb,
225 struct timeval *ts, struct timeval *te)
226{
227 u64 t1, t2;
228
229 t1 = timeval_to_ns(ts);
230 t2 = timeval_to_ns(te);
Bryan O'Donoghuefd489e12015-08-11 13:50:54 +0100231 if (t2 > t1)
232 gb->elapsed_nsecs = t2 - t1;
233 else
234 gb->elapsed_nsecs = NSEC_PER_DAY - t2 + t1;
Bryan O'Donoghue4c192662015-08-11 13:50:53 +0100235}
236
Bryan O'Donoghuea7e60062015-07-28 18:34:35 +0100237static int gb_loopback_sink(struct gb_loopback *gb, u32 len)
Bryan O'Donoghue384a7a32015-07-13 20:20:49 +0100238{
239 struct timeval ts, te;
Bryan O'Donoghue384a7a32015-07-13 20:20:49 +0100240 struct gb_loopback_transfer_request *request;
241 int retval;
242
243 request = kmalloc(len + sizeof(*request), GFP_KERNEL);
244 if (!request)
245 return -ENOMEM;
246
247 request->len = cpu_to_le32(len);
248
249 do_gettimeofday(&ts);
250 retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_SINK,
251 request, len + sizeof(*request), NULL, 0);
Bryan O'Donoghuec3bba872015-07-13 20:20:50 +0100252
Bryan O'Donoghue384a7a32015-07-13 20:20:49 +0100253 do_gettimeofday(&te);
Bryan O'Donoghue4c192662015-08-11 13:50:53 +0100254 gb_loopback_calc_latency(gb, &ts, &te);
Bryan O'Donoghue384a7a32015-07-13 20:20:49 +0100255
256 kfree(request);
257 return retval;
258}
259
Bryan O'Donoghuea7e60062015-07-28 18:34:35 +0100260static int gb_loopback_transfer(struct gb_loopback *gb, u32 len)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200261{
262 struct timeval ts, te;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200263 struct gb_loopback_transfer_request *request;
264 struct gb_loopback_transfer_response *response;
265 int retval;
266
267 request = kmalloc(len + sizeof(*request), GFP_KERNEL);
268 if (!request)
269 return -ENOMEM;
270 response = kmalloc(len + sizeof(*response), GFP_KERNEL);
271 if (!response) {
272 kfree(request);
273 return -ENOMEM;
274 }
275
276 request->len = cpu_to_le32(len);
277
278 do_gettimeofday(&ts);
279 retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_TRANSFER,
280 request, len + sizeof(*request),
281 response, len + sizeof(*response));
282 do_gettimeofday(&te);
Bryan O'Donoghue4c192662015-08-11 13:50:53 +0100283 gb_loopback_calc_latency(gb, &ts, &te);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200284
285 if (retval)
286 goto gb_error;
287
288 if (memcmp(request->data, response->data, len))
289 retval = -EREMOTEIO;
290
291gb_error:
292 kfree(request);
293 kfree(response);
294
295 return retval;
296}
297
Bryan O'Donoghuea7e60062015-07-28 18:34:35 +0100298static int gb_loopback_ping(struct gb_loopback *gb)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200299{
300 struct timeval ts, te;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200301 int retval;
302
303 do_gettimeofday(&ts);
304 retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_PING,
305 NULL, 0, NULL, 0);
306 do_gettimeofday(&te);
Bryan O'Donoghue4c192662015-08-11 13:50:53 +0100307 gb_loopback_calc_latency(gb, &ts, &te);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200308
309 return retval;
310}
311
Alex Elderac1c2842015-05-11 21:16:39 -0500312static int gb_loopback_request_recv(u8 type, struct gb_operation *operation)
313{
314 struct gb_connection *connection = operation->connection;
Bryan O'Donoghuec3bba872015-07-13 20:20:50 +0100315 struct gb_loopback *gb = connection->private;
Alex Elderac1c2842015-05-11 21:16:39 -0500316 struct gb_loopback_transfer_request *request;
317 struct gb_loopback_transfer_response *response;
Bryan O'Donoghuee51eafe2015-07-14 00:53:13 +0100318 size_t len;
Alex Elderac1c2842015-05-11 21:16:39 -0500319
320 /* By convention, the AP initiates the version operation */
321 switch (type) {
322 case GB_LOOPBACK_TYPE_PROTOCOL_VERSION:
323 dev_err(&connection->dev,
324 "module-initiated version operation\n");
325 return -EINVAL;
326 case GB_LOOPBACK_TYPE_PING:
Bryan O'Donoghue384a7a32015-07-13 20:20:49 +0100327 case GB_LOOPBACK_TYPE_SINK:
Alex Elderac1c2842015-05-11 21:16:39 -0500328 return 0;
329 case GB_LOOPBACK_TYPE_TRANSFER:
330 if (operation->request->payload_size < sizeof(*request)) {
331 dev_err(&connection->dev,
332 "transfer request too small (%zu < %zu)\n",
333 operation->request->payload_size,
334 sizeof(*request));
335 return -EINVAL; /* -EMSGSIZE */
336 }
337 request = operation->request->payload;
338 len = le32_to_cpu(request->len);
Bryan O'Donoghuec3bba872015-07-13 20:20:50 +0100339 if (len > gb->size_max) {
340 dev_err(&connection->dev,
341 "transfer request too large (%zu > %zu)\n",
342 len, gb->size_max);
343 return -EINVAL;
344 }
345
Alex Elderac1c2842015-05-11 21:16:39 -0500346 if (len) {
Johan Hovold1c7658c2015-07-17 18:50:25 +0200347 if (!gb_operation_response_alloc(operation, len,
348 GFP_KERNEL)) {
Alex Elderac1c2842015-05-11 21:16:39 -0500349 dev_err(&connection->dev,
350 "error allocating response\n");
351 return -ENOMEM;
352 }
353 response = operation->response->payload;
354 memcpy(response->data, request->data, len);
355 }
356 return 0;
357 default:
358 dev_err(&connection->dev,
359 "unsupported request: %hhu\n", type);
360 return -EINVAL;
361 }
362}
363
Alexandre Bailon355a7052015-03-31 09:51:59 +0200364static void gb_loopback_reset_stats(struct gb_loopback *gb)
365{
366 struct gb_loopback_stats reset = {
Alex Eldere051c822015-08-03 12:57:14 -0500367 .min = U32_MAX,
Alexandre Bailon355a7052015-03-31 09:51:59 +0200368 };
369 memcpy(&gb->latency, &reset, sizeof(struct gb_loopback_stats));
370 memcpy(&gb->throughput, &reset, sizeof(struct gb_loopback_stats));
Bryan O'Donoghue583cbf52015-07-21 23:50:10 +0100371 memcpy(&gb->requests_per_second, &reset,
372 sizeof(struct gb_loopback_stats));
Alexandre Bailon355a7052015-03-31 09:51:59 +0200373}
374
Alex Eldera6e7e532015-08-03 12:57:13 -0500375static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u32 val)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200376{
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100377 if (stats->min > val)
378 stats->min = val;
379 if (stats->max < val)
380 stats->max = val;
381 stats->sum += val;
382 stats->count++;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200383}
384
Bryan O'Donoghue583cbf52015-07-21 23:50:10 +0100385static void gb_loopback_requests_update(struct gb_loopback *gb, u32 latency)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200386{
Bryan O'Donoghue583cbf52015-07-21 23:50:10 +0100387 u32 req = USEC_PER_SEC;
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100388
Bryan O'Donoghue583cbf52015-07-21 23:50:10 +0100389 do_div(req, latency);
390 gb_loopback_update_stats(&gb->requests_per_second, req);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200391}
392
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100393static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200394{
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100395 u32 throughput;
Bryan O'Donoghuef7908e42015-07-13 20:20:51 +0100396 u32 aggregate_size = sizeof(struct gb_operation_msg_hdr) * 2;
397
398 switch (gb->type) {
399 case GB_LOOPBACK_TYPE_PING:
400 break;
401 case GB_LOOPBACK_TYPE_SINK:
402 aggregate_size += sizeof(struct gb_loopback_transfer_request) +
403 gb->size;
404 break;
405 case GB_LOOPBACK_TYPE_TRANSFER:
406 aggregate_size += sizeof(struct gb_loopback_transfer_request) +
407 sizeof(struct gb_loopback_transfer_response) +
408 gb->size * 2;
409 break;
410 default:
411 return;
412 }
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100413
414 /* Calculate bytes per second */
415 throughput = USEC_PER_SEC;
416 do_div(throughput, latency);
417 throughput *= aggregate_size;
418 gb_loopback_update_stats(&gb->throughput, throughput);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200419}
420
Bryan O'Donoghuea7e60062015-07-28 18:34:35 +0100421static void gb_loopback_calculate_stats(struct gb_loopback *gb)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200422{
423 u32 lat;
Johan Hovold1ffc12b2015-04-16 09:53:59 +0200424 u64 tmp;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200425
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100426 /* Express latency in terms of microseconds */
427 tmp = gb->elapsed_nsecs;
428 do_div(tmp, NSEC_PER_USEC);
Johan Hovold1ffc12b2015-04-16 09:53:59 +0200429 lat = tmp;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200430
Alex Elder006335a2015-08-03 12:57:10 -0500431 /* Log latency statistic */
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100432 gb_loopback_update_stats(&gb->latency, lat);
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100433 kfifo_in(&gb->kfifo, (unsigned char *)&lat, sizeof(lat));
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100434
Bryan O'Donoghue583cbf52015-07-21 23:50:10 +0100435 /* Log throughput and requests using latency as benchmark */
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100436 gb_loopback_throughput_update(gb, lat);
Bryan O'Donoghue583cbf52015-07-21 23:50:10 +0100437 gb_loopback_requests_update(gb, lat);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200438}
439
440static int gb_loopback_fn(void *data)
441{
442 int error = 0;
Bryan O'Donoghue85d678c2015-07-28 18:34:36 +0100443 int ms_wait;
Alex Elder3f12c3e2015-08-03 12:57:11 -0500444 struct gb_loopback *gb = data;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200445
Bryan O'Donoghue3dfe8aa2015-07-24 10:02:56 +0100446 while (1) {
447 if (!gb->type)
448 wait_event_interruptible(gb->wq, gb->type ||
449 kthread_should_stop());
450 if (kthread_should_stop())
451 break;
Bryan O'Donoghue85d678c2015-07-28 18:34:36 +0100452
453 mutex_lock(&gb->mutex);
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100454 if (gb->iteration_max) {
455 if (gb->iteration_count < gb->iteration_max) {
456 gb->iteration_count++;
Greg Kroah-Hartmanc2939f42015-07-22 11:09:23 -0700457 sysfs_notify(&gb->connection->dev.kobj, NULL,
458 "iteration_count");
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100459 } else {
460 gb->type = 0;
Bryan O'Donoghue85d678c2015-07-28 18:34:36 +0100461 mutex_unlock(&gb->mutex);
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100462 continue;
463 }
464 }
Bryan O'Donoghuea598f432015-07-13 20:20:48 +0100465 if (gb->type == GB_LOOPBACK_TYPE_PING)
Bryan O'Donoghuea7e60062015-07-28 18:34:35 +0100466 error = gb_loopback_ping(gb);
Bryan O'Donoghuea598f432015-07-13 20:20:48 +0100467 else if (gb->type == GB_LOOPBACK_TYPE_TRANSFER)
Bryan O'Donoghuea7e60062015-07-28 18:34:35 +0100468 error = gb_loopback_transfer(gb, gb->size);
Bryan O'Donoghue384a7a32015-07-13 20:20:49 +0100469 else if (gb->type == GB_LOOPBACK_TYPE_SINK)
Bryan O'Donoghuea7e60062015-07-28 18:34:35 +0100470 error = gb_loopback_sink(gb, gb->size);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200471 if (error)
472 gb->error++;
Bryan O'Donoghuea7e60062015-07-28 18:34:35 +0100473 gb_loopback_calculate_stats(gb);
Bryan O'Donoghue85d678c2015-07-28 18:34:36 +0100474 ms_wait = gb->ms_wait;
475 mutex_unlock(&gb->mutex);
476 if (ms_wait)
477 msleep(ms_wait);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200478 }
479 return 0;
480}
481
482static int gb_loopback_connection_init(struct gb_connection *connection)
483{
484 struct gb_loopback *gb;
485 int retval;
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100486 int minor;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200487
488 gb = kzalloc(sizeof(*gb), GFP_KERNEL);
489 if (!gb)
490 return -ENOMEM;
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100491 gb_loopback_reset_stats(gb);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200492
493 gb->connection = connection;
494 connection->private = gb;
Greg Kroah-Hartman7a51b932015-03-31 23:02:34 +0200495 retval = sysfs_create_groups(&connection->dev.kobj, loopback_groups);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200496 if (retval)
Phong Tran6f8528e2015-05-14 23:03:04 +0700497 goto out_free;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200498
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100499 /* Get a minor number */
500 minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL);
501 if (minor < 0) {
502 retval = minor;
Alex Elder1fb807c2015-08-03 12:57:20 -0500503 goto out_sysfs;
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100504 }
505
Bryan O'Donoghuec3bba872015-07-13 20:20:50 +0100506 /* Calculate maximum payload */
507 gb->size_max = gb_operation_get_payload_size_max(connection);
508 if (gb->size_max <= sizeof(struct gb_loopback_transfer_request)) {
509 retval = -EINVAL;
Alex Elder1fb807c2015-08-03 12:57:20 -0500510 goto out_minor;
Bryan O'Donoghuec3bba872015-07-13 20:20:50 +0100511 }
512 gb->size_max -= sizeof(struct gb_loopback_transfer_request);
513
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100514 /* Allocate kfifo */
515 if (kfifo_alloc(&gb->kfifo, kfifo_depth * sizeof(u32),
516 GFP_KERNEL)) {
517 retval = -ENOMEM;
Alex Elder1fb807c2015-08-03 12:57:20 -0500518 goto out_minor;
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100519 }
520
521 /* Create device entry */
522 gb->dev = MKDEV(loopback_major, minor);
523 cdev_init(&gb->cdev, &loopback_fops);
524 retval = cdev_add(&gb->cdev, gb->dev, 1);
525 if (retval)
Alex Elder1fb807c2015-08-03 12:57:20 -0500526 goto out_kfifo;
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100527
528 gb->device = device_create(loopback_class, &connection->dev, gb->dev,
529 gb, "gb!loopback%d", minor);
530 if (IS_ERR(gb->device)) {
531 retval = PTR_ERR(gb->device);
Alex Elder1fb807c2015-08-03 12:57:20 -0500532 goto out_cdev;
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100533 }
534
535 /* Fork worker thread */
Bryan O'Donoghue3dfe8aa2015-07-24 10:02:56 +0100536 init_waitqueue_head(&gb->wq);
Bryan O'Donoghue85d678c2015-07-28 18:34:36 +0100537 mutex_init(&gb->mutex);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200538 gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback");
539 if (IS_ERR(gb->task)) {
Alex Elder69f60342015-05-11 21:16:35 -0500540 retval = PTR_ERR(gb->task);
Alex Elder1fb807c2015-08-03 12:57:20 -0500541 goto out_device;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200542 }
543
544 return 0;
545
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100546out_device:
Alex Elder1fb807c2015-08-03 12:57:20 -0500547 device_del(gb->device);
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100548out_cdev:
Alex Elder1fb807c2015-08-03 12:57:20 -0500549 cdev_del(&gb->cdev);
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100550out_kfifo:
551 kfifo_free(&gb->kfifo);
Alex Elder1fb807c2015-08-03 12:57:20 -0500552out_minor:
553 ida_simple_remove(&minors, minor);
554out_sysfs:
Phong Tran6f8528e2015-05-14 23:03:04 +0700555 sysfs_remove_groups(&connection->dev.kobj, loopback_groups);
556out_free:
Alex Elder1fb807c2015-08-03 12:57:20 -0500557 connection->private = NULL;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200558 kfree(gb);
Alex Elder1fb807c2015-08-03 12:57:20 -0500559
Alexandre Bailon355a7052015-03-31 09:51:59 +0200560 return retval;
561}
562
563static void gb_loopback_connection_exit(struct gb_connection *connection)
564{
565 struct gb_loopback *gb = connection->private;
566
Alex Elder1fb807c2015-08-03 12:57:20 -0500567 connection->private = NULL;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200568 if (!IS_ERR_OR_NULL(gb->task))
569 kthread_stop(gb->task);
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100570
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100571 device_del(gb->device);
Alex Elder1fb807c2015-08-03 12:57:20 -0500572 cdev_del(&gb->cdev);
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100573 kfifo_free(&gb->kfifo);
Alex Elder1fb807c2015-08-03 12:57:20 -0500574 ida_simple_remove(&minors, MINOR(gb->dev));
Greg Kroah-Hartman7a51b932015-03-31 23:02:34 +0200575 sysfs_remove_groups(&connection->dev.kobj, loopback_groups);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200576 kfree(gb);
577}
578
579static struct gb_protocol loopback_protocol = {
580 .name = "loopback",
581 .id = GREYBUS_PROTOCOL_LOOPBACK,
582 .major = GB_LOOPBACK_VERSION_MAJOR,
583 .minor = GB_LOOPBACK_VERSION_MINOR,
584 .connection_init = gb_loopback_connection_init,
585 .connection_exit = gb_loopback_connection_exit,
Alex Elderac1c2842015-05-11 21:16:39 -0500586 .request_recv = gb_loopback_request_recv,
Alexandre Bailon355a7052015-03-31 09:51:59 +0200587};
588
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100589static int loopback_open(struct inode *inode, struct file *file)
590{
591 struct cdev *cdev = inode->i_cdev;
592 struct gb_loopback *gb = container_of(cdev, struct gb_loopback, cdev);
593
594 file->private_data = gb;
595 return 0;
596}
597
598static ssize_t loopback_read(struct file *file, char __user *buf, size_t count,
599 loff_t *ppos)
600{
601 struct gb_loopback *gb = file->private_data;
602 size_t fifo_len;
603 int retval;
604
Alex Elder006335a2015-08-03 12:57:10 -0500605 if (!count || count % sizeof(u32))
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100606 return -EINVAL;
607
608 mutex_lock(&gb->mutex);
609 fifo_len = kfifo_len(&gb->kfifo);
610 if (kfifo_to_user(&gb->kfifo, buf, min(fifo_len, count), &retval) != 0)
611 retval = -EIO;
612 mutex_unlock(&gb->mutex);
613
614 return retval;
615}
616
617static const struct file_operations loopback_fops = {
618 .owner = THIS_MODULE,
619 .read = loopback_read,
620 .open = loopback_open,
621 .llseek = noop_llseek,
622};
623
624static int loopback_init(void)
625{
626 dev_t dev;
627 int retval;
628
629 loopback_class = class_create(THIS_MODULE, "gb_loopback");
630 if (IS_ERR(loopback_class)) {
631 retval = PTR_ERR(loopback_class);
632 goto error_class;
633 }
634
635 retval = alloc_chrdev_region(&dev, 0, NUM_MINORS, "gb_loopback");
636 if (retval < 0)
637 goto error_chrdev;
638
639 loopback_major = MAJOR(dev);
640
641 retval = gb_protocol_register(&loopback_protocol);
642 if (retval)
643 goto error_gb;
644
645 return 0;
646
647error_gb:
648 unregister_chrdev_region(dev, NUM_MINORS);
649error_chrdev:
650 class_destroy(loopback_class);
651error_class:
652 return retval;
653}
654module_init(loopback_init);
655
656static void __exit loopback_exit(void)
657{
658 gb_protocol_deregister(&loopback_protocol);
659 unregister_chrdev_region(MKDEV(loopback_major, 0), NUM_MINORS);
660 class_destroy(loopback_class);
661 ida_destroy(&minors);
662}
663module_exit(loopback_exit);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200664
665MODULE_LICENSE("GPL v2");