/*
 * libbb/libbb.c - Bitbang library
 *
 * Written 2010 by Werner Almesberger
 * Copyright 2010 Werner Almesberger
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */


#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <sys/wait.h>
#include <sys/mman.h>

#include "libbb.h"


#define	PAGE_SIZE	4096


volatile void *libbb_mem;

static int bbd_fd = -1;


static int recv_fd(int s)
{
	struct msghdr msg;
	struct cmsg {
		struct cmsghdr hdr;
		int fd;
	} cmsg;

	memset(&msg, 0, sizeof(msg));
	msg.msg_control = &cmsg;
	msg.msg_controllen = sizeof(cmsg);
        if (recvmsg(s, &msg, 0) < 0) {
		perror("recvmsg");
		exit(1);
	}
	return cmsg.fd;
}


static void child(void)
{
	execl(LIBBD_PATH, LIBBD_PATH, NULL);
	perror(LIBBD_PATH);
	_exit(1);
}


#if 0
static int recv_fd(int s)
{
	pid_t res;

	res = waitpid(pid, &status, 0);
	if (res < 0) {
		perror("waitpid");
		exit(1);
	}
	if (res != pid) {
		fprintf(stderr, "waitpid returned %d for %d\n",
		    (int) res, (int) pid);
		exit(1);
	}
}
#endif


static int bbd(void)
{
	int s[2];
	pid_t pid;

	if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, s) < 0) {
		perror("socketpair");
		exit(1);
	}
	if (dup2(s[1], 1) < 0) {
		perror("dup2");
		exit(1);
	}

	pid = fork();
	if (pid < 0) {
		perror("fork");
		exit(1);
	}
	if (pid)
		return recv_fd(s[0]);
	else
		child();
	return -1; /* can't happen */
}


void libbb_open(void)
{
	bbd_fd = bbd();
	libbb_mem = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
	   bbd_fd, LIBBB_GPIO_BASE);
	if (libbb_mem == MAP_FAILED) {
		perror("mmap");
		exit(1);
	}
}



void libbb_close(void)
{
	if (close(bbd_fd) < 0) {
		perror("close");
		exit(1);
	}
}
