xen_snd_front.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // SPDX-License-Identifier: GPL-2.0 OR MIT
  2. /*
  3. * Xen para-virtual sound device
  4. *
  5. * Copyright (C) 2016-2018 EPAM Systems Inc.
  6. *
  7. * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
  8. */
  9. #include <linux/delay.h>
  10. #include <linux/module.h>
  11. #include <xen/platform_pci.h>
  12. #include <xen/xen.h>
  13. #include <xen/xenbus.h>
  14. #include <xen/interface/io/sndif.h>
  15. #include "xen_snd_front.h"
  16. static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
  17. {
  18. }
  19. static int sndback_initwait(struct xen_snd_front_info *front_info)
  20. {
  21. return 0;
  22. }
  23. static int sndback_connect(struct xen_snd_front_info *front_info)
  24. {
  25. return 0;
  26. }
  27. static void sndback_disconnect(struct xen_snd_front_info *front_info)
  28. {
  29. xen_snd_drv_fini(front_info);
  30. xenbus_switch_state(front_info->xb_dev, XenbusStateInitialising);
  31. }
  32. static void sndback_changed(struct xenbus_device *xb_dev,
  33. enum xenbus_state backend_state)
  34. {
  35. struct xen_snd_front_info *front_info = dev_get_drvdata(&xb_dev->dev);
  36. int ret;
  37. dev_dbg(&xb_dev->dev, "Backend state is %s, front is %s\n",
  38. xenbus_strstate(backend_state),
  39. xenbus_strstate(xb_dev->state));
  40. switch (backend_state) {
  41. case XenbusStateReconfiguring:
  42. /* fall through */
  43. case XenbusStateReconfigured:
  44. /* fall through */
  45. case XenbusStateInitialised:
  46. /* fall through */
  47. break;
  48. case XenbusStateInitialising:
  49. /* Recovering after backend unexpected closure. */
  50. sndback_disconnect(front_info);
  51. break;
  52. case XenbusStateInitWait:
  53. /* Recovering after backend unexpected closure. */
  54. sndback_disconnect(front_info);
  55. ret = sndback_initwait(front_info);
  56. if (ret < 0)
  57. xenbus_dev_fatal(xb_dev, ret, "initializing frontend");
  58. else
  59. xenbus_switch_state(xb_dev, XenbusStateInitialised);
  60. break;
  61. case XenbusStateConnected:
  62. if (xb_dev->state != XenbusStateInitialised)
  63. break;
  64. ret = sndback_connect(front_info);
  65. if (ret < 0)
  66. xenbus_dev_fatal(xb_dev, ret, "initializing frontend");
  67. else
  68. xenbus_switch_state(xb_dev, XenbusStateConnected);
  69. break;
  70. case XenbusStateClosing:
  71. /*
  72. * In this state backend starts freeing resources,
  73. * so let it go into closed state first, so we can also
  74. * remove ours.
  75. */
  76. break;
  77. case XenbusStateUnknown:
  78. /* fall through */
  79. case XenbusStateClosed:
  80. if (xb_dev->state == XenbusStateClosed)
  81. break;
  82. sndback_disconnect(front_info);
  83. break;
  84. }
  85. }
  86. static int xen_drv_probe(struct xenbus_device *xb_dev,
  87. const struct xenbus_device_id *id)
  88. {
  89. struct xen_snd_front_info *front_info;
  90. front_info = devm_kzalloc(&xb_dev->dev,
  91. sizeof(*front_info), GFP_KERNEL);
  92. if (!front_info)
  93. return -ENOMEM;
  94. front_info->xb_dev = xb_dev;
  95. dev_set_drvdata(&xb_dev->dev, front_info);
  96. return xenbus_switch_state(xb_dev, XenbusStateInitialising);
  97. }
  98. static int xen_drv_remove(struct xenbus_device *dev)
  99. {
  100. struct xen_snd_front_info *front_info = dev_get_drvdata(&dev->dev);
  101. int to = 100;
  102. xenbus_switch_state(dev, XenbusStateClosing);
  103. /*
  104. * On driver removal it is disconnected from XenBus,
  105. * so no backend state change events come via .otherend_changed
  106. * callback. This prevents us from exiting gracefully, e.g.
  107. * signaling the backend to free event channels, waiting for its
  108. * state to change to XenbusStateClosed and cleaning at our end.
  109. * Normally when front driver removed backend will finally go into
  110. * XenbusStateInitWait state.
  111. *
  112. * Workaround: read backend's state manually and wait with time-out.
  113. */
  114. while ((xenbus_read_unsigned(front_info->xb_dev->otherend, "state",
  115. XenbusStateUnknown) != XenbusStateInitWait) &&
  116. to--)
  117. msleep(10);
  118. if (!to) {
  119. unsigned int state;
  120. state = xenbus_read_unsigned(front_info->xb_dev->otherend,
  121. "state", XenbusStateUnknown);
  122. pr_err("Backend state is %s while removing driver\n",
  123. xenbus_strstate(state));
  124. }
  125. xen_snd_drv_fini(front_info);
  126. xenbus_frontend_closed(dev);
  127. return 0;
  128. }
  129. static const struct xenbus_device_id xen_drv_ids[] = {
  130. { XENSND_DRIVER_NAME },
  131. { "" }
  132. };
  133. static struct xenbus_driver xen_driver = {
  134. .ids = xen_drv_ids,
  135. .probe = xen_drv_probe,
  136. .remove = xen_drv_remove,
  137. .otherend_changed = sndback_changed,
  138. };
  139. static int __init xen_drv_init(void)
  140. {
  141. if (!xen_domain())
  142. return -ENODEV;
  143. if (!xen_has_pv_devices())
  144. return -ENODEV;
  145. pr_info("Initialising Xen " XENSND_DRIVER_NAME " frontend driver\n");
  146. return xenbus_register_frontend(&xen_driver);
  147. }
  148. static void __exit xen_drv_fini(void)
  149. {
  150. pr_info("Unregistering Xen " XENSND_DRIVER_NAME " frontend driver\n");
  151. xenbus_unregister_driver(&xen_driver);
  152. }
  153. module_init(xen_drv_init);
  154. module_exit(xen_drv_fini);
  155. MODULE_DESCRIPTION("Xen virtual sound device frontend");
  156. MODULE_LICENSE("GPL");
  157. MODULE_ALIAS("xen:" XENSND_DRIVER_NAME);
  158. MODULE_SUPPORTED_DEVICE("{{ALSA,Virtual soundcard}}");