/* * avrdude - A Downloader/Uploader for AVR device programmers * Copyright (C) 2003 Eric B. Weddington * * 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 #include #include #include #include #include #include #include #include #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