18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci/*
48c2ecf20Sopenharmony_ci * Xen para-virtual sound device
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2016-2018 EPAM Systems Inc.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <xen/page.h>
158c2ecf20Sopenharmony_ci#include <xen/platform_pci.h>
168c2ecf20Sopenharmony_ci#include <xen/xen.h>
178c2ecf20Sopenharmony_ci#include <xen/xenbus.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <xen/xen-front-pgdir-shbuf.h>
208c2ecf20Sopenharmony_ci#include <xen/interface/io/sndif.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "xen_snd_front.h"
238c2ecf20Sopenharmony_ci#include "xen_snd_front_alsa.h"
248c2ecf20Sopenharmony_ci#include "xen_snd_front_evtchnl.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic struct xensnd_req *
278c2ecf20Sopenharmony_cibe_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8 operation)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct xensnd_req *req;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	req = RING_GET_REQUEST(&evtchnl->u.req.ring,
328c2ecf20Sopenharmony_ci			       evtchnl->u.req.ring.req_prod_pvt);
338c2ecf20Sopenharmony_ci	req->operation = operation;
348c2ecf20Sopenharmony_ci	req->id = evtchnl->evt_next_id++;
358c2ecf20Sopenharmony_ci	evtchnl->evt_id = req->id;
368c2ecf20Sopenharmony_ci	return req;
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int be_stream_do_io(struct xen_snd_front_evtchnl *evtchnl)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))
428c2ecf20Sopenharmony_ci		return -EIO;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	reinit_completion(&evtchnl->u.req.completion);
458c2ecf20Sopenharmony_ci	xen_snd_front_evtchnl_flush(evtchnl);
468c2ecf20Sopenharmony_ci	return 0;
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic int be_stream_wait_io(struct xen_snd_front_evtchnl *evtchnl)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	if (wait_for_completion_timeout(&evtchnl->u.req.completion,
528c2ecf20Sopenharmony_ci			msecs_to_jiffies(VSND_WAIT_BACK_MS)) <= 0)
538c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return evtchnl->u.req.resp_status;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ciint xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl,
598c2ecf20Sopenharmony_ci					struct xensnd_query_hw_param *hw_param_req,
608c2ecf20Sopenharmony_ci					struct xensnd_query_hw_param *hw_param_resp)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	struct xensnd_req *req;
638c2ecf20Sopenharmony_ci	int ret;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	mutex_lock(&evtchnl->u.req.req_io_lock);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	mutex_lock(&evtchnl->ring_io_lock);
688c2ecf20Sopenharmony_ci	req = be_stream_prepare_req(evtchnl, XENSND_OP_HW_PARAM_QUERY);
698c2ecf20Sopenharmony_ci	req->op.hw_param = *hw_param_req;
708c2ecf20Sopenharmony_ci	mutex_unlock(&evtchnl->ring_io_lock);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	ret = be_stream_do_io(evtchnl);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (ret == 0)
758c2ecf20Sopenharmony_ci		ret = be_stream_wait_io(evtchnl);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (ret == 0)
788c2ecf20Sopenharmony_ci		*hw_param_resp = evtchnl->u.req.resp.hw_param;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	mutex_unlock(&evtchnl->u.req.req_io_lock);
818c2ecf20Sopenharmony_ci	return ret;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ciint xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl,
858c2ecf20Sopenharmony_ci				 struct xen_front_pgdir_shbuf *shbuf,
868c2ecf20Sopenharmony_ci				 u8 format, unsigned int channels,
878c2ecf20Sopenharmony_ci				 unsigned int rate, u32 buffer_sz,
888c2ecf20Sopenharmony_ci				 u32 period_sz)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct xensnd_req *req;
918c2ecf20Sopenharmony_ci	int ret;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	mutex_lock(&evtchnl->u.req.req_io_lock);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	mutex_lock(&evtchnl->ring_io_lock);
968c2ecf20Sopenharmony_ci	req = be_stream_prepare_req(evtchnl, XENSND_OP_OPEN);
978c2ecf20Sopenharmony_ci	req->op.open.pcm_format = format;
988c2ecf20Sopenharmony_ci	req->op.open.pcm_channels = channels;
998c2ecf20Sopenharmony_ci	req->op.open.pcm_rate = rate;
1008c2ecf20Sopenharmony_ci	req->op.open.buffer_sz = buffer_sz;
1018c2ecf20Sopenharmony_ci	req->op.open.period_sz = period_sz;
1028c2ecf20Sopenharmony_ci	req->op.open.gref_directory =
1038c2ecf20Sopenharmony_ci		xen_front_pgdir_shbuf_get_dir_start(shbuf);
1048c2ecf20Sopenharmony_ci	mutex_unlock(&evtchnl->ring_io_lock);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	ret = be_stream_do_io(evtchnl);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (ret == 0)
1098c2ecf20Sopenharmony_ci		ret = be_stream_wait_io(evtchnl);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	mutex_unlock(&evtchnl->u.req.req_io_lock);
1128c2ecf20Sopenharmony_ci	return ret;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ciint xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	__always_unused struct xensnd_req *req;
1188c2ecf20Sopenharmony_ci	int ret;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	mutex_lock(&evtchnl->u.req.req_io_lock);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	mutex_lock(&evtchnl->ring_io_lock);
1238c2ecf20Sopenharmony_ci	req = be_stream_prepare_req(evtchnl, XENSND_OP_CLOSE);
1248c2ecf20Sopenharmony_ci	mutex_unlock(&evtchnl->ring_io_lock);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	ret = be_stream_do_io(evtchnl);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (ret == 0)
1298c2ecf20Sopenharmony_ci		ret = be_stream_wait_io(evtchnl);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	mutex_unlock(&evtchnl->u.req.req_io_lock);
1328c2ecf20Sopenharmony_ci	return ret;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ciint xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
1368c2ecf20Sopenharmony_ci			       unsigned long pos, unsigned long count)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct xensnd_req *req;
1398c2ecf20Sopenharmony_ci	int ret;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	mutex_lock(&evtchnl->u.req.req_io_lock);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	mutex_lock(&evtchnl->ring_io_lock);
1448c2ecf20Sopenharmony_ci	req = be_stream_prepare_req(evtchnl, XENSND_OP_WRITE);
1458c2ecf20Sopenharmony_ci	req->op.rw.length = count;
1468c2ecf20Sopenharmony_ci	req->op.rw.offset = pos;
1478c2ecf20Sopenharmony_ci	mutex_unlock(&evtchnl->ring_io_lock);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	ret = be_stream_do_io(evtchnl);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (ret == 0)
1528c2ecf20Sopenharmony_ci		ret = be_stream_wait_io(evtchnl);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	mutex_unlock(&evtchnl->u.req.req_io_lock);
1558c2ecf20Sopenharmony_ci	return ret;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ciint xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
1598c2ecf20Sopenharmony_ci			      unsigned long pos, unsigned long count)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct xensnd_req *req;
1628c2ecf20Sopenharmony_ci	int ret;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	mutex_lock(&evtchnl->u.req.req_io_lock);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	mutex_lock(&evtchnl->ring_io_lock);
1678c2ecf20Sopenharmony_ci	req = be_stream_prepare_req(evtchnl, XENSND_OP_READ);
1688c2ecf20Sopenharmony_ci	req->op.rw.length = count;
1698c2ecf20Sopenharmony_ci	req->op.rw.offset = pos;
1708c2ecf20Sopenharmony_ci	mutex_unlock(&evtchnl->ring_io_lock);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	ret = be_stream_do_io(evtchnl);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (ret == 0)
1758c2ecf20Sopenharmony_ci		ret = be_stream_wait_io(evtchnl);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	mutex_unlock(&evtchnl->u.req.req_io_lock);
1788c2ecf20Sopenharmony_ci	return ret;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ciint xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl *evtchnl,
1828c2ecf20Sopenharmony_ci				 int type)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct xensnd_req *req;
1858c2ecf20Sopenharmony_ci	int ret;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	mutex_lock(&evtchnl->u.req.req_io_lock);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	mutex_lock(&evtchnl->ring_io_lock);
1908c2ecf20Sopenharmony_ci	req = be_stream_prepare_req(evtchnl, XENSND_OP_TRIGGER);
1918c2ecf20Sopenharmony_ci	req->op.trigger.type = type;
1928c2ecf20Sopenharmony_ci	mutex_unlock(&evtchnl->ring_io_lock);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	ret = be_stream_do_io(evtchnl);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (ret == 0)
1978c2ecf20Sopenharmony_ci		ret = be_stream_wait_io(evtchnl);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	mutex_unlock(&evtchnl->u.req.req_io_lock);
2008c2ecf20Sopenharmony_ci	return ret;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	xen_snd_front_alsa_fini(front_info);
2068c2ecf20Sopenharmony_ci	xen_snd_front_evtchnl_free_all(front_info);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic int sndback_initwait(struct xen_snd_front_info *front_info)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	int num_streams;
2128c2ecf20Sopenharmony_ci	int ret;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	ret = xen_snd_front_cfg_card(front_info, &num_streams);
2158c2ecf20Sopenharmony_ci	if (ret < 0)
2168c2ecf20Sopenharmony_ci		return ret;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* create event channels for all streams and publish */
2198c2ecf20Sopenharmony_ci	ret = xen_snd_front_evtchnl_create_all(front_info, num_streams);
2208c2ecf20Sopenharmony_ci	if (ret < 0)
2218c2ecf20Sopenharmony_ci		return ret;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return xen_snd_front_evtchnl_publish_all(front_info);
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic int sndback_connect(struct xen_snd_front_info *front_info)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	return xen_snd_front_alsa_init(front_info);
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic void sndback_disconnect(struct xen_snd_front_info *front_info)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	xen_snd_drv_fini(front_info);
2348c2ecf20Sopenharmony_ci	xenbus_switch_state(front_info->xb_dev, XenbusStateInitialising);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic void sndback_changed(struct xenbus_device *xb_dev,
2388c2ecf20Sopenharmony_ci			    enum xenbus_state backend_state)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct xen_snd_front_info *front_info = dev_get_drvdata(&xb_dev->dev);
2418c2ecf20Sopenharmony_ci	int ret;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	dev_dbg(&xb_dev->dev, "Backend state is %s, front is %s\n",
2448c2ecf20Sopenharmony_ci		xenbus_strstate(backend_state),
2458c2ecf20Sopenharmony_ci		xenbus_strstate(xb_dev->state));
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	switch (backend_state) {
2488c2ecf20Sopenharmony_ci	case XenbusStateReconfiguring:
2498c2ecf20Sopenharmony_ci	case XenbusStateReconfigured:
2508c2ecf20Sopenharmony_ci	case XenbusStateInitialised:
2518c2ecf20Sopenharmony_ci		break;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	case XenbusStateInitialising:
2548c2ecf20Sopenharmony_ci		/* Recovering after backend unexpected closure. */
2558c2ecf20Sopenharmony_ci		sndback_disconnect(front_info);
2568c2ecf20Sopenharmony_ci		break;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	case XenbusStateInitWait:
2598c2ecf20Sopenharmony_ci		/* Recovering after backend unexpected closure. */
2608c2ecf20Sopenharmony_ci		sndback_disconnect(front_info);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		ret = sndback_initwait(front_info);
2638c2ecf20Sopenharmony_ci		if (ret < 0)
2648c2ecf20Sopenharmony_ci			xenbus_dev_fatal(xb_dev, ret, "initializing frontend");
2658c2ecf20Sopenharmony_ci		else
2668c2ecf20Sopenharmony_ci			xenbus_switch_state(xb_dev, XenbusStateInitialised);
2678c2ecf20Sopenharmony_ci		break;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	case XenbusStateConnected:
2708c2ecf20Sopenharmony_ci		if (xb_dev->state != XenbusStateInitialised)
2718c2ecf20Sopenharmony_ci			break;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		ret = sndback_connect(front_info);
2748c2ecf20Sopenharmony_ci		if (ret < 0)
2758c2ecf20Sopenharmony_ci			xenbus_dev_fatal(xb_dev, ret, "initializing frontend");
2768c2ecf20Sopenharmony_ci		else
2778c2ecf20Sopenharmony_ci			xenbus_switch_state(xb_dev, XenbusStateConnected);
2788c2ecf20Sopenharmony_ci		break;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	case XenbusStateClosing:
2818c2ecf20Sopenharmony_ci		/*
2828c2ecf20Sopenharmony_ci		 * In this state backend starts freeing resources,
2838c2ecf20Sopenharmony_ci		 * so let it go into closed state first, so we can also
2848c2ecf20Sopenharmony_ci		 * remove ours.
2858c2ecf20Sopenharmony_ci		 */
2868c2ecf20Sopenharmony_ci		break;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	case XenbusStateUnknown:
2898c2ecf20Sopenharmony_ci	case XenbusStateClosed:
2908c2ecf20Sopenharmony_ci		if (xb_dev->state == XenbusStateClosed)
2918c2ecf20Sopenharmony_ci			break;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		sndback_disconnect(front_info);
2948c2ecf20Sopenharmony_ci		break;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic int xen_drv_probe(struct xenbus_device *xb_dev,
2998c2ecf20Sopenharmony_ci			 const struct xenbus_device_id *id)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct xen_snd_front_info *front_info;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	front_info = devm_kzalloc(&xb_dev->dev,
3048c2ecf20Sopenharmony_ci				  sizeof(*front_info), GFP_KERNEL);
3058c2ecf20Sopenharmony_ci	if (!front_info)
3068c2ecf20Sopenharmony_ci		return -ENOMEM;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	front_info->xb_dev = xb_dev;
3098c2ecf20Sopenharmony_ci	dev_set_drvdata(&xb_dev->dev, front_info);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	return xenbus_switch_state(xb_dev, XenbusStateInitialising);
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic int xen_drv_remove(struct xenbus_device *dev)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	struct xen_snd_front_info *front_info = dev_get_drvdata(&dev->dev);
3178c2ecf20Sopenharmony_ci	int to = 100;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	xenbus_switch_state(dev, XenbusStateClosing);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/*
3228c2ecf20Sopenharmony_ci	 * On driver removal it is disconnected from XenBus,
3238c2ecf20Sopenharmony_ci	 * so no backend state change events come via .otherend_changed
3248c2ecf20Sopenharmony_ci	 * callback. This prevents us from exiting gracefully, e.g.
3258c2ecf20Sopenharmony_ci	 * signaling the backend to free event channels, waiting for its
3268c2ecf20Sopenharmony_ci	 * state to change to XenbusStateClosed and cleaning at our end.
3278c2ecf20Sopenharmony_ci	 * Normally when front driver removed backend will finally go into
3288c2ecf20Sopenharmony_ci	 * XenbusStateInitWait state.
3298c2ecf20Sopenharmony_ci	 *
3308c2ecf20Sopenharmony_ci	 * Workaround: read backend's state manually and wait with time-out.
3318c2ecf20Sopenharmony_ci	 */
3328c2ecf20Sopenharmony_ci	while ((xenbus_read_unsigned(front_info->xb_dev->otherend, "state",
3338c2ecf20Sopenharmony_ci				     XenbusStateUnknown) != XenbusStateInitWait) &&
3348c2ecf20Sopenharmony_ci	       --to)
3358c2ecf20Sopenharmony_ci		msleep(10);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (!to) {
3388c2ecf20Sopenharmony_ci		unsigned int state;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci		state = xenbus_read_unsigned(front_info->xb_dev->otherend,
3418c2ecf20Sopenharmony_ci					     "state", XenbusStateUnknown);
3428c2ecf20Sopenharmony_ci		pr_err("Backend state is %s while removing driver\n",
3438c2ecf20Sopenharmony_ci		       xenbus_strstate(state));
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	xen_snd_drv_fini(front_info);
3478c2ecf20Sopenharmony_ci	xenbus_frontend_closed(dev);
3488c2ecf20Sopenharmony_ci	return 0;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic const struct xenbus_device_id xen_drv_ids[] = {
3528c2ecf20Sopenharmony_ci	{ XENSND_DRIVER_NAME },
3538c2ecf20Sopenharmony_ci	{ "" }
3548c2ecf20Sopenharmony_ci};
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic struct xenbus_driver xen_driver = {
3578c2ecf20Sopenharmony_ci	.ids = xen_drv_ids,
3588c2ecf20Sopenharmony_ci	.probe = xen_drv_probe,
3598c2ecf20Sopenharmony_ci	.remove = xen_drv_remove,
3608c2ecf20Sopenharmony_ci	.otherend_changed = sndback_changed,
3618c2ecf20Sopenharmony_ci};
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic int __init xen_drv_init(void)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	if (!xen_domain())
3668c2ecf20Sopenharmony_ci		return -ENODEV;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (!xen_has_pv_devices())
3698c2ecf20Sopenharmony_ci		return -ENODEV;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	/* At the moment we only support case with XEN_PAGE_SIZE == PAGE_SIZE */
3728c2ecf20Sopenharmony_ci	if (XEN_PAGE_SIZE != PAGE_SIZE) {
3738c2ecf20Sopenharmony_ci		pr_err(XENSND_DRIVER_NAME ": different kernel and Xen page sizes are not supported: XEN_PAGE_SIZE (%lu) != PAGE_SIZE (%lu)\n",
3748c2ecf20Sopenharmony_ci		       XEN_PAGE_SIZE, PAGE_SIZE);
3758c2ecf20Sopenharmony_ci		return -ENODEV;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	pr_info("Initialising Xen " XENSND_DRIVER_NAME " frontend driver\n");
3798c2ecf20Sopenharmony_ci	return xenbus_register_frontend(&xen_driver);
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic void __exit xen_drv_fini(void)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	pr_info("Unregistering Xen " XENSND_DRIVER_NAME " frontend driver\n");
3858c2ecf20Sopenharmony_ci	xenbus_unregister_driver(&xen_driver);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cimodule_init(xen_drv_init);
3898c2ecf20Sopenharmony_cimodule_exit(xen_drv_fini);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xen virtual sound device frontend");
3928c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3938c2ecf20Sopenharmony_ciMODULE_ALIAS("xen:" XENSND_DRIVER_NAME);
3948c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{ALSA,Virtual soundcard}}");
395