162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * Xen para-virtual sound device
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2016-2018 EPAM Systems Inc.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <xen/page.h>
1562306a36Sopenharmony_ci#include <xen/platform_pci.h>
1662306a36Sopenharmony_ci#include <xen/xen.h>
1762306a36Sopenharmony_ci#include <xen/xenbus.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <xen/xen-front-pgdir-shbuf.h>
2062306a36Sopenharmony_ci#include <xen/interface/io/sndif.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "xen_snd_front.h"
2362306a36Sopenharmony_ci#include "xen_snd_front_alsa.h"
2462306a36Sopenharmony_ci#include "xen_snd_front_evtchnl.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic struct xensnd_req *
2762306a36Sopenharmony_cibe_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8 operation)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct xensnd_req *req;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	req = RING_GET_REQUEST(&evtchnl->u.req.ring,
3262306a36Sopenharmony_ci			       evtchnl->u.req.ring.req_prod_pvt);
3362306a36Sopenharmony_ci	req->operation = operation;
3462306a36Sopenharmony_ci	req->id = evtchnl->evt_next_id++;
3562306a36Sopenharmony_ci	evtchnl->evt_id = req->id;
3662306a36Sopenharmony_ci	return req;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int be_stream_do_io(struct xen_snd_front_evtchnl *evtchnl)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))
4262306a36Sopenharmony_ci		return -EIO;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	reinit_completion(&evtchnl->u.req.completion);
4562306a36Sopenharmony_ci	xen_snd_front_evtchnl_flush(evtchnl);
4662306a36Sopenharmony_ci	return 0;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int be_stream_wait_io(struct xen_snd_front_evtchnl *evtchnl)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	if (wait_for_completion_timeout(&evtchnl->u.req.completion,
5262306a36Sopenharmony_ci			msecs_to_jiffies(VSND_WAIT_BACK_MS)) <= 0)
5362306a36Sopenharmony_ci		return -ETIMEDOUT;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return evtchnl->u.req.resp_status;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ciint xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl,
5962306a36Sopenharmony_ci					struct xensnd_query_hw_param *hw_param_req,
6062306a36Sopenharmony_ci					struct xensnd_query_hw_param *hw_param_resp)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct xensnd_req *req;
6362306a36Sopenharmony_ci	int ret;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	mutex_lock(&evtchnl->u.req.req_io_lock);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	mutex_lock(&evtchnl->ring_io_lock);
6862306a36Sopenharmony_ci	req = be_stream_prepare_req(evtchnl, XENSND_OP_HW_PARAM_QUERY);
6962306a36Sopenharmony_ci	req->op.hw_param = *hw_param_req;
7062306a36Sopenharmony_ci	mutex_unlock(&evtchnl->ring_io_lock);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	ret = be_stream_do_io(evtchnl);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (ret == 0)
7562306a36Sopenharmony_ci		ret = be_stream_wait_io(evtchnl);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (ret == 0)
7862306a36Sopenharmony_ci		*hw_param_resp = evtchnl->u.req.resp.hw_param;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	mutex_unlock(&evtchnl->u.req.req_io_lock);
8162306a36Sopenharmony_ci	return ret;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ciint xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl,
8562306a36Sopenharmony_ci				 struct xen_front_pgdir_shbuf *shbuf,
8662306a36Sopenharmony_ci				 u8 format, unsigned int channels,
8762306a36Sopenharmony_ci				 unsigned int rate, u32 buffer_sz,
8862306a36Sopenharmony_ci				 u32 period_sz)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct xensnd_req *req;
9162306a36Sopenharmony_ci	int ret;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	mutex_lock(&evtchnl->u.req.req_io_lock);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	mutex_lock(&evtchnl->ring_io_lock);
9662306a36Sopenharmony_ci	req = be_stream_prepare_req(evtchnl, XENSND_OP_OPEN);
9762306a36Sopenharmony_ci	req->op.open.pcm_format = format;
9862306a36Sopenharmony_ci	req->op.open.pcm_channels = channels;
9962306a36Sopenharmony_ci	req->op.open.pcm_rate = rate;
10062306a36Sopenharmony_ci	req->op.open.buffer_sz = buffer_sz;
10162306a36Sopenharmony_ci	req->op.open.period_sz = period_sz;
10262306a36Sopenharmony_ci	req->op.open.gref_directory =
10362306a36Sopenharmony_ci		xen_front_pgdir_shbuf_get_dir_start(shbuf);
10462306a36Sopenharmony_ci	mutex_unlock(&evtchnl->ring_io_lock);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	ret = be_stream_do_io(evtchnl);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (ret == 0)
10962306a36Sopenharmony_ci		ret = be_stream_wait_io(evtchnl);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	mutex_unlock(&evtchnl->u.req.req_io_lock);
11262306a36Sopenharmony_ci	return ret;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ciint xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	__always_unused struct xensnd_req *req;
11862306a36Sopenharmony_ci	int ret;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	mutex_lock(&evtchnl->u.req.req_io_lock);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	mutex_lock(&evtchnl->ring_io_lock);
12362306a36Sopenharmony_ci	req = be_stream_prepare_req(evtchnl, XENSND_OP_CLOSE);
12462306a36Sopenharmony_ci	mutex_unlock(&evtchnl->ring_io_lock);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	ret = be_stream_do_io(evtchnl);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (ret == 0)
12962306a36Sopenharmony_ci		ret = be_stream_wait_io(evtchnl);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	mutex_unlock(&evtchnl->u.req.req_io_lock);
13262306a36Sopenharmony_ci	return ret;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ciint xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
13662306a36Sopenharmony_ci			       unsigned long pos, unsigned long count)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct xensnd_req *req;
13962306a36Sopenharmony_ci	int ret;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	mutex_lock(&evtchnl->u.req.req_io_lock);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	mutex_lock(&evtchnl->ring_io_lock);
14462306a36Sopenharmony_ci	req = be_stream_prepare_req(evtchnl, XENSND_OP_WRITE);
14562306a36Sopenharmony_ci	req->op.rw.length = count;
14662306a36Sopenharmony_ci	req->op.rw.offset = pos;
14762306a36Sopenharmony_ci	mutex_unlock(&evtchnl->ring_io_lock);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	ret = be_stream_do_io(evtchnl);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (ret == 0)
15262306a36Sopenharmony_ci		ret = be_stream_wait_io(evtchnl);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	mutex_unlock(&evtchnl->u.req.req_io_lock);
15562306a36Sopenharmony_ci	return ret;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ciint xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
15962306a36Sopenharmony_ci			      unsigned long pos, unsigned long count)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	struct xensnd_req *req;
16262306a36Sopenharmony_ci	int ret;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	mutex_lock(&evtchnl->u.req.req_io_lock);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	mutex_lock(&evtchnl->ring_io_lock);
16762306a36Sopenharmony_ci	req = be_stream_prepare_req(evtchnl, XENSND_OP_READ);
16862306a36Sopenharmony_ci	req->op.rw.length = count;
16962306a36Sopenharmony_ci	req->op.rw.offset = pos;
17062306a36Sopenharmony_ci	mutex_unlock(&evtchnl->ring_io_lock);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	ret = be_stream_do_io(evtchnl);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (ret == 0)
17562306a36Sopenharmony_ci		ret = be_stream_wait_io(evtchnl);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	mutex_unlock(&evtchnl->u.req.req_io_lock);
17862306a36Sopenharmony_ci	return ret;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ciint xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl *evtchnl,
18262306a36Sopenharmony_ci				 int type)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct xensnd_req *req;
18562306a36Sopenharmony_ci	int ret;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	mutex_lock(&evtchnl->u.req.req_io_lock);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	mutex_lock(&evtchnl->ring_io_lock);
19062306a36Sopenharmony_ci	req = be_stream_prepare_req(evtchnl, XENSND_OP_TRIGGER);
19162306a36Sopenharmony_ci	req->op.trigger.type = type;
19262306a36Sopenharmony_ci	mutex_unlock(&evtchnl->ring_io_lock);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	ret = be_stream_do_io(evtchnl);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (ret == 0)
19762306a36Sopenharmony_ci		ret = be_stream_wait_io(evtchnl);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	mutex_unlock(&evtchnl->u.req.req_io_lock);
20062306a36Sopenharmony_ci	return ret;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	xen_snd_front_alsa_fini(front_info);
20662306a36Sopenharmony_ci	xen_snd_front_evtchnl_free_all(front_info);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic int sndback_initwait(struct xen_snd_front_info *front_info)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	int num_streams;
21262306a36Sopenharmony_ci	int ret;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	ret = xen_snd_front_cfg_card(front_info, &num_streams);
21562306a36Sopenharmony_ci	if (ret < 0)
21662306a36Sopenharmony_ci		return ret;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* create event channels for all streams and publish */
21962306a36Sopenharmony_ci	ret = xen_snd_front_evtchnl_create_all(front_info, num_streams);
22062306a36Sopenharmony_ci	if (ret < 0)
22162306a36Sopenharmony_ci		return ret;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return xen_snd_front_evtchnl_publish_all(front_info);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int sndback_connect(struct xen_snd_front_info *front_info)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	return xen_snd_front_alsa_init(front_info);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic void sndback_disconnect(struct xen_snd_front_info *front_info)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	xen_snd_drv_fini(front_info);
23462306a36Sopenharmony_ci	xenbus_switch_state(front_info->xb_dev, XenbusStateInitialising);
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic void sndback_changed(struct xenbus_device *xb_dev,
23862306a36Sopenharmony_ci			    enum xenbus_state backend_state)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	struct xen_snd_front_info *front_info = dev_get_drvdata(&xb_dev->dev);
24162306a36Sopenharmony_ci	int ret;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	dev_dbg(&xb_dev->dev, "Backend state is %s, front is %s\n",
24462306a36Sopenharmony_ci		xenbus_strstate(backend_state),
24562306a36Sopenharmony_ci		xenbus_strstate(xb_dev->state));
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	switch (backend_state) {
24862306a36Sopenharmony_ci	case XenbusStateReconfiguring:
24962306a36Sopenharmony_ci	case XenbusStateReconfigured:
25062306a36Sopenharmony_ci	case XenbusStateInitialised:
25162306a36Sopenharmony_ci		break;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	case XenbusStateInitialising:
25462306a36Sopenharmony_ci		/* Recovering after backend unexpected closure. */
25562306a36Sopenharmony_ci		sndback_disconnect(front_info);
25662306a36Sopenharmony_ci		break;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	case XenbusStateInitWait:
25962306a36Sopenharmony_ci		/* Recovering after backend unexpected closure. */
26062306a36Sopenharmony_ci		sndback_disconnect(front_info);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		ret = sndback_initwait(front_info);
26362306a36Sopenharmony_ci		if (ret < 0)
26462306a36Sopenharmony_ci			xenbus_dev_fatal(xb_dev, ret, "initializing frontend");
26562306a36Sopenharmony_ci		else
26662306a36Sopenharmony_ci			xenbus_switch_state(xb_dev, XenbusStateInitialised);
26762306a36Sopenharmony_ci		break;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	case XenbusStateConnected:
27062306a36Sopenharmony_ci		if (xb_dev->state != XenbusStateInitialised)
27162306a36Sopenharmony_ci			break;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		ret = sndback_connect(front_info);
27462306a36Sopenharmony_ci		if (ret < 0)
27562306a36Sopenharmony_ci			xenbus_dev_fatal(xb_dev, ret, "initializing frontend");
27662306a36Sopenharmony_ci		else
27762306a36Sopenharmony_ci			xenbus_switch_state(xb_dev, XenbusStateConnected);
27862306a36Sopenharmony_ci		break;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	case XenbusStateClosing:
28162306a36Sopenharmony_ci		/*
28262306a36Sopenharmony_ci		 * In this state backend starts freeing resources,
28362306a36Sopenharmony_ci		 * so let it go into closed state first, so we can also
28462306a36Sopenharmony_ci		 * remove ours.
28562306a36Sopenharmony_ci		 */
28662306a36Sopenharmony_ci		break;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	case XenbusStateUnknown:
28962306a36Sopenharmony_ci	case XenbusStateClosed:
29062306a36Sopenharmony_ci		if (xb_dev->state == XenbusStateClosed)
29162306a36Sopenharmony_ci			break;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		sndback_disconnect(front_info);
29462306a36Sopenharmony_ci		break;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int xen_drv_probe(struct xenbus_device *xb_dev,
29962306a36Sopenharmony_ci			 const struct xenbus_device_id *id)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct xen_snd_front_info *front_info;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	front_info = devm_kzalloc(&xb_dev->dev,
30462306a36Sopenharmony_ci				  sizeof(*front_info), GFP_KERNEL);
30562306a36Sopenharmony_ci	if (!front_info)
30662306a36Sopenharmony_ci		return -ENOMEM;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	front_info->xb_dev = xb_dev;
30962306a36Sopenharmony_ci	dev_set_drvdata(&xb_dev->dev, front_info);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	return xenbus_switch_state(xb_dev, XenbusStateInitialising);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic void xen_drv_remove(struct xenbus_device *dev)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	struct xen_snd_front_info *front_info = dev_get_drvdata(&dev->dev);
31762306a36Sopenharmony_ci	int to = 100;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	xenbus_switch_state(dev, XenbusStateClosing);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/*
32262306a36Sopenharmony_ci	 * On driver removal it is disconnected from XenBus,
32362306a36Sopenharmony_ci	 * so no backend state change events come via .otherend_changed
32462306a36Sopenharmony_ci	 * callback. This prevents us from exiting gracefully, e.g.
32562306a36Sopenharmony_ci	 * signaling the backend to free event channels, waiting for its
32662306a36Sopenharmony_ci	 * state to change to XenbusStateClosed and cleaning at our end.
32762306a36Sopenharmony_ci	 * Normally when front driver removed backend will finally go into
32862306a36Sopenharmony_ci	 * XenbusStateInitWait state.
32962306a36Sopenharmony_ci	 *
33062306a36Sopenharmony_ci	 * Workaround: read backend's state manually and wait with time-out.
33162306a36Sopenharmony_ci	 */
33262306a36Sopenharmony_ci	while ((xenbus_read_unsigned(front_info->xb_dev->otherend, "state",
33362306a36Sopenharmony_ci				     XenbusStateUnknown) != XenbusStateInitWait) &&
33462306a36Sopenharmony_ci	       --to)
33562306a36Sopenharmony_ci		msleep(10);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (!to) {
33862306a36Sopenharmony_ci		unsigned int state;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		state = xenbus_read_unsigned(front_info->xb_dev->otherend,
34162306a36Sopenharmony_ci					     "state", XenbusStateUnknown);
34262306a36Sopenharmony_ci		pr_err("Backend state is %s while removing driver\n",
34362306a36Sopenharmony_ci		       xenbus_strstate(state));
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	xen_snd_drv_fini(front_info);
34762306a36Sopenharmony_ci	xenbus_frontend_closed(dev);
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic const struct xenbus_device_id xen_drv_ids[] = {
35162306a36Sopenharmony_ci	{ XENSND_DRIVER_NAME },
35262306a36Sopenharmony_ci	{ "" }
35362306a36Sopenharmony_ci};
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic struct xenbus_driver xen_driver = {
35662306a36Sopenharmony_ci	.ids = xen_drv_ids,
35762306a36Sopenharmony_ci	.probe = xen_drv_probe,
35862306a36Sopenharmony_ci	.remove = xen_drv_remove,
35962306a36Sopenharmony_ci	.otherend_changed = sndback_changed,
36062306a36Sopenharmony_ci	.not_essential = true,
36162306a36Sopenharmony_ci};
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic int __init xen_drv_init(void)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	if (!xen_domain())
36662306a36Sopenharmony_ci		return -ENODEV;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (!xen_has_pv_devices())
36962306a36Sopenharmony_ci		return -ENODEV;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	/* At the moment we only support case with XEN_PAGE_SIZE == PAGE_SIZE */
37262306a36Sopenharmony_ci	if (XEN_PAGE_SIZE != PAGE_SIZE) {
37362306a36Sopenharmony_ci		pr_err(XENSND_DRIVER_NAME ": different kernel and Xen page sizes are not supported: XEN_PAGE_SIZE (%lu) != PAGE_SIZE (%lu)\n",
37462306a36Sopenharmony_ci		       XEN_PAGE_SIZE, PAGE_SIZE);
37562306a36Sopenharmony_ci		return -ENODEV;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	pr_info("Initialising Xen " XENSND_DRIVER_NAME " frontend driver\n");
37962306a36Sopenharmony_ci	return xenbus_register_frontend(&xen_driver);
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic void __exit xen_drv_fini(void)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	pr_info("Unregistering Xen " XENSND_DRIVER_NAME " frontend driver\n");
38562306a36Sopenharmony_ci	xenbus_unregister_driver(&xen_driver);
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cimodule_init(xen_drv_init);
38962306a36Sopenharmony_cimodule_exit(xen_drv_fini);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ciMODULE_DESCRIPTION("Xen virtual sound device frontend");
39262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
39362306a36Sopenharmony_ciMODULE_ALIAS("xen:" XENSND_DRIVER_NAME);
394