avrdude/avrdude/ppiwin.c

397 lines
7.2 KiB
C

/*
* avrdude - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2003 Eric B. Weddington <eric@ecentral.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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
This is the parallel port interface for Windows built using Cygwin.
In the ppi_* functions that access the parallel port registers,
fd = parallel port address
reg = register as defined in an enum in ppi.h. This must be converted
to a proper offset of the base address.
*/
#if defined(__CYGWIN__)
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <windows.h>
#include <sys/time.h>
#include <w32api/windows.h>
#include "ppi.h"
extern char *progname;
#define DEVICE_LPT1 "lpt1"
#define DEVICE_LPT2 "lpt2"
#define DEVICE_LPT3 "lpt3"
#define DEVICE_MAX 3
typedef struct
{
const char *name;
int base_address;
} winpp;
static const winpp winports[DEVICE_MAX] =
{
{DEVICE_LPT1, 0x378},
{DEVICE_LPT2, 0x278},
{DEVICE_LPT3, 0x3BC},
};
/* FUNCTION PROTOTYPES */
static int winnt_pp_open(void);
static unsigned short port_get(int fd, int reg);
static unsigned char reg2offset(int reg);
static unsigned char inb(unsigned short port);
static void outb(unsigned char value, unsigned short port);
/* FUNCTION DEFINITIONS */
int ppi_open(char *port)
{
unsigned char i;
int fd;
fd = winnt_pp_open();
if(fd < 0)
{
fprintf(stderr, "%s: can't open device \"giveio\"\n\n", progname);
return(-1);
}
/* Search the windows port names for a match */
fd = -1;
for(i = 0; i < DEVICE_MAX; i++)
{
if(strcmp(winports[i].name, port) == 0)
{
/* Set the file descriptor with the Windows parallel port base address. */
fd = winports[i].base_address;
break;
}
}
if(fd < 0)
{
fprintf(stderr, "%s: can't open device \"%s\"\n\n", progname, port);
return(-1);
}
return(fd);
}
#define DRIVERNAME "\\\\.\\giveio"
static int winnt_pp_open(void)
{
// Only try to use giveio under Windows NT/2000/XP.
OSVERSIONINFO ver_info;
memset(&ver_info, 0, sizeof(ver_info));
ver_info.dwOSVersionInfoSize = sizeof(ver_info);
if(!GetVersionEx(&ver_info))
{
return(-1);
}
else if(ver_info.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
HANDLE h = CreateFile(DRIVERNAME,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(h == INVALID_HANDLE_VALUE)
{
return(-1);
}
/* Close immediately. The process now has the rights it needs. */
if(h != NULL)
{
CloseHandle(h);
}
}
return(0);
}
void ppi_close(int fd)
{
return;
}
/*
* set the indicated bit of the specified register.
*/
int ppi_set(int fd, int reg, int bit)
{
unsigned char v;
unsigned short port;
port = port_get(fd, reg);
v = inb(port);
v |= bit;
outb(v, port);
return 0;
}
/*
* clear the indicated bit of the specified register.
*/
int ppi_clr(int fd, int reg, int bit)
{
unsigned char v;
unsigned short port;
port = port_get(fd, reg);
v = inb(port);
v &= ~bit;
outb(v, port);
return 0;
}
/*
* get the indicated bit of the specified register.
*/
int ppi_get(int fd, int reg, int bit)
{
unsigned char v;
v = inb(port_get(fd, reg));
v &= bit;
return(v);
}
/*
* toggle the indicated bit of the specified register.
*/
int ppi_toggle(int fd, int reg, int bit)
{
unsigned char v;
unsigned short port;
port = port_get(fd, reg);
v = inb(port);
v ^= bit;
outb(v, port);
return 0;
}
/*
* get all bits of the specified register.
*/
int ppi_getall(int fd, int reg)
{
unsigned char v;
v = inb(port_get(fd, reg));
return((int)v);
}
/*
* set all bits of the specified register to val.
*/
int ppi_setall(int fd, int reg, int val)
{
outb((unsigned char)val, port_get(fd, reg));
return 0;
}
/* Calculate port address to access. */
static unsigned short port_get(int fd, int reg)
{
return((unsigned short)(fd + reg2offset(reg)));
}
/* Convert register enum to offset of base address. */
static unsigned char reg2offset(int reg)
{
unsigned char offset = 0;
switch(reg)
{
case PPIDATA:
{
offset = 0;
break;
}
case PPISTATUS:
{
offset = 1;
break;
}
case PPICTRL:
{
offset = 2;
break;
}
}
return(offset);
}
/* Read in value from port. */
static unsigned char inb(unsigned short port)
{
unsigned char t;
asm volatile ("in %1, %0"
: "=a" (t)
: "d" (port));
return t;
}
/* Write value to port. */
static void outb(unsigned char value, unsigned short port)
{
asm volatile ("out %1, %0"
:
: "d" (port), "a" (value) );
return;
}
/* These two functions usecPerfDelay and usleep are based on code from the
* uisp project and are a replacement for the cygwin usleep library
* function which seems to not delay for the correct time
*/
BOOL usecPerfDelay(long t)
{
static BOOL perf_counter_checked = FALSE;
static BOOL use_perf_counter = FALSE;
static LARGE_INTEGER freq ;
if (! perf_counter_checked) {
if (QueryPerformanceFrequency(&freq)){
use_perf_counter = TRUE;
}
perf_counter_checked = TRUE;
}
if (! use_perf_counter)
return FALSE;
else {
LARGE_INTEGER now;
LARGE_INTEGER finish;
QueryPerformanceCounter(&now);
finish.QuadPart = now.QuadPart + (t * freq.QuadPart) / 1000000;
do {
QueryPerformanceCounter(&now);
} while (now.QuadPart < finish.QuadPart);
return TRUE;
}
}
/*
WARNING WARNING This function replaces the standard usleep() library function
because it doesn't appear to delay for the correct time.
*/
#ifndef MIN_SLEEP_USEC
#define MIN_SLEEP_USEC 20000
#endif
unsigned usleep( unsigned int uSeconds )
{
struct timeval t1, t2;
struct timespec nanoDelay ;
if (usecPerfDelay(uSeconds))
return(0);
gettimeofday(&t1, NULL);
if( uSeconds > MIN_SLEEP_USEC )
{
nanoDelay.tv_sec = uSeconds / 1000000UL;
nanoDelay.tv_nsec = (uSeconds / 1000000UL) * 1000 ;
nanosleep( &nanoDelay, NULL ) ;
}
/* loop for the remaining time */
t2.tv_sec = uSeconds / 1000000UL;
t2.tv_usec = uSeconds % 1000000UL;
timeradd(&t1, &t2, &t1);
do {
gettimeofday(&t2, NULL);
} while (timercmp(&t2, &t1, <));
return(0);
}
#endif