123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598 |
- /*
- * This file is part of the flashrom project.
- *
- * Copyright (C) 2009 Urja Rannikko <urjaman@gmail.com>
- * Copyright (C) 2009,2010 Carl-Daniel Hailfinger
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
- #include "platform.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <ctype.h>
- #include <fcntl.h>
- #include <sys/stat.h>
- #include <errno.h>
- #include <inttypes.h>
- #if IS_WINDOWS
- #include <conio.h>
- #else
- #include <termios.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/ioctl.h>
- #endif
- #include "flash.h"
- #include "programmer.h"
- fdtype sp_fd = SER_INV_FD;
- /* There is no way defined by POSIX to use arbitrary baud rates. It only defines some macros that can be used to
- * specify respective baud rates and many implementations extend this list with further macros, cf. TERMIOS(3)
- * and http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=include/uapi/asm-generic/termbits.h
- * The code below creates a mapping in sp_baudtable between these macros and the numerical baud rates to deal
- * with numerical user input.
- *
- * On Linux there is a non-standard way to use arbitrary baud rates that flashrom does not support (yet), cf.
- * http://www.downtowndougbrown.com/2013/11/linux-custom-serial-baud-rates/
- *
- * On Windows there exist similar macros (starting with CBR_ instead of B) but they are only defined for
- * backwards compatibility and the API supports arbitrary baud rates in the same manner as the macros, see
- * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx
- */
- #if !IS_WINDOWS
- struct baudentry {
- int flag;
- unsigned int baud;
- };
- #define BAUDENTRY(baud) { B##baud, baud },
- static const struct baudentry sp_baudtable[] = {
- BAUDENTRY(9600) /* unconditional default */
- #ifdef B19200
- BAUDENTRY(19200)
- #endif
- #ifdef B38400
- BAUDENTRY(38400)
- #endif
- #ifdef B57600
- BAUDENTRY(57600)
- #endif
- #ifdef B115200
- BAUDENTRY(115200)
- #endif
- #ifdef B230400
- BAUDENTRY(230400)
- #endif
- #ifdef B460800
- BAUDENTRY(460800)
- #endif
- #ifdef B500000
- BAUDENTRY(500000)
- #endif
- #ifdef B576000
- BAUDENTRY(576000)
- #endif
- #ifdef B921600
- BAUDENTRY(921600)
- #endif
- #ifdef B1000000
- BAUDENTRY(1000000)
- #endif
- #ifdef B1152000
- BAUDENTRY(1152000)
- #endif
- #ifdef B1500000
- BAUDENTRY(1500000)
- #endif
- #ifdef B2000000
- BAUDENTRY(2000000)
- #endif
- #ifdef B2500000
- BAUDENTRY(2500000)
- #endif
- #ifdef B3000000
- BAUDENTRY(3000000)
- #endif
- #ifdef B3500000
- BAUDENTRY(3500000)
- #endif
- #ifdef B4000000
- BAUDENTRY(4000000)
- #endif
- {0, 0} /* Terminator */
- };
- static const struct baudentry *round_baud(unsigned int baud)
- {
- int i;
- /* Round baud rate to next lower entry in sp_baudtable if it exists, else use the lowest entry. */
- for (i = ARRAY_SIZE(sp_baudtable) - 2; i >= 0 ; i--) {
- if (sp_baudtable[i].baud == baud)
- return &sp_baudtable[i];
- if (sp_baudtable[i].baud < baud) {
- msg_pwarn("Warning: given baudrate %d rounded down to %d.\n",
- baud, sp_baudtable[i].baud);
- return &sp_baudtable[i];
- }
- }
- msg_pinfo("Using slowest possible baudrate: %d.\n", sp_baudtable[0].baud);
- return &sp_baudtable[0];
- }
- #endif
- /* Uses msg_perr to print the last system error.
- * Prints "Error: " followed first by \c msg and then by the description of the last error retrieved via
- * strerror() or FormatMessage() and ending with a linebreak. */
- static void msg_perr_strerror(const char *msg)
- {
- msg_perr("Error: %s", msg);
- #if IS_WINDOWS
- char *lpMsgBuf;
- DWORD nErr = GetLastError();
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, nErr,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
- msg_perr(lpMsgBuf);
- /* At least some formatted messages contain a line break at the end. Make sure to always print one */
- if (lpMsgBuf[strlen(lpMsgBuf)-1] != '\n')
- msg_perr("\n");
- LocalFree(lpMsgBuf);
- #else
- msg_perr("%s\n", strerror(errno));
- #endif
- }
- int serialport_config(fdtype fd, int baud)
- {
- if (fd == SER_INV_FD) {
- msg_perr("%s: File descriptor is invalid.\n", __func__);
- return 1;
- }
- #if IS_WINDOWS
- DCB dcb;
- if (!GetCommState(fd, &dcb)) {
- msg_perr_strerror("Could not fetch original serial port configuration: ");
- return 1;
- }
- if (baud >= 0) {
- dcb.BaudRate = baud;
- }
- dcb.ByteSize = 8;
- dcb.Parity = NOPARITY;
- dcb.StopBits = ONESTOPBIT;
- if (!SetCommState(fd, &dcb)) {
- msg_perr_strerror("Could not change serial port configuration: ");
- return 1;
- }
- if (!GetCommState(fd, &dcb)) {
- msg_perr_strerror("Could not fetch new serial port configuration: ");
- return 1;
- }
- msg_pdbg("Baud rate is %ld.\n", dcb.BaudRate);
- #else
- struct termios wanted, observed;
- if (tcgetattr(fd, &observed) != 0) {
- msg_perr_strerror("Could not fetch original serial port configuration: ");
- return 1;
- }
- wanted = observed;
- if (baud >= 0) {
- const struct baudentry *entry = round_baud(baud);
- if (cfsetispeed(&wanted, entry->flag) != 0 || cfsetospeed(&wanted, entry->flag) != 0) {
- msg_perr_strerror("Could not set serial baud rate: ");
- return 1;
- }
- }
- wanted.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
- wanted.c_cflag |= (CS8 | CLOCAL | CREAD);
- wanted.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
- wanted.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL | IGNCR | INLCR);
- wanted.c_oflag &= ~OPOST;
- if (tcsetattr(fd, TCSANOW, &wanted) != 0) {
- msg_perr_strerror("Could not change serial port configuration: ");
- return 1;
- }
- if (tcgetattr(fd, &observed) != 0) {
- msg_perr_strerror("Could not fetch new serial port configuration: ");
- return 1;
- }
- if (observed.c_cflag != wanted.c_cflag ||
- observed.c_lflag != wanted.c_lflag ||
- observed.c_iflag != wanted.c_iflag ||
- observed.c_oflag != wanted.c_oflag) {
- msg_pwarn("Some requested serial options did not stick, continuing anyway.\n");
- msg_pdbg(" observed wanted\n"
- "c_cflag: 0x%08lX 0x%08lX\n"
- "c_lflag: 0x%08lX 0x%08lX\n"
- "c_iflag: 0x%08lX 0x%08lX\n"
- "c_oflag: 0x%08lX 0x%08lX\n",
- (long)observed.c_cflag, (long)wanted.c_cflag,
- (long)observed.c_lflag, (long)wanted.c_lflag,
- (long)observed.c_iflag, (long)wanted.c_iflag,
- (long)observed.c_oflag, (long)wanted.c_oflag
- );
- }
- if (cfgetispeed(&observed) != cfgetispeed(&wanted) ||
- cfgetospeed(&observed) != cfgetospeed(&wanted)) {
- msg_pwarn("Could not set baud rates exactly.\n");
- msg_pdbg("Actual baud flags are: ispeed: 0x%08lX, ospeed: 0x%08lX\n",
- (long)cfgetispeed(&observed), (long)cfgetospeed(&observed));
- }
- // FIXME: display actual baud rate - at least if none was specified by the user.
- #endif
- return 0;
- }
- fdtype sp_openserport(char *dev, int baud)
- {
- fdtype fd;
- #if IS_WINDOWS
- char *dev2 = dev;
- if ((strlen(dev) > 3) &&
- (tolower((unsigned char)dev[0]) == 'c') &&
- (tolower((unsigned char)dev[1]) == 'o') &&
- (tolower((unsigned char)dev[2]) == 'm')) {
- dev2 = malloc(strlen(dev) + 5);
- if (!dev2) {
- msg_perr_strerror("Out of memory: ");
- return SER_INV_FD;
- }
- strcpy(dev2, "\\\\.\\");
- strcpy(dev2 + 4, dev);
- }
- fd = CreateFile(dev2, GENERIC_READ | GENERIC_WRITE, 0, NULL,
- OPEN_EXISTING, 0, NULL);
- if (dev2 != dev)
- free(dev2);
- if (fd == INVALID_HANDLE_VALUE) {
- msg_perr_strerror("Cannot open serial port: ");
- return SER_INV_FD;
- }
- if (serialport_config(fd, baud) != 0) {
- CloseHandle(fd);
- return SER_INV_FD;
- }
- return fd;
- #else
- fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY); // Use O_NDELAY to ignore DCD state
- if (fd < 0) {
- msg_perr_strerror("Cannot open serial port: ");
- return SER_INV_FD;
- }
- /* Ensure that we use blocking I/O */
- const int flags = fcntl(fd, F_GETFL);
- if (flags == -1) {
- msg_perr_strerror("Could not get serial port mode: ");
- goto err;
- }
- if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) != 0) {
- msg_perr_strerror("Could not set serial port mode to blocking: ");
- goto err;
- }
- if (serialport_config(fd, baud) != 0) {
- goto err;
- }
- return fd;
- err:
- close(fd);
- return SER_INV_FD;
- #endif
- }
- void sp_set_pin(enum SP_PIN pin, int val) {
- #if IS_WINDOWS
- DWORD ctl;
- if(pin == PIN_TXD) {
- ctl = val ? SETBREAK: CLRBREAK;
- }
- else if(pin == PIN_DTR) {
- ctl = val ? SETDTR: CLRDTR;
- }
- else {
- ctl = val ? SETRTS: CLRRTS;
- }
- EscapeCommFunction(sp_fd, ctl);
- #else
- int ctl, s;
- if(pin == PIN_TXD) {
- ioctl(sp_fd, val ? TIOCSBRK : TIOCCBRK, 0);
- }
- else {
- s = (pin == PIN_DTR) ? TIOCM_DTR : TIOCM_RTS;
- ioctl(sp_fd, TIOCMGET, &ctl);
- if (val) {
- ctl |= s;
- }
- else {
- ctl &= ~s;
- }
- ioctl(sp_fd, TIOCMSET, &ctl);
- }
- #endif
- }
- int sp_get_pin(enum SP_PIN pin) {
- int s;
- #if IS_WINDOWS
- DWORD ctl;
- s = (pin == PIN_CTS) ? MS_CTS_ON : MS_DSR_ON;
- GetCommModemStatus(sp_fd, &ctl);
- #else
- int ctl;
- s = (pin == PIN_CTS) ? TIOCM_CTS : TIOCM_DSR;
- ioctl(sp_fd, TIOCMGET, &ctl);
- #endif
- return ((ctl & s) ? 1 : 0);
- }
- void sp_flush_incoming(void)
- {
- #if IS_WINDOWS
- PurgeComm(sp_fd, PURGE_RXCLEAR);
- #else
- /* FIXME: error handling */
- tcflush(sp_fd, TCIFLUSH);
- #endif
- return;
- }
- int serialport_shutdown(void *data)
- {
- #if IS_WINDOWS
- CloseHandle(sp_fd);
- #else
- close(sp_fd);
- #endif
- return 0;
- }
- int serialport_write(const unsigned char *buf, unsigned int writecnt)
- {
- #if IS_WINDOWS
- DWORD tmp = 0;
- #else
- ssize_t tmp = 0;
- #endif
- unsigned int empty_writes = 250; /* results in a ca. 125ms timeout */
- while (writecnt > 0) {
- #if IS_WINDOWS
- WriteFile(sp_fd, buf, writecnt, &tmp, NULL);
- #else
- tmp = write(sp_fd, buf, writecnt);
- #endif
- if (tmp == -1) {
- msg_perr("Serial port write error!\n");
- return 1;
- }
- if (!tmp) {
- msg_pdbg2("Empty write\n");
- empty_writes--;
- internal_delay(500);
- if (empty_writes == 0) {
- msg_perr("Serial port is unresponsive!\n");
- return 1;
- }
- }
- writecnt -= tmp;
- buf += tmp;
- }
- return 0;
- }
- int serialport_read(unsigned char *buf, unsigned int readcnt)
- {
- #if IS_WINDOWS
- DWORD tmp = 0;
- #else
- ssize_t tmp = 0;
- #endif
- while (readcnt > 0) {
- #if IS_WINDOWS
- ReadFile(sp_fd, buf, readcnt, &tmp, NULL);
- #else
- tmp = read(sp_fd, buf, readcnt);
- #endif
- if (tmp == -1) {
- msg_perr("Serial port read error!\n");
- return 1;
- }
- if (!tmp)
- msg_pdbg2("Empty read\n");
- readcnt -= tmp;
- buf += tmp;
- }
- return 0;
- }
- /* Tries up to timeout ms to read readcnt characters and places them into the array starting at c. Returns
- * 0 on success, positive values on temporary errors (e.g. timeouts) and negative ones on permanent errors.
- * If really_read is not NULL, this function sets its contents to the number of bytes read successfully. */
- int serialport_read_nonblock(unsigned char *c, unsigned int readcnt, unsigned int timeout, unsigned int *really_read)
- {
- int ret = 1;
- /* disable blocked i/o and declare platform-specific variables */
- #if IS_WINDOWS
- DWORD rv;
- COMMTIMEOUTS oldTimeout;
- COMMTIMEOUTS newTimeout = {
- .ReadIntervalTimeout = MAXDWORD,
- .ReadTotalTimeoutMultiplier = 0,
- .ReadTotalTimeoutConstant = 0,
- .WriteTotalTimeoutMultiplier = 0,
- .WriteTotalTimeoutConstant = 0
- };
- if(!GetCommTimeouts(sp_fd, &oldTimeout)) {
- msg_perr_strerror("Could not get serial port timeout settings: ");
- return -1;
- }
- if(!SetCommTimeouts(sp_fd, &newTimeout)) {
- msg_perr_strerror("Could not set serial port timeout settings: ");
- return -1;
- }
- #else
- ssize_t rv;
- const int flags = fcntl(sp_fd, F_GETFL);
- if (flags == -1) {
- msg_perr_strerror("Could not get serial port mode: ");
- return -1;
- }
- if (fcntl(sp_fd, F_SETFL, flags | O_NONBLOCK) != 0) {
- msg_perr_strerror("Could not set serial port mode to non-blocking: ");
- return -1;
- }
- #endif
- int i;
- int rd_bytes = 0;
- for (i = 0; i < timeout; i++) {
- msg_pspew("readcnt %d rd_bytes %d\n", readcnt, rd_bytes);
- #if IS_WINDOWS
- ReadFile(sp_fd, c + rd_bytes, readcnt - rd_bytes, &rv, NULL);
- msg_pspew("read %lu bytes\n", rv);
- #else
- rv = read(sp_fd, c + rd_bytes, readcnt - rd_bytes);
- msg_pspew("read %zd bytes\n", rv);
- #endif
- if ((rv == -1) && (errno != EAGAIN)) {
- msg_perr_strerror("Serial port read error: ");
- ret = -1;
- break;
- }
- if (rv > 0)
- rd_bytes += rv;
- if (rd_bytes == readcnt) {
- ret = 0;
- break;
- }
- internal_delay(1000); /* 1ms units */
- }
- if (really_read != NULL)
- *really_read = rd_bytes;
- /* restore original blocking behavior */
- #if IS_WINDOWS
- if (!SetCommTimeouts(sp_fd, &oldTimeout)) {
- msg_perr_strerror("Could not restore serial port timeout settings: ");
- ret = -1;
- }
- #else
- if (fcntl(sp_fd, F_SETFL, flags) != 0) {
- msg_perr_strerror("Could not restore serial port mode to blocking: ");
- ret = -1;
- }
- #endif
- return ret;
- }
- /* Tries up to timeout ms to write writecnt characters from the array starting at buf. Returns
- * 0 on success, positive values on temporary errors (e.g. timeouts) and negative ones on permanent errors.
- * If really_wrote is not NULL, this function sets its contents to the number of bytes written successfully. */
- int serialport_write_nonblock(const unsigned char *buf, unsigned int writecnt, unsigned int timeout, unsigned int *really_wrote)
- {
- int ret = 1;
- /* disable blocked i/o and declare platform-specific variables */
- #if IS_WINDOWS
- DWORD rv;
- COMMTIMEOUTS oldTimeout;
- COMMTIMEOUTS newTimeout = {
- .ReadIntervalTimeout = MAXDWORD,
- .ReadTotalTimeoutMultiplier = 0,
- .ReadTotalTimeoutConstant = 0,
- .WriteTotalTimeoutMultiplier = 0,
- .WriteTotalTimeoutConstant = 0
- };
- if(!GetCommTimeouts(sp_fd, &oldTimeout)) {
- msg_perr_strerror("Could not get serial port timeout settings: ");
- return -1;
- }
- if(!SetCommTimeouts(sp_fd, &newTimeout)) {
- msg_perr_strerror("Could not set serial port timeout settings: ");
- return -1;
- }
- #else
- ssize_t rv;
- const int flags = fcntl(sp_fd, F_GETFL);
- if (flags == -1) {
- msg_perr_strerror("Could not get serial port mode: ");
- return -1;
- }
- if (fcntl(sp_fd, F_SETFL, flags | O_NONBLOCK) != 0) {
- msg_perr_strerror("Could not set serial port mode to non-blocking: ");
- return -1;
- }
- #endif
- int i;
- int wr_bytes = 0;
- for (i = 0; i < timeout; i++) {
- msg_pspew("writecnt %d wr_bytes %d\n", writecnt, wr_bytes);
- #if IS_WINDOWS
- WriteFile(sp_fd, buf + wr_bytes, writecnt - wr_bytes, &rv, NULL);
- msg_pspew("wrote %lu bytes\n", rv);
- #else
- rv = write(sp_fd, buf + wr_bytes, writecnt - wr_bytes);
- msg_pspew("wrote %zd bytes\n", rv);
- #endif
- if ((rv == -1) && (errno != EAGAIN)) {
- msg_perr_strerror("Serial port write error: ");
- ret = -1;
- break;
- }
- if (rv > 0) {
- wr_bytes += rv;
- if (wr_bytes == writecnt) {
- msg_pspew("write successful\n");
- ret = 0;
- break;
- }
- }
- internal_delay(1000); /* 1ms units */
- }
- if (really_wrote != NULL)
- *really_wrote = wr_bytes;
- /* restore original blocking behavior */
- #if IS_WINDOWS
- if (!SetCommTimeouts(sp_fd, &oldTimeout)) {
- msg_perr_strerror("Could not restore serial port timeout settings: ");
- return -1;
- }
- #else
- if (fcntl(sp_fd, F_SETFL, flags) != 0) {
- msg_perr_strerror("Could not restore serial port blocking behavior: ");
- return -1;
- }
- #endif
- return ret;
- }
|