/* Camicro - Microscope camera viewer. * * This files add support for direct connexion with USB Moticam 3+. * * Copyright (C) 2019 Nicolas Schodet * * 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 * (at your option) 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. * * Contact : * Web: http://ni.fr.eu.org/ * Email: */ #include #include #include #include #include "device.h" #include "image.h" #include "utils.h" #define ID_VENDOR 0x232f #define ID_PRODUCT 0x0100 struct moticam_device { struct device device; libusb_device_handle *handle; double exposure_ms; double gain; size_t rawbuffer_size; uint8_t *rawbuffer; struct device_image image; }; static char * moticam_usb_poll(struct libusb_device_descriptor *desc); static struct device * moticam_usb_open(libusb_device *usb_device); const struct device_driver moticam_device_driver = { "Moticam USB cameras", &moticam_usb_poll, &moticam_usb_open, }; static const struct device_info_resolution moticam_device_info_resolution[] = { { 2048, 1536 }, { 1024, 768 }, { 512, 384 }, }; static const struct device_info moticam_device_info = { .exposure_min_ms = 1, .exposure_max_ms = 5000, .exposure_default_ms = 100, .exposure_step_ms = 1, .gain_min = 0.33, .gain_max = 42.66, .gain_default = 1, .gain_step = 1.0 / 24, .resolutions = 3, .resolution = moticam_device_info_resolution, }; static void moticam_control_vendor(struct device *device, uint16_t wValue, const uint8_t *data, int data_cnt) { struct moticam_device *mdev = (struct moticam_device *) device; int r = libusb_control_transfer(mdev->handle, LIBUSB_REQUEST_TYPE_VENDOR, 240, wValue, 0, (uint8_t *) data, data_cnt, 0); if (r < 0) utils_fatal("can not send vendor control: %s", libusb_strerror(r)); } static void moticam_control_vendor_w(struct device *device, uint16_t wValue, const uint16_t data0) { uint8_t data[] = { (uint8_t) (data0 >> 8), (uint8_t) data0 }; moticam_control_vendor(device, wValue, data, sizeof(data)); } static void moticam_control_reset(struct device *device) { moticam_control_vendor_w(device, 0xba00, 0x0000); moticam_control_vendor_w(device, 0xba00, 0x0001); } static void moticam_control_exposure(struct device *device, double exposure_ms) { int exposure_w = exposure_ms * 12.82; if (exposure_w < 0x000c) exposure_w = 0x000c; else if (exposure_w > 0xffff) exposure_w = 0xffff; moticam_control_vendor_w(device, 0xba09, exposure_w); } static void moticam_control_gain(struct device *device, double gain) { double gmin, gmax; int xmin, xmax; bool up = false; if (gain <= 1.34) { gmin = 0.33; gmax = 1.33; xmin = 0x08; xmax = 0x20; } else if (gain <= 2.68) { gmin = 1.42; gmax = 2.67; xmin = 0x51; xmax = 0x60; } else { gmin = 3; gmax = 42.67; xmin = 0x1; xmax = 0x78; up = true; } int x = (gain - gmin) / (gmax - gmin) * (xmax - xmin) + xmin; if (x < xmin) x = xmin; else if (x > xmax) x = xmax; if (up) x = (x << 8) | 0x60; moticam_control_vendor_w(device, 0xba2d, x); moticam_control_vendor_w(device, 0xba2b, x); moticam_control_vendor_w(device, 0xba2e, x); moticam_control_vendor_w(device, 0xba2c, x); } static void moticam_control_resolution(struct device *device, int width, int height) { static const uint8_t control_init[] = { 0x00, 0x14, 0x00, 0x20, 0x05, 0xff, 0x07, 0xff }; moticam_control_vendor(device, 0xba01, control_init, sizeof(control_init)); static const uint8_t control_512x384[] = { 0x00, 0x03, 0x00, 0x03 }; static const uint8_t control_1024x768[] = { 0x00, 0x11, 0x00, 0x11 }; static const uint8_t control_2048x1536[] = { 0x00, 0x00, 0x00, 0x00 }; switch (width) { case 512: assert(height == 384); moticam_control_vendor(device, 0xba22, control_512x384, sizeof(control_512x384)); break; case 1024: assert(height == 768); moticam_control_vendor(device, 0xba22, control_1024x768, sizeof(control_1024x768)); break; case 2048: assert(height == 1536); moticam_control_vendor(device, 0xba22, control_2048x1536, sizeof(control_2048x1536)); break; default: assert(0); } } static const struct device_info * moticam_get_info(struct device *device) { return &moticam_device_info; } static void moticam_set_exposure(struct device *device, double exposure_ms) { struct moticam_device *mdev = (struct moticam_device *) device; mdev->exposure_ms = exposure_ms; if (mdev->image.pixels) moticam_control_exposure(device, exposure_ms); } static void moticam_set_gain(struct device *device, double gain) { struct moticam_device *mdev = (struct moticam_device *) device; mdev->gain = gain; if (mdev->image.pixels) moticam_control_gain(device, gain); } static void moticam_set_resolution(struct device *device, int width, int height, int stride) { struct moticam_device *mdev = (struct moticam_device *) device; /* Release previous configuration. */ if (mdev->image.pixels) { free(mdev->rawbuffer); free(mdev->image.pixels); moticam_control_exposure(&mdev->device, 30.0); } /* Choose stride. */ if (!stride) stride = width * sizeof(uint32_t); assert(stride >= width * sizeof(uint32_t)); /* Store parameters. */ mdev->image.width = width; mdev->image.height = height; mdev->image.stride = stride; /* Prepare sizes and buffers, request an extra frame to read the zero * length packet. */ int image_size = width * height; int frame_size = 16384; mdev->rawbuffer_size = (image_size + frame_size) / frame_size * frame_size; mdev->rawbuffer = utils_malloc(mdev->rawbuffer_size); mdev->image.pixels = utils_malloc(stride * height); /* Configure camera. */ moticam_control_gain(&mdev->device, mdev->gain); moticam_control_resolution(&mdev->device, width, height); moticam_control_exposure(&mdev->device, mdev->exposure_ms); utils_delay_us(100000); } static bool moticam_read_raw(struct device *device) { struct moticam_device *mdev = (struct moticam_device *) device; int image_size = mdev->image.width * mdev->image.height; int transfered = 0; int r = libusb_bulk_transfer(mdev->handle, 0x83, mdev->rawbuffer, mdev->rawbuffer_size, &transfered, 0); if (r) utils_fatal("can not read data: %s", libusb_strerror(r)); if (transfered != image_size) { utils_warning("bad image size (%d), drop", transfered); return false; } else { return true; } } static const struct device_image * moticam_read(struct device *device) { struct moticam_device *mdev = (struct moticam_device *) device; assert(mdev->image.pixels); if (moticam_read_raw(device)) { image_bayer2argb(mdev->rawbuffer, mdev->image.pixels, mdev->image.width, mdev->image.height, mdev->image.stride); return &mdev->image; } else { return NULL; } } static void moticam_close(struct device *device) { struct moticam_device *mdev = (struct moticam_device *) device; moticam_control_exposure(device, 0.0); moticam_control_exposure(device, 0.0); moticam_control_exposure(device, 0.0); libusb_close(mdev->handle); if (mdev->image.pixels) { free(mdev->rawbuffer); free(mdev->image.pixels); } free(device); } static char * moticam_usb_poll(struct libusb_device_descriptor *desc) { if (desc->idVendor == ID_VENDOR && desc->idProduct == ID_PRODUCT) return strdup("Moticam 3+"); else return NULL; } static struct device * moticam_usb_open(libusb_device *usb_device) { /* Create context. */ struct moticam_device *mdev = utils_malloc(sizeof(struct moticam_device)); mdev->device.get_info = &moticam_get_info; mdev->device.set_exposure = &moticam_set_exposure; mdev->device.set_gain = &moticam_set_gain; mdev->device.set_resolution = &moticam_set_resolution; mdev->device.read = &moticam_read; mdev->device.close = &moticam_close; mdev->handle = NULL; mdev->exposure_ms = moticam_device_info.exposure_default_ms; mdev->gain = moticam_device_info.gain_default; mdev->rawbuffer_size = 0; mdev->rawbuffer = NULL; mdev->image.width = 0; mdev->image.height = 0; mdev->image.stride = 0; mdev->image.pixels = NULL; /* Open USB device. */ int r = libusb_open(usb_device, &mdev->handle); if (r) utils_fatal("can not open device: %s", libusb_strerror(r)); /* Initialize camera. */ moticam_control_reset(&mdev->device); moticam_control_exposure(&mdev->device, 30.0); /* Done. */ return &mdev->device; }