/*
 * Copyright 2004-2008, François Revol, <revol@free.fr>.
 * Distributed under the terms of the MIT License.
 */
 
 
#include "CamDevice.h"
#include "CamSensor.h"
#include "CamDeframer.h"
#include "CamDebug.h"
#include "AddOn.h"
 
#include <OS.h>
#include <Autolock.h>
 
//#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 = (char *)fFlavorInfoNameStr.String();
		fFlavorInfo.info = (char *)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 "<unknown>";
}
 
 
const char *
CamDevice::ModelName()
{
	if (fCamDeviceAddon.SupportedDevices() && (fSupportedDeviceIndex > -1))
		return fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].product;
	return "<unknown>";
}
 
 
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; i<numPacketDescriptors; i++)
			packetDescriptors[i].request_length = 256;	
 
		int fullPackets = 0;
		int totalPackets = 0;
		while (fTransferEnabled) {
			ssize_t len = -1;
			BAutolock lock(fLocker);
			if (!lock.IsLocked())
				break;
			if (!fIsoIn)
				break;
#ifndef DEBUG_DISCARD_INPUT
			len = fIsoIn->IsochronousTransfer(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 "<unknown>";
}
 
 
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;
}

V570 The 'data' variable is assigned to itself.

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fDeframer, fDataInput, fFirstParameterID, fPumpThread, fDumpFD.

V570 The 'data' variable is assigned to itself.