| /* qcam-Linux.c -- Linux-specific routines for accessing QuickCam */ |
| |
| /* Version 0.1, January 2, 1996 */ |
| /* Version 0.5, August 24, 1996 */ |
| |
| #define LOCALSTATEDIR "" |
| //#define LOCALSTATEDIR "/var/run" |
| |
| /****************************************************************** |
| |
| Copyright (C) 1996 by Scott Laird |
| |
| Permission is hereby granted, free of charge, to any person obtaining |
| a copy of this software and associated documentation files (the |
| "Software"), to deal in the Software without restriction, including |
| without limitation the rights to use, copy, modify, merge, publish, |
| distribute, sublicense, and/or sell copies of the Software, and to |
| permit persons to whom the Software is furnished to do so, subject to |
| the following conditions: |
| |
| The above copyright notice and this permission notice shall be |
| included in all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR |
| OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
| ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| OTHER DEALINGS IN THE SOFTWARE. |
| |
| ******************************************************************/ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <stdio.h> |
| #include <unistd.h> |
| #ifdef TESTING |
| #include <errno.h> |
| #endif |
| #include <sys/io.h> |
| |
| #include <sys/time.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| |
| #include "qcam.h" |
| #include "qcam-Linux.h" |
| |
| int __inline__ |
| read_lpstatus (const struct qcam *q) |
| { |
| return inb (q->port + 1); |
| } |
| |
| int |
| read_lpcontrol (const struct qcam *q) |
| { |
| return inb (q->port + 2); |
| } |
| |
| int |
| read_lpdata (const struct qcam *q) |
| { |
| return inb (q->port); |
| } |
| |
| void |
| write_lpdata (const struct qcam *q, int d) |
| { |
| outb (d, q->port); |
| } |
| |
| void |
| write_lpcontrol (const struct qcam *q, int d) |
| { |
| outb (d, q->port + 2); |
| } |
| |
| int |
| enable_ports (const struct qcam *q) |
| { |
| if (q->port < 0x278) |
| return 1; /* Better safe than sorry */ |
| if (q->port > 0x3bc) |
| return 1; |
| return (ioperm (q->port, 3, 1)); |
| } |
| |
| int |
| disable_ports (const struct qcam *q) |
| { |
| return (ioperm (q->port, 3, 0)); |
| } |
| |
| /* Lock port. This is currently sub-optimal, and is begging to be |
| fixed. It should check for dead locks. Any takers? */ |
| |
| /* qc_lock_wait |
| * This function uses POSIX fcntl-style locking on a file created in the |
| * /tmp directory. Because it uses the Unix record locking facility, locks |
| * are relinquished automatically on process termination, so "dead locks" |
| * are not a problem. (FYI, the lock file will remain after process |
| * termination, but this is actually desired so that the next process need |
| * not re-creat(2)e it... just lock it.) |
| * The wait argument indicates whether or not this funciton should "block" |
| * waiting for the previous lock to be relinquished. This is ideal so that |
| * multiple processes (eg. qcam) taking "snapshots" can peacefully coexist. |
| * - Dave Plonka (plonka@carroll1.cc.edu) |
| */ |
| int |
| qc_lock_wait (struct qcam *q, int wait) |
| { |
| #if 1 |
| static struct flock sfl; |
| |
| if (-1 == q->fd) { /* we've yet to open the lock file */ |
| static char lockfile[128]; |
| |
| sprintf (lockfile, LOCALSTATEDIR "LOCK.qcam.0x%x", q->port); |
| if (-1 == (q->fd = open (lockfile, O_WRONLY | O_CREAT, 0666))) { |
| perror ("open"); |
| return 1; |
| } |
| #ifdef TESTING |
| fprintf (stderr, "%s - %d: %s open(2)ed\n", __FILE__, __LINE__, lockfile); |
| #endif |
| |
| /* initialize the l_type memver to lock the file exclusively */ |
| sfl.l_type = F_WRLCK; |
| } |
| #ifdef TESTING |
| if (0 != fcntl (q->fd, F_SETLK, &sfl)) /* non-blocking set lock */ |
| #else |
| if (0 != fcntl (q->fd, wait ? F_SETLKW : F_SETLK, &sfl)) |
| #endif |
| { |
| #ifdef TESTING |
| perror ("fcntl"); |
| if (EAGAIN != errno || !wait) |
| return 1; |
| |
| fprintf (stderr, "%s - %d: waiting for exclusive lock on fd %d...\n", |
| __FILE__, __LINE__, q->fd); |
| |
| if (0 != fcntl (q->fd, F_SETLKW, &sfl)) /* "blocking" set lock */ |
| #endif |
| { |
| perror ("fcntl"); |
| return 1; |
| } |
| } |
| #ifdef TESTING |
| fprintf (stderr, "%s - %d: fd %d locked exclusively\n", __FILE__, __LINE__, |
| q->fd); |
| #endif |
| |
| #else |
| char lockfile[128], tmp[128]; |
| struct stat statbuf; |
| |
| sprintf (lockfile, LOCALSTATEDIR "/LOCK.qcam.0x%x", q->port); |
| sprintf (tmp, "%s-%d", lockfile, getpid ()); |
| |
| if ((creat (tmp, 0) == -1) || |
| (link (tmp, lockfile) == -1) || |
| (stat (tmp, &statbuf) == -1) || (statbuf.st_nlink == 1)) { |
| #ifdef DEBUGQC |
| perror ("QuickCam Locked"); |
| if (unlink (tmp) == -1) |
| perror ("Error unlinking temp file."); |
| #else |
| unlink (tmp); |
| #endif |
| return 1; |
| } |
| |
| unlink (tmp); |
| if (chown (lockfile, getuid (), getgid ()) == -1) |
| perror ("Chown problems"); |
| #endif |
| |
| return 0; |
| } |
| |
| int |
| qc_lock (struct qcam *q) |
| { |
| #if 1 |
| return qc_lock_wait (q, 1 /*wait */ ); |
| #else |
| return qc_lock_wait (q, 0 /*don't wait */ ); |
| #endif |
| } |
| |
| /* Unlock port */ |
| |
| int |
| qc_unlock (struct qcam *q) |
| { |
| static struct flock sfl; |
| |
| #if 1 |
| if (-1 == q->fd) { /* port was not locked */ |
| return 1; |
| } |
| |
| /* clear the exclusive lock */ |
| sfl.l_type = F_UNLCK; |
| if (0 != fcntl (q->fd, F_SETLK, &sfl)) { |
| perror ("fcntl"); |
| return 1; |
| } |
| #ifdef TESTING |
| fprintf (stderr, "%s - %d: fd %d unlocked\n", __FILE__, __LINE__, q->fd); |
| #endif |
| |
| #else |
| char lockfile[128]; |
| |
| sprintf (lockfile, LOCALSTATEDIR "/LOCK.qcam.0x%x", q->port); |
| unlink (lockfile); /* What would I do with an error? */ |
| #endif |
| |
| return 0; |
| } |
| |
| |
| /* Probe for camera. Returns 0 if found, 1 if not found, sets |
| q->port.*/ |
| |
| int |
| qc_probe (struct qcam *q) |
| { |
| int ioports[] = { 0x378, 0x278, 0x3bc, 0 }; |
| int i = 0; |
| |
| /* Attempt to get permission to access IO ports. Must be root */ |
| |
| while (ioports[i] != 0) { |
| q->port = ioports[i++]; |
| |
| if (qc_open (q)) { |
| perror ("Can't get I/O permission"); |
| exit (1); |
| } |
| |
| if (qc_detect (q)) { |
| fprintf (stderr, "QuickCam detected at 0x%x\n", q->port); |
| qc_close (q); |
| return (0); |
| } else |
| qc_close (q); |
| } |
| |
| return 1; |
| } |
| |
| |
| /* THIS IS UGLY. I need a short delay loop -- somthing well under a |
| millisecond. Unfortunately, adding 2 usleep(1)'s to qc_command slowed |
| it down by a factor of over 1000 over the same loop with 2 |
| usleep(0)'s, and that's too slow -- qc_start was taking over a second |
| to run. This seems to help, but if anyone has a good |
| speed-independent pause routine, please tell me. -- Scott */ |
| |
| void |
| qc_wait (int val) |
| { |
| int i; |
| |
| while (val--) |
| for (i = 0; i < 50000; i++); |
| } |