@@ -5,6 +5,7 @@
* download raw JPEG data.
*
* Copyright (C) 2009 Theodore Kilgore
+ * Copyright (C) 2011 Patrice Chotard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,7 +24,6 @@
#define MODULE_NAME "jeilinj"
-#include <linux/workqueue.h>
#include <linux/slab.h>
#include "gspca.h"
#include "jpeg.h"
@@ -38,25 +38,23 @@ MODULE_LICENSE("GPL");
/* Maximum transfer size to use. */
#define JEILINJ_MAX_TRANSFER 0x200
-
#define FRAME_HEADER_LEN 0x10
+#define FRAME_START 0xFFFFFFFF
/* Structure to hold all of our device specific stuff */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
+ int blocks_left;
const struct v4l2_pix_format *cap_mode;
/* Driver stuff */
- struct work_struct work_struct;
- struct workqueue_struct *work_thread;
u8 quality; /* image quality */
- u8 jpegqual; /* webcam quality */
u8 jpeg_hdr[JPEG_HDR_SZ];
};
- struct jlj_command {
- unsigned char instruction[2];
- unsigned char ack_wanted;
- };
+struct jlj_command {
+ unsigned char instruction[2];
+ unsigned char ack_wanted;
+};
/* AFAICT these cameras will only do 320x240. */
static struct v4l2_pix_format jlj_mode[] = {
@@ -107,6 +105,7 @@ static int jlj_start(struct gspca_dev *gspca_dev)
int i;
int retval = -1;
u8 response = 0xff;
+ struct sd *sd = (struct sd *) gspca_dev;
struct jlj_command start_commands[] = {
{{0x71, 0x81}, 0},
{{0x70, 0x05}, 0},
@@ -136,6 +135,8 @@ static int jlj_start(struct gspca_dev *gspca_dev)
{{0x71, 0x80}, 0},
{{0x70, 0x07}, 0}
};
+
+ sd->blocks_left = 0;
for (i = 0; i < ARRAY_SIZE(start_commands); i++) {
retval = jlj_write2(gspca_dev, start_commands[i].instruction);
if (retval < 0)
@@ -149,104 +150,47 @@ static int jlj_start(struct gspca_dev *gspca_dev)
return retval;
}
-static int jlj_stop(struct gspca_dev *gspca_dev)
-{
- int i;
- int retval;
- struct jlj_command stop_commands[] = {
- {{0x71, 0x00}, 0},
- {{0x70, 0x09}, 0},
- {{0x71, 0x80}, 0},
- {{0x70, 0x05}, 0}
- };
- for (i = 0; i < ARRAY_SIZE(stop_commands); i++) {
- retval = jlj_write2(gspca_dev, stop_commands[i].instruction);
- if (retval < 0)
- return retval;
- }
- return retval;
-}
-
-/* This function is called as a workqueue function and runs whenever the camera
- * is streaming data. Because it is a workqueue function it is allowed to sleep
- * so we can use synchronous USB calls. To avoid possible collisions with other
- * threads attempting to use the camera's USB interface the gspca usb_lock is
- * used when performing the one USB control operation inside the workqueue,
- * which tells the camera to close the stream. In practice the only thing
- * which needs to be protected against is the usb_set_interface call that
- * gspca makes during stream_off. Otherwise the camera doesn't provide any
- * controls that the user could try to change.
- */
-
-static void jlj_dostream(struct work_struct *work)
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, int len)
{
- struct sd *dev = container_of(work, struct sd, work_struct);
- struct gspca_dev *gspca_dev = &dev->gspca_dev;
- int blocks_left; /* 0x200-sized blocks remaining in current frame. */
- int size_in_blocks;
- int act_len;
+ struct sd *sd = (struct sd *) gspca_dev;
int packet_type;
- int ret;
- u8 *buffer;
+ u32 header_marker;
- buffer = kmalloc(JEILINJ_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
- if (!buffer) {
- err("Couldn't allocate USB buffer");
- goto quit_stream;
+ PDEBUG(D_STREAM, "Got %d bytes out of %d for Block 0",
+ len, JEILINJ_MAX_TRANSFER);
+ if (len != JEILINJ_MAX_TRANSFER) {
+ PDEBUG(D_PACK, "bad length");
+ goto discard;
}
- while (gspca_dev->present && gspca_dev->streaming) {
- /*
- * Now request data block 0. Line 0 reports the size
- * to download, in blocks of size 0x200, and also tells the
- * "actual" data size, in bytes, which seems best to ignore.
- */
- ret = usb_bulk_msg(gspca_dev->dev,
- usb_rcvbulkpipe(gspca_dev->dev, 0x82),
- buffer, JEILINJ_MAX_TRANSFER, &act_len,
- JEILINJ_DATA_TIMEOUT);
- PDEBUG(D_STREAM,
- "Got %d bytes out of %d for Block 0",
- act_len, JEILINJ_MAX_TRANSFER);
- if (ret < 0 || act_len < FRAME_HEADER_LEN)
- goto quit_stream;
- size_in_blocks = buffer[0x0a];
- blocks_left = buffer[0x0a] - 1;
- PDEBUG(D_STREAM, "blocks_left = 0x%x", blocks_left);
-
+ /* check if it's start of frame */
+ header_marker = ((u32 *)data)[0];
+ if (header_marker == FRAME_START) {
+ sd->blocks_left = data[0x0a] - 1;
+ PDEBUG(D_STREAM, "blocks_left = 0x%x", sd->blocks_left);
/* Start a new frame, and add the JPEG header, first thing */
gspca_frame_add(gspca_dev, FIRST_PACKET,
- dev->jpeg_hdr, JPEG_HDR_SZ);
+ sd->jpeg_hdr, JPEG_HDR_SZ);
/* Toss line 0 of data block 0, keep the rest. */
gspca_frame_add(gspca_dev, INTER_PACKET,
- buffer + FRAME_HEADER_LEN,
+ data + FRAME_HEADER_LEN,
JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN);
-
- while (blocks_left > 0) {
- if (!gspca_dev->present)
- goto quit_stream;
- ret = usb_bulk_msg(gspca_dev->dev,
- usb_rcvbulkpipe(gspca_dev->dev, 0x82),
- buffer, JEILINJ_MAX_TRANSFER, &act_len,
- JEILINJ_DATA_TIMEOUT);
- if (ret < 0 || act_len < JEILINJ_MAX_TRANSFER)
- goto quit_stream;
- PDEBUG(D_STREAM,
- "%d blocks remaining for frame", blocks_left);
- blocks_left -= 1;
- if (blocks_left == 0)
- packet_type = LAST_PACKET;
- else
- packet_type = INTER_PACKET;
- gspca_frame_add(gspca_dev, packet_type,
- buffer, JEILINJ_MAX_TRANSFER);
- }
- }
-quit_stream:
- mutex_lock(&gspca_dev->usb_lock);
- if (gspca_dev->present)
- jlj_stop(gspca_dev);
- mutex_unlock(&gspca_dev->usb_lock);
- kfree(buffer);
+ } else if (sd->blocks_left > 0) {
+ PDEBUG(D_STREAM, "%d blocks remaining for frame",
+ sd->blocks_left);
+ sd->blocks_left -= 1;
+ if (sd->blocks_left == 0)
+ packet_type = LAST_PACKET;
+ else
+ packet_type = INTER_PACKET;
+ gspca_frame_add(gspca_dev, packet_type,
+ data, JEILINJ_MAX_TRANSFER);
+ } else
+ goto discard;
+ return;
+discard:
+ /* Discard data until a new frame starts. */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
}
/* This function is called at probe time just before sd_init */
@@ -257,31 +201,50 @@ static int sd_config(struct gspca_dev *gspca_dev,
struct sd *dev = (struct sd *) gspca_dev;
dev->quality = 85;
- dev->jpegqual = 85;
PDEBUG(D_PROBE,
"JEILINJ camera detected"
" (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
cam->cam_mode = jlj_mode;
cam->nmodes = 1;
cam->bulk = 1;
- /* We don't use the buffer gspca allocates so make it small. */
- cam->bulk_size = 32;
- INIT_WORK(&dev->work_struct, jlj_dostream);
+ cam->bulk_nurbs = 1;
+ cam->bulk_size = JEILINJ_MAX_TRANSFER;
return 0;
}
-/* called on streamoff with alt==0 and on disconnect */
-/* the usb_lock is held at entry - restore on exit */
-static void sd_stop0(struct gspca_dev *gspca_dev)
+static void sd_stopN(struct gspca_dev *gspca_dev)
{
- struct sd *dev = (struct sd *) gspca_dev;
+ int i;
+ u8 *buf;
+ u8 stop_commands[][2] = {
+ {0x71, 0x00},
+ {0x70, 0x09},
+ {0x71, 0x80},
+ {0x70, 0x05}
+ };
+
+ for (;;) {
+ /* get the image remaining blocks */
+ usb_bulk_msg(gspca_dev->dev,
+ gspca_dev->urb[0]->pipe,
+ gspca_dev->urb[0]->transfer_buffer,
+ JEILINJ_MAX_TRANSFER, NULL,
+ JEILINJ_DATA_TIMEOUT);
- /* wait for the work queue to terminate */
- mutex_unlock(&gspca_dev->usb_lock);
- /* This waits for jlj_dostream to finish */
- destroy_workqueue(dev->work_thread);
- dev->work_thread = NULL;
- mutex_lock(&gspca_dev->usb_lock);
+ /* search for 0xff 0xd9 (EOF for JPEG) */
+ i = 0;
+ buf = gspca_dev->urb[0]->transfer_buffer;
+ while ((i < (JEILINJ_MAX_TRANSFER - 1)) &&
+ ((buf[i] != 0xff) || (buf[i+1] != 0xd9)))
+ i++;
+
+ if (i != (JEILINJ_MAX_TRANSFER - 1))
+ /* last remaining block found */
+ break;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(stop_commands); i++)
+ jlj_write2(gspca_dev, stop_commands[i]);
}
/* this function is called at probe and resume time */
@@ -306,10 +269,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
PDEBUG(D_ERR, "Start streaming command failed");
return ret;
}
- /* Start the workqueue function to do the streaming */
- dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
- queue_work(dev->work_thread, &dev->work_struct);
-
return 0;
}
@@ -327,7 +286,8 @@ static const struct sd_desc sd_desc = {
.config = sd_config,
.init = sd_init,
.start = sd_start,
- .stop0 = sd_stop0,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
};
/* -- device connect -- */
Signed-off-by: Patrice CHOTARD <patricechotard@free.fr> --- drivers/media/video/gspca/jeilinj.c | 194 ++++++++++++++--------------------- 1 files changed, 77 insertions(+), 117 deletions(-)