char-hub.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. * QEMU Character Hub Device
  3. *
  4. * Author: Roman Penyaev <r.peniaev@gmail.com>
  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. #include "qemu/osdep.h"
  25. #include "qapi/error.h"
  26. #include "qemu/option.h"
  27. #include "chardev/char.h"
  28. #include "chardev-internal.h"
  29. /*
  30. * Character hub device aggregates input from multiple backend devices
  31. * and forwards it to a single frontend device. Additionally, hub
  32. * device takes the output from the frontend device and sends it back
  33. * to all the connected backend devices.
  34. */
  35. /*
  36. * Write to all backends. Different backend devices accept data with
  37. * various rate, so it is quite possible that one device returns less,
  38. * then others. In this case we return minimum to the caller,
  39. * expecting caller will repeat operation soon. When repeat happens
  40. * send to the devices which consume data faster must be avoided
  41. * for obvious reasons not to send data, which was already sent.
  42. * Called with chr_write_lock held.
  43. */
  44. static int hub_chr_write(Chardev *chr, const uint8_t *buf, int len)
  45. {
  46. HubChardev *d = HUB_CHARDEV(chr);
  47. int r, i, ret = len;
  48. unsigned int written;
  49. /* Invalidate index on every write */
  50. d->be_eagain_ind = -1;
  51. for (i = 0; i < d->be_cnt; i++) {
  52. if (!d->backends[i].be.chr->be_open) {
  53. /* Skip closed backend */
  54. continue;
  55. }
  56. written = d->be_written[i] - d->be_min_written;
  57. if (written) {
  58. /* Written in the previous call so take into account */
  59. ret = MIN(written, ret);
  60. continue;
  61. }
  62. r = qemu_chr_fe_write(&d->backends[i].be, buf, len);
  63. if (r < 0) {
  64. if (errno == EAGAIN) {
  65. /* Set index and expect to be called soon on watch wake up */
  66. d->be_eagain_ind = i;
  67. }
  68. return r;
  69. }
  70. d->be_written[i] += r;
  71. ret = MIN(r, ret);
  72. }
  73. d->be_min_written += ret;
  74. return ret;
  75. }
  76. static int hub_chr_can_read(void *opaque)
  77. {
  78. HubCharBackend *backend = opaque;
  79. CharBackend *fe = backend->hub->parent.be;
  80. if (fe && fe->chr_can_read) {
  81. return fe->chr_can_read(fe->opaque);
  82. }
  83. return 0;
  84. }
  85. static void hub_chr_read(void *opaque, const uint8_t *buf, int size)
  86. {
  87. HubCharBackend *backend = opaque;
  88. CharBackend *fe = backend->hub->parent.be;
  89. if (fe && fe->chr_read) {
  90. fe->chr_read(fe->opaque, buf, size);
  91. }
  92. }
  93. static void hub_chr_event(void *opaque, QEMUChrEvent event)
  94. {
  95. HubCharBackend *backend = opaque;
  96. HubChardev *d = backend->hub;
  97. CharBackend *fe = d->parent.be;
  98. if (event == CHR_EVENT_OPENED) {
  99. /*
  100. * Catch up with what was already written while this backend
  101. * was closed
  102. */
  103. d->be_written[backend->be_ind] = d->be_min_written;
  104. if (d->be_event_opened_cnt++) {
  105. /* Ignore subsequent open events from other backends */
  106. return;
  107. }
  108. } else if (event == CHR_EVENT_CLOSED) {
  109. if (!d->be_event_opened_cnt) {
  110. /* Don't go below zero. Probably assert is better */
  111. return;
  112. }
  113. if (--d->be_event_opened_cnt) {
  114. /* Serve only the last one close event */
  115. return;
  116. }
  117. }
  118. if (fe && fe->chr_event) {
  119. fe->chr_event(fe->opaque, event);
  120. }
  121. }
  122. static GSource *hub_chr_add_watch(Chardev *s, GIOCondition cond)
  123. {
  124. HubChardev *d = HUB_CHARDEV(s);
  125. Chardev *chr;
  126. ChardevClass *cc;
  127. if (d->be_eagain_ind == -1) {
  128. return NULL;
  129. }
  130. assert(d->be_eagain_ind < d->be_cnt);
  131. chr = qemu_chr_fe_get_driver(&d->backends[d->be_eagain_ind].be);
  132. cc = CHARDEV_GET_CLASS(chr);
  133. if (!cc->chr_add_watch) {
  134. return NULL;
  135. }
  136. return cc->chr_add_watch(chr, cond);
  137. }
  138. static bool hub_chr_attach_chardev(HubChardev *d, Chardev *chr,
  139. Error **errp)
  140. {
  141. bool ret;
  142. if (d->be_cnt >= MAX_HUB) {
  143. error_setg(errp, "hub: too many uses of chardevs '%s'"
  144. " (maximum is " stringify(MAX_HUB) ")",
  145. d->parent.label);
  146. return false;
  147. }
  148. ret = qemu_chr_fe_init(&d->backends[d->be_cnt].be, chr, errp);
  149. if (ret) {
  150. d->backends[d->be_cnt].hub = d;
  151. d->backends[d->be_cnt].be_ind = d->be_cnt;
  152. d->be_cnt += 1;
  153. }
  154. return ret;
  155. }
  156. static void char_hub_finalize(Object *obj)
  157. {
  158. HubChardev *d = HUB_CHARDEV(obj);
  159. int i;
  160. for (i = 0; i < d->be_cnt; i++) {
  161. qemu_chr_fe_deinit(&d->backends[i].be, false);
  162. }
  163. }
  164. static void hub_chr_update_read_handlers(Chardev *chr)
  165. {
  166. HubChardev *d = HUB_CHARDEV(chr);
  167. int i;
  168. for (i = 0; i < d->be_cnt; i++) {
  169. qemu_chr_fe_set_handlers_full(&d->backends[i].be,
  170. hub_chr_can_read,
  171. hub_chr_read,
  172. hub_chr_event,
  173. NULL,
  174. &d->backends[i],
  175. chr->gcontext, true, false);
  176. }
  177. }
  178. static void qemu_chr_open_hub(Chardev *chr,
  179. ChardevBackend *backend,
  180. bool *be_opened,
  181. Error **errp)
  182. {
  183. ChardevHub *hub = backend->u.hub.data;
  184. HubChardev *d = HUB_CHARDEV(chr);
  185. strList *list = hub->chardevs;
  186. d->be_eagain_ind = -1;
  187. if (list == NULL) {
  188. error_setg(errp, "hub: 'chardevs' list is not defined");
  189. return;
  190. }
  191. while (list) {
  192. Chardev *s;
  193. s = qemu_chr_find(list->value);
  194. if (s == NULL) {
  195. error_setg(errp, "hub: chardev can't be found by id '%s'",
  196. list->value);
  197. return;
  198. }
  199. if (CHARDEV_IS_HUB(s) || CHARDEV_IS_MUX(s)) {
  200. error_setg(errp, "hub: multiplexers and hub devices can't be "
  201. "stacked, check chardev '%s', chardev should not "
  202. "be a hub device or have 'mux=on' enabled",
  203. list->value);
  204. return;
  205. }
  206. if (!hub_chr_attach_chardev(d, s, errp)) {
  207. return;
  208. }
  209. list = list->next;
  210. }
  211. /* Closed until an explicit event from backend */
  212. *be_opened = false;
  213. }
  214. static void qemu_chr_parse_hub(QemuOpts *opts, ChardevBackend *backend,
  215. Error **errp)
  216. {
  217. ChardevHub *hub;
  218. strList **tail;
  219. int i;
  220. backend->type = CHARDEV_BACKEND_KIND_HUB;
  221. hub = backend->u.hub.data = g_new0(ChardevHub, 1);
  222. qemu_chr_parse_common(opts, qapi_ChardevHub_base(hub));
  223. tail = &hub->chardevs;
  224. for (i = 0; i < MAX_HUB; i++) {
  225. char optbuf[16];
  226. const char *dev;
  227. snprintf(optbuf, sizeof(optbuf), "chardevs.%u", i);
  228. dev = qemu_opt_get(opts, optbuf);
  229. if (!dev) {
  230. break;
  231. }
  232. QAPI_LIST_APPEND(tail, g_strdup(dev));
  233. }
  234. }
  235. static void char_hub_class_init(ObjectClass *oc, void *data)
  236. {
  237. ChardevClass *cc = CHARDEV_CLASS(oc);
  238. cc->parse = qemu_chr_parse_hub;
  239. cc->open = qemu_chr_open_hub;
  240. cc->chr_write = hub_chr_write;
  241. cc->chr_add_watch = hub_chr_add_watch;
  242. /* We handle events from backends only */
  243. cc->chr_be_event = NULL;
  244. cc->chr_update_read_handler = hub_chr_update_read_handlers;
  245. }
  246. static const TypeInfo char_hub_type_info = {
  247. .name = TYPE_CHARDEV_HUB,
  248. .parent = TYPE_CHARDEV,
  249. .class_init = char_hub_class_init,
  250. .instance_size = sizeof(HubChardev),
  251. .instance_finalize = char_hub_finalize,
  252. };
  253. static void register_types(void)
  254. {
  255. type_register_static(&char_hub_type_info);
  256. }
  257. type_init(register_types);