/* * Copyright 2004-2008, François Revol, . * Distributed under the terms of the MIT License. */ #include "CamDevice.h" #include "CamSensor.h" #include "CamDeframer.h" #include "CamDebug.h" #include "AddOn.h" #include #include //#define DEBUG_WRITE_DUMP //#define DEBUG_DISCARD_DATA //#define DEBUG_READ_DUMP //#define DEBUG_DISCARD_INPUT #undef B_WEBCAM_DECLARE_SENSOR #define B_WEBCAM_DECLARE_SENSOR(sensorclass,sensorname) \ extern "C" CamSensor *Instantiate##sensorclass(CamDevice *cam); #include "CamInternalSensors.h" #undef B_WEBCAM_DECLARE_SENSOR typedef CamSensor *(*SensorInstFunc)(CamDevice *cam); struct { const char *name; SensorInstFunc instfunc; } kSensorTable[] = { #define B_WEBCAM_DECLARE_SENSOR(sensorclass,sensorname) \ { #sensorname, &Instantiate##sensorclass }, #include "CamInternalSensors.h" { NULL, NULL }, }; #undef B_WEBCAM_DECLARE_SENSOR CamDevice::CamDevice(CamDeviceAddon &_addon, BUSBDevice* _device) : fInitStatus(B_NO_INIT), fSensor(NULL), fBulkIn(NULL), fIsoIn(NULL), fLastParameterChanges(0), fCamDeviceAddon(_addon), fDevice(_device), fSupportedDeviceIndex(-1), fChipIsBigEndian(false), fTransferEnabled(false), fLocker("WebcamDeviceLock") { // fill in the generic flavor _addon.WebCamAddOn()->FillDefaultFlavorInfo(&fFlavorInfo); // if we use id matching, cache the index to the list if (fCamDeviceAddon.SupportedDevices()) { fSupportedDeviceIndex = fCamDeviceAddon.Sniff(_device); fFlavorInfoNameStr = ""; fFlavorInfoNameStr << fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].vendor << " USB Webcam"; fFlavorInfoInfoStr = ""; fFlavorInfoInfoStr << fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].vendor; fFlavorInfoInfoStr << " (" << fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].product << ") USB Webcam"; fFlavorInfo.name = fFlavorInfoNameStr.String(); fFlavorInfo.info = fFlavorInfoInfoStr.String(); } #ifdef DEBUG_WRITE_DUMP fDumpFD = open("/boot/home/webcam.out", O_CREAT|O_RDWR, 0644); #endif #ifdef DEBUG_READ_DUMP fDumpFD = open("/boot/home/webcam.out", O_RDONLY, 0644); #endif fBufferLen = 1*B_PAGE_SIZE; fBuffer = (uint8 *)malloc(fBufferLen); } CamDevice::~CamDevice() { close(fDumpFD); free(fBuffer); if (fDeframer) delete fDeframer; } status_t CamDevice::InitCheck() { return fInitStatus; } bool CamDevice::Matches(BUSBDevice* _device) { return _device == fDevice; } BUSBDevice* CamDevice::GetDevice() { return fDevice; } void CamDevice::Unplugged() { fDevice = NULL; fBulkIn = NULL; fIsoIn = NULL; } bool CamDevice::IsPlugged() { return (fDevice != NULL); } const char * CamDevice::BrandName() { if (fCamDeviceAddon.SupportedDevices() && (fSupportedDeviceIndex > -1)) return fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].vendor; return ""; } const char * CamDevice::ModelName() { if (fCamDeviceAddon.SupportedDevices() && (fSupportedDeviceIndex > -1)) return fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].product; return ""; } bool CamDevice::SupportsBulk() { return false; } bool CamDevice::SupportsIsochronous() { return false; } status_t CamDevice::StartTransfer() { status_t err = B_OK; PRINT((CH "()" CT)); if (fTransferEnabled) return EALREADY; fPumpThread = spawn_thread(_DataPumpThread, "USB Webcam Data Pump", 50, this); if (fPumpThread < B_OK) return fPumpThread; if (fSensor) err = fSensor->StartTransfer(); if (err < B_OK) return err; fTransferEnabled = true; resume_thread(fPumpThread); PRINT((CH ": transfer enabled" CT)); return B_OK; } status_t CamDevice::StopTransfer() { status_t err = B_OK; PRINT((CH "()" CT)); if (!fTransferEnabled) return EALREADY; if (fSensor) err = fSensor->StopTransfer(); if (err < B_OK) return err; fTransferEnabled = false; // the thread itself might Lock() fLocker.Unlock(); wait_for_thread(fPumpThread, &err); fLocker.Lock(); return B_OK; } status_t CamDevice::SuggestVideoFrame(uint32 &width, uint32 &height) { if (Sensor()) { width = Sensor()->MaxWidth(); height = Sensor()->MaxHeight(); return B_OK; } return B_NO_INIT; } status_t CamDevice::AcceptVideoFrame(uint32 &width, uint32 &height) { status_t err = ENOSYS; if (Sensor()) err = Sensor()->AcceptVideoFrame(width, height); if (err < B_OK) return err; SetVideoFrame(BRect(0, 0, width - 1, height - 1)); return B_OK; } status_t CamDevice::SetVideoFrame(BRect frame) { fVideoFrame = frame; return B_OK; } status_t CamDevice::SetScale(float scale) { return B_OK; } status_t CamDevice::SetVideoParams(float brightness, float contrast, float hue, float red, float green, float blue) { return B_OK; } void CamDevice::AddParameters(BParameterGroup *group, int32 &index) { fFirstParameterID = index; } status_t CamDevice::GetParameterValue(int32 id, bigtime_t *last_change, void *value, size_t *size) { return B_BAD_VALUE; } status_t CamDevice::SetParameterValue(int32 id, bigtime_t when, const void *value, size_t size) { return B_BAD_VALUE; } size_t CamDevice::MinRawFrameSize() { return 0; } size_t CamDevice::MaxRawFrameSize() { return 0; } bool CamDevice::ValidateStartOfFrameTag(const uint8 *tag, size_t taglen) { return true; } bool CamDevice::ValidateEndOfFrameTag(const uint8 *tag, size_t taglen, size_t datalen) { return true; } status_t CamDevice::WaitFrame(bigtime_t timeout) { if (fDeframer) return WaitFrame(timeout); return EINVAL; } status_t CamDevice::GetFrameBitmap(BBitmap **bm, bigtime_t *stamp) { return EINVAL; } status_t CamDevice::FillFrameBuffer(BBuffer *buffer, bigtime_t *stamp) { return EINVAL; } bool CamDevice::Lock() { return fLocker.Lock(); } status_t CamDevice::PowerOnSensor(bool on) { return B_OK; } ssize_t CamDevice::WriteReg(uint16 address, uint8 *data, size_t count) { return ENOSYS; } ssize_t CamDevice::WriteReg8(uint16 address, uint8 data) { return WriteReg(address, &data, sizeof(uint8)); } ssize_t CamDevice::WriteReg16(uint16 address, uint16 data) { if (fChipIsBigEndian) data = B_HOST_TO_BENDIAN_INT16(data); else data = B_HOST_TO_LENDIAN_INT16(data); return WriteReg(address, (uint8 *)&data, sizeof(uint16)); } ssize_t CamDevice::ReadReg(uint16 address, uint8 *data, size_t count, bool cached) { return ENOSYS; } ssize_t CamDevice::OrReg8(uint16 address, uint8 data, uint8 mask) { uint8 value; if (ReadReg(address, &value, 1, true) < 1) return EIO; value &= mask; value |= data; return WriteReg8(address, value); } ssize_t CamDevice::AndReg8(uint16 address, uint8 data) { uint8 value; if (ReadReg(address, &value, 1, true) < 1) return EIO; value &= data; return WriteReg8(address, value); } /* status_t CamDevice::GetStatusIIC() { return ENOSYS; } */ /*status_t CamDevice::WaitReadyIIC() { return ENOSYS; } */ ssize_t CamDevice::WriteIIC(uint8 address, uint8 *data, size_t count) { return ENOSYS; } ssize_t CamDevice::WriteIIC8(uint8 address, uint8 data) { return WriteIIC(address, &data, 1); } ssize_t CamDevice::WriteIIC16(uint8 address, uint16 data) { if (Sensor() && Sensor()->IsBigEndian()) data = B_HOST_TO_BENDIAN_INT16(data); else data = B_HOST_TO_LENDIAN_INT16(data); return WriteIIC(address, (uint8 *)&data, 2); } ssize_t CamDevice::ReadIIC(uint8 address, uint8 *data) { //TODO: make it mode generic return ENOSYS; } ssize_t CamDevice::ReadIIC8(uint8 address, uint8 *data) { return ReadIIC(address, data); } ssize_t CamDevice::ReadIIC16(uint8 address, uint16 *data) { return ENOSYS; } status_t CamDevice::SetIICBitsMode(size_t bits) { return ENOSYS; } status_t CamDevice::ProbeSensor() { const usb_webcam_support_descriptor *devs; const usb_webcam_support_descriptor *dev = NULL; status_t err; int32 i; PRINT((CH ": probing sensors..." CT)); if (fCamDeviceAddon.SupportedDevices() == NULL) return B_ERROR; devs = fCamDeviceAddon.SupportedDevices(); for (i = 0; devs[i].vendor; i++) { if (GetDevice()->VendorID() != devs[i].desc.vendor) continue; if (GetDevice()->ProductID() != devs[i].desc.product) continue; dev = &devs[i]; break; } if (!dev) return ENODEV; if (!dev->sensors) // no usable sensor return ENOENT; BString sensors(dev->sensors); for (i = 0; i > -1 && i < sensors.Length(); ) { BString name; sensors.CopyInto(name, i, sensors.FindFirst(',', i) - i); PRINT((CH ": probing sensor '%s'..." CT, name.String())); fSensor = CreateSensor(name.String()); if (fSensor) { err = fSensor->Probe(); if (err >= B_OK) return B_OK; PRINT((CH ": sensor '%s' Probe: %s" CT, name.String(), strerror(err))); delete fSensor; fSensor = NULL; } i = sensors.FindFirst(',', i+1); if (i > - 1) i++; } return ENOENT; } CamSensor * CamDevice::CreateSensor(const char *name) { for (int32 i = 0; kSensorTable[i].name; i++) { if (!strcmp(kSensorTable[i].name, name)) return kSensorTable[i].instfunc(this); } PRINT((CH ": sensor '%s' not found" CT, name)); return NULL; } void CamDevice::SetDataInput(BDataIO *input) { fDataInput = input; } status_t CamDevice::DataPumpThread() { if (SupportsBulk()) { PRINT((CH ": using Bulk" CT)); while (fTransferEnabled) { ssize_t len = -1; BAutolock lock(fLocker); if (!lock.IsLocked()) break; if (!fBulkIn) break; #ifndef DEBUG_DISCARD_INPUT len = fBulkIn->BulkTransfer(fBuffer, fBufferLen); #endif //PRINT((CH ": got %ld bytes" CT, len)); #ifdef DEBUG_WRITE_DUMP write(fDumpFD, fBuffer, len); #endif #ifdef DEBUG_READ_DUMP if ((len = read(fDumpFD, fBuffer, fBufferLen)) < fBufferLen) lseek(fDumpFD, 0LL, SEEK_SET); #endif if (len <= 0) { PRINT((CH ": BulkIn: %s" CT, strerror(len))); break; } #ifndef DEBUG_DISCARD_DATA if (fDataInput) { fDataInput->Write(fBuffer, len); // else drop } #endif //snooze(2000); } } #ifdef SUPPORT_ISO else if (SupportsIsochronous()) { int numPacketDescriptors = 16; usb_iso_packet_descriptor packetDescriptors[numPacketDescriptors]; // Initialize packetDescriptor request lengths for (int i = 0; iIsochronousTransfer(fBuffer, fBufferLen, packetDescriptors, numPacketDescriptors); #endif //PRINT((CH ": got %d bytes" CT, len)); #ifdef DEBUG_WRITE_DUMP write(fDumpFD, fBuffer, len); #endif #ifdef DEBUG_READ_DUMP if ((len = read(fDumpFD, fBuffer, fBufferLen)) < fBufferLen) lseek(fDumpFD, 0LL, SEEK_SET); #endif if (len <= 0) { PRINT((CH ": IsoIn: %s" CT, strerror(len))); continue; } #ifndef DEBUG_DISCARD_DATA if (fDataInput) { int fBufferIndex = 0; for (int i = 0; i < numPacketDescriptors; i++) { int actual_length = ((usb_iso_packet_descriptor) packetDescriptors[i]).actual_length; if (actual_length > 0) { fDataInput->Write(&fBuffer[fBufferIndex], actual_length); } fBufferIndex += actual_length; } } #endif //snooze(2000); } } #endif else { PRINT((CH ": No supported transport." CT)); return B_UNSUPPORTED; } return B_OK; } int32 CamDevice::_DataPumpThread(void *_this) { CamDevice *dev = (CamDevice *)_this; return dev->DataPumpThread(); } void CamDevice::DumpRegs() { } status_t CamDevice::SendCommand(uint8 dir, uint8 request, uint16 value, uint16 index, uint16 length, void* data) { ssize_t ret; if (!GetDevice()) return ENODEV; if (length > GetDevice()->MaxEndpoint0PacketSize()) return EINVAL; ret = GetDevice()->ControlTransfer( USB_REQTYPE_VENDOR | USB_REQTYPE_INTERFACE_OUT | dir, request, value, index, length, data); return ret; } CamDeviceAddon::CamDeviceAddon(WebCamMediaAddOn* webcam) : fWebCamAddOn(webcam), fSupportedDevices(NULL) { } CamDeviceAddon::~CamDeviceAddon() { } const char * CamDeviceAddon::BrandName() { return ""; } status_t CamDeviceAddon::Sniff(BUSBDevice *device) { PRINT((CH ": Sniffing for %s" CT, BrandName())); if (!fSupportedDevices) return ENODEV; if (!device) return EINVAL; bool supported = false; for (uint32 i = 0; !supported && fSupportedDevices[i].vendor; i++) { if ((fSupportedDevices[i].desc.vendor != 0 && device->VendorID() != fSupportedDevices[i].desc.vendor) || (fSupportedDevices[i].desc.product != 0 && device->ProductID() != fSupportedDevices[i].desc.product)) continue; if ((fSupportedDevices[i].desc.dev_class == 0 || device->Class() == fSupportedDevices[i].desc.dev_class) && (fSupportedDevices[i].desc.dev_subclass == 0 || device->Subclass() == fSupportedDevices[i].desc.dev_subclass) && (fSupportedDevices[i].desc.dev_protocol == 0 || device->Protocol() == fSupportedDevices[i].desc.dev_protocol)) { supported = true; } #ifdef __HAIKU__ // we have to check all interfaces for matching class/subclass/protocol for (uint32 j = 0; !supported && j < device->CountConfigurations(); j++) { const BUSBConfiguration* cfg = device->ConfigurationAt(j); for (uint32 k = 0; !supported && k < cfg->CountInterfaces(); k++) { const BUSBInterface* intf = cfg->InterfaceAt(k); for (uint32 l = 0; !supported && l < intf->CountAlternates(); l++) { const BUSBInterface* alt = intf->AlternateAt(l); if ((fSupportedDevices[i].desc.dev_class == 0 || alt->Class() == fSupportedDevices[i].desc.dev_class) && (fSupportedDevices[i].desc.dev_subclass == 0 || alt->Subclass() == fSupportedDevices[i].desc.dev_subclass) && (fSupportedDevices[i].desc.dev_protocol == 0 || alt->Protocol() == fSupportedDevices[i].desc.dev_protocol)) { supported = true; } } } } #endif if (supported) return i; } return ENODEV; } CamDevice * CamDeviceAddon::Instantiate(CamRoster &roster, BUSBDevice *from) { return NULL; } void CamDeviceAddon::SetSupportedDevices(const usb_webcam_support_descriptor *devs) { fSupportedDevices = devs; }