forked from Minki/linux
[media] gspca - jeilinj: suppress workqueue
Signed-off-by: Patrice CHOTARD <patricechotard@free.fr> Signed-off-by: Jean-François Moine <moinejf@free.fr> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
ec2a095435
commit
c3d8692758
@ -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,102 +150,47 @@ static int jlj_start(struct gspca_dev *gspca_dev)
|
||||
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 gspca_dev *gspca_dev = &dev->gspca_dev;
|
||||
int blocks_left; /* 0x200-sized blocks remaining in current frame. */
|
||||
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;
|
||||
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 */
|
||||
@ -255,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}
|
||||
};
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
|
||||
/* 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 */
|
||||
@ -304,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;
|
||||
}
|
||||
|
||||
@ -325,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 -- */
|
||||
|
Loading…
Reference in New Issue
Block a user