summaryrefslogtreecommitdiff
path: root/libgphoto2_port/serial/unix.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgphoto2_port/serial/unix.c')
-rw-r--r--libgphoto2_port/serial/unix.c987
1 files changed, 987 insertions, 0 deletions
diff --git a/libgphoto2_port/serial/unix.c b/libgphoto2_port/serial/unix.c
new file mode 100644
index 000000000..355ad869c
--- /dev/null
+++ b/libgphoto2_port/serial/unix.c
@@ -0,0 +1,987 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright © 2001 Lutz Müller <lutz@users.sf.net>
+ * Copyright © 2000 Philippe Marzouk <pmarzouk@bigfoot.com>
+ * Copyright © 2000 Edouard Lafargue <Edouard.Lafargue@bigfoot.com>
+ * Copyright © 1999 Johannes Erdfelt <johannes@erdfelt.com>
+ * Copyright © 1999 Scott Fritzinger <scottf@unr.edu>
+ *
+ * Based on work by:
+ * Copyright © 1999 Beat Christen <spiff@longstreet.ch>
+ * for the toshiba gPhoto library.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <gphoto2/gphoto2-port-library.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+# ifndef CRTSCTS
+# define CRTSCTS 020000000000
+# endif
+#else
+# include <sgtty.h>
+#endif
+
+#ifdef HAVE_RESMGR
+# include <resmgr.h>
+#endif
+
+#ifdef HAVE_BAUDBOY
+# include <baudboy.h>
+#elif defined(HAVE_TTYLOCK)
+# include <ttylock.h>
+#elif defined(HAVE_LOCKDEV)
+# include <lockdev.h>
+#endif
+
+#include <gphoto2/gphoto2-port-result.h>
+#include <gphoto2/gphoto2-port-log.h>
+#include <gphoto2/gphoto2-port.h>
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+# undef _
+# define _(String) dgettext (GETTEXT_PACKAGE, String)
+# ifdef gettext_noop
+# define N_(String) gettext_noop (String)
+# else
+# define N_(String) (String)
+# endif
+#else
+# define textdomain(String) (String)
+# define gettext(String) (String)
+# define dgettext(Domain,Message) (Message)
+# define dcgettext(Domain,Message,Type) (Message)
+# define bindtextdomain(Domain,Directory) (Domain)
+# define _(String) (String)
+# define N_(String) (String)
+#endif
+
+#define CHECK(result) {int r=(result); if (r<0) return (r);}
+
+/* Linux */
+#ifdef __linux__
+/* devfs is accounted for in the implementation */
+#define GP_PORT_SERIAL_PREFIX "/dev/ttyS%i"
+#define GP_PORT_SERIAL_RANGE_LOW 0
+#define GP_PORT_SERIAL_RANGE_HIGH 32
+#endif
+
+/* FreeBSD */
+#ifdef __FreeBSD__
+#if __FreeBSD_version < 600000
+#define GP_PORT_SERIAL_PREFIX "/dev/cuaa%x"
+#else
+#define GP_PORT_SERIAL_PREFIX "/dev/cuad%x"
+#endif
+#define GP_PORT_SERIAL_RANGE_LOW 0
+#define GP_PORT_SERIAL_RANGE_HIGH (0xf)
+#endif
+
+/* OpenBSD */
+/* devices appear to go up to /dev/cuac7, but we just list the first 4 */
+#ifdef __OpenBSD__
+# define GP_PORT_SERIAL_PREFIX "/dev/cua%02x"
+# define GP_PORT_SERIAL_RANGE_LOW 0
+# define GP_PORT_SERIAL_RANGE_HIGH 3
+#endif
+
+/* NetBSD */
+#ifdef __NetBSD__
+#define GP_PORT_SERIAL_PREFIX "/dev/tty0%i"
+#define GP_PORT_SERIAL_RANGE_LOW 0
+#define GP_PORT_SERIAL_RANGE_HIGH 32
+#endif
+
+/* Tru64 UNIX */
+#ifdef __osf__
+#define GP_PORT_SERIAL_PREFIX "/dev/tty%02i"
+#define GP_PORT_SERIAL_RANGE_LOW 0
+#define GP_PORT_SERIAL_RANGE_HIGH 4
+#endif
+
+/* Darwin */
+#ifdef __APPLE__
+/* This is the Keyspan USB serial adapter device (UNTESTED) */
+#define GP_PORT_SERIAL_PREFIX "/dev/tty.KeyUSA28X%i"
+#define GP_PORT_SERIAL_RANGE_LOW 111
+#define GP_PORT_SERIAL_RANGE_HIGH 1112
+#endif
+
+/* Solaris */
+#ifdef sun
+#define GP_PORT_SERIAL_PREFIX "/dev/tty%c"
+#define GP_PORT_SERIAL_RANGE_LOW 'a'
+#define GP_PORT_SERIAL_RANGE_HIGH 'z'
+#endif
+
+/* BeOS */
+#ifdef beos
+/* ????????????? */
+#define GP_PORT_SERIAL_PREFIX NULL
+#define GP_PORT_SERIAL_RANGE_LOW 0
+#define GP_PORT_SERIAL_RANGE_HIGH 0
+#endif
+
+/* Windows */
+#ifdef WIN32
+#define GP_PORT_SERIAL_PREFIX "COM%i:"
+#define GP_PORT_SERIAL_RANGE_LOW 1
+#define GP_PORT_SERIAL_RANGE_HIGH 4
+#endif
+
+#ifdef OS2
+#define GP_PORT_SERIAL_PREFIX "COM%i"
+#define GP_PORT_SERIAL_RANGE_LOW 1
+#define GP_PORT_SERIAL_RANGE_HIGH 4
+#endif
+
+/* IRIX */
+#if defined(__sgi)
+#define GP_PORT_SERIAL_PREFIX "/dev/ttyd%i"
+#define GP_PORT_SERIAL_RANGE_LOW 1
+#define GP_PORT_SERIAL_RANGE_HIGH 11
+#endif
+
+/* Others? */
+
+/* Default */
+#ifndef GP_PORT_SERIAL_PREFIX
+#define GP_PORT_SERIAL_PREFIX "/dev/cua%i"
+#define GP_PORT_SERIAL_RANGE_LOW 0
+#define GP_PORT_SERIAL_RANGE_HIGH 0
+#endif
+
+#define GP_MODULE "serial"
+
+struct _GPPortPrivateLibrary {
+ int fd; /* Device handle */
+ int baudrate; /* Current speed */
+};
+
+static int gp_port_serial_check_speed (GPPort *dev);
+
+GPPortType
+gp_port_library_type ()
+{
+ return (GP_PORT_SERIAL);
+}
+
+static int
+gp_port_serial_lock (GPPort *dev, const char *path)
+{
+#if defined(HAVE_LOCKDEV)
+ int pid;
+#endif
+
+ gp_log (GP_LOG_DEBUG, "gphoto2-port-serial",
+ "Trying to lock '%s'...", path);
+
+#ifdef HAVE_RESMGR
+ if (!rsm_lock_device(path)) {
+ return GP_OK;
+ }
+ /* fallthrough */
+#define __HAVE_LOCKING
+#endif
+
+#if defined(HAVE_TTYLOCK) || defined(HAVE_BAUDBOY)
+ if (ttylock ((char*) path)) {
+ if (dev)
+ gp_port_set_error (dev, _("Could not lock device "
+ "'%s'"), path);
+ return (GP_ERROR_IO_LOCK);
+ }
+#define __HAVE_LOCKING
+#elif defined(HAVE_LOCKDEV)
+ pid = dev_lock (path);
+ if (pid) {
+ if (dev) {
+ if (pid > 0)
+ gp_port_set_error (dev, _("Device '%s' is "
+ "locked by pid %d"), path, pid);
+ else
+ gp_port_set_error (dev, _("Device '%s' could "
+ "not be locked (dev_lock returned "
+ "%d)"), path, pid);
+ }
+ return (GP_ERROR_IO_LOCK);
+ }
+#define __HAVE_LOCKING
+#endif
+
+#ifndef __HAVE_LOCKING
+# ifdef __GCC__
+# warning No locking library found.
+# warning You will run into problems if you use
+# warning gphoto2 with a serial (RS232) camera in
+# warning combination with Konqueror (KDE) or Nautilus (GNOME).
+# warning This will *not* concern USB cameras.
+# endif
+#endif
+
+ return (GP_OK);
+}
+
+static int
+gp_port_serial_unlock (GPPort *dev, const char *path)
+{
+#ifdef HAVE_RESMGR
+ if (!rsm_unlock_device(path))
+ return GP_OK;
+ /* fallback through other unlock styles */
+#endif
+
+#if defined(HAVE_TTYLOCK) || defined(HAVE_BAUDBOY)
+ if (ttyunlock ((char*) path)) {
+ if (dev)
+ gp_port_set_error (dev, _("Device '%s' could not be "
+ "unlocked."), path);
+ return (GP_ERROR_IO_LOCK);
+ }
+#elif defined(HAVE_LOCKDEV)
+
+ int pid;
+
+ pid = dev_unlock (path, 0);
+ if (pid) {
+ if (dev) {
+ if (pid > 0)
+ gp_port_set_error (dev, _("Device '%s' could "
+ "not be unlocked as it is locked by "
+ "pid %d."), path, pid);
+ else
+ gp_port_set_error (dev, _("Device '%s' could "
+ "not be unlocked (dev_unlock "
+ "returned %d)"), path, pid);
+ }
+ return (GP_ERROR_IO_LOCK);
+ }
+#endif /* !HAVE_LOCKDEV */
+
+ return (GP_OK);
+}
+
+int
+gp_port_library_list (GPPortInfoList *list)
+{
+ GPPortInfo info;
+ char path[1024], prefix[1024];
+ int x, fd;
+ struct stat s;
+#ifdef OS2
+ int r, fh, option;
+#endif
+
+ /* Copy in the serial port prefix */
+ strcpy (prefix, GP_PORT_SERIAL_PREFIX);
+
+ /* On Linux systems, check for devfs */
+#ifdef __linux
+ if (!stat ("/dev/tts", &s))
+ strcpy (prefix, "/dev/tts/%i");
+#endif
+
+ for (x=GP_PORT_SERIAL_RANGE_LOW; x<=GP_PORT_SERIAL_RANGE_HIGH; x++) {
+ sprintf (path, prefix, x);
+
+ /* OS/2 seems to need an additional check */
+#ifdef OS2
+ r = DosOpen (path, &fh, &option, 0, 0, 1,
+ OPEN_FLAGS_FAIL_ON_ERROR |
+ OPEN_SHARE_DENYREADWRITE, 0);
+ DosClose(fh);
+ if (r)
+ continue;
+#endif
+ /* Very first of all, if the device node is not there,
+ * there is no need to try locking. */
+ if ((stat (path, &s) == -1) && ((errno == ENOENT) || (errno == ENODEV)))
+ continue;
+
+ /* First of all, try to lock the device */
+ if (gp_port_serial_lock (NULL, path) < 0)
+ continue;
+
+ /* Device locked. Try to open the device. */
+ fd = -1;
+#ifdef HAVE_RESMGR
+ /* resmgr has its own API, which calls to a server and
+ * communicates over UNIX domain sockets.
+ */
+ fd = rsm_open_device(path, O_RDONLY | O_NDELAY);
+ /* fall through to standard open if this failed */
+#endif
+ if (fd == -1)
+ fd = open (path, O_RDONLY | O_NDELAY);
+ if (fd < 0) {
+ gp_port_serial_unlock (NULL, path);
+ continue;
+ }
+
+ /*
+ * Device locked and opened. Close it, unlock it, and add
+ * it to the list.
+ */
+ close (fd);
+ gp_port_serial_unlock (NULL, path);
+ info.type = GP_PORT_SERIAL;
+ strncpy (info.path, "serial:", sizeof (info.path));
+ strncat (info.path, path, sizeof (info.path) - strlen (info.path) - 1);
+ snprintf (info.name, sizeof (info.name),
+ _("Serial Port %i"), x);
+ CHECK (gp_port_info_list_append (list, info));
+ }
+
+ /*
+ * Generic support. Append it to the list without checking for
+ * return values, because this entry will not be counted.
+ */
+ info.type = GP_PORT_SERIAL;
+ strncpy (info.path, "^serial", sizeof (info.path));
+ memset (info.name, 0, sizeof (info.name));
+ gp_port_info_list_append (list, info);
+
+ return (GP_OK);
+}
+
+static int
+gp_port_serial_init (GPPort *dev)
+{
+ if (!dev)
+ return (GP_ERROR_BAD_PARAMETERS);
+
+ dev->pl = malloc (sizeof (GPPortPrivateLibrary));
+ if (!dev->pl)
+ return (GP_ERROR_NO_MEMORY);
+ memset (dev->pl, 0, sizeof (GPPortPrivateLibrary));
+
+ /* There is no default speed. */
+ dev->pl->baudrate = -1;
+
+ return GP_OK;
+}
+
+static int
+gp_port_serial_exit (GPPort *dev)
+{
+ if (!dev)
+ return (GP_ERROR_BAD_PARAMETERS);
+
+ if (dev->pl) {
+ free (dev->pl);
+ dev->pl = NULL;
+ }
+
+ return GP_OK;
+}
+
+static int
+gp_port_serial_open (GPPort *dev)
+{
+ int result, max_tries = 5, i;
+#ifdef OS2
+ int fd;
+#endif
+ char *port;
+
+ /* Ports are named "serial:/dev/whatever/port" */
+ port = strchr (dev->settings.serial.port, ':');
+ if (!port)
+ return GP_ERROR_UNKNOWN_PORT;
+ port++;
+
+ result = gp_port_serial_lock (dev, port);
+ if (result != GP_OK) {
+ for (i = 0; i < max_tries; i++) {
+ result = gp_port_serial_lock (dev, port);
+ if (result == GP_OK)
+ break;
+ gp_log (GP_LOG_DEBUG, "gphoto2-port-serial",
+ "Failed to get a lock, trying again...");
+ sleep (1);
+ }
+ CHECK (result);
+ }
+ dev->pl->fd = -1;
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+ dev->pl->fd = open (port, O_RDWR | O_NOCTTY | O_NONBLOCK);
+#elif OS2
+ fd = open (port, O_RDWR | O_BINARY);
+ dev->pl->fd = open (port, O_RDWR | O_BINARY);
+ close(fd);
+#else
+# ifdef HAVE_RESMGR
+ dev->pl->fd = rsm_open_device (port, O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK);
+ /* fall back trying old style open if not possible */
+# endif
+ if (dev->pl->fd == -1)
+ dev->pl->fd = open (port, O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK);
+#endif
+ if (dev->pl->fd == -1) {
+ gp_port_set_error (dev, _("Failed to open '%s' (%m)."), port);
+ dev->pl->fd = 0;
+ return GP_ERROR_IO;
+ }
+
+ return GP_OK;
+}
+
+static int
+gp_port_serial_close (GPPort *dev)
+{
+ const char *path;
+
+ if (!dev)
+ return (GP_OK);
+
+ if (dev->pl->fd) {
+ if (close (dev->pl->fd) == -1) {
+ gp_port_set_error (dev, _("Could not close "
+ "'%s' (%m)."), dev->settings.serial.port);
+ return GP_ERROR_IO;
+ }
+ dev->pl->fd = 0;
+ }
+
+ /* Unlock the port */
+ path = strchr (dev->settings.serial.port, ':');
+ path++;
+ CHECK (gp_port_serial_unlock (dev, path));
+
+#if defined(__sgi)
+ /*
+ * On IRIX, we need to set the baudrate each time after opening
+ * the port.
+ */
+ dev->pl->baudrate = 0;
+#endif
+
+ return GP_OK;
+}
+
+static int
+gp_port_serial_write (GPPort *dev, const char *bytes, int size)
+{
+ int len, ret;
+
+ if (!dev)
+ return (GP_ERROR_BAD_PARAMETERS);
+
+ /* The device needs to be opened for that operation */
+ if (!dev->pl->fd)
+ CHECK (gp_port_serial_open (dev));
+
+ /* Make sure we are operating at the specified speed */
+ CHECK (gp_port_serial_check_speed (dev));
+
+ len = 0;
+ while (len < size) {
+
+ /*
+ * Make sure we write all data while handling
+ * the harmless errors
+ */
+ ret = write (dev->pl->fd, bytes, size - len);
+ if (ret == -1) {
+ switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ ret = 0;
+ break;
+ default:
+ gp_port_set_error (dev, _("Could not write "
+ "to port (%m)"));
+ return GP_ERROR_IO_WRITE;
+ }
+ }
+ len += ret;
+ }
+
+ /* wait till all bytes are really sent */
+#ifndef OS2
+#ifdef HAVE_TERMIOS_H
+ tcdrain (dev->pl->fd);
+#else
+ ioctl (dev->pl->fd, TCDRAIN, 0);
+#endif
+#endif
+ return GP_OK;
+}
+
+
+static int
+gp_port_serial_read (GPPort *dev, char *bytes, int size)
+{
+ struct timeval timeout;
+ fd_set readfs; /* file descriptor set */
+ int readen = 0, now;
+
+ if (!dev)
+ return (GP_ERROR_BAD_PARAMETERS);
+
+ /* The device needs to be opened for that operation */
+ if (!dev->pl->fd)
+ CHECK (gp_port_serial_open (dev));
+
+ /* Make sure we are operating at the specified speed */
+ CHECK (gp_port_serial_check_speed (dev));
+
+ FD_ZERO (&readfs);
+ FD_SET (dev->pl->fd, &readfs);
+
+ while (readen < size) {
+
+ /* Set timeout value within input loop */
+ timeout.tv_usec = (dev->timeout % 1000) * 1000;
+ timeout.tv_sec = (dev->timeout / 1000);
+
+ /* Any data available? */
+ if (!select (dev->pl->fd + 1, &readfs, NULL, NULL, &timeout))
+ return GP_ERROR_TIMEOUT;
+ if (!FD_ISSET (dev->pl->fd, &readfs))
+ return (GP_ERROR_TIMEOUT);
+
+ if (dev->settings.serial.parity != GP_PORT_SERIAL_PARITY_OFF) {
+ now = read (dev->pl->fd, bytes, 1);
+ if (now < 0)
+ return GP_ERROR_IO_READ;
+ if (bytes[0] == (char)-1) {
+ now = read (dev->pl->fd, bytes, 1);
+ if (now < 0)
+ return GP_ERROR_IO_READ;
+
+ /* Parity errors are signaled by the serial layer
+ * as 0xff 0x00 sequence.
+ *
+ * 0xff sent by the camera are escaped as
+ * 0xff 0xff sequence.
+ *
+ * All other 0xff 0xXX sequences are errors.
+ *
+ * cf. man tcsetattr, description of PARMRK.
+ */
+ if (bytes[0] == 0x00) {
+ gp_port_set_error (dev, _("Parity error."));
+ return GP_ERROR_IO_READ;
+ }
+ if (bytes[0] != (char)-1) {
+ gp_port_set_error (dev, _("Unexpected parity response sequence 0xff 0x%02x."), bytes[0]);
+ return GP_ERROR_IO_READ;
+ }
+ /* Ok, we read 1 byte and it is 0xff */
+ /* FALLTHROUGH */
+ }
+ } else {
+ /* Just read the bytes */
+ now = read (dev->pl->fd, bytes, size - readen);
+ if (now < 0)
+ return GP_ERROR_IO_READ;
+ }
+ bytes += now;
+ readen += now;
+ }
+
+ return readen;
+}
+
+#ifdef HAVE_TERMIOS_H
+static int
+get_termios_bit (GPPort *dev, GPPin pin, int *bit)
+{
+ switch (pin) {
+ case GP_PIN_RTS:
+ *bit = TIOCM_RTS;
+ break;
+ case GP_PIN_DTR:
+ *bit = TIOCM_DTR;
+ break;
+ case GP_PIN_CTS:
+ *bit = TIOCM_CTS;
+ break;
+ case GP_PIN_DSR:
+ *bit = TIOCM_DSR;
+ break;
+ case GP_PIN_CD:
+ *bit = TIOCM_CD;
+ break;
+ case GP_PIN_RING:
+ *bit = TIOCM_RNG;
+ break;
+ default:
+ gp_port_set_error (dev, _("Unknown pin %i."), pin);
+ return GP_ERROR_IO;
+ }
+ return (GP_OK);
+}
+#endif
+
+static int
+gp_port_serial_get_pin (GPPort *dev, GPPin pin, GPLevel *level)
+{
+#ifdef HAVE_TERMIOS_H
+ int j, bit;
+#endif
+
+ if (!dev || !level)
+ return (GP_ERROR_BAD_PARAMETERS);
+
+ *level = 0;
+
+#ifdef HAVE_TERMIOS_H
+ CHECK (get_termios_bit (dev, pin, &bit));
+ if (ioctl (dev->pl->fd, TIOCMGET, &j) < 0) {
+ gp_port_set_error (dev, _("Could not get level of pin %i "
+ "(%m)."), pin);
+ return GP_ERROR_IO;
+ }
+ *level = j & bit;
+#else
+# ifdef __GCC__
+# warning ACCESSING PINS IS NOT IMPLEMENTED FOR NON-TERMIOS SYSTEMS!
+# endif
+#endif
+
+ return (GP_OK);
+}
+
+static int
+gp_port_serial_set_pin (GPPort *dev, GPPin pin, GPLevel level)
+{
+#ifdef HAVE_TERMIOS_H
+ int bit, request;
+#endif
+
+ if (!dev)
+ return (GP_ERROR_BAD_PARAMETERS);
+
+#ifdef HAVE_TERMIOS_H
+ CHECK (get_termios_bit (dev, pin, &bit));
+ switch (level) {
+ case GP_LEVEL_LOW:
+ request = TIOCMBIS;
+ break;
+ default:
+ request = TIOCMBIC;
+ break;
+ }
+ if (ioctl (dev->pl->fd, request, &bit) < 0) {
+ gp_port_set_error (dev, _("Could not set level of pin %i to "
+ "%i (%m)."), pin, level);
+ return GP_ERROR_IO;
+ }
+#else
+# ifdef __GCC__
+# warning ACCESSING PINS IS NOT IMPLEMENTED FOR NON-TERMIOS SYSTEMS!
+# endif
+#endif
+
+ return GP_OK;
+}
+
+static int
+gp_port_serial_flush (GPPort *dev, int direction)
+{
+ /* The device needs to be opened for that operation */
+ if (!dev->pl->fd)
+ CHECK (gp_port_serial_open (dev));
+
+ /* Make sure we are operating at the specified speed */
+ CHECK (gp_port_serial_check_speed (dev));
+
+#ifdef HAVE_TERMIOS_H
+ if (tcflush (dev->pl->fd, direction ? TCOFLUSH : TCIFLUSH) < 0) {
+ gp_port_set_error (dev, _("Could not flush '%s' (%m)."),
+ dev->settings.serial.port);
+ return (GP_ERROR_IO);
+ }
+#else
+# ifdef __GCC__
+# warning SERIAL FLUSH NOT IMPLEMENTED FOR NON TERMIOS SYSTEMS!
+# endif
+#endif
+
+ return (GP_OK);
+}
+
+static speed_t
+gp_port_serial_baudconv (int baudrate)
+{
+#define BAUDCASE(x) case (x): { ret = B##x; break; }
+ speed_t ret;
+
+ switch (baudrate) {
+
+ /* POSIX defined baudrates */
+ BAUDCASE(0);
+ BAUDCASE(50);
+ BAUDCASE(75);
+ BAUDCASE(110);
+ BAUDCASE(134);
+ BAUDCASE(150);
+ BAUDCASE(200);
+ BAUDCASE(300);
+ BAUDCASE(600);
+ BAUDCASE(1200);
+ BAUDCASE(1800);
+ BAUDCASE(2400);
+ BAUDCASE(4800);
+ BAUDCASE(9600);
+ BAUDCASE(19200);
+ BAUDCASE(38400);
+
+ /* non POSIX values */
+#ifdef B7200
+ BAUDCASE(7200);
+#endif
+#ifdef B14400
+ BAUDCASE(14400);
+#endif
+#ifdef B28800
+ BAUDCASE(28800);
+#endif
+#ifdef B57600
+ BAUDCASE(57600);
+#endif
+#ifdef B115200
+ BAUDCASE(115200);
+#endif
+#ifdef B230400
+ BAUDCASE(230400);
+#endif
+ default:
+ ret = (speed_t) baudrate;
+ gp_log (GP_LOG_DEBUG, "gphoto2-port-serial", "Baudrate %d "
+ "unknown - using as is", baudrate);
+ }
+
+ return ret;
+#undef BAUDCASE
+}
+
+static int
+gp_port_serial_check_speed (GPPort *dev)
+{
+ speed_t speed;
+#ifdef OS2
+ ULONG rc;
+ ULONG ulParmLen = 2; /* Maximum size of the parameter packet */
+#else
+#ifdef HAVE_TERMIOS_H
+ struct termios tio;
+#else
+ struct sgttyb ttyb;
+#endif
+#endif
+
+ /*
+ * We need an open device in order to set the speed. If there is no
+ * open device, postpone setting of speed.
+ */
+ if (!dev->pl->fd)
+ return (GP_OK);
+
+ /* If baudrate is up to date, do nothing */
+ if (dev->pl->baudrate == dev->settings.serial.speed)
+ return (GP_OK);
+
+ gp_log (GP_LOG_DEBUG, "gphoto2-port-serial",
+ "Setting baudrate to %d...", dev->settings.serial.speed);
+ speed = gp_port_serial_baudconv (dev->settings.serial.speed);
+
+#ifdef OS2
+ rc = DosDevIOCtl (dev->pl->fd, /* Device handle */
+ 0x0001, /* Serial-device control */
+ 0x0043, /* Sets bit rate */
+ (PULONG) &(dev->settings.serial.speed),
+ sizeof(baudrate), /* Max. size of parameter list */
+ &ulParmLen, /* Size of parameter packet */
+ NULL, /* No data packet */
+ 0, /* Maximum size of data packet */
+ NULL); /* Size of data packet */
+ if(rc != 0)
+ printf("DosDevIOCtl baudrate error:%d\n",rc);
+#else /* !OS2 */
+#ifdef HAVE_TERMIOS_H
+ if (tcgetattr(dev->pl->fd, &tio) < 0) {
+ gp_port_set_error (dev, _("Could not set the baudrate to %d"),
+ dev->settings.serial.speed);
+ return GP_ERROR_IO_SERIAL_SPEED;
+ }
+ tio.c_cflag = (tio.c_cflag & ~CSIZE) | CS8;
+
+ /* Set into raw, no echo mode */
+ tio.c_iflag &= ~(IGNBRK | IGNCR | INLCR | ICRNL |
+ IXANY | IXON | IXOFF | INPCK | ISTRIP);
+#ifdef IUCLC
+ tio.c_iflag &= ~IUCLC;
+#endif
+ tio.c_iflag |= (BRKINT | IGNPAR);
+ tio.c_oflag &= ~OPOST;
+ tio.c_lflag &= ~(ICANON | ISIG | ECHO | ECHONL | ECHOE |
+ ECHOK | IEXTEN);
+ tio.c_cflag &= ~(CRTSCTS | PARENB | PARODD);
+ tio.c_cflag |= CLOCAL | CREAD;
+
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 0;
+
+ if (dev->settings.serial.parity != GP_PORT_SERIAL_PARITY_OFF) {
+ tio.c_iflag &= ~IGNPAR;
+ tio.c_iflag |= INPCK | PARMRK ;
+ tio.c_cflag |= PARENB;
+ if (dev->settings.serial.parity == GP_PORT_SERIAL_PARITY_ODD)
+ tio.c_cflag |= PARODD;
+ }
+
+ /* Set the new speed. */
+ cfsetispeed (&tio, speed);
+ cfsetospeed (&tio, speed);
+ if (tcsetattr (dev->pl->fd, TCSANOW, &tio) < 0) {
+ GP_DEBUG ("Error on 'tcsetattr'.");
+ return GP_ERROR_IO_SERIAL_SPEED;
+ }
+
+ /* Clear O_NONBLOCK. */
+ if (fcntl (dev->pl->fd, F_SETFL, 0) < 0) {
+ GP_DEBUG ("Error on 'fcntl'.");
+ return GP_ERROR_IO_SERIAL_SPEED;
+ }
+
+ /*
+ * Verify if the speed change has been successful.
+ * This is needed because some Solaris systems just ignore unsupported
+ * speeds (like 115200) instead of complaining.
+ *
+ * Only perform the check if we really changed to some speed.
+ */
+ if (speed != B0) {
+ if (tcgetattr (dev->pl->fd, &tio)) {
+ GP_DEBUG ("Error on 'tcgetattr'.");
+ return (GP_ERROR_IO_SERIAL_SPEED);
+ }
+ if ((cfgetispeed (&tio) != speed) ||
+ (cfgetospeed (&tio) != speed)) {
+ GP_DEBUG ("Cannot set baudrate to %d...",
+ dev->settings.serial.speed);
+ return (GP_ERROR_NOT_SUPPORTED);
+ }
+ }
+
+#else /* !HAVE_TERMIOS_H */
+ if (ioctl (dev->pl->fd, TIOCGETP, &ttyb) < 0) {
+ perror("ioctl(TIOCGETP)");
+ return GP_ERROR_IO_SERIAL_SPEED;
+ }
+ ttyb.sg_ispeed = dev->settings.serial.speed;
+ ttyb.sg_ospeed = dev->settings.serial.speed;
+ ttyb.sg_flags = 0;
+
+ if (ioctl (dev->pl->fd, TIOCSETP, &ttyb) < 0) {
+ perror("ioctl(TIOCSETP)");
+ return GP_ERROR_IO_SERIAL_SPEED;
+ }
+#endif
+#endif
+
+ dev->pl->baudrate = dev->settings.serial.speed;
+ return GP_OK;
+}
+
+static int
+gp_port_serial_update (GPPort *dev)
+{
+ memcpy (&dev->settings, &dev->settings_pending, sizeof (dev->settings));
+
+ CHECK (gp_port_serial_check_speed (dev));
+
+ return GP_OK;
+}
+
+static int
+gp_port_serial_send_break (GPPort *dev, int duration)
+{
+ /* The device needs to be opened for that operation */
+ if (!dev->pl->fd)
+ CHECK (gp_port_serial_open (dev));
+
+ /* Make sure we are operating at the specified speed */
+ CHECK (gp_port_serial_check_speed (dev));
+
+ /* Duration is in milliseconds */
+#ifdef HAVE_TERMIOS_H
+ tcsendbreak (dev->pl->fd, duration / 310);
+ tcdrain (dev->pl->fd);
+#else
+# ifdef __GCC__
+# warning SEND BREAK NOT IMPLEMENTED FOR NON TERMIOS SYSTEMS!
+# endif
+#endif
+
+ return GP_OK;
+}
+
+GPPortOperations *
+gp_port_library_operations ()
+{
+ GPPortOperations *ops;
+
+ ops = malloc (sizeof (GPPortOperations));
+ if (!ops)
+ return (NULL);
+ memset (ops, 0, sizeof (GPPortOperations));
+
+ ops->init = gp_port_serial_init;
+ ops->exit = gp_port_serial_exit;
+ ops->open = gp_port_serial_open;
+ ops->close = gp_port_serial_close;
+ ops->read = gp_port_serial_read;
+ ops->write = gp_port_serial_write;
+ ops->update = gp_port_serial_update;
+ ops->get_pin = gp_port_serial_get_pin;
+ ops->set_pin = gp_port_serial_set_pin;
+ ops->send_break = gp_port_serial_send_break;
+ ops->flush = gp_port_serial_flush;
+
+ return (ops);
+}