diff options
author | Hans de Goede <hdegoede@redhat.com> | 2010-03-29 09:25:54 +0000 |
---|---|---|
committer | Hans de Goede <hdegoede@redhat.com> | 2010-03-29 09:25:54 +0000 |
commit | 004777f2877233fa3e2374664cf2726d237bd214 (patch) | |
tree | 8c0ae93cb68458741c02d18c948caf4e51ec6bdd /libgphoto2_port/usbscsi | |
parent | afe1e7bfffea54655a55e25215245989c24bf445 (diff) | |
download | libgphoto2-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.am | 13 | ||||
-rw-r--r-- | libgphoto2_port/usbscsi/linux.c | 383 |
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); +} |