/*
 * avrdude - A Downloader/Uploader for AVR device programmers
 * Copyright (C) 2000-2004 Brian S. Dean <bsd@bdmicro.com>
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#if !defined(WIN32)
#include <ac_cfg.h>

#if HAVE_PARPORT
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include "freebsd_ppi.h"
#elif defined(__linux__)
#include "linux_ppdev.h"
#elif defined(__sun__) || defined(__sun)        // Solaris
#include "solaris_ecpp.h"
#endif

#include "avrdude.h"
#include "libavrdude.h"

#include "ppi.h"

enum {
  PPI_READ,
  PPI_WRITE,
  PPI_SHADOWREAD
};

static int ppi_shadow_access(const union filedescriptor *fdp, int reg, unsigned char *v, unsigned char action) {
  int shadow_num;

  switch(reg) {
  case PPIDATA:
    shadow_num = 0;
    break;
  case PPICTRL:
    shadow_num = 1;
    break;
  case PPISTATUS:
    shadow_num = 2;
    break;
  default:
    pmsg_error("invalid register=%d\n", reg);
    return -1;
    break;
  }

  switch(action) {
  case PPI_SHADOWREAD:
    *v = cx->ppi_shadow[shadow_num];
    break;
  case PPI_READ:
    DO_PPI_READ(fdp->ifd, reg, v);
    cx->ppi_shadow[shadow_num] = *v;
    break;
  case PPI_WRITE:
    cx->ppi_shadow[shadow_num] = *v;
    DO_PPI_WRITE(fdp->ifd, reg, v);
    break;
  }
  return 0;
}

// Set the indicated bit of the specified register
int ppi_set(const union filedescriptor *fdp, int reg, int bit) {
  unsigned char v;
  int rc;

  rc = ppi_shadow_access(fdp, reg, &v, PPI_SHADOWREAD);
  v |= bit;
  rc |= ppi_shadow_access(fdp, reg, &v, PPI_WRITE);

  if(rc)
    return -1;

  return 0;
}

// Clear the indicated bit of the specified register
int ppi_clr(const union filedescriptor *fdp, int reg, int bit) {
  unsigned char v;
  int rc;

  rc = ppi_shadow_access(fdp, reg, &v, PPI_SHADOWREAD);
  v &= ~bit;
  rc |= ppi_shadow_access(fdp, reg, &v, PPI_WRITE);

  if(rc)
    return -1;

  return 0;
}

// Get the indicated bit of the specified register
int ppi_get(const union filedescriptor *fdp, int reg, int bit) {
  unsigned char v;
  int rc;

  rc = ppi_shadow_access(fdp, reg, &v, PPI_READ);
  v &= bit;

  if(rc)
    return -1;

  return v;                     // v == bit
}

// Toggle the indicated bit of the specified register
int ppi_toggle(const union filedescriptor *fdp, int reg, int bit) {
  unsigned char v;
  int rc;

  rc = ppi_shadow_access(fdp, reg, &v, PPI_SHADOWREAD);
  v ^= bit;
  rc |= ppi_shadow_access(fdp, reg, &v, PPI_WRITE);

  if(rc)
    return -1;

  return 0;
}

// Get all bits of the specified register
int ppi_getall(const union filedescriptor *fdp, int reg) {
  unsigned char v;
  int rc;

  rc = ppi_shadow_access(fdp, reg, &v, PPI_READ);

  if(rc)
    return -1;

  return v;                     // v == bit
}

// Set all bits of the specified register to val
int ppi_setall(const union filedescriptor *fdp, int reg, int val) {
  unsigned char v;
  int rc;

  v = val;
  rc = ppi_shadow_access(fdp, reg, &v, PPI_WRITE);

  if(rc)
    return -1;

  return 0;
}

void ppi_open(const char *port, union filedescriptor *fdp) {
  int fd;
  unsigned char v;

  fd = open(port, O_RDWR);
  if(fd < 0) {
    pmsg_ext_error("cannot open port %s: %s\n", port, strerror(errno));
    fdp->ifd = -1;
    return;
  }

  ppi_claim(fd);

  // Initialize shadow registers
  ppi_shadow_access(fdp, PPIDATA, &v, PPI_READ);
  ppi_shadow_access(fdp, PPICTRL, &v, PPI_READ);
  ppi_shadow_access(fdp, PPISTATUS, &v, PPI_READ);

  fdp->ifd = fd;
}

void ppi_close(const union filedescriptor *fdp) {
  ppi_release(fdp->ifd);
  close(fdp->ifd);
}
#endif                          // HAVE_PARPORT
#endif                          // ! WIN32
