summaryrefslogtreecommitdiff
path: root/sys/qcam
diff options
context:
space:
mode:
authorThomas Vander Stichele <thomas@apestaart.org>2001-12-17 19:03:13 +0000
committerThomas Vander Stichele <thomas@apestaart.org>2001-12-17 19:03:13 +0000
commitbb8c10821177de574bda97d0d11b2b69e9aaf9ff (patch)
treeb995b623bab9eb6ddaad49177c6d26d05d5b440c /sys/qcam
parent49d345d9ef32b3a019cb7ec1f8d4546145c15b59 (diff)
first batch
Original commit message from CVS: first batch
Diffstat (limited to 'sys/qcam')
-rw-r--r--sys/qcam/Makefile.am10
-rw-r--r--sys/qcam/dark.c253
-rw-r--r--sys/qcam/exposure.c284
-rw-r--r--sys/qcam/gstqcamsrc.c444
-rw-r--r--sys/qcam/gstqcamsrc.h79
-rw-r--r--sys/qcam/qcam-Linux.c246
-rw-r--r--sys/qcam/qcam-Linux.h32
-rw-r--r--sys/qcam/qcam-lib.c788
-rw-r--r--sys/qcam/qcam-os.c246
-rw-r--r--sys/qcam/qcam-os.h32
-rw-r--r--sys/qcam/qcam.h134
-rw-r--r--sys/qcam/qcamip.h67
12 files changed, 2615 insertions, 0 deletions
diff --git a/sys/qcam/Makefile.am b/sys/qcam/Makefile.am
new file mode 100644
index 000000000..c6e30c6f1
--- /dev/null
+++ b/sys/qcam/Makefile.am
@@ -0,0 +1,10 @@
+plugindir = $(libdir)/gst
+
+plugin_LTLIBRARIES = libgstqcam.la
+
+EXTRA_DIST = qcam-os.c qcam-Linux.c
+
+libgstqcam_la_SOURCES = gstqcamsrc.c qcam-lib.c exposure.c
+libgstqcam_la_CFLAGS = -O2 $(GLIB_CFLAGS)
+
+noinst_HEADERS = gstqcamsrc.h qcam-os.h qcam.h qcamip.h qcam-Linux.h
diff --git a/sys/qcam/dark.c b/sys/qcam/dark.c
new file mode 100644
index 000000000..15dbbb4e8
--- /dev/null
+++ b/sys/qcam/dark.c
@@ -0,0 +1,253 @@
+/******************************************************************
+
+Copyright (C) 1996 by Brian Scearce
+
+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,
+and/or distribute copies of the Software, and to permit persons to whom
+the Software is furnished to do so, subject to the following conditions:
+
+1. The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+2. Redistribution for profit requires the express, written permission of
+ the author.
+
+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 BRIAN SCEARCE 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.
+******************************************************************/
+
+/** Fixdark
+ Routine to repair dark current artifacts in qcam output.
+ Basic idea: the Qcam CCD suffers from "dark current";
+ that is, some of the CCD pixels will leak current under
+ long exposures, even if they're in the dark, and this
+ shows up as ugly speckling on images taken in low light.
+
+ Fortunately, the leaky pixels are the same from shot to
+ shot. So, we can figure out which pixels are leaky by
+ taking some establishing shots in the dark, and try to
+ fix those pixels on subsequent shots. The dark
+ establishing shots need only be done once per camera.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "qcam.h"
+#define MAX_LOOPS 10
+#define FNAME "qcam.darkfile"
+
+static unsigned char master_darkmask1[MAX_HEIGHT][MAX_WIDTH];
+static unsigned char master_darkmask2[MAX_HEIGHT/2+1][MAX_WIDTH/2+1];
+static unsigned char master_darkmask4[MAX_HEIGHT/4+1][MAX_WIDTH/4+1];
+
+/*
+int
+read_darkmask()
+{
+ int x, y;
+ int min_bright;
+ char darkfile[BUFSIZ], *p;
+ FILE *fp;
+
+ strcpy(darkfile, CONFIG_FILE);
+ if ( (p = strrchr(darkfile, '/'))) {
+ strcpy(p+1, FNAME);
+ } else {
+ strcpy(darkfile, FNAME);
+ }
+
+ if (!(fp = fopen(darkfile, "r"))) {
+#ifdef DEBUG
+ fprintf(stderr, "Can't open darkfile %s\n", darkfile);
+#endif
+ return 0;
+ }
+
+ if (fread(master_darkmask1, sizeof(unsigned char), MAX_WIDTH*MAX_HEIGHT, fp) !=
+ MAX_WIDTH*MAX_HEIGHT) {
+#ifdef DEBUG
+ fprintf(stderr, "Error reading darkfile\n");
+#endif
+ return 0;
+ }
+
+ for (y = 0; y < MAX_HEIGHT; y += 2) {
+ for (x = 0; x < MAX_WIDTH; x += 2) {
+ min_bright = master_darkmask1[y][x];
+ if (y < MAX_HEIGHT-1 && master_darkmask1[y+1][x] < min_bright)
+ min_bright = master_darkmask1[y+1][x];
+ if (x < MAX_WIDTH-1 && master_darkmask1[y][x+1] < min_bright)
+ min_bright = master_darkmask1[y][x+1];
+ if (y < MAX_HEIGHT-1 && x < MAX_WIDTH-1 && master_darkmask1[y+1][x+1] < min_bright)
+ min_bright = master_darkmask1[y+1][x+1];
+ master_darkmask2[y/2][x/2] = min_bright;
+ assert(y/2 < MAX_HEIGHT/2+1);
+ assert(x/2 < MAX_WIDTH/2+1);
+ }
+ }
+
+ for (y = 0; y < MAX_HEIGHT/2; y += 2) {
+ for (x = 0; x < MAX_WIDTH/2; x += 2) {
+ min_bright = master_darkmask2[y][x];
+ if (y < MAX_HEIGHT/2-1 && master_darkmask2[y+1][x] < min_bright)
+ min_bright = master_darkmask2[y+1][x];
+ if (x < MAX_WIDTH/2-1 && master_darkmask2[y][x+1] < min_bright)
+ min_bright = master_darkmask2[y][x+1];
+ if (y < MAX_HEIGHT/2-1 && x < MAX_WIDTH-1 && master_darkmask2[y+1][x+1] < min_bright)
+ min_bright = master_darkmask2[y+1][x+1];
+ master_darkmask4[y/2][x/2] = min_bright;
+ assert(y/2 < MAX_HEIGHT/4+1);
+ assert(x/2 < MAX_WIDTH/4+1);
+ }
+ }
+
+ fclose(fp);
+ return 1;
+}
+*/
+
+
+/** fixdark
+ We first record a list of bad leaky pixels, by making a
+ number of exposures in the dark. master_darkmask holds
+ this information. It's a map of the CCD.
+ master_darkmask[y][x] == val means that the pixel is
+ unreliable for brightnesses of "val" and above.
+
+ We go over the image. If a pixel is bad, look at the
+ adjacent four pixels, average the ones that have good
+ values, and use that instead.
+*/
+
+int
+fixdark(const struct qcam *q, scanbuf *scan)
+{
+ static int init = 0;
+ static int smallest_dm = 255;
+ unsigned char darkmask[MAX_HEIGHT][MAX_WIDTH];
+ unsigned char new_image[MAX_HEIGHT][MAX_WIDTH];
+ int width, height;
+ int max_width, max_height;
+ int x, y;
+ int ccd_x, ccd_y;
+ int pixelcount, pixeltotal;
+ int again, loopcount = 0;
+ int val;
+ int brightness = q->brightness;
+ int scale = q->transfer_scale;
+
+ if (!init) {
+ if (!read_darkmask()) return 0;
+ for (y = 0; y < MAX_HEIGHT; y++)
+ for (x = 0; x < MAX_HEIGHT; x++)
+ if (master_darkmask1[y][x] < smallest_dm) {
+ smallest_dm = master_darkmask1[y][x];
+#ifdef DEBUG
+ fprintf(stderr, "Smallest mask is %d at (%d, %d)\n",
+ smallest_dm, x, y);
+#endif
+ }
+ init = 1;
+ }
+
+ if (brightness < smallest_dm) {
+#ifdef DEBUG
+ fprintf(stderr, "Brightness %d (dark current starts at %d), no fixup needed\n",
+ brightness, smallest_dm);
+#endif
+ return 1;
+ }
+
+ width = q->width / scale;
+ height = q->height / scale;
+
+ max_height = MAX_HEIGHT / scale;
+ max_width = MAX_WIDTH / scale;
+ for (y = 0; y < max_height; y++)
+ for (x = 0; x < max_width; x++)
+ if (scale == 1) {
+ darkmask[y][x] = master_darkmask1[y][x];
+ } else if (scale == 2) {
+ darkmask[y][x] = master_darkmask2[y][x];
+ } else if (scale == 4) {
+ darkmask[y][x] = master_darkmask4[y][x];
+ } else {
+#ifdef DEBUG
+ fprintf(stderr, "Bad transfer_scale in darkmask assignment!\n");
+#endif
+ return 0;
+ }
+
+ do {
+ again = 0;
+ ccd_y = (q->top-1)/scale;
+ for (y = 0; y < height; y++, ccd_y++) {
+ ccd_x = q->left-1;
+ ccd_x /= 2;
+ ccd_x *= 2;
+ ccd_x /= scale;
+ for (x = 0; x < width; x++, ccd_x++) {
+ val = scan[y*width + x];
+ if (brightness < darkmask[ccd_y][ccd_x]) { /* good pixel */
+ new_image[y][x] = val;
+ } else { /* bad pixel */
+ /* look at nearby pixels, average the good values */
+ pixelcount = 0;
+ pixeltotal = 0;
+ if (x > 0) { /* left */
+ if (brightness < darkmask[ccd_y][ccd_x-1]) {
+ pixelcount++;
+ pixeltotal += scan[y*width + x - 1];
+ }
+ }
+ if (x < width-1) { /* right */
+ if (brightness < darkmask[ccd_y][ccd_x+1]) {
+ pixelcount++;
+ pixeltotal += scan[y*width + x + 1];
+ }
+ }
+ if (y > 0) { /* above */
+ if (brightness < darkmask[ccd_y-1][ccd_x]) {
+ pixelcount++;
+ pixeltotal += scan[(y-1)*width + x];
+ }
+ }
+ if (y < height-1) { /* below */
+ if (brightness < darkmask[ccd_y+1][ccd_x]) {
+ pixelcount++;
+ pixeltotal += scan[(y+1)*width + x];
+ }
+ }
+
+ if (pixelcount == 0) { /* no valid neighbors! */
+ again = 1;
+ } else {
+ new_image[y][x] = pixeltotal / pixelcount;
+ /* mark this pixel as valid, so we don't loop forever */
+ darkmask[ccd_y][ccd_x] = 255;
+ }
+ }
+ }
+ }
+
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ scan[y*width + x] = new_image[y][x];
+
+ } while (loopcount++ < MAX_LOOPS && again);
+#ifdef DEBUG
+ fprintf(stderr, "Darkmask fix took %d loop%s\n",
+ loopcount, (loopcount == 1)?"":"s");
+#endif
+ return 1;
+}
+
diff --git a/sys/qcam/exposure.c b/sys/qcam/exposure.c
new file mode 100644
index 000000000..139aebf3a
--- /dev/null
+++ b/sys/qcam/exposure.c
@@ -0,0 +1,284 @@
+/* exposure.c
+ *
+ * Time-stamp: <02 Sep 96 11:52:21 HST edo@eosys.com>
+ *
+ * Version 0.2
+ */
+
+
+/******************************************************************
+
+Copyright (C) 1996 by Ed Orcutt Systems
+
+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, and/or distribute copies of the
+Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+1. The above copyright notice and this permission notice shall
+ be included in all copies or substantial portions of the
+ Software.
+
+2. Redistribution for profit requires the express, written
+ permission of the author.
+
+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 ED ORCUTT SYSTEMS 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.
+
+******************************************************************/
+
+#include <stdio.h>
+#include "qcam.h"
+#include "qcamip.h"
+
+/* Prototypes for private (static) functions used by the routines
+ * within this file. Externally visible functions should be
+ * prototyped in qcamip.h
+ */
+
+static int qcip_pixel_average(struct qcam *q, scanbuf *scan);
+static int qcip_luminance_std(struct qcam *q, scanbuf *scan, int avg);
+
+/* Private data used by the auto exposure routine */
+
+static int luminance_target = -1;
+static int luminance_tolerance = 0;
+static int luminance_std_target = -1;
+static int luminance_std_tolerance = 0;
+static int ae_mode = AE_ALL_AVG;
+
+/* Calculate average pixel value for entire image */
+
+static int
+qcip_pixel_average(struct qcam *q, scanbuf *scan)
+{
+ int count = 0;
+ int sum = 0;
+ int pixels;
+ int i;
+
+ pixels = q->height / q->transfer_scale;
+ pixels *= q->width / q->transfer_scale;
+
+ for (i = 0; i < pixels; i++) {
+ sum += scan[i];
+ count++;
+ }
+ return (sum / count);
+}
+
+/* Calculate average pixel value for center of image */
+
+static int
+qcip_pixel_average_center(struct qcam *q, scanbuf *scan)
+{
+ int count = 0;
+ int sum = 0;
+ int height, width;
+ int maxrow, maxcol;
+ int i, j;
+
+ /* actual image width & height after scaling */
+ width = q->width / q->transfer_scale;
+ height = q->height / q->transfer_scale;
+
+ maxcol = width * 2 / 3;
+ maxrow = height * 2 / 3;
+
+ for (i = width/3; i < maxcol; i++) {
+ for (j = height/3; j < maxrow; j++) {
+ sum += scan[j*width+i];
+ count++;
+ }
+ }
+ return (sum / count);
+}
+
+int
+qcip_set_luminance_target(struct qcam *q, int val)
+{
+ const int max_pixel_val = q->bpp == 6 ? 63 : 15;
+
+ if ((val - luminance_tolerance) >= 0 &&
+ (val + luminance_tolerance) <= max_pixel_val) {
+ luminance_target = val;
+ return QCIP_XPSR_OK;
+ }
+ return QCIP_XPSR_LUM_INVLD;
+}
+
+int
+qcip_set_luminance_tolerance(struct qcam *q, int val)
+{
+ const int max_pixel_val = q->bpp == 6 ? 63 : 15;
+
+ /* set target if it has not been explicitly set */
+ if (luminance_target == -1) {
+ luminance_target = q->bpp == 6 ? 32 : 8;
+ }
+
+ if ((luminance_target - val) >= 0 &&
+ (luminance_target + val) <= max_pixel_val) {
+ luminance_tolerance = val;
+ return QCIP_XPSR_OK;
+ }
+ return QCIP_XPSR_LUM_INVLD;
+}
+
+int
+qcip_set_luminance_std_target(struct qcam *q, int val)
+{
+ luminance_std_target = val;
+ return QCIP_XPSR_OK;
+}
+
+int
+qcip_set_luminance_std_tolerance(struct qcam *q, int val)
+{
+ luminance_std_tolerance = val;
+ return QCIP_XPSR_OK;
+}
+
+int
+qcip_set_autoexposure_mode(int val)
+{
+ ae_mode = val;
+ return 0;
+}
+
+/* Calculate standard deviation of pixel value for entire image */
+
+static int
+qcip_luminance_std(struct qcam *q, scanbuf *scan, int avg)
+{
+ int count = 0;
+ int sum = 0;
+ int pixels;
+ int i;
+
+ pixels = q->height / q->transfer_scale;
+ pixels *= q->width / q->transfer_scale;
+
+ for (i = 0; i < pixels; i++) {
+ if (scan[i] < avg) {
+ sum += avg - scan[i];
+ } else {
+ sum += scan[i] - avg;
+ }
+ count++;
+ }
+ return (sum / count);
+}
+
+
+/* If necessary adjust the brightness in an attempt to achieve
+ * a target average pixel value: 32 for 6 bpp, 8 for 4bpp.
+ * This routine *will* modify the brightness value in preparation
+ * for another scan unless the target average pixel values has
+ * been reached. If the exposure is correct (yes, I realize that
+ * this is subjective) QCIP_XPSR_OK will be returned, otherwise
+ * return QCIP_XPSR_RSCN after adjusting the exposure.
+ *
+ * Caveat: If the new calculated brightness value is invalid,
+ * QCIP_XPSR_ERR will be returned.
+ */
+
+int
+qcip_autoexposure(struct qcam *q, scanbuf *scan)
+{
+ int luminance_dif;
+ int luminance_avg;
+ int brightness_adj;
+ int lum_min, lum_max;
+ int lum_std, lum_std_min, lum_std_max;
+ int ret = QCIP_XPSR_OK;
+
+#ifdef DEBUG
+ fprintf(stderr, "Brightness: %d Contrast: %d\n",
+ qc_getbrightness(q), qc_getcontrast(q));
+#endif
+
+ switch (ae_mode) {
+ case AE_CTR_AVG:
+ luminance_avg = qcip_pixel_average_center(q, scan);
+ break;
+ case AE_STD_AVG:
+ luminance_avg = qcip_pixel_average(q, scan);
+ lum_std = qcip_luminance_std(q, scan, luminance_avg);
+ break;
+ case AE_ALL_AVG:
+ default:
+ luminance_avg = qcip_pixel_average(q, scan);
+ break;
+ }
+
+ /* ==>> Contrast adjustment <<== */
+
+ if (ae_mode == AE_STD_AVG) {
+
+ /* set target if it has not been explicitly set */
+ if (luminance_std_target == -1) {
+ luminance_std_target = q->bpp == 6 ? 10 : 2;
+ }
+
+ /* Adjust contrast to reach target luminance standard deviation */
+ lum_std_min = luminance_std_target - luminance_std_tolerance;
+ lum_std_max = luminance_std_target + luminance_std_tolerance;
+
+ if (lum_std < lum_std_min || lum_std > lum_std_max) {
+ ret = QCIP_XPSR_RSCN;
+ if (qc_setcontrast(q, luminance_std_target - lum_std + qc_getcontrast(q))) {
+ return QCIP_XPSR_ERR;
+ }
+ }
+#ifdef DEBUG
+ fprintf(stderr, "Luminance std/target/tolerance: %d/%d/%d\n",
+ lum_std, luminance_std_target, luminance_std_tolerance );
+#endif
+ }
+
+ /* ==>> Brightness adjustment <<== */
+
+ /* set target if it has not been explicitly set */
+ if (luminance_target == -1) {
+ luminance_target = q->bpp == 6 ? 32 : 8;
+ }
+
+ lum_min = luminance_target - luminance_tolerance;
+ lum_max = luminance_target + luminance_tolerance;
+
+#ifdef DEBUG
+ fprintf(stderr, "Luminance avg/target/tolerance: %d/%d/%d\n",
+ luminance_avg, luminance_target, luminance_tolerance );
+#endif
+
+ /* check for luminance within target range */
+ if (luminance_avg < lum_min || luminance_avg > lum_max) {
+ ret = QCIP_XPSR_RSCN;
+ /* we need to adjust the brighness, which way? */
+ luminance_dif = luminance_target - luminance_avg;
+ if (luminance_dif > 0) {
+ brightness_adj = luminance_dif / 2 + 1;
+ } else {
+ brightness_adj = luminance_dif / 2 - 1;
+ }
+
+ /* Adjusted brightness is out of range ..
+ * throw in the towel ... auto-exposure has failed!
+ */
+ if (qc_setbrightness(q, brightness_adj + qc_getbrightness(q))) {
+ return QCIP_XPSR_ERR;
+ }
+ }
+
+ return ret;
+}
diff --git a/sys/qcam/gstqcamsrc.c b/sys/qcam/gstqcamsrc.c
new file mode 100644
index 000000000..ed1afe98a
--- /dev/null
+++ b/sys/qcam/gstqcamsrc.c
@@ -0,0 +1,444 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <string.h>
+
+//#define DEBUG_ENABLED
+#include <gstqcamsrc.h>
+
+#include "qcamip.h"
+
+static GstElementDetails
+gst_qcamsrc_details =
+{
+ "QCam Source",
+ "Source/Video",
+ "Read from a QuickCam device",
+ VERSION,
+ "Wim Taymans <wim.taymans@chello.be>",
+ "(C) 2001",
+};
+
+#define AE_NONE 3
+
+#define DEF_WIDTH 320
+#define DEF_HEIGHT 224
+#define DEF_BRIGHTNESS 226
+#define DEF_WHITEBAL 128
+#define DEF_CONTRAST 72
+#define DEF_TOP 1
+#define DEF_LEFT 14
+#define DEF_TRANSFER_SCALE 2
+#define DEF_DEPTH 6
+#define DEF_PORT 0x378
+#define DEF_AUTOEXP AE_NONE
+
+GST_PADTEMPLATE_FACTORY (gst_qcamsrc_src_factory,
+ "src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_CAPS_NEW (
+ "gstqcam_src",
+ "video/raw",
+ "format", GST_PROPS_FOURCC (GST_STR_FOURCC ("I420")),
+ "width", GST_PROPS_INT_RANGE (0, 320),
+ "height", GST_PROPS_INT_RANGE (0, 240)
+ )
+)
+
+#define GST_TYPE_AUTOEXP_MODE (gst_autoexp_mode_get_type())
+static GType
+gst_autoexp_mode_get_type (void)
+{
+ static GType autoexp_mode_type = 0;
+ static GEnumValue autoexp_modes[] = {
+ { AE_ALL_AVG, "0", "Average Picture" },
+ { AE_CTR_AVG, "1", "Average Center" },
+ { AE_STD_AVG, "2", "Standard Deviation" },
+ { AE_NONE, "3", "None" },
+ { 0, NULL, NULL },
+ };
+ if (!autoexp_mode_type) {
+ autoexp_mode_type = g_enum_register_static ("GstAutoExposureMode", autoexp_modes);
+ }
+ return autoexp_mode_type;
+}
+
+/* QCamSrc signals and args */
+enum {
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+enum {
+ ARG_0,
+ ARG_WIDTH,
+ ARG_HEIGHT,
+ ARG_BRIGHTNESS,
+ ARG_WHITEBAL,
+ ARG_CONTRAST,
+ ARG_TOP,
+ ARG_LEFT,
+ ARG_TRANSFER_SCALE,
+ ARG_DEPTH,
+ ARG_PORT,
+ ARG_AUTOEXP,
+};
+
+static void gst_qcamsrc_class_init (GstQCamSrcClass *klass);
+static void gst_qcamsrc_init (GstQCamSrc *qcamsrc);
+
+static void gst_qcamsrc_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void gst_qcamsrc_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static GstElementStateReturn gst_qcamsrc_change_state (GstElement *element);
+static void gst_qcamsrc_close (GstQCamSrc *src);
+static gboolean gst_qcamsrc_open (GstQCamSrc *src);
+
+static GstBuffer* gst_qcamsrc_get (GstPad *pad);
+
+static GstElementClass *parent_class = NULL;
+////static guint gst_qcamsrc_signals[LAST_SIGNAL] = { 0 };
+
+GType
+gst_qcamsrc_get_type (void)
+{
+ static GType qcamsrc_type = 0;
+
+ if (!qcamsrc_type) {
+ static const GTypeInfo qcamsrc_info = {
+ sizeof(GstQCamSrcClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)gst_qcamsrc_class_init,
+ NULL,
+ NULL,
+ sizeof(GstQCamSrc),
+ 0,
+ (GInstanceInitFunc)gst_qcamsrc_init,
+ NULL
+ };
+ qcamsrc_type = g_type_register_static(GST_TYPE_ELEMENT, "GstQCamSrc", &qcamsrc_info, 0);
+ }
+ return qcamsrc_type;
+}
+
+static void
+gst_qcamsrc_class_init (GstQCamSrcClass *klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass*)klass;
+ gstelement_class = (GstElementClass*)klass;
+
+ parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
+
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_WIDTH,
+ g_param_spec_int ("width", "width", "width",
+ 0, 320, DEF_WIDTH, G_PARAM_READWRITE));
+ g_object_class_install_property (G_OBJECT_CLASS(klass), ARG_HEIGHT,
+ g_param_spec_int ("height", "height", "height",
+ 0, 240, DEF_HEIGHT, G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BRIGHTNESS,
+ g_param_spec_int ("brightness", "brightness", "brightness",
+ 0, 255, DEF_BRIGHTNESS, G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_WHITEBAL,
+ g_param_spec_int ("whitebal", "whitebal", "whitebal",
+ 0, 255, DEF_WHITEBAL, G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_CONTRAST,
+ g_param_spec_int ("contrast", "contrast", "contrast",
+ 0, 255, DEF_CONTRAST, G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_TOP,
+ g_param_spec_int ("top", "top", "top",
+ 0, 240, DEF_TOP, G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_LEFT,
+ g_param_spec_int ("left", "left", "left",
+ 0, 320, DEF_LEFT, G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_TRANSFER_SCALE,
+ g_param_spec_int ("transfer_scale", "transfer_scale", "transfer_scale",
+ 1, 4, DEF_TRANSFER_SCALE, G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEPTH,
+ g_param_spec_int ("depth", "depth", "depth",
+ 4, 6, DEF_DEPTH, G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_PORT,
+ g_param_spec_int ("port","port","port",
+ 0, G_MAXINT, DEF_PORT, G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_AUTOEXP,
+ g_param_spec_enum ("autoexposure", "autoexposure", "autoexposure",
+ GST_TYPE_AUTOEXP_MODE, DEF_AUTOEXP, G_PARAM_READWRITE));
+
+ gobject_class->set_property = gst_qcamsrc_set_property;
+ gobject_class->get_property = gst_qcamsrc_get_property;
+
+ gstelement_class->change_state = gst_qcamsrc_change_state;
+}
+
+static void
+gst_qcamsrc_init (GstQCamSrc *qcamsrc)
+{
+ qcamsrc->srcpad = gst_pad_new_from_template (
+ GST_PADTEMPLATE_GET (gst_qcamsrc_src_factory), "src");
+ gst_element_add_pad(GST_ELEMENT(qcamsrc),qcamsrc->srcpad);
+ gst_pad_set_get_function (qcamsrc->srcpad,gst_qcamsrc_get);
+
+ /* if the destination cannot say what it wants, we give this */
+ qcamsrc->qcam = qc_init();
+ qcamsrc->qcam->port = DEF_PORT;
+ qc_setwidth (qcamsrc->qcam, DEF_WIDTH);
+ qc_setheight (qcamsrc->qcam, DEF_HEIGHT);
+ qc_setbrightness (qcamsrc->qcam, DEF_BRIGHTNESS);
+ qc_setwhitebal (qcamsrc->qcam, DEF_WHITEBAL);
+ qc_setcontrast (qcamsrc->qcam, DEF_CONTRAST);
+ qc_settop (qcamsrc->qcam, DEF_TOP);
+ qc_setleft (qcamsrc->qcam, DEF_LEFT);
+ qc_settransfer_scale (qcamsrc->qcam, DEF_TRANSFER_SCALE);
+ qc_setbitdepth (qcamsrc->qcam, DEF_DEPTH);
+ qcamsrc->autoexposure = DEF_AUTOEXP;
+ if (qcamsrc->autoexposure != AE_NONE)
+ qcip_set_autoexposure_mode (qcamsrc->autoexposure);
+}
+
+static GstBuffer*
+gst_qcamsrc_get (GstPad *pad)
+{
+ GstQCamSrc *qcamsrc;
+ GstBuffer *buf;
+ scanbuf *scan;
+ guchar *outdata;
+ gint i, frame, scale, convert;
+
+ g_return_val_if_fail (pad != NULL, NULL);
+
+ qcamsrc = GST_QCAMSRC (gst_pad_get_parent (pad));
+
+ scale = qc_gettransfer_scale (qcamsrc->qcam);
+
+ frame = qcamsrc->qcam->width * qcamsrc->qcam->height / (scale * scale);
+
+ buf = gst_buffer_new();
+ outdata = GST_BUFFER_DATA(buf) = g_malloc0((frame * 3) / 2);
+ GST_BUFFER_SIZE(buf) = (frame * 3) / 2;
+
+ qc_set (qcamsrc->qcam);
+ if (!GST_PAD_CAPS (pad)) {
+ gst_pad_set_caps (pad, GST_CAPS_NEW (
+ "qcam_caps",
+ "video/raw",
+ "format", GST_PROPS_FOURCC (GST_STR_FOURCC ("I420")),
+ "width", GST_PROPS_INT (qcamsrc->qcam->width / scale),
+ "height", GST_PROPS_INT (qcamsrc->qcam->height / scale)
+ ));
+ }
+ scan = qc_scan (qcamsrc->qcam);
+
+ // FIXME, this doesn't seem to work...
+ //fixdark(qcamsrc->qcam, scan);
+
+ if (qcamsrc->autoexposure != AE_NONE)
+ qcip_autoexposure(qcamsrc->qcam, scan);
+
+ convert = (qcamsrc->qcam->bpp==4?4:2);
+
+ for (i=frame; i; i--) {
+ outdata[i] = scan[i]<<convert;
+ }
+ memset (outdata+frame, 128, frame>>1);
+ g_free (scan);
+
+ return buf;
+}
+
+static void
+gst_qcamsrc_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ GstQCamSrc *src;
+
+ /* it's not null if we got it, but it might not be ours */
+ g_return_if_fail(GST_IS_QCAMSRC(object));
+ src = GST_QCAMSRC(object);
+
+ switch (prop_id) {
+ case ARG_WIDTH:
+ qc_setwidth (src->qcam, g_value_get_int (value));
+ break;
+ case ARG_HEIGHT:
+ qc_setheight (src->qcam, g_value_get_int (value));
+ break;
+ case ARG_BRIGHTNESS:
+ qc_setbrightness (src->qcam, g_value_get_int (value));
+ break;
+ case ARG_WHITEBAL:
+ qc_setwhitebal (src->qcam, g_value_get_int (value));
+ break;
+ case ARG_CONTRAST:
+ qc_setcontrast (src->qcam, g_value_get_int (value));
+ break;
+ case ARG_TOP:
+ qc_settop (src->qcam, g_value_get_int (value));
+ break;
+ case ARG_LEFT:
+ qc_setleft (src->qcam, g_value_get_int (value));
+ break;
+ case ARG_TRANSFER_SCALE:
+ qc_settransfer_scale (src->qcam, g_value_get_int (value));
+ break;
+ case ARG_DEPTH:
+ qc_setbitdepth (src->qcam, g_value_get_int (value));
+ break;
+ case ARG_PORT:
+ src->qcam->port = g_value_get_int (value);
+ break;
+ case ARG_AUTOEXP:
+ src->autoexposure = g_value_get_enum (value);
+ if (src->autoexposure != AE_NONE)
+ qcip_set_autoexposure_mode (src->autoexposure);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+gst_qcamsrc_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ GstQCamSrc *src;
+
+ /* it's not null if we got it, but it might not be ours */
+ g_return_if_fail(GST_IS_QCAMSRC(object));
+ src = GST_QCAMSRC(object);
+
+ switch (prop_id) {
+ case ARG_WIDTH:
+ g_value_set_int (value, qc_getwidth (src->qcam));
+ break;
+ case ARG_HEIGHT:
+ g_value_set_int (value, qc_getheight (src->qcam));
+ break;
+ case ARG_BRIGHTNESS:
+ g_value_set_int (value, qc_getbrightness (src->qcam));
+ break;
+ case ARG_WHITEBAL:
+ g_value_set_int (value, qc_getwhitebal (src->qcam));
+ break;
+ case ARG_CONTRAST:
+ g_value_set_int (value, qc_getcontrast (src->qcam));
+ break;
+ case ARG_TOP:
+ g_value_set_int (value, qc_gettop (src->qcam));
+ break;
+ case ARG_LEFT:
+ g_value_set_int (value, qc_getleft (src->qcam));
+ break;
+ case ARG_TRANSFER_SCALE:
+ g_value_set_int (value, qc_gettransfer_scale (src->qcam));
+ break;
+ case ARG_DEPTH:
+ g_value_set_int (value, qc_getbitdepth (src->qcam));
+ break;
+ case ARG_PORT:
+ g_value_set_int (value, src->qcam->port);
+ break;
+ case ARG_AUTOEXP:
+ g_value_set_enum (value, src->autoexposure);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GstElementStateReturn
+gst_qcamsrc_change_state (GstElement *element)
+{
+ g_return_val_if_fail(GST_IS_QCAMSRC(element), FALSE);
+
+ /* if going down into NULL state, close the file if it's open */
+ if (GST_STATE_PENDING(element) == GST_STATE_NULL) {
+ if (GST_FLAG_IS_SET(element,GST_QCAMSRC_OPEN))
+ gst_qcamsrc_close(GST_QCAMSRC(element));
+ /* otherwise (READY or higher) we need to open the sound card */
+ } else {
+ if (!GST_FLAG_IS_SET(element,GST_QCAMSRC_OPEN)) {
+ gst_info ("qcamsrc: opening\n");
+ if (!gst_qcamsrc_open(GST_QCAMSRC(element))) {
+ gst_info ("qcamsrc: open failed\n");
+ return GST_STATE_FAILURE;
+ }
+ }
+ }
+
+ if (GST_ELEMENT_CLASS(parent_class)->change_state)
+ return GST_ELEMENT_CLASS(parent_class)->change_state(element);
+
+ return GST_STATE_SUCCESS;
+}
+
+static gboolean
+gst_qcamsrc_open (GstQCamSrc *qcamsrc)
+{
+ if (qc_open (qcamsrc->qcam)) {
+ g_warning("qcamsrc: Cannot open QuickCam.\n");
+ return FALSE;
+ }
+
+ GST_FLAG_SET(qcamsrc, GST_QCAMSRC_OPEN);
+
+ return TRUE;
+}
+
+static void
+gst_qcamsrc_close (GstQCamSrc *src)
+{
+ qc_close (src->qcam);
+ GST_FLAG_UNSET(src, GST_QCAMSRC_OPEN);
+}
+
+static gboolean
+plugin_init (GModule *module, GstPlugin *plugin)
+{
+ GstElementFactory *factory;
+
+ /* create an elementfactory for the qcamsrcparse element */
+ factory = gst_elementfactory_new("qcamsrc",GST_TYPE_QCAMSRC,
+ &gst_qcamsrc_details);
+ g_return_val_if_fail(factory != NULL, FALSE);
+
+ gst_elementfactory_add_padtemplate (factory,
+ GST_PADTEMPLATE_GET (gst_qcamsrc_src_factory));
+
+ gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
+
+ return TRUE;
+}
+
+GstPluginDesc plugin_desc = {
+ GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "qcamsrc",
+ plugin_init
+};
+
diff --git a/sys/qcam/gstqcamsrc.h b/sys/qcam/gstqcamsrc.h
new file mode 100644
index 000000000..c4608b6ef
--- /dev/null
+++ b/sys/qcam/gstqcamsrc.h
@@ -0,0 +1,79 @@
+/* Gnome-Streamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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.
+ */
+
+
+#ifndef __GST_QCAMSRC_H__
+#define __GST_QCAMSRC_H__
+
+
+#include <config.h>
+#include <gst/gst.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** QuickCam include files */
+#include "qcam.h"
+#include "qcam-os.h"
+
+#define GST_TYPE_QCAMSRC \
+ (gst_qcamsrc_get_type())
+#define GST_QCAMSRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_QCAMSRC,GstQCamSrc))
+#define GST_QCAMSRC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_QCAMSRC,GstQCamSrcClass))
+#define GST_IS_QCAMSRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_QCAMSRC))
+#define GST_IS_QCAMSRC_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_QCAMSRC))
+
+// NOTE: per-element flags start with 16 for now
+typedef enum {
+ GST_QCAMSRC_OPEN = GST_ELEMENT_FLAG_LAST,
+
+ GST_QCAMSRC_FLAG_LAST = GST_ELEMENT_FLAG_LAST+2,
+} GstQCamSrcFlags;
+
+typedef struct _GstQCamSrc GstQCamSrc;
+typedef struct _GstQCamSrcClass GstQCamSrcClass;
+
+struct _GstQCamSrc {
+ GstElement element;
+
+ /* pads */
+ GstPad *srcpad;
+
+ struct qcam *qcam;
+ gboolean autoexposure;
+ gint port;
+};
+
+struct _GstQCamSrcClass {
+ GstElementClass parent_class;
+};
+
+GType gst_qcamsrc_get_type(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GST_QCAMSRC_H__ */
diff --git a/sys/qcam/qcam-Linux.c b/sys/qcam/qcam-Linux.c
new file mode 100644
index 000000000..f6383e015
--- /dev/null
+++ b/sys/qcam/qcam-Linux.c
@@ -0,0 +1,246 @@
+/* qcam-Linux.c -- Linux-specific routines for accessing QuickCam */
+
+/* Version 0.1, January 2, 1996 */
+/* Version 0.5, August 24, 1996 */
+
+
+/******************************************************************
+
+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.
+
+******************************************************************/
+
+
+#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,"/var/run/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,"/var/run/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,"/var/run/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++);
+}
diff --git a/sys/qcam/qcam-Linux.h b/sys/qcam/qcam-Linux.h
new file mode 100644
index 000000000..46dcbe519
--- /dev/null
+++ b/sys/qcam/qcam-Linux.h
@@ -0,0 +1,32 @@
+/* qcam-linux.h -- Linux-specific routines for accessing QuickCam */
+
+/* Version 0.1, January 2, 1996 */
+/* Version 0.5, August 24, 1996 */
+
+
+/******************************************************************
+
+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.
+
+******************************************************************/
+
+
diff --git a/sys/qcam/qcam-lib.c b/sys/qcam/qcam-lib.c
new file mode 100644
index 000000000..f3b49fb03
--- /dev/null
+++ b/sys/qcam/qcam-lib.c
@@ -0,0 +1,788 @@
+/* qcam-lib.c -- Library for programming with the Connectix QuickCam.
+ * See the included documentation for usage instructions and details
+ * of the protocol involved. */
+
+
+/* Version 0.5, August 4, 1996 */
+/* Version 0.7, August 27, 1996 */
+/* Version 0.9, November 17, 1996 */
+
+
+/******************************************************************
+
+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.
+
+******************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <unistd.h>
+
+#include <assert.h>
+
+#include "qcam.h"
+#include "qcam-os.h"
+#include "qcam-os.c"
+
+/* Prototypes for static functions. Externally visible functions
+ * should be prototyped in qcam.h */
+
+static int qc_waithand(const struct qcam *q, int val);
+static int qc_command(const struct qcam *q, int command);
+static int qc_readparam(const struct qcam *q);
+static int qc_setscanmode(struct qcam *q);
+static int qc_readbytes(const struct qcam *q, char buffer[]);
+
+/* The next several functions are used for controlling the qcam
+ * structure. They aren't used inside this library, but they should
+ * provide a clean interface for external programs.*/
+
+/* Gets/sets the brightness. */
+
+int qc_getbrightness(const struct qcam *q)
+{
+ return q->brightness;
+}
+
+int qc_setbrightness(struct qcam *q, int val)
+{
+ if (val >= 0 && val <= 255) {
+ q->brightness=val;
+ return 0;
+ }
+ return 1;
+}
+
+
+/* Gets/sets the contrast */
+
+int qc_getcontrast(const struct qcam *q)
+{
+ return q->contrast;
+}
+
+int qc_setcontrast(struct qcam *q, int val)
+{
+ if (val >= 0 && val <= 255) {
+ q->contrast=val;
+ return 0;
+ }
+ return 1;
+}
+
+
+/* Gets/sets the white balance */
+
+int qc_getwhitebal(const struct qcam *q)
+{
+ return q->whitebal;
+}
+
+int qc_setwhitebal(struct qcam *q, int val)
+{
+ if (val >= 0 && val <= 255) {
+ q->whitebal=val;
+ return 0;
+ }
+ return 1;
+}
+
+
+/* Gets/sets the resolution */
+
+void qc_getresolution(const struct qcam *q, int *x, int *y)
+{
+ *x=q->width;
+ *y=q->height;
+}
+
+int qc_setresolution(struct qcam *q, int x, int y)
+{
+ if (x >= 0 && x <= 336 && y >= 0 && y <= 243) {
+ q->width=x;
+ q->height=y;
+ return 0;
+ }
+ return 1;
+}
+
+int qc_getheight(const struct qcam *q)
+{
+ return q->height;
+}
+
+int qc_setheight(struct qcam *q, int y)
+{
+ if (y >= 0 && y <= 243) {
+ q->height=y;
+ return 0;
+ }
+ return 1;
+}
+
+int qc_getwidth(const struct qcam *q)
+{
+ return q->width;
+}
+
+int qc_setwidth(struct qcam *q, int x)
+{
+ if (x >= 0 && x <= 336) {
+ q->width=x;
+ return 0;
+ }
+ return 1;
+}
+
+/* Gets/sets the bit depth */
+
+int qc_getbitdepth(const struct qcam *q)
+{
+ return q->bpp;
+}
+
+int qc_setbitdepth(struct qcam *q, int val)
+{
+ if (val == 4 || val == 6) {
+ q->bpp=val;
+ return qc_setscanmode(q);
+ }
+ return 1;
+}
+
+int qc_gettop(const struct qcam *q)
+{
+ return q->top;
+}
+
+int qc_settop(struct qcam *q, int val)
+{
+ if (val >= 1 && val <= 243) {
+ q->top = val;
+ return 0;
+ }
+ return 1;
+}
+
+int qc_getleft(const struct qcam *q)
+{
+ return q->left;
+}
+
+int qc_setleft(struct qcam *q, int val)
+{
+ if (val % 2 == 0 && val >= 2 && val <= 336) {
+ q->left = val;
+ return 0;
+ }
+ return 1;
+}
+
+int qc_gettransfer_scale(const struct qcam *q)
+{
+ return q->transfer_scale;
+}
+
+int qc_settransfer_scale(struct qcam *q, int val)
+{
+ if (val == 1 || val == 2 || val == 4) {
+ q->transfer_scale = val;
+ return qc_setscanmode(q);
+ }
+ return 1;
+}
+
+int
+qc_calibrate(struct qcam *q)
+/* bugfix by Hanno Mueller hmueller@kabel.de, Mai 21 96 */
+/* The white balance is an individiual value for each */
+/* quickcam. Run calibration once, write the value down */
+/* and put it in your qcam.conf file. You won't need to */
+/* recalibrate your camera again. */
+{
+ int value;
+#ifdef DEBUG
+ int count = 0;
+#endif
+
+ qc_command(q, 27); /* AutoAdjustOffset */
+ qc_command(q, 0); /* Dummy Parameter, ignored by the camera */
+
+ /* GetOffset (33) will read 255 until autocalibration */
+ /* is finished. After that, a value of 1-254 will be */
+ /* returned. */
+
+ do {
+ qc_command(q, 33); value = qc_readparam(q);
+#ifdef DEBUG
+ count++;
+#endif
+ } while (value == 0xff);
+
+ q->whitebal = value;
+
+#ifdef DEBUG
+ fprintf(stderr, "%d loops to calibrate\n", count);
+ fprintf(stderr, "Calibrated to %d\n", value);
+#endif
+
+ return value;
+}
+
+int
+qc_forceunidir(struct qcam *q)
+{
+ q->port_mode = (q->port_mode & ~QC_FORCE_MASK) | QC_FORCE_UNIDIR;
+ return 0;
+}
+
+
+/* Initialize the QuickCam driver control structure. This is where
+ * defaults are set for people who don't have a config file.*/
+struct qcam *
+qc_init(void)
+{
+ struct qcam *q;
+
+ q=malloc(sizeof(struct qcam));
+
+ q->port=0; /* Port 0 == Autoprobe */
+ q->port_mode=(QC_ANY | QC_NOTSET);
+ q->width=160;
+ q->height=120;
+ q->bpp=4;
+ q->transfer_scale = 2;
+ q->contrast=104;
+ q->brightness=150;
+ q->whitebal=150;
+ q->top = 1;
+ q->left = 14;
+ q->mode = -1;
+ q->fd=-1; /* added initialization of fd member
+ * BTW, there doesn't seem to be a place to close this fd...
+ * I think we need a qc_free function.
+ * - Dave Plonka (plonka@carroll1.cc.edu)
+ */
+
+ return q;
+}
+
+
+/* qc_open enables access to the port specified in q->port. It takes
+ * care of locking and enabling I/O port access by calling the
+ * appropriate routines.
+ *
+ * Returns 0 for success, 1 for opening error, 2 for locking error,
+ * and 3 for qcam not found */
+
+int qc_open(struct qcam *q)
+{
+ if(q->port==0)
+ if(qc_probe(q)) {
+ fprintf(stderr,"Qcam not found\n");
+ return 3;
+ }
+
+ if(qc_lock(q)) {
+ fprintf(stderr,"Cannot lock qcam.\n");
+ return 2;
+ }
+
+ if(enable_ports(q)) {
+ fprintf(stderr,"Cannot open QuickCam -- permission denied.");
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+/* qc_close closes and unlocks the driver. You *need* to call this,
+ * or lockfiles will be left behind and everything will be screwed. */
+
+int qc_close(struct qcam *q)
+{
+ qc_unlock(q);
+
+ disable_ports(q);
+ return 0;
+}
+
+
+/* qc_command is probably a bit of a misnomer -- it's used to send
+ * bytes *to* the camera. Generally, these bytes are either commands
+ * or arguments to commands, so the name fits, but it still bugs me a
+ * bit. See the documentation for a list of commands. */
+
+static int qc_command(const struct qcam *q, int command)
+{
+ int n1, n2;
+ int cmd;
+
+ write_lpdata(q, command);
+ write_lpcontrol(q,6);
+
+ n1 = qc_waithand(q,1);
+
+ write_lpcontrol(q,0xe);
+ n2 = qc_waithand(q,0);
+
+ cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
+#ifdef DEBUG
+ if (cmd != command) {
+ fprintf(stderr, "Command 0x%02x sent, 0x%02x echoed", command, cmd);
+ n2 = read_lpstatus(q);
+ cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
+ if (cmd != command) fprintf(stderr, " (re-read does not help)\n");
+ else fprintf(stderr, " (fixed on re-read)\n");
+ }
+#endif
+ return cmd;
+}
+
+static int
+qc_readparam(const struct qcam *q)
+{
+ int n1, n2;
+ int cmd;
+
+ write_lpcontrol(q,6);
+ n1 = qc_waithand(q,1);
+
+ write_lpcontrol(q,0xe);
+ n2 = qc_waithand(q,0);
+
+ cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
+ return cmd;
+}
+
+/* qc_waithand busy-waits for a handshake signal from the QuickCam.
+ * Almost all communication with the camera requires handshaking. */
+
+static int qc_waithand(const struct qcam *q, int val)
+{
+ int status;
+
+ if (val)
+ while(! ((status = read_lpstatus(q))&8))
+ ;
+ else
+ while (((status = read_lpstatus(q))&8))
+ ;
+
+ return status;
+}
+
+/* Waithand2 is used when the qcam is in bidirectional mode, and the
+ * handshaking signal is CamRdy2 (bit 0 of data reg) instead of CamRdy1
+ * (bit 3 of status register). It also returns the last value read,
+ * since this data is useful. */
+
+static unsigned int
+qc_waithand2(const struct qcam *q, int val)
+{
+ unsigned int status;
+ do {
+ status = read_lpdata(q);
+ } while ( (status & 1) != val);
+
+ return status;
+}
+
+
+/* Try to detect a QuickCam. It appears to flash the upper 4 bits of
+ the status register at 5-10 Hz. This is only used in the autoprobe
+ code. Be aware that this isn't the way Connectix detects the
+ camera (they send a reset and try to handshake), but this should be
+ almost completely safe, while their method screws up my printer if
+ I plug it in before the camera. */
+
+int qc_detect(const struct qcam *q)
+{
+ int reg,lastreg;
+ int count=0;
+ int i;
+
+ lastreg=reg=read_lpstatus(q)&0xf0;
+
+ for(i=0;i<30;i++) {
+ reg=read_lpstatus(q)&0xf0;
+ if(reg!=lastreg) count++;
+ lastreg=reg;
+ usleep(10000);
+ }
+
+ /* Be liberal in what you accept... */
+
+ if(count>3&&count<15)
+ return 1; /* found */
+ else
+ return 0; /* not found */
+}
+
+
+/* Reset the QuickCam. This uses the same sequence the Windows
+ * QuickPic program uses. Someone with a bi-directional port should
+ * check that bi-directional mode is detected right, and then
+ * implement bi-directional mode in qc_readbyte(). */
+
+void qc_reset(struct qcam *q)
+{
+ switch (q->port_mode & QC_FORCE_MASK)
+ {
+ case QC_FORCE_UNIDIR:
+ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR;
+ break;
+
+ case QC_FORCE_BIDIR:
+ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR;
+ break;
+
+ case QC_ANY:
+ write_lpcontrol(q,0x20);
+ write_lpdata(q,0x75);
+
+ if (read_lpdata(q) != 0x75) {
+ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR;
+ } else {
+ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR;
+ }
+ break;
+
+ case QC_FORCE_SERIAL:
+ default:
+ fprintf(stderr, "Illegal port_mode %x\n", q->port_mode);
+ break;
+ }
+
+ /* usleep(250);*/
+ write_lpcontrol(q,0xb);
+ usleep(250);
+ write_lpcontrol(q,0xe);
+ (void)qc_setscanmode(q); /* in case port_mode changed */
+}
+
+
+/* Decide which scan mode to use. There's no real requirement that
+ * the scanmode match the resolution in q->height and q-> width -- the
+ * camera takes the picture at the resolution specified in the
+ * "scanmode" and then returns the image at the resolution specified
+ * with the resolution commands. If the scan is bigger than the
+ * requested resolution, the upper-left hand corner of the scan is
+ * returned. If the scan is smaller, then the rest of the image
+ * returned contains garbage. */
+
+static int qc_setscanmode(struct qcam *q)
+{
+ switch (q->transfer_scale) {
+ case 1: q->mode = 0; break;
+ case 2: q->mode = 4; break;
+ case 4: q->mode = 8; break;
+ default: return 1;
+ }
+
+ switch (q->bpp) {
+ case 4: break;
+ case 6: q->mode+=2; break;
+ default:
+ fprintf(stderr,"Error: Unsupported bit depth\n");
+ return 1;
+ }
+
+ switch (q->port_mode & QC_MODE_MASK) {
+ case QC_BIDIR: q->mode += 1; break;
+ case QC_NOTSET:
+ case QC_UNIDIR: break;
+ default: return 1;
+ }
+ return 0;
+}
+
+
+/* Reset the QuickCam and program for brightness, contrast,
+ * white-balance, and resolution. */
+
+void qc_set(struct qcam *q)
+{
+ int val;
+ int val2;
+
+ qc_reset(q);
+
+ /* Set the brightness. Yes, this is repetitive, but it works.
+ * Shorter versions seem to fail subtly. Feel free to try :-). */
+ /* I think the problem was in qc_command, not here -- bls */
+ qc_command(q,0xb);
+ qc_command(q,q->brightness);
+
+ val = q->height / q->transfer_scale;
+ qc_command(q,0x11); qc_command(q, val);
+ if ((q->port_mode & QC_MODE_MASK) == QC_UNIDIR && q->bpp == 6) {
+ /* The normal "transfers per line" calculation doesn't seem to work
+ as expected here (and yet it works fine in qc_scan). No idea
+ why this case is the odd man out. Fortunately, Laird's original
+ working version gives me a good way to guess at working values.
+ -- bls */
+ val = q->width;
+ val2 = q->transfer_scale * 4;
+ } else {
+ val = q->width * q->bpp;
+ val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR)?24:8) *
+ q->transfer_scale;
+ }
+ val = (val + val2 - 1) / val2;
+ qc_command(q,0x13); qc_command(q, val);
+
+ /* I still don't know what these do! */
+ /* They're setting top and left -- bls */
+ qc_command(q,0xd); qc_command(q,q->top);
+ qc_command(q,0xf); qc_command(q,q->left/2);
+
+ qc_command(q,0x19); qc_command(q,q->contrast);
+ qc_command(q,0x1f); qc_command(q,q->whitebal);
+}
+
+
+/* Qc_readbytes reads some bytes from the QC and puts them in
+ the supplied buffer. It returns the number of bytes read,
+ or -1 on error. */
+
+static int
+__inline__
+qc_readbytes(const struct qcam *q, char buffer[])
+{
+ int ret;
+ unsigned int hi, lo;
+ unsigned int hi2, lo2;
+ static unsigned int saved_bits;
+ static int state = 0;
+
+ if (buffer == NULL) {
+ state = 0;
+ return 0;
+ }
+
+ switch (q->port_mode & QC_MODE_MASK) {
+ case QC_BIDIR: /* Bi-directional Port */
+ write_lpcontrol(q, 0x26);
+ lo = (qc_waithand2(q, 1) >> 1);
+ hi = (read_lpstatus(q) >> 3) & 0x1f;
+ write_lpcontrol(q, 0x2e);
+ lo2 = (qc_waithand2(q, 0) >> 1);
+ hi2 = (read_lpstatus(q) >> 3) & 0x1f;
+ switch (q->bpp) {
+ case 4:
+ buffer[0] = lo & 0xf;
+ buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3);
+ buffer[2] = (hi & 0x1e) >> 1;
+ buffer[3] = lo2 & 0xf;
+ buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3);
+ buffer[5] = (hi2 & 0x1e) >> 1;
+ ret = 6;
+ break;
+ case 6:
+ buffer[0] = lo & 0x3f;
+ buffer[1] = ((lo & 0x40) >> 6) | (hi << 1);
+ buffer[2] = lo2 & 0x3f;
+ buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1);
+ ret = 4;
+ break;
+ default:
+ fprintf(stderr, "Bad bidir pixel depth %d\n", q->bpp);
+ ret = -1;
+ break;
+ }
+ break;
+
+ case QC_UNIDIR: /* Unidirectional Port */
+ write_lpcontrol(q,6);
+ lo = (qc_waithand(q,1) & 0xf0) >> 4;
+ write_lpcontrol(q,0xe);
+ hi = (qc_waithand(q,0) & 0xf0) >> 4;
+
+ switch (q->bpp) {
+ case 4:
+ buffer[0] = lo;
+ buffer[1] = hi;
+ ret = 2;
+ break;
+ case 6:
+ switch (state) {
+ case 0:
+ buffer[0] = (lo << 2) | ((hi & 0xc) >> 2);
+ saved_bits = (hi & 3) << 4;
+ state = 1; ret = 1; break;
+ case 1:
+ buffer[0] = lo | saved_bits;
+ saved_bits = hi << 2;
+ state = 2; ret = 1; break;
+ case 2:
+ buffer[0] = ((lo & 0xc) >> 2) | saved_bits;
+ buffer[1] = ((lo & 3) << 4) | hi;
+ state = 0; ret = 2; break;
+ default:
+ fprintf(stderr, "Unidir 6-bit state %d?\n", state);
+ ret = -1;
+ break;
+ }
+ break;
+ default:
+ fprintf(stderr, "Bad unidir pixel depth %d\n", q->bpp);
+ ret = -1;
+ break;
+ }
+ break;
+ case QC_SERIAL: /* Serial Interface. Just in case.*/
+ default:
+ fprintf(stderr,"Mode %x not supported\n",q->port_mode);
+ ret=-1;
+ break;
+ }
+ return ret;
+}
+
+/* Read a scan from the QC. This takes the qcam structure and
+ * requests a scan from the camera. It sends the correct instructions
+ * to the camera and then reads back the correct number of bytes. In
+ * previous versions of this routine the return structure contained
+ * the raw output from the camera, and there was a 'qc_convertscan'
+ * function that converted that to a useful format. In version 0.3 I
+ * rolled qc_convertscan into qc_scan and now I only return the
+ * converted scan. The format is just an one-dimensional array of
+ * characters, one for each pixel, with 0=black up to n=white, where
+ * n=2^(bit depth)-1. Ask me for more details if you don't understand
+ * this. */
+
+scanbuf *qc_scan(const struct qcam *q)
+{
+ unsigned char *ret;
+ int i, j, k;
+ int bytes;
+ int linestotrans, transperline;
+ int divisor;
+ int pixels_per_line;
+ int pixels_read;
+ char buffer[6];
+ char invert;
+
+ if (q->mode != -1) {
+ qc_command(q, 0x7);
+ qc_command(q, q->mode);
+ } else {
+ struct qcam bogus_cam;
+ /* We're going through these odd hoops to retain the "const"
+ qualification on q. We can't do a qc_setscanmode directly on q,
+ so we copy it, do a setscanmode on that, and pass in the newly
+ computed mode. -- bls 11/21/96
+ */
+
+#ifdef DEBUG
+ fprintf(stderr, "Warning! qc->mode not set!\n");
+#endif
+ bogus_cam = *q;
+ (void)qc_setscanmode(&bogus_cam);
+ qc_command(q, 0x7);
+ qc_command(q, bogus_cam.mode);
+ }
+
+ if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) {
+ write_lpcontrol(q, 0x2e); /* turn port around */
+ write_lpcontrol(q, 0x26);
+ (void) qc_waithand(q, 1);
+ write_lpcontrol(q, 0x2e);
+ (void) qc_waithand(q, 0);
+ }
+
+ /* strange -- should be 15:63 below, but 4bpp is odd */
+ invert = (q->bpp == 4) ? 16 : 63;
+
+ linestotrans = q->height / q->transfer_scale;
+ pixels_per_line = q->width / q->transfer_scale;
+ transperline = q->width * q->bpp;
+ divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR)?24:8) *
+ q->transfer_scale;
+ transperline = (transperline + divisor - 1) / divisor;
+
+ ret = malloc(linestotrans * pixels_per_line);
+ assert(ret);
+
+#ifdef DEBUG
+ fprintf(stderr, "%s %d bpp\n%d lines of %d transfers each\n",
+ ((q->port_mode & QC_MODE_MASK) == QC_BIDIR)?"Bidir":"Unidir",
+ q->bpp, linestotrans, transperline);
+#endif
+
+ for (i = 0; i < linestotrans; i++) {
+ for (pixels_read = j = 0; j < transperline; j++) {
+ bytes = qc_readbytes(q, buffer);
+ assert(bytes > 0);
+ for (k = 0; k < bytes && (pixels_read + k) < pixels_per_line; k++) {
+ assert (buffer[k] <= invert);
+ assert (buffer[k] >= 0);
+ if (buffer[k] == 0 && invert == 16) {
+ /* 4bpp is odd (again) -- inverter is 16, not 15, but output
+ must be 0-15 -- bls */
+ buffer[k] = 16;
+ }
+ ret[i*pixels_per_line + pixels_read + k] = invert - buffer[k];
+ }
+ pixels_read += bytes;
+ }
+ (void) qc_readbytes(q, 0); /* reset state machine */
+ }
+
+ if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) {
+ write_lpcontrol(q, 2);
+ write_lpcontrol(q, 6);
+ usleep(3);
+ write_lpcontrol(q, 0xe);
+ }
+
+ return ret;
+}
+
+
+void
+qc_dump(const struct qcam *q, char *fname)
+{
+ FILE *fp;
+ time_t t;
+
+ if ((fp = fopen(fname, "w")) == 0)
+ {
+ fprintf(stderr, "Error: cannot open %s\n", fname);
+ return;
+ }
+
+ fprintf(fp, "# Version 0.9\n");
+ time(&t);
+ fprintf(fp, "# Created %s", ctime(&t));
+ fprintf(fp, "Width %d\nHeight %d\n", q->width, q->height);
+ fprintf(fp, "Top %d\nLeft %d\n", q->top, q->left);
+ fprintf(fp, "Bpp %d\nContrast %d\n", q->bpp, q->contrast);
+ fprintf(fp, "Brightness %d\nWhitebal %d\n", q->brightness, q->whitebal);
+ fprintf(fp, "Port 0x%x\nScale %d\n", q->port, q->transfer_scale);
+ fclose(fp);
+}
diff --git a/sys/qcam/qcam-os.c b/sys/qcam/qcam-os.c
new file mode 100644
index 000000000..f6383e015
--- /dev/null
+++ b/sys/qcam/qcam-os.c
@@ -0,0 +1,246 @@
+/* qcam-Linux.c -- Linux-specific routines for accessing QuickCam */
+
+/* Version 0.1, January 2, 1996 */
+/* Version 0.5, August 24, 1996 */
+
+
+/******************************************************************
+
+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.
+
+******************************************************************/
+
+
+#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,"/var/run/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,"/var/run/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,"/var/run/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++);
+}
diff --git a/sys/qcam/qcam-os.h b/sys/qcam/qcam-os.h
new file mode 100644
index 000000000..46dcbe519
--- /dev/null
+++ b/sys/qcam/qcam-os.h
@@ -0,0 +1,32 @@
+/* qcam-linux.h -- Linux-specific routines for accessing QuickCam */
+
+/* Version 0.1, January 2, 1996 */
+/* Version 0.5, August 24, 1996 */
+
+
+/******************************************************************
+
+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.
+
+******************************************************************/
+
+
diff --git a/sys/qcam/qcam.h b/sys/qcam/qcam.h
new file mode 100644
index 000000000..35b2dc267
--- /dev/null
+++ b/sys/qcam/qcam.h
@@ -0,0 +1,134 @@
+/* qcam.h -- routines for accessing the Connectix QuickCam */
+
+/* Version 0.1, January 2, 1996 */
+/* Version 0.5, August 24, 1996 */
+/* Version 0.7, August 26, 1996 */
+
+
+/******************************************************************
+
+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.
+
+******************************************************************/
+
+#ifndef _QCAM_H
+#define _QCAM_H
+
+#define QC_VERSION "0.91"
+
+/* One from column A... */
+#define QC_NOTSET 0
+#define QC_UNIDIR 1
+#define QC_BIDIR 2
+#define QC_SERIAL 3
+
+/* ... and one from column B */
+#define QC_ANY 0x00
+#define QC_FORCE_UNIDIR 0x10
+#define QC_FORCE_BIDIR 0x20
+#define QC_FORCE_SERIAL 0x30
+/* in the port_mode member */
+
+#define QC_MODE_MASK 0x07
+#define QC_FORCE_MASK 0x70
+
+#define MAX_HEIGHT 243
+#define MAX_WIDTH 336
+
+struct qcam {
+ int width, height;
+ int bpp;
+ int mode;
+ int contrast, brightness, whitebal;
+ int port;
+ int port_mode;
+ int transfer_scale;
+ int top, left;
+ int fd; /* lock file descriptor
+ * It was, unfortunately, necessary to add this member to the
+ * struct qcam to conveniently implement POSIX fcntl-style locking.
+ * We need a seperate lock file for each struct qcam, for instance,
+ * if the same process (using qcam-lib) is accessing multiple
+ * QuickCams on (of course) multiple ports.
+ * - Dave Plonka (plonka@carroll1.cc.edu)
+ */
+};
+
+typedef unsigned char scanbuf;
+
+/* General QuickCam handling routines */
+
+int qc_getbrightness(const struct qcam *q);
+int qc_setbrightness(struct qcam *q, int val);
+int qc_getcontrast(const struct qcam *q);
+int qc_setcontrast(struct qcam *q, int val);
+int qc_getwhitebal(const struct qcam *q);
+int qc_setwhitebal(struct qcam *q, int val);
+void qc_getresolution(const struct qcam *q, int *x, int *y);
+int qc_setresolution(struct qcam *q, int x, int y);
+int qc_getbitdepth(const struct qcam *q);
+int qc_setbitdepth(struct qcam *q, int val);
+int qc_getheight(const struct qcam *q);
+int qc_setheight(struct qcam *q, int y);
+int qc_getwidth(const struct qcam *q);
+int qc_setwidth(struct qcam *q, int x);
+int qc_gettop(const struct qcam *q);
+int qc_settop(struct qcam *q, int val);
+int qc_getleft(const struct qcam *q);
+int qc_setleft(struct qcam *q, int val);
+int qc_gettransfer_scale(const struct qcam *q);
+int qc_settransfer_scale(struct qcam *q, int val);
+int qc_calibrate(struct qcam *q);
+int qc_forceunidir(struct qcam *q);
+void qc_dump(const struct qcam *q, char *file);
+
+struct qcam *qc_init(void);
+int qc_initfile(struct qcam *q, char *fname);
+int qc_open(struct qcam *q);
+int qc_close(struct qcam *q);
+int qc_detect(const struct qcam *q);
+void qc_reset(struct qcam *q);
+void qc_set(struct qcam *q);
+scanbuf *qc_scan(const struct qcam *q);
+scanbuf *qc_convertscan(struct qcam *q, scanbuf *scan);
+void qc_writepgm(const struct qcam *q, FILE *f, scanbuf *scan);
+void qc_wait(int val);
+
+/* OS/hardware specific routines */
+
+int read_lpstatus(const struct qcam *q);
+int read_lpcontrol(const struct qcam *q);
+int read_lpdata(const struct qcam *q);
+void write_lpdata(const struct qcam *q, int d);
+void write_lpcontrol(const struct qcam *q, int d);
+int enable_ports(const struct qcam *q);
+int disable_ports(const struct qcam *q);
+int qc_unlock(struct qcam *q);
+int qc_lock(struct qcam *q);
+void qc_wait(int val);
+int qc_probe(struct qcam *q);
+
+/* Image processing routines */
+int fixdark(const struct qcam *q, scanbuf *scan);
+int qc_edge_detect(const struct qcam *q, scanbuf *scan, int tolerance);
+
+#endif /*! _QCAM_H*/
diff --git a/sys/qcam/qcamip.h b/sys/qcam/qcamip.h
new file mode 100644
index 000000000..3bab6fafb
--- /dev/null
+++ b/sys/qcam/qcamip.h
@@ -0,0 +1,67 @@
+/*
+ * qcamip.h - Connectix QuickCam Image Processing routines
+ *
+ * Time-stamp: <02 Sep 96 11:19:27 HST edo@eosys.com>
+ *
+ * Version 0.2
+ */
+
+/******************************************************************
+
+Copyright (C) 1996 by Ed Orcutt Systems
+
+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, and/or distribute copies of the
+Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+1. The above copyright notice and this permission notice shall
+ be included in all copies or substantial portions of the
+ Software.
+
+2. Redistribution for profit requires the express, written
+ permission of the author.
+
+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 ED ORCUTT SYSTEMS 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.
+
+******************************************************************/
+
+#ifndef _QCAMIP_H
+#define _QCAMIP_H
+#include "qcam.h"
+
+/* Auto exposure modes */
+
+#define AE_ALL_AVG 0
+#define AE_CTR_AVG 1
+#define AE_STD_AVG 2
+
+/* Return value of image processing routines */
+
+#define QCIP_XPSR_OK 0
+#define QCIP_XPSR_RSCN 1
+#define QCIP_XPSR_ERR 2
+#define QCIP_XPSR_LUM_INVLD 3
+
+/* Prototypes for image processing routines */
+
+int qcip_autoexposure(struct qcam *q, scanbuf *scan);
+int qcip_set_luminance_target(struct qcam *q, int val);
+int qcip_set_luminance_tolerance(struct qcam *q, int val);
+int qcip_set_luminance_std_target(struct qcam *q, int val);
+int qcip_set_luminance_std_tolerance(struct qcam *q, int val);
+int qcip_set_autoexposure_mode(int val);
+void qcip_histogram(struct qcam *q, scanbuf *scan, int *histogram);
+void qcip_display_histogram(struct qcam *q, scanbuf *scan);
+
+#endif /*! _QCAMIP_H*/