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? 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