Table of Contents
1. The CHANGES document
ver 0.10
- Fixed the eof from bidirectional cam_read(). (the previous version
was based upon code from qcam-0.9pre6, which for some reason reads
six bytes at a time to get the eof - this works fine in normal mode
because the eof seems to come in the last of the six bytes, but when
zoomed in the eof comes in the first three bytes and the camera
interprets the three extra bytes as the beginning of the
port-direction change handshake). Mine now works for all zooms :)
- turned takepic into a useful program that outputs a sequence of
jpegs with a delay between each one - this can be used to make
mpegs for instance...
- VERY preliminary skeleton of a client-server scheme in the
./sock directory - server takes a picture, jpeg compresses it,
accepts a connection and sends it, client opens connection,
gets picture, writes it to a file. This is UGLY at the
moment, and there is no real protocol set yet (server simply
sends size of picture, then picture).
- Changed all accesses to the CONTROL register to keep the two junk
pins high - this seems to be necessary on some dodgy bi-dir ports.
ver 0.09
- Added eof to bidirectional cam_read() (used to just cam_reset()).
- Changed takepic.c to write a .jpg file using libjpeg. The final
server program will probably send jpegs, because of the amazing
compression (from 230400 bytes to 8300 or so).
ver 0.08
- Wrote a preliminary README file :)
- minor driver changes
- made the panning in vidcam actually work
- included a new program, takepic, which just throws out a ppm file
onto stdout (I have plans for this program :).
ver 0.07
- BIDIRECTIONAL PARALLEL PORT SUPPORT!! :)
I've finally got my act together and bought a bi-dir card for my
old 386 - it's now 3 times faster... a little shakey (like instead
of looking for EOF, I simply do a cam_reset() after getting the
picture *grin*), but it works. Bidir is defined in the Makefile
with -DBIDIR and is a compile-time choice (it's faster that way,
and who changes their parallel port all that often anyways?:)
- made cam_ioctl() actually check whether ioctl values are legal
ver 0.06
- Moved the definition of the Io_Base to the Makefile (so that the
user can define it before compiling
- moved the qcam struct to a struct called quickcam in cam.h, thus
letting applications have the same interface as the driver. The
video_buffer has therefore been moved out of the struct and into
a global pointer
- got rid of all the ioctls and made one single ioctl which takes
as its parameter a quickcam struct (because sizeof(unsigned long)
is equal to sizeof(void*), so I can use the argument to an ioctl
as a pointer to userspace). This is faster (only 1 cam_init) and
easier to use :)
- got rid of create_module() (what the hell is create_module() for
anyways? Someone want to explain this to me?
- made init_module a little safer.
ver 0.05
- tidied up the comments and code
- added some inline functions to make PC_Ack play more understandable
- created SetParameter() to make sending commands look better
- made init_module() check for errors :)
- tried to give vidcam some control over the cam (changing zoom,
panning, changing picture size etc)
ver 0.04
- added some useful programs! photo displays a picture on the screen
for preview (using svgalib), and lets you save it if you like, and
vidcam shows pictures on the screen as fast as it can get them from
the cam
- moved the address of the cam from the qcam struct to #defines, to
speed things up on my 386 (see explanation in cam.c)
- got rid of some paranoia hacks after checking the documentation
thoroughly (and making sure that they _aren't_ needed)
- added a Makefile to make life easier :)
ver 0.03
- added some more IOCTLS in cam.h (to change top/left/numv/numh)
- defined FRAME_SIZE to work with the cam qcam struct and zoom
- added Zoom to the qcam struct
- changed cam_read so that the zoom has some effect (different
parameter, use of FRAME_SIZE etc)
ver 0.02
- qcam internal struct has grown to include all the parameters for the
camera
- get_values() function added to initialize default values.
- fixed cam_write so that a write returns -1 (ie. fails :)
- added lots of IOCTLS to change the parameters.
- gave the camera device number 60 hard-coded instead of letting kernel
assign a number (60 is assigned for experimental devices).
2. Makefile included with camera package
# User Definable area
# This is the place to change the port number
# (0x378 is lp0, 0x278 is lp1)
# Also, comment out the -DBIDIR if you only have
# a uni-directional parallel port.
DATA = 0x378
MODE = -DBIDIR
####### Shouldn't need to change the rest of this, really...
####### If you want to try using a different compiler/linker (why?)
####### you can change the CC/LD definitions, and you'll probably have
####### to change the various flags too.
CC = gcc
LD = gcc
DEFS = -DDATA=$(DATA) $(MODE) -DMODVERSIONS -include /usr/include/linux/modversions.h -D__KERNEL__ -DMODULE
CFLAGS = -O3 -Wall -funsigned-char -ffast-math -fomit-frame-pointer -m386
LDFLAGS = -s
MODDIR = /lib/modules/`uname -r`/misc
MODMODE = 0644
VGALIB = -lvgagl -lvga
all: cam.o vidcam.o photo.o takepic.o camClient.class
cam.o: cam.c cam.h
$(CC) $(CFLAGS) $(DEFS) -c cam.c -o $@
install -o root -g root -m $(MODMODE) $@ $(MODDIR)
depmod -a
vidcam.o: vidcam.c
$(CC) $(CFLAGS) -c vidcam.c -o $@
$(LD) $(LDFLAGS) $(VGALIB) $@ -o vidcam
photo.o: photo.c
$(CC) $(CFLAGS) -c photo.c -o $@
$(LD) $(LDFLAGS) $(VGALIB) $@ -o photo
takepic.o: takepic.c
$(CC) $(CFLAGS) -c takepic.c -o $@
$(LD) $(LDFLAGS) -ljpeg $@ -o takepic
camClient.class: camClient.java
javac camClient.java
install:: all
rm -f /dev/cam
mknod /dev/cam c 60 0
clean:
rm -f *.o photo vidcam takepic camClient.class
3. README file distributed with software
(C) Simon Kapadia 1997 as part of a MSc Project
Please do not use this software. It probably won't work, and may break your
machine (and I am not responsible if anything at all happens bad, ok?)
"If it breaks, you get to keep the pieces..."
Driver for the Colour Quickcam under Linux 2.0.30
-------------------------------------------------
This driver contains the following files:
cam.c - The actual driver.
cam.h - General header file for cam applications.
takepic.c - Application that takes a series of pictures
with a pause between each one
photo.c - Uses SVGALIB to display pictures, can write
them to files if wanted.
vidcam.c - Uses SVGALIB to display a stream of pictures
as fast as the camera can send them.
Makefile - General Linux Makefile
INSTALLATION
First of all, change the two user-definable settings in the Makefile, the
port address and port status (see Makefile for instructions). Then type
make, and pray :) Provided nothing goes wrong and you have SVGALIB installed,
you should be fine. Finally, type make install to actually create the driver
file (/dev/cam). That's it (in theory).
USAGE
The driver is a kernel module, which means that it must be inserted into the
running kernel code before it can be used. If you are using kerneld, add the
following entry to /etc/conf.modules:
alias char-major-60 cam
and kerneld should sort everything out for you. If you do these things by
hand, type insmod cam to load the module and rmmod cam to remove it. Of
course, you have to have your kernel set up for module usage and modversions,
but for that you go to the module-HOWTO.
takepic is a simple program which just reads a picture from the cam and throws
it out to the standard output - redirect it to a file (eg takepic > test.ppm)
and use something like zgv to view it.
photo is somewhat more complicated, in that it will show you the picture on the
screen before you save it. Press any key to take a new picture, s to save the
picture (you will be prompted for a filename), and q to quit.
vidcam turns the qcam into a primitive kind of video camera - it will display
pictures on your screen as fast as the camera can send them. Use the z key to
increase the decimation (worse quality but faster pictures), and Z to decrease
it. The standard vi movement keys (h = left, j=down, k=up, l=right) will pan
the image to the limits of the camera.
4. Listing of cam.c - the main camera driver
/* Copyright (C) 1997 by Simon Kapadia as part of an MSc Project
* This is PRE PRE ALPHA software - it probably doesn't work at all, so
* don't use it :)
*/
#define FRAME_SIZE (((cam->Hlines*2)/cam->Zoom)*((cam->Vlines)/cam->Zoom)*3)
#define STATUS DATA+1
#define CONTROL DATA+2
/* The Qcam Commands (Section 5.1, page 44 Technical Specifications) */
#define SendVideoFrame 0x07
#define SetBrightness 0x0B
#define SetTop 0x0D
#define SetLeft 0x0F
#define SetVlines 0x11
#define SetHlines 0x13
#define SendVersion 0x17
#define LoadRAM 0x1B
#define SetBlack 0x1D
#define SetWhite 0x1F
#define SetHue 0x21
#define SetSaturation 0x23
#define SetContrast 0x25
#define SendStatus 0x29
#define SetSpeed 0x2D
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ioctl.h>
#include <linux/malloc.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include "cam.h"
unsigned char *video_buffer;
char in_use=0;
/* The cam is global atm, because I don't know quite what to do with it :)
* I'll probably get around to changing the frame buffer to a virtual one so
* that it doesn't constantly take up space, but I'm not sure of the
* implications of vmallocing and vfreeing before and after every frame
* (again for the speed on my 386)
*/
struct quickcam camera, *cam=&camera;
void get_default_values(void)
{
cam->Brightness=130;
cam->White=120;
cam->Black=128;
cam->Hue=128;
cam->Saturation=100;
cam->Contrast=104;
cam->Top=1;
cam->Left=7;
cam->Vlines=240;
cam->Hlines=160;
cam->Zoom=1;
}
static inline void Raise_PCAck(void)
{
outb(0xC6, CONTROL);
return;
}
static inline void Lower_PCAck(void)
{
outb(0xCe, CONTROL);
return;
}
#ifdef BIDIR
static inline void Raise_PCAck_bi(void)
{
outb(0xF6, CONTROL);
return;
}
static inline void Lower_PCAck_bi(void)
{
outb(0xFe, CONTROL);
return;
}
#endif
unsigned int handshake1(int value)
{
unsigned int i=20000, test;
switch (value){
case 0:
Raise_PCAck();
while(--i && (((test=inb(STATUS)) & 0x08) != 0x08));
return (i? test : -1);
case 1:
Lower_PCAck();
while(--i && (((test=inb(STATUS)) & 0x08) != 0x00));
return (i? test : -1);
#ifdef BIDIR
case 2:
Raise_PCAck_bi();
while(--i && (((test=inb(STATUS)) & 0x08) != 0x08));
return (i? test : -1);
case 3:
Lower_PCAck_bi();
while(--i && (((test=inb(STATUS)) & 0x08) != 0x00));
return (i? test : -1);
#endif
default:
return -1;
}
}
#ifdef BIDIR
static inline unsigned int handshake2(int value)
{
unsigned int i=20000, test=0;
if(value==0) {
Raise_PCAck_bi();
while(--i && (((test=inw(DATA)) & 0x01) != 0x01));
return (i? test : -1);
}
else if(value==1) {
Lower_PCAck_bi();
while(--i && (((test=inw(DATA)) & 0x01) != 0x00));
return (i? test : -1);
}
else return -1;
}
#endif
static inline void SendData(int Data)
{
/* 'By the Book' Send (Technical Specifications, page 15)
* Write some data to Cmd[0-7]
* Wait a short while
* Set PCAck
* Wait for the camera to set CamRdy1
* Read the top nibble which should echo DataHigh
* Reset PCAck
* Wait for the camera to reset CamRdy1
* Read the bottom nibble which should echo DataLow
*/
outb(Data,DATA);
udelay(5000);
if(handshake1(0)==-1) printk("CamRdy1 Timed out.0);
if((inb(STATUS) >> 4)!=(Data >> 4)) printk("Echo Error: DataHigh0);
if(handshake1(1)==-1) printk("CamRdy1 Timed out.0);
if((inb(STATUS) >> 4)!=(Data & 0x0F))printk("Echo Error: DataLow0);
return;
}
void SetParameter(int parameter, int value)
{
SendData(parameter);
SendData(value);
return;
}
int ReadParameter(void)
{
/* 'By the Book' Parameter Read (Technical Specifications, page 17)
* Set PCAck
* Wait for CamRdy1
* Read High Nibble
* Reset PCAck
* Wait for CamRdy1 to return
* Read Low Nibble
*/
int HighNibble, LowNibble;
if((HighNibble=handshake1(0))==-1) printk("CamRdy1 Timed out.0);
if((LowNibble=handshake1(1))==-1) printk("CamRdy1 Timed out.0);
return ((HighNibble & 0xF0) | (LowNibble >> 4));
}
void cam_reset(void)
{
outb(0xCF, CONTROL);
outb(0xCB, CONTROL);
udelay(5000);
outb(0xCF, CONTROL);
}
int cam_init(void)
{
/* 'By the book' Initialization (Technical Specifications, page 24)
* Reset the camera, Check Camera Version, Set the speed to 2.5 Mbits
* per second, Ignore the Ram-Table - we are not using compressed mode,
* Set the Black Level and wait for it to complete, Set the Saturation,
* Hue, Contrast, Exposure Time, White Balance, top of image, left border
* (first 14 columns are blank), Vertical and Horizontal Lines.
*/
int temp,i=32000;
cam_reset();
SendData(SendVersion);
if((temp=ReadParameter())!=0x10) {
printk("Error: Colour Qcam not Detected.0);
printk("Qcam returned Camera Version %x0,temp);
return -1;
}
if((temp=ReadParameter())!=0x04) {
printk("Error: Supported Connector not Detected.0);
printk("Qcam returned Connector Version %x0,temp);
return -1;
}
SetParameter(SetSpeed,2);
SetParameter(SetHue,cam->Hue);
SetParameter(SetSaturation,cam->Saturation);
SetParameter(SetContrast,cam->Contrast);
SetParameter(SetBlack,cam->Black);
do {
SendData(SendStatus);
temp=ReadParameter();
} while (--i && (temp & 0x40));
if(!i) {
printk("BlackBalance timed out0);
return -1;
}
SetParameter(SetBrightness,cam->Brightness);
SetParameter(SetWhite,cam->White);
SetParameter(SetTop,cam->Top);
SetParameter(SetLeft,cam->Left);
SetParameter(SetVlines,cam->Vlines);
SetParameter(SetHlines,cam->Hlines);
return 0;
}
int cam_open(struct inode *inode, struct file *file)
{
/* Initialise on opening the cam so that some defaults are set,
* Give it a video-buffer which is a static frame size for now,
* and increase the module usage count.
*/
if (in_use!=0) {
printk("Camera in use by another program0);
return -EBUSY;
}
in_use=1;
cam_init();
if(!video_buffer)
video_buffer=(unsigned char *) vmalloc(320*240*3);
MOD_INC_USE_COUNT;
return(0);
}
void cam_close(struct inode *inode, struct file *file)
{
if(video_buffer) vfree(video_buffer);
video_buffer=NULL;
cam_reset();
in_use=0;
MOD_DEC_USE_COUNT;
}
#ifdef BIDIR
int cam_read(struct inode *inode, struct file *file, char *buffer, int count)
{
unsigned int total=0, low1, low2, high1, high2,i,save=-1, byte=0, ok=0;
unsigned char buf[3];
if(count < 0) return -EINVAL;
SetParameter(SendVideoFrame,((0x19 | (cam->Zoom & 0x06))+1));
low1=low2=inb(STATUS) & 0xF0;
while ((inb(STATUS) & 0x80) != 0x80){
for(high1=0;high1<500;high1++) {
low1=inb(STATUS) & 0xF0;
if(low1!=low2) break;
low2=low1;
udelay(10000);
}
if (low2==low1){printk("500 failed0);cam_reset();return(0);}
}
outb(0xFE,CONTROL); // This should set bidir
udelay(50000);
if(handshake1(2)==-1) {printk("Muck up 10);cam_reset();return 0;}
if(handshake1(3)==-1) {printk("Muck up 20);cam_reset();return 0;}
for(total=0;total<FRAME_SIZE;total+=3) {
if((low1=handshake2(0))==-1) {printk("10);return total;}
high1 = (low1 >> 8) & 0xFF;
low1 = (low1 >> 1) & 0x7F;
if((low2=handshake2(1))==-1) {printk("20);return total;}
high2 = (low2 >> 8) & 0xFF;
low2 = (low2 >> 1) & 0x7F;
high1 ^= 0x80;
high2 ^= 0x80;
video_buffer[total]=((high1 & 0x08) << 4) | low1;
video_buffer[total+1]=(high1 & 0xF0) | (high2 >> 4);
video_buffer[total+2]=((high2 & 0x08) << 4) | low2;
}
do {
if((low1=handshake2(0))==-1) {printk("10);return total;}
high1 = (low1 >> 8) & 0xFF;
low1 = (low1 >> 1) & 0x7F;
if((low2=handshake2(1))==-1) {printk("20);return total;}
high2 = (low2 >> 8) & 0xFF;
low2 = (low2 >> 1) & 0x7F;
high1 ^= 0x80;
high2 ^= 0x80;
buf[0]=((high1 & 0x08) << 4) | low1;
buf[1]=(high1 & 0xF0) | (high2 >> 4);
buf[2]=((high2 & 0x08) << 4) | low2;
if (buf[0]==0x7E) continue;
if (buf[1]==0x7E) continue;
if (buf[2]==0x7E) continue;
for (i=0;i<3;i++, byte++) {
if (buf[i] == 0x0E) {
ok = 1;
save = byte;
}
else if ((buf[i] == 0x00) && (ok == 1)) {
if (save == byte-1) {
ok = 2;
save = byte;
}
else ok = 0;
}
else if ((buf[i] == 0x0F) && (ok == 2)) {
if (save == byte-1) ok = 3;
else ok = 0;
}
}
} while (ok != 3);
outb(0xCE,CONTROL); // This should set unidir
if (buffer) memcpy_tofs(buffer, video_buffer, FRAME_SIZE);
udelay(50000);
if(handshake1(0)==-1) {printk("Muck up 30);cam_reset();return total;}
if(handshake1(1)==-1) {printk("Muck up 40);cam_reset();return total;}
SendData(0x00);
return total;
}
#else
int cam_read(struct inode *inode, struct file *file, char *buffer, int count)
{
/* Send the SendVideoFrame command and its parameter according to the
* current zoom, set PCAck and wait a while for the cam to set CamRdy1.
* Then fill up the buffer by reading bytes.
* The cam should then send lots of 0x7E bytes, then 0x0E 0x00 0x0F
* (ie E0F = End 0f Frame). Because Nibble3 is inverted in hardware,
* 0x7E = 0xF6 (ie. 0111 = 1111 and 1110 = 0110), and likewise, 0x0E = 0x86,
* 0x00 = 0x88 and 0x0F = 0x87.
* Finally, send 0x00, which will error out if the echo is not the same.
* The picture is put into the buffer allocated by the calling program.
*/
int total=0, HighNibble, LowNibble, test;
if(count < 0) return -EINVAL;
SetParameter(SendVideoFrame,0x99|(cam->Zoom & 0x06));
handshake1(0);
for(total=0;total<FRAME_SIZE;total++) {
if((test=handshake1(0))==-1) {
printk("CamRdy1 Timed out. (1)0);
return(total);
}
HighNibble=(test & 0xF0) ^ 0x80;
if((test=handshake1(1))==-1) {
printk("CamRdy1 Timed out. (2)0);
return(total);
}
LowNibble=(test>>4) ^ 0x08;
video_buffer[total]=(HighNibble | LowNibble);
}
do {
test=ReadParameter();
} while (test==0xF6);
if(test!=0x86) printk("Bad E in EOF: %x0,test);
if((test=ReadParameter())!=0x88) printk("Bad O in EOF: %x0,test);
if((test=ReadParameter())!=0x87) printk("Bad F in EOF: %x0,test);
SendData(0x00);
memcpy_tofs(buffer, video_buffer, FRAME_SIZE);
return total;
}
#endif
int cam_write(struct inode *inode, struct file *file, const char *buffer, int count)
{
printk("Cannot WRITE to the quickcam!0);
return -EINVAL;
}
int cam_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct quickcam *new=vmalloc(sizeof(struct quickcam));
switch(cmd) {
case CHANGE_STATUS:
memcpy_fromfs(new,(struct quickcam *) arg,
sizeof(struct quickcam));
if(new->White>=0 && new->White<=255)
cam->White=new->White;
if(new->Black>=0 && new->Black<=255)
cam->Black=new->Black;
if(new->Contrast>=0 && new->Contrast<=255)
cam->Contrast=new->Contrast;
if(new->Hue>=0 && new->Hue<=255)
cam->Hue=new->Hue;
if(new->Brightness>=0 && new->Brightness<=255)
cam->Brightness=new->Brightness;
if(new->Saturation>=0 && new->Saturation<=255)
cam->Saturation=new->Saturation;
if(new->Vlines>=1 && new->Vlines<=250)
cam->Vlines=new->Vlines;
if(new->Hlines>=1 && new->Hlines<=340)
cam->Hlines=new->Hlines/2;
if(new->Top>=0 && new->Top<=240)
cam->Top=new->Top;
if(new->Left>=0 && new->Left<=170)
cam->Left=(6+(new->Left/2));
if(new->Zoom==1 || new->Zoom==2 || new->Zoom==4)
cam->Zoom=new->Zoom;
cam_init();
return 0;
default:
return -1;
}
}
static struct file_operations fops = {
NULL,
cam_read,
cam_write,
NULL,
NULL,
cam_ioctl,
NULL,
cam_open,
cam_close,
NULL
};
int init_module(void)
{
int devno;
if (check_region(DATA, 0x03) == -EBUSY) {
printk("Error: %x is in use0,DATA);
return -EBUSY;
}
devno=register_chrdev (60, "cam", &fops);
if (devno!=0) {
printk("Error: Failed to register Device0);
if(devno==-EBUSY) printk("Major device already allocated0);
return -ENODEV;
}
request_region(DATA,3,"cam");
get_default_values();
if (cam_init()!=0) return -ENODEV;
return 0;
}
void cleanup_module(void)
{
release_region(DATA,3);
unregister_chrdev(60,"cam");
}
5. Listing of cam.h
#ifndef __CAM_H_
#define __CAM_H_
#include <linux/ioctl.h>
#define CHANGE_STATUS _IOW (60, 1, int)
struct quickcam
{
int White;
int Black;
int Contrast;
int Hue;
int Brightness;
int Saturation;
int Vlines;
int Hlines;
int Top;
int Left;
int Zoom;
};
<applet code=camClient.class width=320 height=240>
</applet>
6. Listing of camClient.java
import java.io.*;
import java.awt.*;
import java.applet.Applet;
import java.net.*;
public class camClient extends Applet implements Runnable {
Thread animator = null;
Image image = null;
Image image2 = null;
URL serverAddress = null;
Socket camSocket = null;
BufferedReader is = null;
DataOutputStream os = null;
String responseLine;
public void connect() {
System.out.println("Running connect()");
try {
camSocket = new Socket(getCodeBase().getHost(),4444);
is = new BufferedReader(new InputStreamReader(camSocket.getInputStream()));
os = new DataOutputStream(camSocket.getOutputStream());
if (camSocket != null && os != null && is != null) {
os.writeBytes("HELO0);
}
responseLine=(is.readLine());
if(responseLine.compareTo("CAM")!=0)
System.err.println("Muck up");
} catch (java.net.UnknownHostException bad) {
System.err.println("bad host: " + bad);
} catch (java.io.IOException bad) {
System.err.println("io error: " + bad);
}
}
public void init() {
try {
responseLine = getCodeBase().getHost();
if (responseLine.compareTo("")==0)
responseLine=new String("loopback");
serverAddress=new URL(getCodeBase().toString()+responseLine+"1.jpg");
System.err.println(serverAddress.toString());
} catch (java.net.MalformedURLException bad) {
System.err.println("Bad url: " +bad);
}
connect();
}
public void run() {
while (true){
image2 = getImage(serverAddress);
try {
os.writeBytes("SEND0);
responseLine=(is.readLine());
if(responseLine.compareTo("DONE")!=0)
System.err.println("No DONE");
image2.flush();
image = createImage(image2.getSource());
repaint();
Thread.sleep(1000);
} catch (java.io.IOException bad) {
System.err.println("io error: " + bad);
} catch (java.lang.InterruptedException bad) {
System.err.println("Interrupt: " +bad);
}
}
}
public void start() {
System.out.println("Starting Client...");
if (animator == null) {
animator = new Thread(this);
animator.start();
}
}
public void paint(Graphics g){
if (image != null) {
g.drawImage(image,0,0,this);
}
}
public void stop() {
animator = null;
}
public void destroy() {
try {
camSocket.close();
} catch (java.io.IOException bad) {
System.err.println("io error: " + bad);
}
}
}
7. Listing of photo.c - a demo application
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <vga.h>
#include <vgagl.h>
#include "cam.h"
#define FRAMESIZE ((cam->Vlines/cam->Zoom) * (cam->Hlines/cam->Zoom)*3)
#define VIDEO_MODE G640x480x32K
struct quickcam cam1={120,128,104,128,150,130,240,320,1,1,1}, *cam=&cam1;
void
main()
{
int filedesc, c,x,y,a,b,filep;
GraphicsContext screen, hidden;
unsigned char buf[FRAMESIZE];
char string[]="P6020 240 255";
char filename[30]={''};
filedesc = open("/dev/cam", O_RDONLY );
if (filedesc<=0) exit(0);
ioctl(filedesc, CHANGE_STATUS, cam);
vga_init();
vga_setmode(VIDEO_MODE);
gl_setcontextvga(VIDEO_MODE);
gl_getcontext(&screen);
gl_setcontextvgavirtual(VIDEO_MODE);
gl_getcontext(&hidden);
gl_setrgbpalette();
c=0;
// for(n=0;n<10;n++)
while(1)
{
read(filedesc, buf, FRAMESIZE);
for(y=0;y < cam->Vlines ;y+=cam->Zoom)
{
for(x=0;x < cam->Hlines;x+=cam->Zoom)
{
for(b=0;b<cam->Zoom;b++)
{
for(a=0;a<cam->Zoom;a++)
gl_setpixelrgb(x+a,y+b, buf[c],buf[c+1],buf[c+2]);
}
c+=3;
}
}
c=0;
gl_copyscreen(&screen);
switch(getchar())
{
case 'q':
close(filedesc);
vga_setmode(TEXT);
exit(0);
case 's':
vga_setmode(TEXT);
printf("Input file name:0);
gets(filename);
filep=open((const char *)filename,O_WRONLY|O_CREAT,0644);
sprintf(string,"P60i %i 255",cam->Hlines,cam->Vlines);
write(filep,string,sizeof(string));
write(filep,buf,FRAMESIZE);
close(filep);
vga_setmode(VIDEO_MODE);
}
}
close(filedesc);
vga_setmode(TEXT);
exit(0);
}
8. Listing of server.pl
#!/usr/bin/perl -Tw
## Copyright (C) 1997 by Simon Kapadia as part of a Master's project.
## This is based on information and examples in the perl manual.
require 5.002; # ensure Perl 5.002 or above.
use strict; # masochism
use Carp; # for confess()
use Socket; # interface to socket(2)
use Sys::Syslog; # interface to syslog(3)
# set the PATH to a safe (known) value
BEGIN { $ENV{PATH} = '/usr/bin:/bin' }
# fork a child process to execute arbitrary code in a coderef
sub spawn
{
# get the code that the child will process.
my $coderef = shift;
# ensure that it's a coderef.
unless (@_ == 0 && $coderef && ref($coderef) eq 'CODE') {
confess "usage: spawn CODEREF";
}
# try to fork the child code
my $pid;
if (!defined($pid = fork)) {
syslog('warning', 'cannot fork: %m');
return;
} elsif ($pid) { # parent
syslog('info', 'start: %d', $pid);
return;
}
# this is the child process
open(STDIN, "<&Client") or die "can't dup client to stdin";
open(STDOUT, ">&Client") or die "can't dup client to stdout";
# execute the code and die
exit &$coderef();
}
## The port and protocol we're using - take the port from the 1st parameter
## or default to 4444 if no 1st parameter.
my $port = shift || 4444;
my $proto = getprotobyname('tcp');
if ($port =~ /D/) { $port = getservbyname($port, 'tcp') }
## Get a socket, set options, bind, listen and log that we have started;
## Die if anything fails.
socket(Server, PF_INET, SOCK_STREAM, $proto) or die "socket: $!";
setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1))
or die "setsockopt: $!";
bind(Server, sockaddr_in($port, INADDR_ANY)) or die "bind: $!";
listen(Server, SOMAXCONN) or die "listen: $!";
# start logging to the syslog.
my $ident = 'camserver';
my $logopt = 'ndelay';
my $facility = 'user';
my $priority = 'info';
openlog $ident, $logopt, $facility;
syslog $priority, 'started on port %d', $port;
my $waitedpid = 0;
my $paddr;
## sub to kill off 'die'ing children to avoid zombies.
sub REAPER {
$SIG{CHLD} = REAPER;
$waitedpid = wait;
syslog('info', 'exit: %s', $waitedpid.($? ? " with exit $?" : ''));
}
$SIG{CHLD} = REAPER;
for ( ; $paddr = accept(Client,Server); close Client)
{
my($port, $iaddr) = sockaddr_in($paddr);
my $name = gethostbyaddr($iaddr,AF_INET);
syslog('info', 'connect: %s (%s:%d)', $name, inet_ntoa($iaddr), $port);
## this code negotiates with the client
spawn sub {
die "No HELO" unless ((substr(<STDIN>, 0, -1)) eq "HELO");
syswrite(Client, "CAM0,4);
syslog('info', 'HELO from %s:%d', $name, $port);
while (substr(<STDIN>,0,-1) eq "SEND") {
syslog('info', 'SEND from %s:%d', $name, $port);
`/bin/takepic 1 0 $name`;
syslog('info', 'Took picture for %s:%d', $name, $port);
syswrite(Client, "DONE0, 5);
}
};
9. Listing of takepic.c - a demo application
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include "cam.h"
#include <jpeglib.h>
#include <stdlib.h>
#include <limits.h>
void usage(char *filename)
{
fprintf(stderr,"0sage:t%s numframes pause prefix0,filename);
fprintf(stderr,"0tnumframest number of pictures to take0);
fprintf(stderr,"tpausett time to wait between pictures (defaults to 0)0);
fprintf(stderr,"tprefixtt prefix for each filename (optional)0n");
exit(0);
}
int
main(int argc, char *argv[])
{
int filedesc,n,sleeptime;
long numpics;
char *prefix=NULL;
unsigned char buf[320*240*3],string[10];
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * outfile;
JSAMPROW row_pointer[1];
if (argc==1) usage(argv[0]);
else numpics=((n=strtol(argv[1], (char**)NULL, 10))!=LONG_MIN
&& n!=LONG_MAX) ? n : 0;
if (numpics==0) usage(argv[0]);
if(argv[2]==NULL) sleeptime=0;
else {
sleeptime=strtol(argv[2], (char**)NULL, 10);
if(sleeptime==LONG_MIN || sleeptime==LONG_MAX) usage(argv[0]);
}
if (argc>3) prefix=argv[3];
else prefix=NULL;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
cinfo.image_width = 320;
cinfo.image_height = 240;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
filedesc = open("/dev/cam", O_RDONLY );
if (filedesc<=0) exit(0);
for (n=1;n<=numpics;n++) {
if(prefix==NULL) sprintf(string,"%i.jpg",n);
else sprintf(string,"%s%i.jpg",prefix,n);
outfile = fopen(string, "wb");
jpeg_stdio_dest(&cinfo, outfile);
jpeg_start_compress(&cinfo, TRUE);
printf("Scanning frame %i0,n);
read(filedesc, buf, 320*240*3);
printf("Compressing frame %i0,n);
while(cinfo.next_scanline < cinfo.image_height) {
row_pointer[0] = &buf[cinfo.next_scanline * 960];
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
fclose(outfile);
if (sleeptime>0) {
printf("Sleeping for %i seconds0,sleeptime);
sleep(sleeptime);
}
}
close(filedesc);
jpeg_destroy_compress(&cinfo);
exit(0);
}
10. Listing of vidcam.c - a demo application
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <vga.h>
#include <vgagl.h>
#include "cam.h"
#define FRAMESIZE ((cam->Vlines/cam->Zoom) * (cam->Hlines/cam->Zoom)*3)
#define VIDEO_MODE G640x480x16M
struct quickcam cam1={120,128,104,128,150,90,240,320,0,0,2}, *cam=&cam1;
void
main()
{
int filedesc, c,x,y,a,b,yoff,xoff;
GraphicsContext screen, hidden;
unsigned char buf[FRAMESIZE];
filedesc = open("/dev/cam", O_RDONLY );
ioctl(filedesc, CHANGE_STATUS, cam);
vga_init();
vga_setmode(VIDEO_MODE);
gl_setcontextvga(VIDEO_MODE);
gl_getcontext(&screen);
gl_setcontextvgavirtual(VIDEO_MODE);
gl_getcontext(&hidden);
c=0,yoff=120,xoff=160;
while(1)
{
read(filedesc, buf, FRAMESIZE);
for(y=yoff;y < cam->Vlines+yoff ;y+=cam->Zoom)
{
for(x=xoff;x < cam->Hlines+xoff;x+=cam->Zoom)
{
for(a=0;a<cam->Zoom;a++)
{
for(b=0;b<cam->Zoom;b++)
gl_setpixelrgb(x+a,y+b,
buf[c],buf[c+1],buf[c+2]);
}
c+=3;
}
}
c=0;
gl_copyscreen(&screen);
switch(vga_getkey())
{
case 'q':
close(filedesc);
vga_setmode(TEXT);
exit(0);
case 'z':
if(cam->Zoom==1 || cam->Zoom==2) {
cam->Zoom*=2;
ioctl(filedesc, CHANGE_STATUS, cam);
}
break;
case 'Z':
if(cam->Zoom==2 || cam->Zoom==4) {
cam->Zoom/=2;
ioctl(filedesc, CHANGE_STATUS, cam);
}
break;
case 'h':
if(cam->Left>=21) {
cam->Left-=20;
ioctl(filedesc, CHANGE_STATUS, cam);
}
break;
case 'l':
if(cam->Left <= (319 - cam->Hlines)) {
cam->Left+=20;
ioctl(filedesc, CHANGE_STATUS, cam);
}
break;
case 'k':
if(cam->Top>=21) {
cam->Top-=20;
ioctl(filedesc, CHANGE_STATUS, cam);
}
break;
case 'j':
if(cam->Top <= (239 - cam->Vlines)) {
cam->Top+=20;
ioctl(filedesc, CHANGE_STATUS, cam);
}
break;
}
}
}