aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/unisys/visorutil/charqueue.c
blob: c91752a2d06b6f385f137a08a44cd82622c321c1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/* charqueue.c
 *
 * Copyright (C) 2010 - 2013 UNISYS CORPORATION
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 * NON INFRINGEMENT.  See the GNU General Public License for more
 * details.
 */

/*
 *  Simple character queue implementation for Linux kernel mode.
 */

#include "charqueue.h"

#define MYDRVNAME "charqueue"

#define IS_EMPTY(charqueue) (charqueue->head == charqueue->tail)

struct charqueue {
	int alloc_size;
	int nslots;
	spinlock_t lock; /* read/write lock for this structure */
	int head, tail;
	unsigned char buf[0];
};

struct charqueue *visor_charqueue_create(ulong nslots)
{
	int alloc_size = sizeof(struct charqueue) + nslots + 1;
	struct charqueue *cq;

	cq = kmalloc(alloc_size, GFP_KERNEL|__GFP_NORETRY);
	if (cq == NULL)
		return NULL;
	cq->alloc_size = alloc_size;
	cq->nslots = nslots;
	cq->head = 0;
	cq->tail = 0;
	spin_lock_init(&cq->lock);
	return cq;
}
EXPORT_SYMBOL_GPL(visor_charqueue_create);

void visor_charqueue_enqueue(struct charqueue *charqueue, unsigned char c)
{
	int alloc_slots = charqueue->nslots+1;  /* 1 slot is always empty */

	spin_lock(&charqueue->lock);
	charqueue->head = (charqueue->head+1) % alloc_slots;
	if (charqueue->head == charqueue->tail)
		/* overflow; overwrite the oldest entry */
		charqueue->tail = (charqueue->tail+1) % alloc_slots;
	charqueue->buf[charqueue->head] = c;
	spin_unlock(&charqueue->lock);
}
EXPORT_SYMBOL_GPL(visor_charqueue_enqueue);

BOOL visor_charqueue_is_empty(struct charqueue *charqueue)
{
	BOOL b;

	spin_lock(&charqueue->lock);
	b = IS_EMPTY(charqueue);
	spin_unlock(&charqueue->lock);
	return b;
}
EXPORT_SYMBOL_GPL(visor_charqueue_is_empty);

static int charqueue_dequeue_1(struct charqueue *charqueue)
{
	int alloc_slots = charqueue->nslots + 1;  /* 1 slot is always empty */

	if (IS_EMPTY(charqueue))
		return -1;
	charqueue->tail = (charqueue->tail+1) % alloc_slots;
	return charqueue->buf[charqueue->tail];
}

int charqueue_dequeue(struct charqueue *charqueue)
{
	int rc;

	spin_lock(&charqueue->lock);
	rc = charqueue_dequeue_1(charqueue);
	spin_unlock(&charqueue->lock);
	return rc;
}

int visor_charqueue_dequeue_n(struct charqueue *charqueue, unsigned char *buf,
			      int n)
{
	int rc, counter = 0, c;

	spin_lock(&charqueue->lock);
	for (;;) {
		if (n <= 0)
			break;  /* no more buffer space */
		c = charqueue_dequeue_1(charqueue);
		if (c < 0)
			break;  /* no more input */
		*buf = (unsigned char)(c);
		buf++;
		n--;
		counter++;
	}
	rc = counter;
	spin_unlock(&charqueue->lock);
	return rc;
}
EXPORT_SYMBOL_GPL(visor_charqueue_dequeue_n);

void visor_charqueue_destroy(struct charqueue *charqueue)
{
	if (charqueue == NULL)
		return;
	kfree(charqueue);
}
EXPORT_SYMBOL_GPL(visor_charqueue_destroy);