diff -Nru linux/drivers/char/c-qcam.c linux.work/drivers/char/c-qcam.c --- linux/drivers/char/c-qcam.c Mon Jun 19 21:59:42 2000 +++ linux.work/drivers/char/c-qcam.c Mon Jul 24 18:51:17 2000 @@ -21,6 +21,10 @@ * Fixed data format to BGR, added force_rgb parameter. Added missing * parport_unregister_driver() on module removal. * -- May 28, 2000 Claudio Matsuoka + * + * Added mmap support. VIDIOCMCAPTURE implemented. Changed brightness/ + * contrast/whiteness defaults to more reasonable values. + * -- Jul 3, 2000 Claudio Matsuoka */ #include @@ -35,8 +39,10 @@ #include #include #include -#include +#include #include +#include +#include struct qcam_device { struct video_device vdev; @@ -49,6 +55,7 @@ int top, left; unsigned int bidirectional; struct semaphore lock; + unsigned char *fbuf; }; /* cameras maximum */ @@ -64,7 +71,10 @@ #define QC_DECIMATION_2 2 #define QC_DECIMATION_4 4 -#define BANNER "Colour QuickCam for Video4Linux v0.05" +#define BANNER "Colour QuickCam for Video4Linux v0.06" + +#define MAX_FRAME_SIZE (320 * 240 * 3) +#define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval)) static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 }; static int probe = 2; @@ -358,28 +368,26 @@ #define BUFSZ 150 -static long qc_capture(struct qcam_device *q, char *buf, unsigned long len) +static long qc_capture(struct qcam_device *q, unsigned long len) { unsigned lines, pixelsperline, bitsperxfer; unsigned int is_bi_dir = q->bidirectional; - size_t wantlen, outptr = 0; + size_t wantlen; + char *outptr; char tmpbuf[BUFSZ]; - if (verify_area(VERIFY_WRITE, buf, len)) - return -EFAULT; - /* Wait for camera to become ready */ - for (;;) - { + for (;;) { int i = qcam_get(q, 41); + if (i == -1) { qc_setup(q); return -EIO; } if ((i & 0x80) == 0) break; - else - schedule(); + + schedule(); } if (qcam_set(q, 7, (q->mode | (is_bi_dir?1:0)) + 1)) @@ -389,8 +397,7 @@ pixelsperline = q->width; bitsperxfer = (is_bi_dir) ? 24 : 8; - if (is_bi_dir) - { + if (is_bi_dir) { /* Turn the port around */ parport_data_reverse(q->pport); mdelay(3); @@ -408,28 +415,20 @@ wantlen = lines * pixelsperline * 24 / 8; - while (wantlen) - { + outptr = q->fbuf; + while (wantlen) { size_t t, s; - s = (wantlen > BUFSZ)?BUFSZ:wantlen; - t = qcam_read_bytes(q, tmpbuf, s); - if (outptr < len) - { - size_t sz = len - outptr; - if (sz > t) sz = t; - if (__copy_to_user(buf+outptr, tmpbuf, sz)) - break; - outptr += sz; - } + s = (wantlen > BUFSZ) ? BUFSZ : wantlen; + t = qcam_read_bytes(q, outptr, s); + outptr += t; wantlen -= t; if (t < s) break; + if (current->need_resched) schedule(); } - len = outptr; - if (wantlen) { printk("qcam: short read.\n"); @@ -442,6 +441,7 @@ if (is_bi_dir) { int l; + do { l = qcam_read_bytes(q, tmpbuf, 3); if (current->need_resched) @@ -500,12 +500,35 @@ static int qcam_open(struct video_device *dev, int flags) { + struct qcam_device *qcam=(struct qcam_device *)dev; + int err = 0; + MOD_INC_USE_COUNT; - return 0; + down(&qcam->lock); + + err = -ENOMEM; + qcam->fbuf = rvmalloc(MAX_FRAME_SIZE); + if (!qcam->fbuf) + goto out; + + err = 0; + +out: + up(&qcam->lock); + if (err) + MOD_DEC_USE_COUNT; + + return err; } static void qcam_close(struct video_device *dev) { + struct qcam_device *qcam=(struct qcam_device *)dev; + + down(&qcam->lock); + rvfree(qcam->fbuf, MAX_FRAME_SIZE); + up(&qcam->lock); + MOD_DEC_USE_COUNT; } @@ -694,6 +717,26 @@ return -EFAULT; return 0; } + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + + if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) + return -EFAULT; + + if (vm.format != VIDEO_PALETTE_RGB24) + return -EINVAL; + + if (vm.frame != 0 && vm.frame != 1) + return -EINVAL; + + if (vm.width > 320 || vm.height > 240) + return -EINVAL; + + qc_capture (qcam, vm.width * vm.height * 3); + + return 0; + } case VIDIOCCAPTURE: return -EINVAL; case VIDIOCGFBUF: @@ -716,6 +759,33 @@ return 0; } +static int qcam_mmap(struct video_device *dev, const char *adr, + unsigned long size) +{ + struct qcam_device *qcam=(struct qcam_device *)dev; + unsigned long start = (unsigned long)adr; + unsigned long page, pos; + + if (size > (((2 * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) + return -EINVAL; + + pos = (unsigned long)qcam->fbuf; + + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + static long qcam_read(struct video_device *v, char *buf, unsigned long count, int noblock) { struct qcam_device *qcam=(struct qcam_device *)v; @@ -724,9 +794,11 @@ down(&qcam->lock); parport_claim_or_block(qcam->pdev); /* Probably should have a semaphore against multiple users */ - len = qc_capture(qcam, buf,count); + qc_capture(qcam, count); + len = count - __copy_to_user (buf, qcam->fbuf, count); parport_release(qcam->pdev); up(&qcam->lock); + return len; } @@ -742,7 +814,7 @@ qcam_write, NULL, qcam_ioctl, - NULL, + qcam_mmap, qcam_init_done, NULL, 0, @@ -779,8 +851,8 @@ q->width = q->ccd_width = 320; q->height = q->ccd_height = 240; q->mode = QC_MILLIONS | QC_DECIMATION_1; - q->contrast = 192; - q->brightness = 240; + q->contrast = 128; + q->brightness = 128; q->whitebal = 128; q->top = 1; q->left = 14;