// Listens on parallel port for MSIO communication with ATMEL.
//
// Compile with:
// gcc -Wall -o msio_host msio_host.c
//
// Controlling the parallel port is copy&paster from on avrdude source
// code.
//
// TODO:
// - share code with avrdude instead of copy&paste to support:
// - other adapters. However, only adaptors on which we can
// control all lines are suitable (good: par, bad: stk500).
// - other OSes
//
// R. Nijlunsing
// License: GPL (same as AVRdude)
//////////////////// Configuration
// Device of parallel port
#define PARDEV "/dev/parport0"
//// Pin layout. Pins on parallel port just like the DAPA/PPI in
//// /etc/avrdude.conf .
// Reset pin. If undefined, does not reset ATMEL at start.
#define RESET 16
#define SCK 1
#define MOSI 2
#define MISO 11
//////////////////// End of configuration
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define OBSOLETE__IOW _IOW
#define PPISDATA PPWDATA
#define PPIGDATA PPRDATA
#define PPISCTRL PPWCONTROL
#define PPIGCTRL PPRCONTROL
#define PPISSTATUS PPWSTATUS
#define PPIGSTATUS PPRSTATUS
static char * progname = "msio_host";
enum {
PPIDATA,
PPICTRL,
PPISTATUS
};
enum {
PPI_READ,
PPI_WRITE,
PPI_SHADOWREAD
};
#define ppi_claim(fd) \
if (ioctl(fd, PPCLAIM) < 0) { \
fprintf(stderr, "%s: can't claim device: %s\n\n", \
progname, strerror(errno)); \
close(fd); \
exit(1); \
}
#define ppi_release(fd) \
if (ioctl(fd, PPRELEASE) < 0) { \
fprintf(stderr, "%s: can't release device: %s\n\n", \
progname, strerror(errno)); \
exit(1); \
}
int ppi_shadow_access(int fd, int reg, unsigned char *v, unsigned char action)
{
static unsigned char shadow[3];
int shadow_num;
unsigned long set, get;
int rc = 0;
switch (reg) {
case PPIDATA: set = PPISDATA; get = PPIGDATA; shadow_num = 0; break;
case PPICTRL: set = PPISCTRL; get = PPIGCTRL; shadow_num = 1; break;
case PPISTATUS: set = PPISSTATUS; get = PPIGSTATUS; shadow_num = 2; break;
default:
fprintf(stderr, "%s: avr_set(): invalid register=%d\n",
progname, reg);
return -1;
break;
}
switch (action) {
case PPI_SHADOWREAD: *v = shadow[shadow_num]; break;
case PPI_READ: rc = ioctl(fd, get, v); shadow[shadow_num]=*v; break;
case PPI_WRITE: shadow[shadow_num]=*v; rc = ioctl(fd, set, v); break;
}
return rc;
}
/*
* set the indicated bit of the specified register.
*/
int ppi_set(int fd, int reg, int bit)
{
unsigned char v;
int rc;
rc = ppi_shadow_access(fd, reg, &v, PPI_SHADOWREAD);
v |= bit;
rc |= ppi_shadow_access(fd, reg, &v, PPI_WRITE);
if (rc)
return -1;
return 0;
}
/*
* clear the indicated bit of the specified register.
*/
int ppi_clr(int fd, int reg, int bit)
{
unsigned char v;
int rc;
rc = ppi_shadow_access(fd, reg, &v, PPI_SHADOWREAD);
v &= ~bit;
rc |= ppi_shadow_access(fd, reg, &v, PPI_WRITE);
if (rc)
return -1;
return 0;
}
/*
* get the indicated bit of the specified register.
*/
int ppi_get(int fd, int reg, int bit)
{
unsigned char v;
int rc;
rc = ppi_shadow_access(fd, 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(int fd, int reg, int bit)
{
unsigned char v;
int rc;
rc = ppi_shadow_access(fd, reg, &v, PPI_SHADOWREAD);
v ^= bit;
rc |= ppi_shadow_access(fd, reg, &v, PPI_WRITE);
if (rc)
return -1;
return 0;
}
/*
* get all bits of the specified register.
*/
int ppi_getall(int fd, int reg)
{
unsigned char v;
int rc;
rc = ppi_shadow_access(fd, reg, &v, PPI_READ);
if (rc)
return -1;
return v; /* v == bit */
}
/*
* set all bits of the specified register to val.
*/
int ppi_setall(int fd, int reg, int val)
{
unsigned char v;
int rc;
v = val;
rc = ppi_shadow_access(fd, reg, &v, PPI_WRITE);
if (rc)
return -1;
return 0;
}
int ppi_open(char * port)
{
int fd;
unsigned char v;
fd = open(port, O_RDWR);
if (fd < 0) {
fprintf(stderr, "%s: can't open device \"%s\": %s\n",
progname, port, strerror(errno));
return -1;
}
/*
* Initialize shadow registers
*/
ppi_claim(fd);
ppi_shadow_access (fd, PPIDATA, &v, PPI_READ);
ppi_shadow_access (fd, PPICTRL, &v, PPI_READ);
ppi_shadow_access (fd, PPISTATUS, &v, PPI_READ);
return fd;
}
void ppi_close(int fd) { ppi_release(fd); close(fd); }
enum {
PPI_AVR_VCC=1,
PPI_AVR_BUFF,
PIN_AVR_RESET,
PIN_AVR_SCK,
PIN_AVR_MOSI,
PIN_AVR_MISO,
PIN_LED_ERR,
PIN_LED_RDY,
PIN_LED_PGM,
PIN_LED_VFY,
N_PINS
};
#define PIN_INVERSE 0x80 /* flag for inverted pin in serbb */
#define PIN_MASK 0x7f
struct ppipins_t {
int pin;
int reg;
int bit;
int inverted;
};
struct ppipins_t ppipins[] = {
{ 1, PPICTRL, 0x01, 1 },
{ 2, PPIDATA, 0x01, 0 },
{ 3, PPIDATA, 0x02, 0 },
{ 4, PPIDATA, 0x04, 0 },
{ 5, PPIDATA, 0x08, 0 },
{ 6, PPIDATA, 0x10, 0 },
{ 7, PPIDATA, 0x20, 0 },
{ 8, PPIDATA, 0x40, 0 },
{ 9, PPIDATA, 0x80, 0 },
{ 10, PPISTATUS, 0x40, 0 },
{ 11, PPISTATUS, 0x80, 1 },
{ 12, PPISTATUS, 0x20, 0 },
{ 13, PPISTATUS, 0x10, 0 },
{ 14, PPICTRL, 0x02, 1 },
{ 15, PPISTATUS, 0x08, 0 },
{ 16, PPICTRL, 0x04, 0 },
{ 17, PPICTRL, 0x08, 1 }
};
int par_setpin(int fd, int pin, int value)
{
pin &= PIN_MASK;
if (pin < 1 || pin > 17) return -1;
pin--;
if (ppipins[pin].inverted) value = !value;
if (value)
ppi_set(fd, ppipins[pin].reg, ppipins[pin].bit);
else
ppi_clr(fd, ppipins[pin].reg, ppipins[pin].bit);
return 0;
}
int par_getpin(int fd, int pin)
{
int value;
pin &= PIN_MASK;
if (pin < 1 || pin > 17) return -1;
pin--;
value = ppi_get(fd, ppipins[pin].reg, ppipins[pin].bit);
if (value) value = 1;
if (ppipins[pin].inverted) value = !value;
return value;
}
//////////////////// MSIO specific part:
static int fd;
void reset_atmel(void) {
#ifdef RESET
printf("Resetting ATMEL...\n");
par_setpin(fd, RESET, 0);
usleep(100000);
par_setpin(fd, RESET, 1);
usleep(500000);
#endif
}
void set_sck(int enabled) { par_setpin(fd, SCK, enabled); }
void set_mosi(int enabled) { par_setpin(fd, MOSI, enabled); }
int get_miso(void) { return par_getpin(fd, MISO); }
// Time to wait between the bits.
void wait_bit(void) {
// Fast, but host PC speed dependant and unreliable:
// int i; for (i = 0; i < 5000; i++) ;
// Slow (same baud as HZ of Linux kernel) but more reliable:
usleep(0);
}
unsigned char receive_byte(void) {
unsigned char res = 0;
int i;
int sck = 1;
// We're not responding yet
set_sck(sck);
// Wait for attention-needed signal from ATMEL. Poll at a relative
// low frequency.
// printf("Waiting for attention...\n");
while (get_miso()) usleep(1);
for (i = 0; i < 8; i++) {
int bit;
sck ^= 1;
set_sck(sck);
wait_bit();
bit = get_miso();
// printf("%i", bit); fflush(stdout);
res += (1 << i) * bit;
}
// printf(" %i\n", res);
// Send stop bit
set_sck(0);
wait_bit();
set_sck(1);
return res;
}
unsigned int gettime(void) {
struct timeval t;
gettimeofday(&t, NULL);
return (t.tv_sec * 1000000) + t.tv_usec;
}
int main(void) {
fd = ppi_open(PARDEV);
set_sck(1); // State idle.
reset_atmel();
while (1) {
unsigned int start, elapsed;
char c;
start = gettime();
c = receive_byte();
printf("%c", c); fflush(stdout);
// elapsed = gettime() - start; printf("%i\n", elapsed);
}
ppi_close(fd);
return 0;
}