summaryrefslogtreecommitdiff
path: root/libgphoto2_port/usbscsi
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2010-03-29 09:25:54 +0000
committerHans de Goede <hdegoede@redhat.com>2010-03-29 09:25:54 +0000
commit004777f2877233fa3e2374664cf2726d237bd214 (patch)
tree8c0ae93cb68458741c02d18c948caf4e51ec6bdd /libgphoto2_port/usbscsi
parentafe1e7bfffea54655a55e25215245989c24bf445 (diff)
downloadlibgphoto2-004777f2877233fa3e2374664cf2726d237bd214.tar.gz
Add ax203 camlib for ax203 based picture frames
This makes it possible to download pictures from / upload pictures to ax203 based picture frames It consists of 2 parts: 1) an ax203 camlib, ax203 access, image table parsing, and decompress / compress code. 2) usbscsi port driver, this is a *Linux only* (for now) port driver for accessing devices which require sending custom scsi commands to an usb mass storage device (using the /dev/sg# device under Linux). git-svn-id: https://svn.code.sf.net/p/gphoto/code/trunk/libgphoto2@12854 67ed7778-7388-44ab-90cf-0a291f65f57c
Diffstat (limited to 'libgphoto2_port/usbscsi')
-rw-r--r--libgphoto2_port/usbscsi/Makefile.am13
-rw-r--r--libgphoto2_port/usbscsi/linux.c383
2 files changed, 396 insertions, 0 deletions
diff --git a/libgphoto2_port/usbscsi/Makefile.am b/libgphoto2_port/usbscsi/Makefile.am
new file mode 100644
index 000000000..04e3e1d5d
--- /dev/null
+++ b/libgphoto2_port/usbscsi/Makefile.am
@@ -0,0 +1,13 @@
+## Compile the IO library into a libtool module shared library
+iolib_LTLIBRARIES = usbscsi.la
+
+usbscsi_la_LDFLAGS = -module -no-undefined -avoid-version \
+ -export-dynamic \
+ -export-symbols $(top_srcdir)/iolib.sym
+usbscsi_la_CPPFLAGS = $(AM_CPPFLAGS) $(INTL_CFLAGS) $(CPPFLAGS)
+usbscsi_la_DEPENDENCIES = $(top_srcdir)/iolib.sym
+usbscsi_la_LIBADD = \
+ $(top_builddir)/libgphoto2_port/libgphoto2_port.la \
+ $(SERIAL_LIBS) \
+ $(INTLLIBS)
+usbscsi_la_SOURCES = linux.c
diff --git a/libgphoto2_port/usbscsi/linux.c b/libgphoto2_port/usbscsi/linux.c
new file mode 100644
index 000000000..aad18c20f
--- /dev/null
+++ b/libgphoto2_port/usbscsi/linux.c
@@ -0,0 +1,383 @@
+/* SCSI commands to USB Mass storage devices port library for Linux
+ *
+ * Copyright (c) 2010 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This program 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.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+#include "config.h"
+#include <gphoto2/gphoto2-port-library.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <scsi/sg.h>
+#ifdef 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);}
+
+struct _GPPortPrivateLibrary {
+ int fd; /* Device handle */
+};
+
+GPPortType
+gp_port_library_type ()
+{
+ return GP_PORT_USB_SCSI;
+}
+
+static int
+gp_port_usbscsi_lock (GPPort *port, const char *path)
+{
+#ifdef HAVE_LOCKDEV
+ int pid;
+
+ gp_log (GP_LOG_DEBUG, "gphoto2-port-usbscsi",
+ "Trying to lock '%s'...", path);
+
+ pid = dev_lock (path);
+ if (pid) {
+ if (port) {
+ if (pid > 0)
+ gp_port_set_error (port, _("Device '%s' is "
+ "locked by pid %d"), path, pid);
+ else
+ gp_port_set_error (port, _("Device '%s' could "
+ "not be locked (dev_lock returned "
+ "%d)"), path, pid);
+ }
+ return GP_ERROR_IO_LOCK;
+ }
+#else
+# ifdef __GCC__
+# warning No locking library found.
+# warning You will run into problems if you use
+# warning gphoto2 with a usbscsi picframe 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_usbscsi_unlock (GPPort *port, const char *path)
+{
+#ifdef HAVE_LOCKDEV
+ int pid;
+
+ pid = dev_unlock (path, 0);
+ if (pid) {
+ if (port) {
+ if (pid > 0)
+ gp_port_set_error (port, _("Device '%s' could "
+ "not be unlocked as it is locked by "
+ "pid %d."), path, pid);
+ else
+ gp_port_set_error (port, _("Device '%s' could "
+ "not be unlocked (dev_unlock "
+ "returned %d)"), path, pid);
+ }
+ return GP_ERROR_IO_LOCK;
+ }
+#endif /* !HAVE_LOCKDEV */
+
+ return GP_OK;
+}
+
+static const char *
+gp_port_usbscsi_resolve_symlink (const char *link)
+{
+ ssize_t ret;
+ static char path[PATH_MAX + 1];
+ char *slash, buf[PATH_MAX + 1];
+ struct stat st;
+ int len;
+
+ snprintf (path, sizeof(path), "%s", link);
+
+ do {
+ ret = readlink (path, buf, PATH_MAX);
+ if (ret < 0)
+ return NULL;
+ buf[ret] = 0;
+
+ slash = strrchr (path, '/');
+ if (buf[0] == '/' || slash == NULL) {
+ snprintf (path, sizeof(path), "%s", buf);
+ } else {
+ *slash = 0;
+ len = strlen (path);
+ snprintf (path + len, sizeof (path) - len, "/%s", buf);
+ }
+
+ if (stat (path, &st))
+ return NULL;
+ } while (S_ISLNK(st.st_mode));
+
+ return path;
+}
+
+static int
+gp_port_usbscsi_get_usb_id (const char *sg,
+ unsigned short *vendor_id, unsigned short *product_id)
+{
+ FILE *f;
+ char c, *s, buf[32], path[PATH_MAX + 1];
+
+ snprintf (path, sizeof (path), "/sys/class/scsi_generic/%s", sg);
+ snprintf (path, sizeof (path), "%s/../../../../../modalias",
+ gp_port_usbscsi_resolve_symlink(path));
+
+ f = fopen (path, "r");
+ if (!f)
+ return GP_ERROR_IO_SUPPORTED_USB;
+
+ s = fgets (buf, sizeof(buf), f);
+ fclose (f);
+
+ if (!s)
+ return GP_ERROR_IO_SUPPORTED_USB;
+
+ if (sscanf (s, "usb:v%4hxp%4hx%c", vendor_id, product_id, &c) != 3 ||
+ c != 'd')
+ return GP_ERROR_IO_SUPPORTED_USB;
+
+ return GP_OK;
+}
+
+int
+gp_port_library_list (GPPortInfoList *list)
+{
+ DIR *dir;
+ struct dirent *dirent;
+ GPPortInfo info;
+ unsigned short vendor_id, product_id;
+
+ dir = opendir ("/sys/class/scsi_generic");
+ if (dir == NULL)
+ return GP_OK;
+
+ while ((dirent = readdir (dir))) {
+ if (gp_port_usbscsi_get_usb_id (dirent->d_name,
+ &vendor_id, &product_id) != GP_OK)
+ continue; /* Not a usb device */
+
+ info.type = GP_PORT_USB_SCSI;
+ snprintf (info.path, sizeof (info.path),
+ "usbscsi:/dev/%s",
+ dirent->d_name);
+ snprintf (info.name, sizeof (info.name),
+ _("USB Mass Storage raw SCSI"));
+ CHECK (gp_port_info_list_append (list, info))
+ }
+
+ return GP_OK;
+}
+
+static int
+gp_port_usbscsi_init (GPPort *port)
+{
+ port->pl = calloc (1, sizeof (GPPortPrivateLibrary));
+ if (!port->pl)
+ return GP_ERROR_NO_MEMORY;
+
+ port->pl->fd = -1;
+
+ return GP_OK;
+}
+
+static int
+gp_port_usbscsi_exit (GPPort *port)
+{
+ if (!port)
+ return GP_ERROR_BAD_PARAMETERS;
+
+ if (port->pl) {
+ free (port->pl);
+ port->pl = NULL;
+ }
+
+ return GP_OK;
+}
+
+static int
+gp_port_usbscsi_open (GPPort *port)
+{
+ int result, i;
+ const int max_tries = 5;
+ const char *path = port->settings.usbscsi.path;
+
+ result = gp_port_usbscsi_lock (port, path);
+ if (result != GP_OK) {
+ for (i = 0; i < max_tries; i++) {
+ result = gp_port_usbscsi_lock (port, path);
+ if (result == GP_OK)
+ break;
+ gp_log (GP_LOG_DEBUG, "gphoto2-port-usbscsi",
+ "Failed to get a lock, trying again...");
+ sleep (1);
+ }
+ CHECK (result)
+ }
+ port->pl->fd = open (path, O_RDWR);
+ if (port->pl->fd == -1) {
+ gp_port_usbscsi_unlock (port, path);
+ gp_port_set_error (port, _("Failed to open '%s' (%m)."), path);
+ return GP_ERROR_IO;
+ }
+
+ return GP_OK;
+}
+
+static int
+gp_port_usbscsi_close (GPPort *port)
+{
+ if (!port || port->pl->fd == -1)
+ return GP_OK;
+
+ if (close (port->pl->fd) == -1) {
+ gp_port_set_error (port, _("Could not close "
+ "'%s' (%m)."), port->settings.usbscsi.path);
+ return GP_ERROR_IO;
+ }
+ port->pl->fd = -1;
+
+ CHECK (gp_port_usbscsi_unlock (port,
+ port->settings.usbscsi.path))
+
+ return GP_OK;
+}
+
+static int gp_port_usbscsi_send_scsi_cmd (GPPort *port, int to_dev, char *cmd,
+ int cmd_size, char *sense, int sense_size, char *data, int data_size)
+{
+ sg_io_hdr_t io_hdr;
+
+ if (!port)
+ return GP_ERROR_BAD_PARAMETERS;
+
+ /* The device needs to be opened for that operation */
+ if (port->pl->fd == -1)
+ CHECK (gp_port_usbscsi_open (port))
+
+ memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+ if (to_dev)
+ io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
+ else
+ io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_hdr.interface_id = 'S';
+ io_hdr.cmdp = (unsigned char *)cmd;
+ io_hdr.cmd_len = cmd_size;
+ io_hdr.sbp = (unsigned char *)sense;
+ io_hdr.mx_sb_len = sense_size;
+ io_hdr.dxferp = (unsigned char *)data;
+ io_hdr.dxfer_len = data_size;
+ io_hdr.timeout = 500;
+
+ if (ioctl (port->pl->fd, SG_IO, &io_hdr) < 0)
+ {
+ gp_port_set_error (port, _("Could not send scsi command to: "
+ "'%s' (%m)."), port->settings.usbscsi.path);
+ return GP_ERROR_IO;
+ }
+
+ return GP_OK;
+}
+
+static int
+gp_port_usbscsi_update (GPPort *port)
+{
+ if (!port)
+ return GP_ERROR_BAD_PARAMETERS;
+
+ memcpy (&port->settings, &port->settings_pending,
+ sizeof (port->settings));
+
+ return GP_OK;
+}
+
+static int
+gp_port_usbscsi_find_device(GPPort *port, int idvendor, int idproduct)
+{
+ unsigned short vendor_id, product_id;
+ const char *sg;
+
+ if (!port)
+ return GP_ERROR_BAD_PARAMETERS;
+
+ sg = strrchr (port->settings.usbscsi.path, '/');
+ if (!sg)
+ return GP_ERROR_BAD_PARAMETERS;
+ sg++;
+
+ CHECK (gp_port_usbscsi_get_usb_id (sg, &vendor_id, &product_id))
+ if (vendor_id != idvendor || product_id != idproduct)
+ return GP_ERROR_IO_USB_FIND;
+
+ 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_usbscsi_init;
+ ops->exit = gp_port_usbscsi_exit;
+ ops->open = gp_port_usbscsi_open;
+ ops->close = gp_port_usbscsi_close;
+ ops->send_scsi_cmd = gp_port_usbscsi_send_scsi_cmd;
+ ops->update = gp_port_usbscsi_update;
+ ops->find_device = gp_port_usbscsi_find_device;
+
+ return (ops);
+}