Commit c3d86927 authored by Patrice Chotard's avatar Patrice Chotard Committed by Mauro Carvalho Chehab

[media] gspca - jeilinj: suppress workqueue

Signed-off-by: default avatarPatrice CHOTARD <patricechotard@free.fr>
Signed-off-by: default avatarJean-François Moine <moinejf@free.fr>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent ec2a0954
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* download raw JPEG data. * download raw JPEG data.
* *
* Copyright (C) 2009 Theodore Kilgore * Copyright (C) 2009 Theodore Kilgore
* Copyright (C) 2011 Patrice Chotard
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -23,7 +24,6 @@ ...@@ -23,7 +24,6 @@
#define MODULE_NAME "jeilinj" #define MODULE_NAME "jeilinj"
#include <linux/workqueue.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "gspca.h" #include "gspca.h"
#include "jpeg.h" #include "jpeg.h"
...@@ -38,25 +38,23 @@ MODULE_LICENSE("GPL"); ...@@ -38,25 +38,23 @@ MODULE_LICENSE("GPL");
/* Maximum transfer size to use. */ /* Maximum transfer size to use. */
#define JEILINJ_MAX_TRANSFER 0x200 #define JEILINJ_MAX_TRANSFER 0x200
#define FRAME_HEADER_LEN 0x10 #define FRAME_HEADER_LEN 0x10
#define FRAME_START 0xFFFFFFFF
/* Structure to hold all of our device specific stuff */ /* Structure to hold all of our device specific stuff */
struct sd { struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */ struct gspca_dev gspca_dev; /* !! must be the first item */
int blocks_left;
const struct v4l2_pix_format *cap_mode; const struct v4l2_pix_format *cap_mode;
/* Driver stuff */ /* Driver stuff */
struct work_struct work_struct;
struct workqueue_struct *work_thread;
u8 quality; /* image quality */ u8 quality; /* image quality */
u8 jpegqual; /* webcam quality */
u8 jpeg_hdr[JPEG_HDR_SZ]; u8 jpeg_hdr[JPEG_HDR_SZ];
}; };
struct jlj_command { struct jlj_command {
unsigned char instruction[2]; unsigned char instruction[2];
unsigned char ack_wanted; unsigned char ack_wanted;
}; };
/* AFAICT these cameras will only do 320x240. */ /* AFAICT these cameras will only do 320x240. */
static struct v4l2_pix_format jlj_mode[] = { static struct v4l2_pix_format jlj_mode[] = {
...@@ -107,6 +105,7 @@ static int jlj_start(struct gspca_dev *gspca_dev) ...@@ -107,6 +105,7 @@ static int jlj_start(struct gspca_dev *gspca_dev)
int i; int i;
int retval = -1; int retval = -1;
u8 response = 0xff; u8 response = 0xff;
struct sd *sd = (struct sd *) gspca_dev;
struct jlj_command start_commands[] = { struct jlj_command start_commands[] = {
{{0x71, 0x81}, 0}, {{0x71, 0x81}, 0},
{{0x70, 0x05}, 0}, {{0x70, 0x05}, 0},
...@@ -136,6 +135,8 @@ static int jlj_start(struct gspca_dev *gspca_dev) ...@@ -136,6 +135,8 @@ static int jlj_start(struct gspca_dev *gspca_dev)
{{0x71, 0x80}, 0}, {{0x71, 0x80}, 0},
{{0x70, 0x07}, 0} {{0x70, 0x07}, 0}
}; };
sd->blocks_left = 0;
for (i = 0; i < ARRAY_SIZE(start_commands); i++) { for (i = 0; i < ARRAY_SIZE(start_commands); i++) {
retval = jlj_write2(gspca_dev, start_commands[i].instruction); retval = jlj_write2(gspca_dev, start_commands[i].instruction);
if (retval < 0) if (retval < 0)
...@@ -149,102 +150,47 @@ static int jlj_start(struct gspca_dev *gspca_dev) ...@@ -149,102 +150,47 @@ static int jlj_start(struct gspca_dev *gspca_dev)
return retval; return retval;
} }
static int jlj_stop(struct gspca_dev *gspca_dev) static void sd_pkt_scan(struct gspca_dev *gspca_dev,
{ u8 *data, int len)
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)
{ {
struct sd *dev = container_of(work, struct sd, work_struct); struct sd *sd = (struct sd *) gspca_dev;
struct gspca_dev *gspca_dev = &dev->gspca_dev;
int blocks_left; /* 0x200-sized blocks remaining in current frame. */
int act_len;
int packet_type; int packet_type;
int ret; u32 header_marker;
u8 *buffer;
buffer = kmalloc(JEILINJ_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); PDEBUG(D_STREAM, "Got %d bytes out of %d for Block 0",
if (!buffer) { len, JEILINJ_MAX_TRANSFER);
err("Couldn't allocate USB buffer"); if (len != JEILINJ_MAX_TRANSFER) {
goto quit_stream; PDEBUG(D_PACK, "bad length");
goto discard;
} }
while (gspca_dev->present && gspca_dev->streaming) { /* check if it's start of frame */
/* header_marker = ((u32 *)data)[0];
* Now request data block 0. Line 0 reports the size if (header_marker == FRAME_START) {
* to download, in blocks of size 0x200, and also tells the sd->blocks_left = data[0x0a] - 1;
* "actual" data size, in bytes, which seems best to ignore. PDEBUG(D_STREAM, "blocks_left = 0x%x", sd->blocks_left);
*/
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;
blocks_left = buffer[0x0a] - 1;
PDEBUG(D_STREAM, "blocks_left = 0x%x", blocks_left);
/* Start a new frame, and add the JPEG header, first thing */ /* Start a new frame, and add the JPEG header, first thing */
gspca_frame_add(gspca_dev, FIRST_PACKET, 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. */ /* Toss line 0 of data block 0, keep the rest. */
gspca_frame_add(gspca_dev, INTER_PACKET, gspca_frame_add(gspca_dev, INTER_PACKET,
buffer + FRAME_HEADER_LEN, data + FRAME_HEADER_LEN,
JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN); JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN);
} else if (sd->blocks_left > 0) {
while (blocks_left > 0) { PDEBUG(D_STREAM, "%d blocks remaining for frame",
if (!gspca_dev->present) sd->blocks_left);
goto quit_stream; sd->blocks_left -= 1;
ret = usb_bulk_msg(gspca_dev->dev, if (sd->blocks_left == 0)
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; packet_type = LAST_PACKET;
else else
packet_type = INTER_PACKET; packet_type = INTER_PACKET;
gspca_frame_add(gspca_dev, packet_type, gspca_frame_add(gspca_dev, packet_type,
buffer, JEILINJ_MAX_TRANSFER); data, JEILINJ_MAX_TRANSFER);
} } else
} goto discard;
quit_stream: return;
mutex_lock(&gspca_dev->usb_lock); discard:
if (gspca_dev->present) /* Discard data until a new frame starts. */
jlj_stop(gspca_dev); gspca_dev->last_packet_type = DISCARD_PACKET;
mutex_unlock(&gspca_dev->usb_lock);
kfree(buffer);
} }
/* This function is called at probe time just before sd_init */ /* This function is called at probe time just before sd_init */
...@@ -255,31 +201,50 @@ static int sd_config(struct gspca_dev *gspca_dev, ...@@ -255,31 +201,50 @@ static int sd_config(struct gspca_dev *gspca_dev,
struct sd *dev = (struct sd *) gspca_dev; struct sd *dev = (struct sd *) gspca_dev;
dev->quality = 85; dev->quality = 85;
dev->jpegqual = 85;
PDEBUG(D_PROBE, PDEBUG(D_PROBE,
"JEILINJ camera detected" "JEILINJ camera detected"
" (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
cam->cam_mode = jlj_mode; cam->cam_mode = jlj_mode;
cam->nmodes = 1; cam->nmodes = 1;
cam->bulk = 1; cam->bulk = 1;
/* We don't use the buffer gspca allocates so make it small. */ cam->bulk_nurbs = 1;
cam->bulk_size = 32; cam->bulk_size = JEILINJ_MAX_TRANSFER;
INIT_WORK(&dev->work_struct, jlj_dostream);
return 0; return 0;
} }
/* called on streamoff with alt==0 and on disconnect */ static void sd_stopN(struct gspca_dev *gspca_dev)
/* the usb_lock is held at entry - restore on exit */
static void sd_stop0(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 */ /* search for 0xff 0xd9 (EOF for JPEG) */
mutex_unlock(&gspca_dev->usb_lock); i = 0;
/* This waits for jlj_dostream to finish */ buf = gspca_dev->urb[0]->transfer_buffer;
destroy_workqueue(dev->work_thread); while ((i < (JEILINJ_MAX_TRANSFER - 1)) &&
dev->work_thread = NULL; ((buf[i] != 0xff) || (buf[i+1] != 0xd9)))
mutex_lock(&gspca_dev->usb_lock); 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 */ /* this function is called at probe and resume time */
...@@ -304,10 +269,6 @@ static int sd_start(struct gspca_dev *gspca_dev) ...@@ -304,10 +269,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
PDEBUG(D_ERR, "Start streaming command failed"); PDEBUG(D_ERR, "Start streaming command failed");
return ret; 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; return 0;
} }
...@@ -325,7 +286,8 @@ static const struct sd_desc sd_desc = { ...@@ -325,7 +286,8 @@ static const struct sd_desc sd_desc = {
.config = sd_config, .config = sd_config,
.init = sd_init, .init = sd_init,
.start = sd_start, .start = sd_start,
.stop0 = sd_stop0, .stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
}; };
/* -- device connect -- */ /* -- device connect -- */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment