avrdude/src/serbb_posix.c

318 lines
7.2 KiB
C

/*
* avrdude - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2000, 2001, 2002, 2003 Brian S. Dean <bsd@bsdhome.com>
* Copyright (C) 2005 Juliane Holzt <avrdude@juliane.holzt.de>
* Copyright (C) 2006 Joerg Wunsch <j@uriah.heep.sax.de>
*
* 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/>.
*/
/* $Id$ */
/*
* Posix serial bitbanging interface for avrdude.
*/
#if !defined(WIN32)
#include "ac_cfg.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <termios.h>
#include "avrdude.h"
#include "libavrdude.h"
#include "bitbang.h"
#include "serbb.h"
#undef DEBUG
static struct termios oldmode;
/*
serial port/pin mapping
1 cd <-
2 (rxd) <-
3 txd ->
4 dtr ->
5 GND
6 dsr <-
7 rts ->
8 cts <-
9 ri <-
*/
#define DB9PINS 9
static int serregbits[DB9PINS + 1] =
{ 0, TIOCM_CD, 0, 0, TIOCM_DTR, 0, TIOCM_DSR, TIOCM_RTS, TIOCM_CTS, TIOCM_RI };
#ifdef DEBUG
static char *serpins[DB9PINS + 1] =
{ "NONE", "CD", "RXD", "TXD", "DTR", "GND", "DSR", "RTS", "CTS", "RI" };
#endif
static int serbb_setpin(const PROGRAMMER *pgm, int pinfunc, int value) {
unsigned int ctl;
int r;
if(pinfunc < 0 || pinfunc >= N_PINS)
return -1;
int pin = pgm->pinno[pinfunc]; // get its value
if (pin & PIN_INVERSE)
{
value = !value;
pin &= PIN_MASK;
}
if ( pin < 1 || pin > DB9PINS )
return -1;
#ifdef DEBUG
msg_info("%s to %d\n", serpins[pin], value);
#endif
switch ( pin )
{
case 3: /* txd */
r = ioctl(pgm->fd.ifd, value ? TIOCSBRK : TIOCCBRK, 0);
if (r < 0) {
pmsg_ext_error("ioctl(\"TIOCxBRK\"): %s\n", strerror(errno));
return -1;
}
break;
case 4: /* dtr */
case 7: /* rts */
r = ioctl(pgm->fd.ifd, TIOCMGET, &ctl);
if (r < 0) {
pmsg_ext_error("ioctl(\"TIOCMGET\"): %s\n", strerror(errno));
return -1;
}
if (value)
ctl |= serregbits[pin];
else
ctl &= ~(serregbits[pin]);
r = ioctl(pgm->fd.ifd, TIOCMSET, &ctl);
if (r < 0) {
pmsg_ext_error("ioctl(\"TIOCMSET\"): %s\n", strerror(errno));
return -1;
}
break;
default: /* impossible */
return -1;
}
if (pgm->ispdelay > 1)
bitbang_delay(pgm->ispdelay);
return 0;
}
static int serbb_getpin(const PROGRAMMER *pgm, int pinfunc) {
unsigned int ctl;
unsigned char invert;
int r;
if(pinfunc < 0 || pinfunc >= N_PINS)
return -1;
int pin = pgm->pinno[pinfunc]; // get its value
if (pin & PIN_INVERSE)
{
invert = 1;
pin &= PIN_MASK;
} else
invert = 0;
if ( pin < 1 || pin > DB9PINS )
return(-1);
switch ( pin )
{
case 2: /* rxd, currently not implemented, FIXME */
return(-1);
case 1: /* cd */
case 6: /* dsr */
case 8: /* cts */
case 9: /* ri */
r = ioctl(pgm->fd.ifd, TIOCMGET, &ctl);
if (r < 0) {
pmsg_ext_error("ioctl(\"TIOCMGET\"): %s\n", strerror(errno));
return -1;
}
if ( !invert )
{
#ifdef DEBUG
msg_info("%s is %d\n", serpins[pin], ctl & serregbits[pin]? 1: 0);
#endif
return ctl & serregbits[pin]? 1: 0;
}
else
{
#ifdef DEBUG
msg_info("%s is %d (~)\n", serpins[pin], ctl & serregbits[pin]? 0: 1);
#endif
return ctl & serregbits[pin]? 0: 1;
}
default: /* impossible */
return(-1);
}
}
static int serbb_highpulsepin(const PROGRAMMER *pgm, int pinfunc) {
if (pinfunc < 0 || pinfunc >= N_PINS)
return -1;
int pin = pgm->pinno[pinfunc]; // replace pin name by its value
if ( (pin & PIN_MASK) < 1 || (pin & PIN_MASK) > DB9PINS )
return -1;
serbb_setpin(pgm, pinfunc, 1);
serbb_setpin(pgm, pinfunc, 0);
return 0;
}
static void serbb_display(const PROGRAMMER *pgm, const char *p) {
/* MAYBE */
}
static void serbb_enable(PROGRAMMER *pgm, const AVRPART *p) {
/* nothing */
}
static void serbb_disable(const PROGRAMMER *pgm) {
/* nothing */
}
static void serbb_powerup(const PROGRAMMER *pgm) {
/* nothing */
}
static void serbb_powerdown(const PROGRAMMER *pgm) {
/* nothing */
}
static int serbb_open(PROGRAMMER *pgm, const char *port) {
struct termios mode;
int flags;
int r;
if (bitbang_check_prerequisites(pgm) < 0)
return -1;
/* adapted from uisp code */
pgm->fd.ifd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (pgm->fd.ifd < 0) {
pmsg_ext_error("%s: %s\n", port, strerror(errno));
return(-1);
}
r = tcgetattr(pgm->fd.ifd, &mode);
if (r < 0) {
pmsg_ext_error("%s, tcgetattr(): %s\n", port, strerror(errno));
return(-1);
}
oldmode = mode;
mode.c_iflag = IGNBRK | IGNPAR;
mode.c_oflag = 0;
mode.c_cflag = CLOCAL | CREAD | CS8 | B9600;
mode.c_cc [VMIN] = 1;
mode.c_cc [VTIME] = 0;
r = tcsetattr(pgm->fd.ifd, TCSANOW, &mode);
if (r < 0) {
pmsg_ext_error("%s, tcsetattr(): %s", port, strerror(errno));
return(-1);
}
/* Clear O_NONBLOCK flag. */
flags = fcntl(pgm->fd.ifd, F_GETFL, 0);
if (flags == -1)
{
pmsg_ext_error("cannot get flags: %s\n", strerror(errno));
return(-1);
}
flags &= ~O_NONBLOCK;
if (fcntl(pgm->fd.ifd, F_SETFL, flags) == -1)
{
pmsg_ext_error("cannot clear nonblock flag: %s\n", strerror(errno));
return(-1);
}
return(0);
}
static void serbb_close(PROGRAMMER *pgm) {
if (pgm->fd.ifd != -1)
{
(void)tcsetattr(pgm->fd.ifd, TCSANOW, &oldmode);
pgm->setpin(pgm, PIN_AVR_RESET, 1);
close(pgm->fd.ifd);
}
return;
}
const char serbb_desc[] = "Serial port bitbanging";
void serbb_initpgm(PROGRAMMER *pgm) {
strcpy(pgm->type, "SERBB");
pgm_fill_old_pins(pgm); // TODO to be removed if old pin data no longer needed
pgm->rdy_led = bitbang_rdy_led;
pgm->err_led = bitbang_err_led;
pgm->pgm_led = bitbang_pgm_led;
pgm->vfy_led = bitbang_vfy_led;
pgm->initialize = bitbang_initialize;
pgm->display = serbb_display;
pgm->enable = serbb_enable;
pgm->disable = serbb_disable;
pgm->powerup = serbb_powerup;
pgm->powerdown = serbb_powerdown;
pgm->program_enable = bitbang_program_enable;
pgm->chip_erase = bitbang_chip_erase;
pgm->cmd = bitbang_cmd;
pgm->cmd_tpi = bitbang_cmd_tpi;
pgm->open = serbb_open;
pgm->close = serbb_close;
pgm->setpin = serbb_setpin;
pgm->getpin = serbb_getpin;
pgm->highpulsepin = serbb_highpulsepin;
pgm->read_byte = avr_read_byte_default;
pgm->write_byte = avr_write_byte_default;
}
#endif /* WIN32 */