greybus: es-drivers: add outbound timestamp to connection
[linux-2.6-block.git] / drivers / staging / greybus / loopback.c
CommitLineData
355a7058
AB
1/*
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>
85d678c0 11#include <linux/mutex.h>
355a7058
AB
12#include <linux/slab.h>
13#include <linux/kthread.h>
14#include <linux/delay.h>
15#include <linux/random.h>
5679f783 16#include <linux/sizes.h>
cbd204b4
BD
17#include <linux/cdev.h>
18#include <linux/fs.h>
19#include <linux/kfifo.h>
20
1ffc12be
JH
21#include <asm/div64.h>
22
355a7058
AB
23#include "greybus.h"
24
25struct gb_loopback_stats {
e051c82b
AE
26 u32 min;
27 u32 max;
00af6583 28 u64 sum;
e13fc28f 29 u32 count;
355a7058
AB
30};
31
32struct gb_loopback {
33 struct gb_connection *connection;
355a7058 34
cbd204b4 35 struct kfifo kfifo;
85d678c0 36 struct mutex mutex;
355a7058 37 struct task_struct *task;
3dfe8aaa 38 wait_queue_head_t wq;
cbd204b4
BD
39 dev_t dev;
40 struct cdev cdev;
41 struct device *device;
355a7058
AB
42
43 int type;
44 u32 size;
00af6583
BD
45 u32 iteration_max;
46 u32 iteration_count;
e51eafeb 47 size_t size_max;
355a7058
AB
48 int ms_wait;
49
50 struct gb_loopback_stats latency;
51 struct gb_loopback_stats throughput;
583cbf50 52 struct gb_loopback_stats requests_per_second;
355a7058
AB
53 u64 elapsed_nsecs;
54 u32 error;
55};
56
cbd204b4
BD
57#define GB_LOOPBACK_FIFO_DEFAULT 8192
58
59static struct class *loopback_class;
60static int loopback_major;
61static const struct file_operations loopback_fops;
62static DEFINE_IDA(minors);
63static unsigned kfifo_depth = GB_LOOPBACK_FIFO_DEFAULT;
64module_param(kfifo_depth, uint, 0444);
65
66/* Number of minor devices this driver supports */
67#define NUM_MINORS 256
68
69/* Maximum size of any one send data buffer we support */
70#define MAX_PACKET_SIZE (PAGE_SIZE * 2)
71
91262c3a 72#define GB_LOOPBACK_MS_WAIT_MAX 1000
355a7058 73
355a7058 74/* interface sysfs attributes */
19c2a443 75#define gb_loopback_ro_attr(field) \
355a7058
AB
76static ssize_t field##_show(struct device *dev, \
77 struct device_attribute *attr, \
78 char *buf) \
79{ \
80 struct gb_connection *connection = to_gb_connection(dev); \
3f12c3ed 81 struct gb_loopback *gb = connection->private; \
19c2a443 82 return sprintf(buf, "%u\n", gb->field); \
355a7058
AB
83} \
84static DEVICE_ATTR_RO(field)
85
86#define gb_loopback_ro_stats_attr(name, field, type) \
87static ssize_t name##_##field##_show(struct device *dev, \
88 struct device_attribute *attr, \
89 char *buf) \
90{ \
91 struct gb_connection *connection = to_gb_connection(dev); \
3f12c3ed 92 struct gb_loopback *gb = connection->private; \
355a7058
AB
93 return sprintf(buf, "%"#type"\n", gb->name.field); \
94} \
95static DEVICE_ATTR_RO(name##_##field)
96
7a135a96
AE
97#define gb_loopback_ro_avg_attr(name) \
98static ssize_t name##_avg_show(struct device *dev, \
99 struct device_attribute *attr, \
100 char *buf) \
101{ \
102 struct gb_connection *connection = to_gb_connection(dev); \
103 struct gb_loopback *gb = connection->private; \
ff71d395
AE
104 struct gb_loopback_stats *stats = &gb->name; \
105 u32 count = stats->count ? stats->count : 1; \
106 u64 avg = stats->sum + count / 2; /* round closest */ \
107 u32 rem = do_div(avg, count); \
108 return sprintf(buf, "%llu.%06u\n", avg, 1000000 * rem / count); \
7a135a96
AE
109} \
110static DEVICE_ATTR_RO(name##_avg)
111
355a7058 112#define gb_loopback_stats_attrs(field) \
e051c82b
AE
113 gb_loopback_ro_stats_attr(field, min, u); \
114 gb_loopback_ro_stats_attr(field, max, u); \
7a135a96 115 gb_loopback_ro_avg_attr(field);
355a7058
AB
116
117#define gb_loopback_attr(field, type) \
118static ssize_t field##_show(struct device *dev, \
119 struct device_attribute *attr, \
120 char *buf) \
121{ \
122 struct gb_connection *connection = to_gb_connection(dev); \
3f12c3ed 123 struct gb_loopback *gb = connection->private; \
355a7058
AB
124 return sprintf(buf, "%"#type"\n", gb->field); \
125} \
126static ssize_t field##_store(struct device *dev, \
127 struct device_attribute *attr, \
128 const char *buf, \
129 size_t len) \
130{ \
131 int ret; \
132 struct gb_connection *connection = to_gb_connection(dev); \
3f12c3ed 133 struct gb_loopback *gb = connection->private; \
85d678c0 134 mutex_lock(&gb->mutex); \
355a7058 135 ret = sscanf(buf, "%"#type, &gb->field); \
355a7058 136 if (ret != 1) \
85d678c0
BD
137 len = -EINVAL; \
138 else \
cb60f496 139 gb_loopback_check_attr(connection, gb); \
85d678c0 140 mutex_unlock(&gb->mutex); \
355a7058
AB
141 return len; \
142} \
143static DEVICE_ATTR_RW(field)
144
145static void gb_loopback_reset_stats(struct gb_loopback *gb);
cb60f496
BD
146static void gb_loopback_check_attr(struct gb_connection *connection,
147 struct gb_loopback *gb)
355a7058 148{
3dfe8aaa
BD
149 if (gb->ms_wait > GB_LOOPBACK_MS_WAIT_MAX)
150 gb->ms_wait = GB_LOOPBACK_MS_WAIT_MAX;
151 if (gb->size > gb->size_max)
152 gb->size = gb->size_max;
153 gb->error = 0;
154 gb->iteration_count = 0;
155 gb_loopback_reset_stats(gb);
156
cb60f496
BD
157 if (kfifo_depth < gb->iteration_max) {
158 dev_warn(&connection->dev,
159 "iteration_max %u kfifo_depth %u cannot log all data\n",
160 gb->iteration_max, kfifo_depth);
161 }
162
a598f438
BD
163 switch (gb->type) {
164 case GB_LOOPBACK_TYPE_PING:
165 case GB_LOOPBACK_TYPE_TRANSFER:
384a7a3c 166 case GB_LOOPBACK_TYPE_SINK:
cbd204b4 167 kfifo_reset_out(&gb->kfifo);
3dfe8aaa 168 wake_up(&gb->wq);
a598f438
BD
169 break;
170 default:
171 gb->type = 0;
172 break;
173 }
355a7058
AB
174}
175
176/* Time to send and receive one message */
177gb_loopback_stats_attrs(latency);
583cbf50
BD
178/* Number of requests sent per second on this cport */
179gb_loopback_stats_attrs(requests_per_second);
355a7058
AB
180/* Quantity of data sent and received on this cport */
181gb_loopback_stats_attrs(throughput);
e140c75e 182/* Number of errors encountered during loop */
19c2a443 183gb_loopback_ro_attr(error);
e140c75e 184/* The current index of the for (i = 0; i < iteration_max; i++) loop */
19c2a443 185gb_loopback_ro_attr(iteration_count);
355a7058
AB
186
187/*
799a3f03 188 * Type of loopback message to send based on protocol type definitions
355a7058 189 * 0 => Don't send message
799a3f03 190 * 2 => Send ping message continuously (message without payload)
006335a0 191 * 3 => Send transfer message continuously (message with payload,
799a3f03
BD
192 * payload returned in response)
193 * 4 => Send a sink message (message with payload, no payload in response)
355a7058
AB
194 */
195gb_loopback_attr(type, d);
196/* Size of transfer message payload: 0-4096 bytes */
197gb_loopback_attr(size, u);
48f19ee8 198/* Time to wait between two messages: 0-1000 ms */
355a7058 199gb_loopback_attr(ms_wait, d);
00af6583
BD
200/* Maximum iterations for a given operation: 1-(2^32-1), 0 implies infinite */
201gb_loopback_attr(iteration_max, u);
355a7058
AB
202
203#define dev_stats_attrs(name) \
204 &dev_attr_##name##_min.attr, \
205 &dev_attr_##name##_max.attr, \
206 &dev_attr_##name##_avg.attr
207
208static struct attribute *loopback_attrs[] = {
209 dev_stats_attrs(latency),
583cbf50 210 dev_stats_attrs(requests_per_second),
355a7058
AB
211 dev_stats_attrs(throughput),
212 &dev_attr_type.attr,
213 &dev_attr_size.attr,
214 &dev_attr_ms_wait.attr,
00af6583
BD
215 &dev_attr_iteration_count.attr,
216 &dev_attr_iteration_max.attr,
355a7058
AB
217 &dev_attr_error.attr,
218 NULL,
219};
220ATTRIBUTE_GROUPS(loopback);
221
a7e60062 222static int gb_loopback_sink(struct gb_loopback *gb, u32 len)
384a7a3c
BD
223{
224 struct timeval ts, te;
384a7a3c
BD
225 struct gb_loopback_transfer_request *request;
226 int retval;
227
228 request = kmalloc(len + sizeof(*request), GFP_KERNEL);
229 if (!request)
230 return -ENOMEM;
231
232 request->len = cpu_to_le32(len);
233
234 do_gettimeofday(&ts);
235 retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_SINK,
236 request, len + sizeof(*request), NULL, 0);
c3bba87a 237
384a7a3c 238 do_gettimeofday(&te);
a7e60062 239 gb->elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts);
384a7a3c
BD
240
241 kfree(request);
242 return retval;
243}
244
a7e60062 245static int gb_loopback_transfer(struct gb_loopback *gb, u32 len)
355a7058
AB
246{
247 struct timeval ts, te;
355a7058
AB
248 struct gb_loopback_transfer_request *request;
249 struct gb_loopback_transfer_response *response;
250 int retval;
251
252 request = kmalloc(len + sizeof(*request), GFP_KERNEL);
253 if (!request)
254 return -ENOMEM;
255 response = kmalloc(len + sizeof(*response), GFP_KERNEL);
256 if (!response) {
257 kfree(request);
258 return -ENOMEM;
259 }
260
261 request->len = cpu_to_le32(len);
262
263 do_gettimeofday(&ts);
264 retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_TRANSFER,
265 request, len + sizeof(*request),
266 response, len + sizeof(*response));
267 do_gettimeofday(&te);
a7e60062 268 gb->elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts);
355a7058
AB
269
270 if (retval)
271 goto gb_error;
272
273 if (memcmp(request->data, response->data, len))
274 retval = -EREMOTEIO;
275
276gb_error:
277 kfree(request);
278 kfree(response);
279
280 return retval;
281}
282
a7e60062 283static int gb_loopback_ping(struct gb_loopback *gb)
355a7058
AB
284{
285 struct timeval ts, te;
355a7058
AB
286 int retval;
287
288 do_gettimeofday(&ts);
289 retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_PING,
290 NULL, 0, NULL, 0);
291 do_gettimeofday(&te);
a7e60062 292 gb->elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts);
355a7058
AB
293
294 return retval;
295}
296
ac1c2840
AE
297static int gb_loopback_request_recv(u8 type, struct gb_operation *operation)
298{
299 struct gb_connection *connection = operation->connection;
c3bba87a 300 struct gb_loopback *gb = connection->private;
ac1c2840
AE
301 struct gb_loopback_transfer_request *request;
302 struct gb_loopback_transfer_response *response;
e51eafeb 303 size_t len;
ac1c2840
AE
304
305 /* By convention, the AP initiates the version operation */
306 switch (type) {
307 case GB_LOOPBACK_TYPE_PROTOCOL_VERSION:
308 dev_err(&connection->dev,
309 "module-initiated version operation\n");
310 return -EINVAL;
311 case GB_LOOPBACK_TYPE_PING:
384a7a3c 312 case GB_LOOPBACK_TYPE_SINK:
ac1c2840
AE
313 return 0;
314 case GB_LOOPBACK_TYPE_TRANSFER:
315 if (operation->request->payload_size < sizeof(*request)) {
316 dev_err(&connection->dev,
317 "transfer request too small (%zu < %zu)\n",
318 operation->request->payload_size,
319 sizeof(*request));
320 return -EINVAL; /* -EMSGSIZE */
321 }
322 request = operation->request->payload;
323 len = le32_to_cpu(request->len);
c3bba87a
BD
324 if (len > gb->size_max) {
325 dev_err(&connection->dev,
326 "transfer request too large (%zu > %zu)\n",
327 len, gb->size_max);
328 return -EINVAL;
329 }
330
ac1c2840 331 if (len) {
1c7658cf
JH
332 if (!gb_operation_response_alloc(operation, len,
333 GFP_KERNEL)) {
ac1c2840
AE
334 dev_err(&connection->dev,
335 "error allocating response\n");
336 return -ENOMEM;
337 }
338 response = operation->response->payload;
339 memcpy(response->data, request->data, len);
340 }
341 return 0;
342 default:
343 dev_err(&connection->dev,
344 "unsupported request: %hhu\n", type);
345 return -EINVAL;
346 }
347}
348
355a7058
AB
349static void gb_loopback_reset_stats(struct gb_loopback *gb)
350{
351 struct gb_loopback_stats reset = {
e051c82b 352 .min = U32_MAX,
355a7058
AB
353 };
354 memcpy(&gb->latency, &reset, sizeof(struct gb_loopback_stats));
355 memcpy(&gb->throughput, &reset, sizeof(struct gb_loopback_stats));
583cbf50
BD
356 memcpy(&gb->requests_per_second, &reset,
357 sizeof(struct gb_loopback_stats));
355a7058
AB
358}
359
a6e7e535 360static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u32 val)
355a7058 361{
00af6583
BD
362 if (stats->min > val)
363 stats->min = val;
364 if (stats->max < val)
365 stats->max = val;
366 stats->sum += val;
367 stats->count++;
355a7058
AB
368}
369
583cbf50 370static void gb_loopback_requests_update(struct gb_loopback *gb, u32 latency)
355a7058 371{
583cbf50 372 u32 req = USEC_PER_SEC;
00af6583 373
583cbf50
BD
374 do_div(req, latency);
375 gb_loopback_update_stats(&gb->requests_per_second, req);
355a7058
AB
376}
377
00af6583 378static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency)
355a7058 379{
00af6583 380 u32 throughput;
f7908e4d
BD
381 u32 aggregate_size = sizeof(struct gb_operation_msg_hdr) * 2;
382
383 switch (gb->type) {
384 case GB_LOOPBACK_TYPE_PING:
385 break;
386 case GB_LOOPBACK_TYPE_SINK:
387 aggregate_size += sizeof(struct gb_loopback_transfer_request) +
388 gb->size;
389 break;
390 case GB_LOOPBACK_TYPE_TRANSFER:
391 aggregate_size += sizeof(struct gb_loopback_transfer_request) +
392 sizeof(struct gb_loopback_transfer_response) +
393 gb->size * 2;
394 break;
395 default:
396 return;
397 }
00af6583
BD
398
399 /* Calculate bytes per second */
400 throughput = USEC_PER_SEC;
401 do_div(throughput, latency);
402 throughput *= aggregate_size;
403 gb_loopback_update_stats(&gb->throughput, throughput);
355a7058
AB
404}
405
a7e60062 406static void gb_loopback_calculate_stats(struct gb_loopback *gb)
355a7058
AB
407{
408 u32 lat;
1ffc12be 409 u64 tmp;
355a7058 410
00af6583
BD
411 /* Express latency in terms of microseconds */
412 tmp = gb->elapsed_nsecs;
413 do_div(tmp, NSEC_PER_USEC);
1ffc12be 414 lat = tmp;
355a7058 415
006335a0 416 /* Log latency statistic */
00af6583 417 gb_loopback_update_stats(&gb->latency, lat);
cbd204b4 418 kfifo_in(&gb->kfifo, (unsigned char *)&lat, sizeof(lat));
00af6583 419
583cbf50 420 /* Log throughput and requests using latency as benchmark */
00af6583 421 gb_loopback_throughput_update(gb, lat);
583cbf50 422 gb_loopback_requests_update(gb, lat);
355a7058
AB
423}
424
425static int gb_loopback_fn(void *data)
426{
427 int error = 0;
85d678c0 428 int ms_wait;
3f12c3ed 429 struct gb_loopback *gb = data;
355a7058 430
3dfe8aaa
BD
431 while (1) {
432 if (!gb->type)
433 wait_event_interruptible(gb->wq, gb->type ||
434 kthread_should_stop());
435 if (kthread_should_stop())
436 break;
85d678c0
BD
437
438 mutex_lock(&gb->mutex);
00af6583
BD
439 if (gb->iteration_max) {
440 if (gb->iteration_count < gb->iteration_max) {
441 gb->iteration_count++;
c2939f41
GKH
442 sysfs_notify(&gb->connection->dev.kobj, NULL,
443 "iteration_count");
00af6583
BD
444 } else {
445 gb->type = 0;
85d678c0 446 mutex_unlock(&gb->mutex);
00af6583
BD
447 continue;
448 }
449 }
a598f438 450 if (gb->type == GB_LOOPBACK_TYPE_PING)
a7e60062 451 error = gb_loopback_ping(gb);
a598f438 452 else if (gb->type == GB_LOOPBACK_TYPE_TRANSFER)
a7e60062 453 error = gb_loopback_transfer(gb, gb->size);
384a7a3c 454 else if (gb->type == GB_LOOPBACK_TYPE_SINK)
a7e60062 455 error = gb_loopback_sink(gb, gb->size);
355a7058
AB
456 if (error)
457 gb->error++;
a7e60062 458 gb_loopback_calculate_stats(gb);
85d678c0
BD
459 ms_wait = gb->ms_wait;
460 mutex_unlock(&gb->mutex);
461 if (ms_wait)
462 msleep(ms_wait);
355a7058
AB
463 }
464 return 0;
465}
466
467static int gb_loopback_connection_init(struct gb_connection *connection)
468{
469 struct gb_loopback *gb;
470 int retval;
cbd204b4 471 int minor;
355a7058
AB
472
473 gb = kzalloc(sizeof(*gb), GFP_KERNEL);
474 if (!gb)
475 return -ENOMEM;
cbd204b4 476 gb_loopback_reset_stats(gb);
355a7058
AB
477
478 gb->connection = connection;
479 connection->private = gb;
7a51b936 480 retval = sysfs_create_groups(&connection->dev.kobj, loopback_groups);
355a7058 481 if (retval)
6f8528e0 482 goto out_free;
355a7058 483
cbd204b4
BD
484 /* Get a minor number */
485 minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL);
486 if (minor < 0) {
487 retval = minor;
1fb807cf 488 goto out_sysfs;
cbd204b4
BD
489 }
490
c3bba87a
BD
491 /* Calculate maximum payload */
492 gb->size_max = gb_operation_get_payload_size_max(connection);
493 if (gb->size_max <= sizeof(struct gb_loopback_transfer_request)) {
494 retval = -EINVAL;
1fb807cf 495 goto out_minor;
c3bba87a
BD
496 }
497 gb->size_max -= sizeof(struct gb_loopback_transfer_request);
498
cbd204b4
BD
499 /* Allocate kfifo */
500 if (kfifo_alloc(&gb->kfifo, kfifo_depth * sizeof(u32),
501 GFP_KERNEL)) {
502 retval = -ENOMEM;
1fb807cf 503 goto out_minor;
cbd204b4
BD
504 }
505
506 /* Create device entry */
507 gb->dev = MKDEV(loopback_major, minor);
508 cdev_init(&gb->cdev, &loopback_fops);
509 retval = cdev_add(&gb->cdev, gb->dev, 1);
510 if (retval)
1fb807cf 511 goto out_kfifo;
cbd204b4
BD
512
513 gb->device = device_create(loopback_class, &connection->dev, gb->dev,
514 gb, "gb!loopback%d", minor);
515 if (IS_ERR(gb->device)) {
516 retval = PTR_ERR(gb->device);
1fb807cf 517 goto out_cdev;
cbd204b4
BD
518 }
519
520 /* Fork worker thread */
3dfe8aaa 521 init_waitqueue_head(&gb->wq);
85d678c0 522 mutex_init(&gb->mutex);
355a7058
AB
523 gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback");
524 if (IS_ERR(gb->task)) {
69f60347 525 retval = PTR_ERR(gb->task);
1fb807cf 526 goto out_device;
355a7058
AB
527 }
528
529 return 0;
530
cbd204b4 531out_device:
1fb807cf 532 device_del(gb->device);
cbd204b4 533out_cdev:
1fb807cf 534 cdev_del(&gb->cdev);
cbd204b4
BD
535out_kfifo:
536 kfifo_free(&gb->kfifo);
1fb807cf
AE
537out_minor:
538 ida_simple_remove(&minors, minor);
539out_sysfs:
6f8528e0
PT
540 sysfs_remove_groups(&connection->dev.kobj, loopback_groups);
541out_free:
1fb807cf 542 connection->private = NULL;
355a7058 543 kfree(gb);
1fb807cf 544
355a7058
AB
545 return retval;
546}
547
548static void gb_loopback_connection_exit(struct gb_connection *connection)
549{
550 struct gb_loopback *gb = connection->private;
551
1fb807cf 552 connection->private = NULL;
355a7058
AB
553 if (!IS_ERR_OR_NULL(gb->task))
554 kthread_stop(gb->task);
cbd204b4 555
cbd204b4 556 device_del(gb->device);
1fb807cf 557 cdev_del(&gb->cdev);
cbd204b4 558 kfifo_free(&gb->kfifo);
1fb807cf 559 ida_simple_remove(&minors, MINOR(gb->dev));
7a51b936 560 sysfs_remove_groups(&connection->dev.kobj, loopback_groups);
355a7058
AB
561 kfree(gb);
562}
563
564static struct gb_protocol loopback_protocol = {
565 .name = "loopback",
566 .id = GREYBUS_PROTOCOL_LOOPBACK,
567 .major = GB_LOOPBACK_VERSION_MAJOR,
568 .minor = GB_LOOPBACK_VERSION_MINOR,
569 .connection_init = gb_loopback_connection_init,
570 .connection_exit = gb_loopback_connection_exit,
ac1c2840 571 .request_recv = gb_loopback_request_recv,
355a7058
AB
572};
573
cbd204b4
BD
574static int loopback_open(struct inode *inode, struct file *file)
575{
576 struct cdev *cdev = inode->i_cdev;
577 struct gb_loopback *gb = container_of(cdev, struct gb_loopback, cdev);
578
579 file->private_data = gb;
580 return 0;
581}
582
583static ssize_t loopback_read(struct file *file, char __user *buf, size_t count,
584 loff_t *ppos)
585{
586 struct gb_loopback *gb = file->private_data;
587 size_t fifo_len;
588 int retval;
589
006335a0 590 if (!count || count % sizeof(u32))
cbd204b4
BD
591 return -EINVAL;
592
593 mutex_lock(&gb->mutex);
594 fifo_len = kfifo_len(&gb->kfifo);
595 if (kfifo_to_user(&gb->kfifo, buf, min(fifo_len, count), &retval) != 0)
596 retval = -EIO;
597 mutex_unlock(&gb->mutex);
598
599 return retval;
600}
601
602static const struct file_operations loopback_fops = {
603 .owner = THIS_MODULE,
604 .read = loopback_read,
605 .open = loopback_open,
606 .llseek = noop_llseek,
607};
608
609static int loopback_init(void)
610{
611 dev_t dev;
612 int retval;
613
614 loopback_class = class_create(THIS_MODULE, "gb_loopback");
615 if (IS_ERR(loopback_class)) {
616 retval = PTR_ERR(loopback_class);
617 goto error_class;
618 }
619
620 retval = alloc_chrdev_region(&dev, 0, NUM_MINORS, "gb_loopback");
621 if (retval < 0)
622 goto error_chrdev;
623
624 loopback_major = MAJOR(dev);
625
626 retval = gb_protocol_register(&loopback_protocol);
627 if (retval)
628 goto error_gb;
629
630 return 0;
631
632error_gb:
633 unregister_chrdev_region(dev, NUM_MINORS);
634error_chrdev:
635 class_destroy(loopback_class);
636error_class:
637 return retval;
638}
639module_init(loopback_init);
640
641static void __exit loopback_exit(void)
642{
643 gb_protocol_deregister(&loopback_protocol);
644 unregister_chrdev_region(MKDEV(loopback_major, 0), NUM_MINORS);
645 class_destroy(loopback_class);
646 ida_destroy(&minors);
647}
648module_exit(loopback_exit);
355a7058
AB
649
650MODULE_LICENSE("GPL v2");