 copy of the GNU Lesser General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */
  
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <sys/mman.h>
#include <netinet/in.h>
#include <netdb.h>
#include "aserver.h"

#ifndef PIC
/* entry for static linking */
const char *_snd_module_control_shm = "";
#endif

#ifndef DOC_HIDDEN
typedef struct {
	int socket;
	volatile snd_ctl_shm_ctrl_t *ctrl;
} snd_ctl_shm_t;
#endif

static int snd_ctl_shm_action(snd_ctl_t *ctl)
{
	snd_ctl_shm_t *shm = ctl->private_data;
	int err;
	char buf[1];
	volatile snd_ctl_shm_ctrl_t *ctrl = shm->ctrl;
	err = write(shm->socket, buf, 1);
	if (err!= 1)
		return -EBADFD;
	err = read(shm->socket, buf, 1);
	if (err!= 1)
		return -EBADFD;
	if (ctrl->cmd) {
		SNDERR("Server has not done the cmd");
		return -EBADFD;
	}
	return ctrl->result;
}

static int snd_ctl_shm_action_fd(snd_ctl_t *ctl, int *fd)
{
	snd_ctl_shm_t *shm = ctl->private_data;
	int err;
	char buf[1];
	volatile snd_ctl_shm_ctrl_t *ctrl = shm->ctrl;
	err = write(shm->socket, buf, 1);
	if (err!= 1)
		return -EBADFD;
	err = snd_receive_fd(shm->socket, buf, 1, fd);
	if (err!= 1)
		return -EBADFD;
	if (ctrl->cmd) {
		SNDERR("Server has not done the cmd");
		return -EBADFD;
	}
	return ctrl->result;
}

static int snd_ctl_shm_close(snd_ctl_t *ctl)
{
	snd_ctl_shm_t *shm = ctl->private_data;
	volatile snd_ctl_shm_ctrl_t *ctrl = shm->ctrl;
	int result;
	ctrl->cmd = SND_CTL_IOCTL_CLOSE;
	result = snd_ctl_shm_action(ctl);
	shmdt((void *)ctrl);
	close(shm->socket);
	free(shm);
	return result;
}

static int snd_ctl_shm_nonblock(snd_ctl_t *handle ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
{
	return 0;
}

static int snd_ctl_shm_async(snd_ctl_t *ctl, int sig, pid_t pid)
{
	snd_ctl_shm_t *shm = ctl->private_data;
	volatile snd_ctl_shm_ctrl_t *ctrl = shm->ctrl;
	ctrl->cmd = SND_CTL_IOCTL_ASYNC;
	ctrl->u.async.sig = sig;
	if (pid == 0)
		pid = getpid();
	ctrl->u.async.pid = pid;
	return snd_ctl_shm_action(ctl);
}

static int snd_ctl_shm_poll_descriptor(snd_ctl_t *ctl)
{
	snd_ctl_shm_t *shm = ctl->private_data;
	volatile snd_ctl_shm_ctrl_t *ctrl = shm->ctrl;
	int fd, err;
	ctrl->cmd = SND_CTL_IOCTL_POLL_DESCRIPTOR;
	err = snd_ctl_shm_action_fd(ctl, &fd);
	if (err < 0)
		return err;
	return fd;
}

static int snd_ctl_shm_subscribe_events(snd_ctl_t *ctl, int subscribe)
{
	snd_ctl_shm_t *shm = ctl->private_data;
	volatile snd_ctl_shm_ctrl_t *ctrl = shm->ctrl;
	ctrl->cmd = SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS;
	ctrl->u.subscribe_events = subscribe;
	return snd_ctl_shm_action(ctl);
}

static int snd_ctl_shm_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info)
{
	snd_ctl_shm_t *shm = ctl->private_data;
	volatile snd_ctl_shm_ctrl_t *ctrl = shm->ctrl;
	int err;
//	ctrl->u.card_info = *info;
	ctrl->cmd = SNDRV_CTL_IOCTL_CARD_INFO;
	err = snd_ctl_shm_action(ctl);
	if (err < 0)
		return err;
	*info = ctrl->u.card_info;
	return err;
}

static int snd_ctl_shm_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list)
{
	snd_ctl_shm_t *shm = ctl->private_data;
	volatile snd_ctl_shm_ctrl_t *ctrl = shm->ctrl;
	size_t maxsize = CTL_SHM_DATA_MAXLEN;
	size_t bytes = list->space * sizeof(*list->pids);
	int err;
	snd_ctl_elem_id_t *pids = list->pids;
	if (bytes > maxsize)
		return -EINVAL;
	ctrl->u.element_list = *list;
	ctrl->cmd = SNDRV_CTL_IOCTL_ELEM_LIST;
	err = snd_ctl_shm_action(ctl);
	if (err < 0)
		return err;
	*list = ctrl->u.element_list;
	list->pids = pids;
	bytes = list->used * sizeof(*list->pids);
	memcpy(pids, (void *)ctrl->data, bytes);
	return err;
}

static int snd_ctl_shm_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info)
{
	snd_ctl_shm_t *shm = ctl->private_data;
	volatile snd_ctl_shm_ctrl_t *ctrl = shm->ctrl;
	int err;
	ctrl->u.element_info = *info;
	ctrl->cmd = SNDRV_CTL_IOCTL_ELEM_INFO;
	err = snd_ctl_shm_action(ctl);
	if (err < 0)
		return err;
	*info = ctrl->u.element_info;
	return err;
}

static int snd_ctl_shm_elem_read(snd_ctl_t *ctl, snd_ctl_elem_value_t *control)
{
	snd_ctl_shm_t *shm = ctl->private_data;
	volatile snd_ctl_shm_ctrl_t *ctrl = shm->ctrl;
	int err;
	ctrl->u.element_read = *control;
	ctrl->cmd = SNDRV_CTL_IOCTL_ELEM_READ;
	err = snd_ctl_shm_action(ctl);
	if (err < 0)
		return err;
	*control = ctrl->u.element_read;
	return err;
}

static int snd_ctl_shm_elem_write(snd_ctl_t *ctl, snd_ctl_elem_value_t *control)
{
	snd_ctl_