| /* | 
 |  * QEMU Char Device for testsuite control | 
 |  * | 
 |  * Copyright (c) 2014 Red Hat, Inc. | 
 |  * | 
 |  * Author: Paolo Bonzini <pbonzini@redhat.com> | 
 |  * | 
 |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
 |  * of this software and associated documentation files (the "Software"), to deal | 
 |  * in the Software without restriction, including without limitation the rights | 
 |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
 |  * copies of the Software, and to permit persons to whom the Software is | 
 |  * furnished to do so, subject to the following conditions: | 
 |  * | 
 |  * The above copyright notice and this permission notice shall be included in | 
 |  * all copies or substantial portions of the Software. | 
 |  * | 
 |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
 |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
 |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 
 |  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
 |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
 |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
 |  * THE SOFTWARE. | 
 |  */ | 
 |  | 
 | #include "qemu/osdep.h" | 
 | #include "qemu/module.h" | 
 | #include "chardev/char.h" | 
 | #include "qom/object.h" | 
 |  | 
 | #define BUF_SIZE 32 | 
 |  | 
 | struct TestdevChardev { | 
 |     Chardev parent; | 
 |  | 
 |     uint8_t in_buf[32]; | 
 |     int in_buf_used; | 
 | }; | 
 | typedef struct TestdevChardev TestdevChardev; | 
 |  | 
 | #define TYPE_CHARDEV_TESTDEV "chardev-testdev" | 
 | DECLARE_INSTANCE_CHECKER(TestdevChardev, TESTDEV_CHARDEV, | 
 |                          TYPE_CHARDEV_TESTDEV) | 
 |  | 
 | /* Try to interpret a whole incoming packet */ | 
 | static int testdev_eat_packet(TestdevChardev *testdev) | 
 | { | 
 |     const uint8_t *cur = testdev->in_buf; | 
 |     int len = testdev->in_buf_used; | 
 |     uint8_t c; | 
 |     int arg; | 
 |  | 
 | #define EAT(c) do { \ | 
 |     if (!len--) {   \ | 
 |         return 0;   \ | 
 |     }               \ | 
 |     c = *cur++;     \ | 
 | } while (0) | 
 |  | 
 |     EAT(c); | 
 |  | 
 |     while (isspace(c)) { | 
 |         EAT(c); | 
 |     } | 
 |  | 
 |     arg = 0; | 
 |     while (isdigit(c)) { | 
 |         arg = arg * 10 + c - '0'; | 
 |         EAT(c); | 
 |     } | 
 |  | 
 |     while (isspace(c)) { | 
 |         EAT(c); | 
 |     } | 
 |  | 
 |     switch (c) { | 
 |     case 'q': | 
 |         exit((arg << 1) | 1); | 
 |         break; | 
 |     default: | 
 |         break; | 
 |     } | 
 |     return cur - testdev->in_buf; | 
 | } | 
 |  | 
 | /* The other end is writing some data.  Store it and try to interpret */ | 
 | static int testdev_chr_write(Chardev *chr, const uint8_t *buf, int len) | 
 | { | 
 |     TestdevChardev *testdev = TESTDEV_CHARDEV(chr); | 
 |     int tocopy, eaten, orig_len = len; | 
 |  | 
 |     while (len) { | 
 |         /* Complete our buffer as much as possible */ | 
 |         tocopy = MIN(len, BUF_SIZE - testdev->in_buf_used); | 
 |  | 
 |         memcpy(testdev->in_buf + testdev->in_buf_used, buf, tocopy); | 
 |         testdev->in_buf_used += tocopy; | 
 |         buf += tocopy; | 
 |         len -= tocopy; | 
 |  | 
 |         /* Interpret it as much as possible */ | 
 |         while (testdev->in_buf_used > 0 && | 
 |                (eaten = testdev_eat_packet(testdev)) > 0) { | 
 |             memmove(testdev->in_buf, testdev->in_buf + eaten, | 
 |                     testdev->in_buf_used - eaten); | 
 |             testdev->in_buf_used -= eaten; | 
 |         } | 
 |     } | 
 |     return orig_len; | 
 | } | 
 |  | 
 | static void char_testdev_class_init(ObjectClass *oc, void *data) | 
 | { | 
 |     ChardevClass *cc = CHARDEV_CLASS(oc); | 
 |  | 
 |     cc->chr_write = testdev_chr_write; | 
 | } | 
 |  | 
 | static const TypeInfo char_testdev_type_info = { | 
 |     .name = TYPE_CHARDEV_TESTDEV, | 
 |     .parent = TYPE_CHARDEV, | 
 |     .instance_size = sizeof(TestdevChardev), | 
 |     .class_init = char_testdev_class_init, | 
 | }; | 
 |  | 
 | static void register_types(void) | 
 | { | 
 |     type_register_static(&char_testdev_type_info); | 
 | } | 
 |  | 
 | type_init(register_types); |