Re: [PATCH] gspca_amscope: AmScope MT1000 camera driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On 02/13/2014 01:32 PM, Todd Brandt wrote:
> Hello, I'm working on adding a new GSPCA driver for the AmScope MT1000
> series microscope camera and I'm having a bit of trouble. The patch below
> is my work-in-progress. I can succesfully get a video stream from the device
> at 1280x1024 (the default) and can set the exposure value, but I seem to be
> having trouble with data corruption while retrieving the image data. I'm sure
> I'll figure it out eventually but any suggestions would be appreciated.
> 
> I retrieve the frames as the camera sends them out through a bulk DMA
> transfer into the URB's transfer buffer. The data should come out as
> (1280 * 1024) each time but it's always a few kilobytes shy of that 
> number. The missing data appears to be at random points in the image
> and occurs at roughly 32kb intervals. The resulting image looks like it's
> been cut into horizontal ribbons with about 1/10 of the image missing at
> the bottom.
> 
> Here's what the image should look like (from windows):
> http://imgur.com/fFXyB9c
> 
> Here's how it currently looks with my WIP driver:
> http://imgur.com/sLDMKbx
> 
> I'm assuming this is because I'm losing some of the packets at the USB
> layer, but I don't really know where to look to fix it. I've tried tweaking
> the URB buffer to all different sizes and I find that anything less than
> 32Kb makes the ribbon effect worse, so I'm keeping it large. Has anyone seen
> this behavior before, and if so, is it a problem with my gspca config, the
> device, or something further down in the USB layer?

You could try submitting multiple urbs at once. IOW change bulk_nurbs to
something larger then you've now. As for the packet size, if you've windows
traces those should show you what size of transfers windows is using, try
mimicking those.

Once size which comes to mind to use is 5120 bytes, as that is the lowest
multiple of the sensor width, which also is a multiple of the bulk endpoint
maxpacketsize.

Last, you could check to see that you've all the sensor / bridge registers
set up as windows does. This could be the bridge and sensor chip loosing
sync, or some bridge fifo settings which are off.

Regards,

Hans


> 
> This is the lsusb -vv output:
> 
> Bus 001 Device 004: ID 0547:92a0 Anchor Chips, Inc. 
> Device Descriptor:
>   bLength                18
>   bDescriptorType         1
>   bcdUSB               2.00
>   bDeviceClass            0 (Defined at Interface level)
>   bDeviceSubClass         0 
>   bDeviceProtocol         0 
>   bMaxPacketSize0        64
>   idVendor           0x0547 Anchor Chips, Inc.
>   idProduct          0x92a0 
>   bcdDevice            0.00
>   iManufacturer           1 DO3THINK
>   iProduct                2 10M USBCam
>   iSerial                 0 
>   bNumConfigurations      1
>   Configuration Descriptor:
>     bLength                 9
>     bDescriptorType         2
>     wTotalLength           32
>     bNumInterfaces          1
>     bConfigurationValue     1
>     iConfiguration          0 
>     bmAttributes         0x80
>       (Bus Powered)
>     MaxPower              100mA
>     Interface Descriptor:
>       bLength                 9
>       bDescriptorType         4
>       bInterfaceNumber        0
>       bAlternateSetting       0
>       bNumEndpoints           2
>       bInterfaceClass       255 Vendor Specific Class
>       bInterfaceSubClass      0 
>       bInterfaceProtocol      0 
>       iInterface              0 
>       Endpoint Descriptor:
>         bLength                 7
>         bDescriptorType         5
>         bEndpointAddress     0x81  EP 1 IN
>         bmAttributes            3
>           Transfer Type            Interrupt
>           Synch Type               None
>           Usage Type               Data
>         wMaxPacketSize     0x0004  1x 4 bytes
>         bInterval               0
>       Endpoint Descriptor:
>         bLength                 7
>         bDescriptorType         5
>         bEndpointAddress     0x82  EP 2 IN
>         bmAttributes            2
>           Transfer Type            Bulk
>           Synch Type               None
>           Usage Type               Data
>         wMaxPacketSize     0x0200  1x 512 bytes
>         bInterval               0
> 
> 
> Signed-off-by: Todd Brandt <todd.eric.brandt@xxxxxxxxx>
> ---
>  drivers/media/usb/gspca/Kconfig   |   9 ++
>  drivers/media/usb/gspca/Makefile  |   2 +
>  drivers/media/usb/gspca/amscope.c | 317 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 328 insertions(+)
> 
> diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig
> index 4f0c6d5..320d225 100644
> --- a/drivers/media/usb/gspca/Kconfig
> +++ b/drivers/media/usb/gspca/Kconfig
> @@ -21,6 +21,15 @@ source "drivers/media/usb/gspca/m5602/Kconfig"
>  source "drivers/media/usb/gspca/stv06xx/Kconfig"
>  source "drivers/media/usb/gspca/gl860/Kconfig"
>  
> +config USB_GSPCA_AMSCOPE
> +	tristate "AmScope USB Camera Driver"
> +	depends on VIDEO_V4L2 && USB_GSPCA
> +	help
> +	  Say Y here if you want support for AmScope series cameras
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called gspca_amscope.
> +
>  config USB_GSPCA_BENQ
>  	tristate "Benq USB Camera Driver"
>  	depends on VIDEO_V4L2 && USB_GSPCA
> diff --git a/drivers/media/usb/gspca/Makefile b/drivers/media/usb/gspca/Makefile
> index 5855131..7fd7ddb 100644
> --- a/drivers/media/usb/gspca/Makefile
> +++ b/drivers/media/usb/gspca/Makefile
> @@ -1,4 +1,5 @@
>  obj-$(CONFIG_USB_GSPCA)          += gspca_main.o
> +obj-$(CONFIG_USB_GSPCA_AMSCOPE)  += gspca_amscope.o
>  obj-$(CONFIG_USB_GSPCA_BENQ)     += gspca_benq.o
>  obj-$(CONFIG_USB_GSPCA_CONEX)    += gspca_conex.o
>  obj-$(CONFIG_USB_GSPCA_CPIA1)    += gspca_cpia1.o
> @@ -45,6 +46,7 @@ obj-$(CONFIG_USB_GSPCA_XIRLINK_CIT) += gspca_xirlink_cit.o
>  obj-$(CONFIG_USB_GSPCA_ZC3XX)    += gspca_zc3xx.o
>  
>  gspca_main-objs     := gspca.o autogain_functions.o
> +gspca_amscope-objs  := amscope.o
>  gspca_benq-objs     := benq.o
>  gspca_conex-objs    := conex.o
>  gspca_cpia1-objs    := cpia1.o
> diff --git a/drivers/media/usb/gspca/amscope.c b/drivers/media/usb/gspca/amscope.c
> new file mode 100644
> index 0000000..04df71e
> --- /dev/null
> +++ b/drivers/media/usb/gspca/amscope.c
> @@ -0,0 +1,317 @@
> +/*
> + * AmScope series microscope camera driver
> + *
> + * Todd Brandt <todd.eric.brandt@xxxxxxxxx>
> + *
> + * 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
> + * the Free Software Foundation; either version 2 of the License, or
> + * any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + */
> +#define MODULE_NAME "amscope"
> +#define DEBUG 1
> +
> +#include "gspca.h"
> +
> +MODULE_AUTHOR("Todd Brandt <todd.eric.brandt@xxxxxxxxx>");
> +MODULE_DESCRIPTION("AmScope USB Camera Driver");
> +MODULE_LICENSE("GPL");
> +
> +#define BULK_SIZE (20 * 16384)
> +
> +/* specific webcam descriptor */
> +struct sd {
> +	struct gspca_dev gspca_dev;	/* !! must be the first item */
> +	unsigned int count;
> +};
> +
> +static const struct v4l2_pix_format vga_mode[] = {
> +/*
> +	{640, 480,
> +		V4L2_PIX_FMT_SGRBG8,
> +		V4L2_FIELD_NONE,
> +		.bytesperline = 640,
> +		.sizeimage = 640 * 480,
> +		.colorspace = V4L2_COLORSPACE_SRGB},
> +*/
> +	{1280, 1024,
> +		V4L2_PIX_FMT_SGRBG8,
> +		V4L2_FIELD_NONE,
> +		.bytesperline = 1280,
> +		.sizeimage = 1280 * 1024,
> +		.colorspace = V4L2_COLORSPACE_SRGB},
> +/*
> +	{3856, 2764,
> +		V4L2_PIX_FMT_GREY,
> +		V4L2_FIELD_NONE,
> +		.bytesperline = 3856,
> +		.sizeimage = 3856 * 2764,
> +		.colorspace = V4L2_COLORSPACE_SRGB},
> +*/
> +};
> +
> +static int usb_dev_write(struct gspca_dev *gspca_dev,
> +		u8 request, u16 value, u16 index, u16 length)
> +{
> +	int rc;
> +
> +	rc = usb_control_msg(gspca_dev->dev,
> +		usb_sndctrlpipe(gspca_dev->dev, 0), request,
> +		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +		value, index, gspca_dev->usb_buf, length, 500);
> +
> +	return rc;
> +}
> +
> +static int usb_dev_read(struct gspca_dev *gspca_dev,
> +		u8 request, u16 value, u16 index, u16 length)
> +{
> +	int rc;
> +
> +	memset(gspca_dev->usb_buf, 0, length);
> +	rc = usb_control_msg(gspca_dev->dev,
> +		usb_rcvctrlpipe(gspca_dev->dev, 0), request,
> +		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +		value, index, gspca_dev->usb_buf, length, 500);
> +
> +	return rc;
> +}
> +
> +/* called at probe time (after sd_probe) */
> +static int sd_config(struct gspca_dev *gspca_dev,
> +		     const struct usb_device_id *id)
> +{
> +	struct cam *cam = &gspca_dev->cam;
> +
> +	pr_devel("sd_config, Vendor=%04x, Product=%04x",
> +		id->idVendor, id->idProduct);
> +
> +	cam->cam_mode = vga_mode;
> +	cam->nmodes = ARRAY_SIZE(vga_mode);
> +	cam->no_urb_create = 0;
> +	cam->bulk_nurbs = 1;
> +	cam->bulk_size = BULK_SIZE;
> +	cam->bulk = 1;
> +
> +	return 0;
> +}
> +
> +/* called at probe (after sd_config), and resume time */
> +static int sd_init(struct gspca_dev *gspca_dev)
> +{
> +	return 0;
> +}
> +
> +static void setexposure(struct gspca_dev *gspca_dev, s32 val)
> +{
> +	if (val < 0)
> +		val = 0;
> +	else if (val > 0xf0)
> +		val = 0xf0;
> +
> +	/* exposure (0x1800 - 0x18f0) */
> +	usb_dev_write(gspca_dev, 0x9, 0x1800+val, 1, 0);
> +}
> +
> +/* -- start the camera -- */
> +static int sd_start(struct gspca_dev *gspca_dev)
> +{
> +	struct sd *sd = (struct sd *) gspca_dev;
> +#if 0
> +	__u8 *b = gspca_dev->usb_buf;
> +	__u8 data[160], data2[136];
> +	u16 idx;
> +#endif
> +	pr_devel("sd_start");
> +	sd->count = 0;
> +
> +	usb_dev_write(gspca_dev, 0x2, 0x1, 0xf, 0);
> +#if 0
> +	for (idx = 0; idx < 0xa0; idx += 2) {
> +		usb_dev_read(gspca_dev, 0x11, 0, idx, 3);
> +		data[idx] = b[0];
> +		data[idx + 1] = b[1];
> +	}
> +	usb_dev_read(gspca_dev, 0xa, 0x17a0, 0x16a0, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x16a0, 0x16a3, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x16a0, 0x16a4, 1);
> +#endif
> +	usb_dev_write(gspca_dev, 0x9, 0, 0, 0);
> +	usb_dev_read(gspca_dev, 0xa, 0x16a0, 0x16a4, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x1580, 0x14e4, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x1bbd, 0x14e8, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x1630, 0x14e6, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x1ead, 0x14ea, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x1563, 0x27e0, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x17a0, 0x13a0, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x17b0, 0x13a4, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x12a0, 0x14ec, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x1460, 0x14ee, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x13f1, 0x14e0, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x1cf4, 0x14e2, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x17a0, 0x16a4, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x17a1, 0x16a4, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x17a0, 0x16a0, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x17a2, 0x14a4, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x1788, 0x14a6, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x17a5, 0x14a0, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x17a1, 0x14a2, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x17aa, 0x14a8, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x17a2, 0x14aa, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x8778, 0x27ba, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x17a0, 0x16a4, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x16a0, 0x16a0, 1);
> +	usb_dev_read(gspca_dev, 0xa, 0x18c0, 0x27b2, 1);
> +#if 0
> +	usb_dev_read(gspca_dev, 0x11, 0, 0x24c0, 3);
> +	usb_dev_read(gspca_dev, 0x11, 0, 0x24cc, 3);
> +	usb_dev_read(gspca_dev, 0x11, 0, 0x24ce, 3);
> +	for (idx = 0; idx < 0x88; idx += 2) {
> +		usb_dev_read(gspca_dev, 0x11, 0, idx + 0x2820, 3);
> +		data2[idx] = b[0];
> +		data2[idx + 1] = b[1];
> +	}
> +#endif
> +	usb_dev_write(gspca_dev, 0x3, 0x1, 0xe, 0);
> +	return 0;
> +}
> +
> +static void sd_stopN(struct gspca_dev *gspca_dev)
> +{
> +	pr_devel("sd_stop");
> +	usb_dev_write(gspca_dev, 0x2, 0x0, 0xf, 0);
> +	usb_dev_write(gspca_dev, 0x3, 0x0, 0xe, 0);
> +}
> +
> +static void sd_pkt_scan(struct gspca_dev *gspca_dev,
> +			u8 *data, /* bulk URB data */
> +			int len)  /* bulk URB data length */
> +{
> +	struct sd *sd = (struct sd *) gspca_dev;
> +#if 0
> +	size_t sizeimage = vga_mode[gspca_dev->curr_mode].sizeimage;
> +	int packet_type;
> +
> +	pr_devel("packet: %d", len);
> +	if (sd->count <= 0) {
> +		packet_type = FIRST_PACKET;
> +		sd->count += len;
> +	} else if (sd->count + len >= sizeimage) {
> +		len = sizeimage - sd->count;
> +		packet_type = LAST_PACKET;
> +		sd->count = 0;
> +	} else {
> +		packet_type = INTER_PACKET;
> +		sd->count += len;
> +	}
> +#else
> +	int packet_type = INTER_PACKET;
> +
> +	if (len == BULK_SIZE) {
> +		if (sd->count <= 0)
> +			packet_type = FIRST_PACKET;
> +		sd->count++;
> +	} else {
> +		pr_devel("frame: %d, %d", sd->count, len);
> +		sd->count = 0;
> +		packet_type = LAST_PACKET;
> +	}
> +#endif
> +	gspca_frame_add(gspca_dev, packet_type, data, len);
> +}
> +
> +static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct gspca_dev *gspca_dev =
> +		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
> +
> +	pr_devel("sd_s_ctrl");
> +	gspca_dev->usb_err = 0;
> +
> +	if (!gspca_dev->streaming)
> +		return 0;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_EXPOSURE:
> +		pr_devel("set exposure %x", ctrl->val);
> +		setexposure(gspca_dev, ctrl->val);
> +		break;
> +	}
> +
> +	return gspca_dev->usb_err;
> +}
> +
> +static const struct v4l2_ctrl_ops sd_ctrl_ops = {
> +	.s_ctrl = sd_s_ctrl,
> +};
> +
> +/* called at probe (after sd_init) */
> +static int sd_init_controls(struct gspca_dev *gspca_dev)
> +{
> +	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
> +	pr_devel("sd_init_controls");
> +
> +	gspca_dev->vdev.ctrl_handler = hdl;
> +	v4l2_ctrl_handler_init(hdl, 2);
> +
> +	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
> +			V4L2_CID_EXPOSURE, 0, 0xf0, 1, 0x5a);
> +
> +	if (hdl->error) {
> +		pr_err("Could not initialize controls\n");
> +		return hdl->error;
> +	}
> +	return 0;
> +}
> +
> +/* sub-driver description */
> +static const struct sd_desc sd_desc = {
> +	.name = MODULE_NAME,
> +	.config = sd_config,
> +	.init = sd_init,
> +	.init_controls = sd_init_controls,
> +	.start = sd_start,
> +	.stopN = sd_stopN,
> +	.pkt_scan = sd_pkt_scan,
> +};
> +
> +/* supported device list */
> +static const struct usb_device_id device_table[] = {
> +	{USB_DEVICE(0x0547, 0x92a0)}, /* AmScope MT1000 */
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(usb, device_table);
> +
> +/* -- device connect -- */
> +static int sd_probe(struct usb_interface *intf,
> +		    const struct usb_device_id *id)
> +{
> +	pr_devel("sd_probe, alt=%d, vendor=%04x, product=%04x",
> +		intf->num_altsetting, id->idVendor, id->idProduct);
> +	return gspca_dev_probe(intf, id, &sd_desc,
> +		sizeof(struct sd), THIS_MODULE);
> +}
> +
> +static struct usb_driver sd_driver = {
> +	.name = MODULE_NAME,
> +	.id_table = device_table,
> +	.probe = sd_probe,
> +	.disconnect = gspca_disconnect,
> +#ifdef CONFIG_PM
> +	.suspend = gspca_suspend,
> +	.resume = gspca_resume,
> +#endif
> +};
> +
> +module_usb_driver(sd_driver);
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux