qemu

Форк
0
/
char-ringbuf.c 
255 строк · 6.8 Кб
1
/*
2
 * QEMU System Emulator
3
 *
4
 * Copyright (c) 2003-2008 Fabrice Bellard
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
 * THE SOFTWARE.
23
 */
24

25
#include "qemu/osdep.h"
26
#include "chardev/char.h"
27
#include "qapi/error.h"
28
#include "qapi/qapi-commands-char.h"
29
#include "qemu/base64.h"
30
#include "qemu/module.h"
31
#include "qemu/option.h"
32
#include "qom/object.h"
33

34
/* Ring buffer chardev */
35

36
struct RingBufChardev {
37
    Chardev parent;
38
    size_t size;
39
    size_t prod;
40
    size_t cons;
41
    uint8_t *cbuf;
42
};
43
typedef struct RingBufChardev RingBufChardev;
44

45
DECLARE_INSTANCE_CHECKER(RingBufChardev, RINGBUF_CHARDEV,
46
                         TYPE_CHARDEV_RINGBUF)
47

48
static size_t ringbuf_count(const Chardev *chr)
49
{
50
    const RingBufChardev *d = RINGBUF_CHARDEV(chr);
51

52
    return d->prod - d->cons;
53
}
54

55
static int ringbuf_chr_write(Chardev *chr, const uint8_t *buf, int len)
56
{
57
    RingBufChardev *d = RINGBUF_CHARDEV(chr);
58
    int i;
59

60
    if (!buf || (len < 0)) {
61
        return -1;
62
    }
63

64
    for (i = 0; i < len; i++) {
65
        d->cbuf[d->prod++ & (d->size - 1)] = buf[i];
66
        if (d->prod - d->cons > d->size) {
67
            d->cons = d->prod - d->size;
68
        }
69
    }
70

71
    return len;
72
}
73

74
static int ringbuf_chr_read(Chardev *chr, uint8_t *buf, int len)
75
{
76
    RingBufChardev *d = RINGBUF_CHARDEV(chr);
77
    int i;
78

79
    qemu_mutex_lock(&chr->chr_write_lock);
80
    for (i = 0; i < len && d->cons != d->prod; i++) {
81
        buf[i] = d->cbuf[d->cons++ & (d->size - 1)];
82
    }
83
    qemu_mutex_unlock(&chr->chr_write_lock);
84

85
    return i;
86
}
87

88
static void char_ringbuf_finalize(Object *obj)
89
{
90
    RingBufChardev *d = RINGBUF_CHARDEV(obj);
91

92
    g_free(d->cbuf);
93
}
94

95
static void qemu_chr_open_ringbuf(Chardev *chr,
96
                                  ChardevBackend *backend,
97
                                  bool *be_opened,
98
                                  Error **errp)
99
{
100
    ChardevRingbuf *opts = backend->u.ringbuf.data;
101
    RingBufChardev *d = RINGBUF_CHARDEV(chr);
102

103
    d->size = opts->has_size ? opts->size : 65536;
104

105
    /* The size must be power of 2 */
106
    if (d->size & (d->size - 1)) {
107
        error_setg(errp, "size of ringbuf chardev must be power of two");
108
        return;
109
    }
110

111
    d->prod = 0;
112
    d->cons = 0;
113
    d->cbuf = g_malloc0(d->size);
114
}
115

116
void qmp_ringbuf_write(const char *device, const char *data,
117
                       bool has_format, enum DataFormat format,
118
                       Error **errp)
119
{
120
    Chardev *chr;
121
    const uint8_t *write_data;
122
    int ret;
123
    gsize write_count;
124

125
    chr = qemu_chr_find(device);
126
    if (!chr) {
127
        error_setg(errp, "Device '%s' not found", device);
128
        return;
129
    }
130

131
    if (!CHARDEV_IS_RINGBUF(chr)) {
132
        error_setg(errp, "%s is not a ringbuf device", device);
133
        return;
134
    }
135

136
    if (has_format && (format == DATA_FORMAT_BASE64)) {
137
        write_data = qbase64_decode(data, -1,
138
                                    &write_count,
139
                                    errp);
140
        if (!write_data) {
141
            return;
142
        }
143
    } else {
144
        write_data = (uint8_t *)data;
145
        write_count = strlen(data);
146
    }
147

148
    ret = ringbuf_chr_write(chr, write_data, write_count);
149

150
    if (write_data != (uint8_t *)data) {
151
        g_free((void *)write_data);
152
    }
153

154
    if (ret < 0) {
155
        error_setg(errp, "Failed to write to device %s", device);
156
        return;
157
    }
158
}
159

160
char *qmp_ringbuf_read(const char *device, int64_t size,
161
                       bool has_format, enum DataFormat format,
162
                       Error **errp)
163
{
164
    Chardev *chr;
165
    uint8_t *read_data;
166
    size_t count;
167
    char *data;
168

169
    chr = qemu_chr_find(device);
170
    if (!chr) {
171
        error_setg(errp, "Device '%s' not found", device);
172
        return NULL;
173
    }
174

175
    if (!CHARDEV_IS_RINGBUF(chr)) {
176
        error_setg(errp, "%s is not a ringbuf device", device);
177
        return NULL;
178
    }
179

180
    if (size <= 0) {
181
        error_setg(errp, "size must be greater than zero");
182
        return NULL;
183
    }
184

185
    count = ringbuf_count(chr);
186
    size = size > count ? count : size;
187
    read_data = g_malloc(size + 1);
188

189
    ringbuf_chr_read(chr, read_data, size);
190

191
    if (has_format && (format == DATA_FORMAT_BASE64)) {
192
        data = g_base64_encode(read_data, size);
193
        g_free(read_data);
194
    } else {
195
        /*
196
         * FIXME should read only complete, valid UTF-8 characters up
197
         * to @size bytes.  Invalid sequences should be replaced by a
198
         * suitable replacement character.  Except when (and only
199
         * when) ring buffer lost characters since last read, initial
200
         * continuation characters should be dropped.
201
         */
202
        read_data[size] = 0;
203
        data = (char *)read_data;
204
    }
205

206
    return data;
207
}
208

209
static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
210
                                   Error **errp)
211
{
212
    int val;
213
    ChardevRingbuf *ringbuf;
214

215
    backend->type = CHARDEV_BACKEND_KIND_RINGBUF;
216
    ringbuf = backend->u.ringbuf.data = g_new0(ChardevRingbuf, 1);
217
    qemu_chr_parse_common(opts, qapi_ChardevRingbuf_base(ringbuf));
218

219
    val = qemu_opt_get_size(opts, "size", 0);
220
    if (val != 0) {
221
        ringbuf->has_size = true;
222
        ringbuf->size = val;
223
    }
224
}
225

226
static void char_ringbuf_class_init(ObjectClass *oc, void *data)
227
{
228
    ChardevClass *cc = CHARDEV_CLASS(oc);
229

230
    cc->parse = qemu_chr_parse_ringbuf;
231
    cc->open = qemu_chr_open_ringbuf;
232
    cc->chr_write = ringbuf_chr_write;
233
}
234

235
static const TypeInfo char_ringbuf_type_info = {
236
    .name = TYPE_CHARDEV_RINGBUF,
237
    .parent = TYPE_CHARDEV,
238
    .class_init = char_ringbuf_class_init,
239
    .instance_size = sizeof(RingBufChardev),
240
    .instance_finalize = char_ringbuf_finalize,
241
};
242

243
/* Bug-compatibility: */
244
static const TypeInfo char_memory_type_info = {
245
    .name = TYPE_CHARDEV_MEMORY,
246
    .parent = TYPE_CHARDEV_RINGBUF,
247
};
248

249
static void register_types(void)
250
{
251
    type_register_static(&char_ringbuf_type_info);
252
    type_register_static(&char_memory_type_info);
253
}
254

255
type_init(register_types);
256

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.