diff options
Diffstat (limited to 'sys/qcam')
-rw-r--r-- | sys/qcam/Makefile.am | 10 | ||||
-rw-r--r-- | sys/qcam/dark.c | 253 | ||||
-rw-r--r-- | sys/qcam/exposure.c | 284 | ||||
-rw-r--r-- | sys/qcam/gstqcamsrc.c | 444 | ||||
-rw-r--r-- | sys/qcam/gstqcamsrc.h | 79 | ||||
-rw-r--r-- | sys/qcam/qcam-Linux.c | 246 | ||||
-rw-r--r-- | sys/qcam/qcam-Linux.h | 32 | ||||
-rw-r--r-- | sys/qcam/qcam-lib.c | 788 | ||||
-rw-r--r-- | sys/qcam/qcam-os.c | 246 | ||||
-rw-r--r-- | sys/qcam/qcam-os.h | 32 | ||||
-rw-r--r-- | sys/qcam/qcam.h | 134 | ||||
-rw-r--r-- | sys/qcam/qcamip.h | 67 |
12 files changed, 2615 insertions, 0 deletions
diff --git a/sys/qcam/Makefile.am b/sys/qcam/Makefile.am new file mode 100644 index 00000000..c6e30c6f --- /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 00000000..15dbbb4e --- /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 00000000..139aebf3 --- /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 00000000..ed1afe98 --- /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 00000000..c4608b6e --- /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 00000000..f6383e01 --- /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 00000000..46dcbe51 --- /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 00000000..f3b49fb0 --- /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 00000000..f6383e01 --- /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 00000000..46dcbe51 --- /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 00000000..35b2dc26 --- /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 00000000..3bab6faf --- /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*/ |