xref: /haiku/src/add-ons/media/media-add-ons/usb_webcam/CamDevice.cpp (revision 268f99dd7dc4bd7474a8bd2742d3f1ec1de6752a)
1 /*
2  * Copyright 2004-2008, François Revol, <revol@free.fr>.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "CamDevice.h"
8 #include "CamSensor.h"
9 #include "CamDeframer.h"
10 #include "CamDebug.h"
11 #include "AddOn.h"
12 
13 #include <OS.h>
14 #include <Autolock.h>
15 
16 //#define DEBUG_WRITE_DUMP
17 //#define DEBUG_DISCARD_DATA
18 //#define DEBUG_READ_DUMP
19 //#define DEBUG_DISCARD_INPUT
20 
21 #undef B_WEBCAM_DECLARE_SENSOR
22 #define B_WEBCAM_DECLARE_SENSOR(sensorclass,sensorname) \
23 extern "C" CamSensor *Instantiate##sensorclass(CamDevice *cam);
24 #include "CamInternalSensors.h"
25 #undef B_WEBCAM_DECLARE_SENSOR
26 typedef CamSensor *(*SensorInstFunc)(CamDevice *cam);
27 struct { const char *name; SensorInstFunc instfunc; } kSensorTable[] = {
28 #define B_WEBCAM_DECLARE_SENSOR(sensorclass,sensorname) \
29 { #sensorname, &Instantiate##sensorclass },
30 #include "CamInternalSensors.h"
31 { NULL, NULL },
32 };
33 #undef B_WEBCAM_DECLARE_SENSOR
34 
35 
CamDevice(CamDeviceAddon & _addon,BUSBDevice * _device)36 CamDevice::CamDevice(CamDeviceAddon &_addon, BUSBDevice* _device)
37 	: fInitStatus(B_NO_INIT),
38 	  fSensor(NULL),
39 	  fBulkIn(NULL),
40 	  fIsoIn(NULL),
41 	  fLastParameterChanges(0),
42 	  fCamDeviceAddon(_addon),
43 	  fDevice(_device),
44 	  fSupportedDeviceIndex(-1),
45 	  fChipIsBigEndian(false),
46 	  fTransferEnabled(false),
47 	  fLocker("WebcamDeviceLock")
48 {
49 	// fill in the generic flavor
50 	_addon.WebCamAddOn()->FillDefaultFlavorInfo(&fFlavorInfo);
51 	// if we use id matching, cache the index to the list
52 	if (fCamDeviceAddon.SupportedDevices()) {
53 		fSupportedDeviceIndex = fCamDeviceAddon.Sniff(_device);
54 		fFlavorInfoNameStr = "";
55 		fFlavorInfoNameStr << fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].vendor << " USB Webcam";
56 		fFlavorInfoInfoStr = "";
57 		fFlavorInfoInfoStr << fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].vendor;
58 		fFlavorInfoInfoStr << " (" << fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].product << ") USB Webcam";
59 		fFlavorInfo.name = fFlavorInfoNameStr.String();
60 		fFlavorInfo.info = fFlavorInfoInfoStr.String();
61 	}
62 #ifdef DEBUG_WRITE_DUMP
63 	fDumpFD = open("/boot/home/webcam.out", O_CREAT|O_RDWR, 0644);
64 #endif
65 #ifdef DEBUG_READ_DUMP
66 	fDumpFD = open("/boot/home/webcam.out", O_RDONLY, 0644);
67 #endif
68 	fBufferLen = 1*B_PAGE_SIZE;
69 	fBuffer = (uint8 *)malloc(fBufferLen);
70 }
71 
72 
~CamDevice()73 CamDevice::~CamDevice()
74 {
75 	close(fDumpFD);
76 	free(fBuffer);
77 	if (fDeframer)
78 		delete fDeframer;
79 }
80 
81 
82 status_t
InitCheck()83 CamDevice::InitCheck()
84 {
85 	return fInitStatus;
86 }
87 
88 
89 bool
Matches(BUSBDevice * _device)90 CamDevice::Matches(BUSBDevice* _device)
91 {
92 	return _device == fDevice;
93 }
94 
95 
96 BUSBDevice*
GetDevice()97 CamDevice::GetDevice()
98 {
99 	return fDevice;
100 }
101 
102 
103 void
Unplugged()104 CamDevice::Unplugged()
105 {
106 	fDevice = NULL;
107 	fBulkIn = NULL;
108 	fIsoIn = NULL;
109 }
110 
111 
112 bool
IsPlugged()113 CamDevice::IsPlugged()
114 {
115 	return (fDevice != NULL);
116 }
117 
118 
119 const char *
BrandName()120 CamDevice::BrandName()
121 {
122 	if (fCamDeviceAddon.SupportedDevices() && (fSupportedDeviceIndex > -1))
123 		return fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].vendor;
124 	return "<unknown>";
125 }
126 
127 
128 const char *
ModelName()129 CamDevice::ModelName()
130 {
131 	if (fCamDeviceAddon.SupportedDevices() && (fSupportedDeviceIndex > -1))
132 		return fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].product;
133 	return "<unknown>";
134 }
135 
136 
137 bool
SupportsBulk()138 CamDevice::SupportsBulk()
139 {
140 	return false;
141 }
142 
143 
144 bool
SupportsIsochronous()145 CamDevice::SupportsIsochronous()
146 {
147 	return false;
148 }
149 
150 
151 status_t
StartTransfer()152 CamDevice::StartTransfer()
153 {
154 	status_t err = B_OK;
155 	PRINT((CH "()" CT));
156 	if (fTransferEnabled)
157 		return EALREADY;
158 	fPumpThread = spawn_thread(_DataPumpThread, "USB Webcam Data Pump", 50,
159 		this);
160 	if (fPumpThread < B_OK)
161 		return fPumpThread;
162 	if (fSensor)
163 		err = fSensor->StartTransfer();
164 	if (err < B_OK)
165 		return err;
166 	fTransferEnabled = true;
167 	resume_thread(fPumpThread);
168 	PRINT((CH ": transfer enabled" CT));
169 	return B_OK;
170 }
171 
172 
173 status_t
StopTransfer()174 CamDevice::StopTransfer()
175 {
176 	status_t err = B_OK;
177 	PRINT((CH "()" CT));
178 	if (!fTransferEnabled)
179 		return EALREADY;
180 	if (fSensor)
181 		err = fSensor->StopTransfer();
182 	if (err < B_OK)
183 		return err;
184 	fTransferEnabled = false;
185 
186 	// the thread itself might Lock()
187 	fLocker.Unlock();
188 	wait_for_thread(fPumpThread, &err);
189 	fLocker.Lock();
190 
191 	return B_OK;
192 }
193 
194 
195 status_t
SuggestVideoFrame(uint32 & width,uint32 & height)196 CamDevice::SuggestVideoFrame(uint32 &width, uint32 &height)
197 {
198 	if (Sensor()) {
199 		width = Sensor()->MaxWidth();
200 		height = Sensor()->MaxHeight();
201 		return B_OK;
202 	}
203 	return B_NO_INIT;
204 }
205 
206 
207 status_t
AcceptVideoFrame(uint32 & width,uint32 & height)208 CamDevice::AcceptVideoFrame(uint32 &width, uint32 &height)
209 {
210 	status_t err = ENOSYS;
211 	if (Sensor())
212 		err = Sensor()->AcceptVideoFrame(width, height);
213 	if (err < B_OK)
214 		return err;
215 	SetVideoFrame(BRect(0, 0, width - 1, height - 1));
216 	return B_OK;
217 }
218 
219 
220 status_t
SetVideoFrame(BRect frame)221 CamDevice::SetVideoFrame(BRect frame)
222 {
223 	fVideoFrame = frame;
224 	return B_OK;
225 }
226 
227 
228 status_t
SetScale(float scale)229 CamDevice::SetScale(float scale)
230 {
231 	return B_OK;
232 }
233 
234 
235 status_t
SetVideoParams(float brightness,float contrast,float hue,float red,float green,float blue)236 CamDevice::SetVideoParams(float brightness, float contrast, float hue,
237 	float red, float green, float blue)
238 {
239 	return B_OK;
240 }
241 
242 
243 void
AddParameters(BParameterGroup * group,int32 & index)244 CamDevice::AddParameters(BParameterGroup *group, int32 &index)
245 {
246 	fFirstParameterID = index;
247 }
248 
249 
250 status_t
GetParameterValue(int32 id,bigtime_t * last_change,void * value,size_t * size)251 CamDevice::GetParameterValue(int32 id, bigtime_t *last_change, void *value,
252 	size_t *size)
253 {
254 	return B_BAD_VALUE;
255 }
256 
257 
258 status_t
SetParameterValue(int32 id,bigtime_t when,const void * value,size_t size)259 CamDevice::SetParameterValue(int32 id, bigtime_t when, const void *value,
260 	size_t size)
261 {
262 	return B_BAD_VALUE;
263 }
264 
265 
266 size_t
MinRawFrameSize()267 CamDevice::MinRawFrameSize()
268 {
269 	return 0;
270 }
271 
272 
273 size_t
MaxRawFrameSize()274 CamDevice::MaxRawFrameSize()
275 {
276 	return 0;
277 }
278 
279 
280 bool
ValidateStartOfFrameTag(const uint8 * tag,size_t taglen)281 CamDevice::ValidateStartOfFrameTag(const uint8 *tag, size_t taglen)
282 {
283 	return true;
284 }
285 
286 
287 bool
ValidateEndOfFrameTag(const uint8 * tag,size_t taglen,size_t datalen)288 CamDevice::ValidateEndOfFrameTag(const uint8 *tag, size_t taglen,
289 	size_t datalen)
290 {
291 	return true;
292 }
293 
294 
295 status_t
WaitFrame(bigtime_t timeout)296 CamDevice::WaitFrame(bigtime_t timeout)
297 {
298 	if (fDeframer)
299 		return WaitFrame(timeout);
300 	return EINVAL;
301 }
302 
303 
304 status_t
GetFrameBitmap(BBitmap ** bm,bigtime_t * stamp)305 CamDevice::GetFrameBitmap(BBitmap **bm, bigtime_t *stamp)
306 {
307 	return EINVAL;
308 }
309 
310 
311 status_t
FillFrameBuffer(BBuffer * buffer,bigtime_t * stamp)312 CamDevice::FillFrameBuffer(BBuffer *buffer, bigtime_t *stamp)
313 {
314 	return EINVAL;
315 }
316 
317 
318 bool
Lock()319 CamDevice::Lock()
320 {
321 	return fLocker.Lock();
322 }
323 
324 
325 status_t
PowerOnSensor(bool on)326 CamDevice::PowerOnSensor(bool on)
327 {
328 	return B_OK;
329 }
330 
331 
332 ssize_t
WriteReg(uint16 address,uint8 * data,size_t count)333 CamDevice::WriteReg(uint16 address, uint8 *data, size_t count)
334 {
335 	return ENOSYS;
336 }
337 
338 
339 ssize_t
WriteReg8(uint16 address,uint8 data)340 CamDevice::WriteReg8(uint16 address, uint8 data)
341 {
342 	return WriteReg(address, &data, sizeof(uint8));
343 }
344 
345 
346 ssize_t
WriteReg16(uint16 address,uint16 data)347 CamDevice::WriteReg16(uint16 address, uint16 data)
348 {
349 	if (fChipIsBigEndian)
350 		data = B_HOST_TO_BENDIAN_INT16(data);
351 	else
352 		data = B_HOST_TO_LENDIAN_INT16(data);
353 	return WriteReg(address, (uint8 *)&data, sizeof(uint16));
354 }
355 
356 
357 ssize_t
ReadReg(uint16 address,uint8 * data,size_t count,bool cached)358 CamDevice::ReadReg(uint16 address, uint8 *data, size_t count, bool cached)
359 {
360 	return ENOSYS;
361 }
362 
363 
364 ssize_t
OrReg8(uint16 address,uint8 data,uint8 mask)365 CamDevice::OrReg8(uint16 address, uint8 data, uint8 mask)
366 {
367 	uint8 value;
368 	if (ReadReg(address, &value, 1, true) < 1)
369 		return EIO;
370 	value &= mask;
371 	value |= data;
372 	return WriteReg8(address, value);
373 }
374 
375 
376 ssize_t
AndReg8(uint16 address,uint8 data)377 CamDevice::AndReg8(uint16 address, uint8 data)
378 {
379 	uint8 value;
380 	if (ReadReg(address, &value, 1, true) < 1)
381 		return EIO;
382 	value &= data;
383 	return WriteReg8(address, value);
384 }
385 
386 
387 /*
388 status_t
389 CamDevice::GetStatusIIC()
390 {
391 	return ENOSYS;
392 }
393 */
394 
395 /*status_t
396 CamDevice::WaitReadyIIC()
397 {
398 	return ENOSYS;
399 }
400 */
401 
402 ssize_t
WriteIIC(uint8 address,uint8 * data,size_t count)403 CamDevice::WriteIIC(uint8 address, uint8 *data, size_t count)
404 {
405 	return ENOSYS;
406 }
407 
408 
409 ssize_t
WriteIIC8(uint8 address,uint8 data)410 CamDevice::WriteIIC8(uint8 address, uint8 data)
411 {
412 	return WriteIIC(address, &data, 1);
413 }
414 
415 
416 ssize_t
WriteIIC16(uint8 address,uint16 data)417 CamDevice::WriteIIC16(uint8 address, uint16 data)
418 {
419 	if (Sensor() && Sensor()->IsBigEndian())
420 		data = B_HOST_TO_BENDIAN_INT16(data);
421 	else
422 		data = B_HOST_TO_LENDIAN_INT16(data);
423 	return WriteIIC(address, (uint8 *)&data, 2);
424 }
425 
426 
427 ssize_t
ReadIIC(uint8 address,uint8 * data)428 CamDevice::ReadIIC(uint8 address, uint8 *data)
429 {
430 	//TODO: make it mode generic
431 	return ENOSYS;
432 }
433 
434 
435 ssize_t
ReadIIC8(uint8 address,uint8 * data)436 CamDevice::ReadIIC8(uint8 address, uint8 *data)
437 {
438 	return ReadIIC(address, data);
439 }
440 
441 
442 ssize_t
ReadIIC16(uint8 address,uint16 * data)443 CamDevice::ReadIIC16(uint8 address, uint16 *data)
444 {
445 	return ENOSYS;
446 }
447 
448 
449 status_t
SetIICBitsMode(size_t bits)450 CamDevice::SetIICBitsMode(size_t bits)
451 {
452 	return ENOSYS;
453 }
454 
455 
456 status_t
ProbeSensor()457 CamDevice::ProbeSensor()
458 {
459 	const usb_webcam_support_descriptor *devs;
460 	const usb_webcam_support_descriptor *dev = NULL;
461 	status_t err;
462 	int32 i;
463 
464 	PRINT((CH ": probing sensors..." CT));
465 	if (fCamDeviceAddon.SupportedDevices() == NULL)
466 		return B_ERROR;
467 	devs = fCamDeviceAddon.SupportedDevices();
468 	for (i = 0; devs[i].vendor; i++) {
469 		if (GetDevice()->VendorID() != devs[i].desc.vendor)
470 			continue;
471 		if (GetDevice()->ProductID() != devs[i].desc.product)
472 			continue;
473 		dev = &devs[i];
474 		break;
475 	}
476 	if (!dev)
477 		return ENODEV;
478 	if (!dev->sensors) // no usable sensor
479 		return ENOENT;
480 	BString sensors(dev->sensors);
481 	for (i = 0; i > -1 && i < sensors.Length(); ) {
482 		BString name;
483 		sensors.CopyInto(name, i, sensors.FindFirst(',', i) - i);
484 		PRINT((CH ": probing sensor '%s'..." CT, name.String()));
485 
486 		fSensor = CreateSensor(name.String());
487 		if (fSensor) {
488 			err = fSensor->Probe();
489 			if (err >= B_OK)
490 				return B_OK;
491 
492 			PRINT((CH ": sensor '%s' Probe: %s" CT, name.String(),
493 				strerror(err)));
494 
495 			delete fSensor;
496 			fSensor = NULL;
497 		}
498 
499 		i = sensors.FindFirst(',', i+1);
500 		if (i > - 1)
501 			i++;
502 	}
503 	return ENOENT;
504 }
505 
506 
507 CamSensor *
CreateSensor(const char * name)508 CamDevice::CreateSensor(const char *name)
509 {
510 	for (int32 i = 0; kSensorTable[i].name; i++) {
511 		if (!strcmp(kSensorTable[i].name, name))
512 			return kSensorTable[i].instfunc(this);
513 	}
514 	PRINT((CH ": sensor '%s' not found" CT, name));
515 	return NULL;
516 }
517 
518 
519 void
SetDataInput(BDataIO * input)520 CamDevice::SetDataInput(BDataIO *input)
521 {
522 	fDataInput = input;
523 }
524 
525 
526 status_t
DataPumpThread()527 CamDevice::DataPumpThread()
528 {
529 	if (SupportsBulk()) {
530 		PRINT((CH ": using Bulk" CT));
531 		while (fTransferEnabled) {
532 			ssize_t len = -1;
533 			BAutolock lock(fLocker);
534 			if (!lock.IsLocked())
535 				break;
536 			if (!fBulkIn)
537 				break;
538 #ifndef DEBUG_DISCARD_INPUT
539 			len = fBulkIn->BulkTransfer(fBuffer, fBufferLen);
540 #endif
541 
542 			//PRINT((CH ": got %ld bytes" CT, len));
543 #ifdef DEBUG_WRITE_DUMP
544 			write(fDumpFD, fBuffer, len);
545 #endif
546 #ifdef DEBUG_READ_DUMP
547 			if ((len = read(fDumpFD, fBuffer, fBufferLen)) < fBufferLen)
548 				lseek(fDumpFD, 0LL, SEEK_SET);
549 #endif
550 
551 			if (len <= 0) {
552 				PRINT((CH ": BulkIn: %s" CT, strerror(len)));
553 				break;
554 			}
555 
556 #ifndef DEBUG_DISCARD_DATA
557 			if (fDataInput) {
558 				fDataInput->Write(fBuffer, len);
559 				// else drop
560 			}
561 #endif
562 			//snooze(2000);
563 		}
564 	}
565 #ifdef SUPPORT_ISO
566 	else if (SupportsIsochronous()) {
567 		int numPacketDescriptors = 16;
568 		usb_iso_packet_descriptor packetDescriptors[numPacketDescriptors];
569 
570 		// Initialize packetDescriptor request lengths
571 		for (int i = 0; i<numPacketDescriptors; i++)
572 			packetDescriptors[i].request_length = 256;
573 
574 		while (fTransferEnabled) {
575 			ssize_t len = -1;
576 			BAutolock lock(fLocker);
577 			if (!lock.IsLocked())
578 				break;
579 			if (!fIsoIn)
580 				break;
581 #ifndef DEBUG_DISCARD_INPUT
582 			len = fIsoIn->IsochronousTransfer(fBuffer, fBufferLen, packetDescriptors,
583 				numPacketDescriptors);
584 #endif
585 
586 			//PRINT((CH ": got %d bytes" CT, len));
587 #ifdef DEBUG_WRITE_DUMP
588 			write(fDumpFD, fBuffer, len);
589 #endif
590 #ifdef DEBUG_READ_DUMP
591 			if ((len = read(fDumpFD, fBuffer, fBufferLen)) < fBufferLen)
592 				lseek(fDumpFD, 0LL, SEEK_SET);
593 #endif
594 
595 			if (len <= 0) {
596 				PRINT((CH ": IsoIn: %s" CT, strerror(len)));
597 				continue;
598 			}
599 
600 #ifndef DEBUG_DISCARD_DATA
601 			if (fDataInput) {
602 				int fBufferIndex = 0;
603 				for (int i = 0; i < numPacketDescriptors; i++) {
604 					int actual_length = ((usb_iso_packet_descriptor)
605 						packetDescriptors[i]).actual_length;
606 					if (actual_length > 0) {
607 						fDataInput->Write(&fBuffer[fBufferIndex],
608 							actual_length);
609 					}
610 					fBufferIndex += actual_length;
611 				}
612 			}
613 #endif
614 			//snooze(2000);
615 		}
616 	}
617 #endif
618 	else {
619 		PRINT((CH ": No supported transport." CT));
620 		return B_UNSUPPORTED;
621 	}
622 	return B_OK;
623 }
624 
625 
626 int32
_DataPumpThread(void * _this)627 CamDevice::_DataPumpThread(void *_this)
628 {
629 	CamDevice *dev = (CamDevice *)_this;
630 	return dev->DataPumpThread();
631 }
632 
633 
634 void
DumpRegs()635 CamDevice::DumpRegs()
636 {
637 }
638 
639 
640 status_t
SendCommand(uint8 dir,uint8 request,uint16 value,uint16 index,uint16 length,void * data)641 CamDevice::SendCommand(uint8 dir, uint8 request, uint16 value,
642 	uint16 index, uint16 length, void* data)
643 {
644 	ssize_t ret;
645 	if (!GetDevice())
646 		return ENODEV;
647 	if (length > GetDevice()->MaxEndpoint0PacketSize())
648 		return EINVAL;
649 	ret = GetDevice()->ControlTransfer(
650 		USB_REQTYPE_VENDOR | USB_REQTYPE_INTERFACE_OUT | dir,
651 		request, value, index, length, data);
652 	return ret;
653 }
654 
655 
CamDeviceAddon(WebCamMediaAddOn * webcam)656 CamDeviceAddon::CamDeviceAddon(WebCamMediaAddOn* webcam)
657 	: fWebCamAddOn(webcam),
658 	  fSupportedDevices(NULL)
659 {
660 }
661 
662 
~CamDeviceAddon()663 CamDeviceAddon::~CamDeviceAddon()
664 {
665 }
666 
667 
668 const char *
BrandName()669 CamDeviceAddon::BrandName()
670 {
671 	return "<unknown>";
672 }
673 
674 
675 status_t
Sniff(BUSBDevice * device)676 CamDeviceAddon::Sniff(BUSBDevice *device)
677 {
678 	PRINT((CH ": Sniffing for %s" CT, BrandName()));
679 	if (!fSupportedDevices)
680 		return ENODEV;
681 	if (!device)
682 		return EINVAL;
683 
684 	bool supported = false;
685 	for (uint32 i = 0; !supported && fSupportedDevices[i].vendor; i++) {
686 		if ((fSupportedDevices[i].desc.vendor != 0
687 			&& device->VendorID() != fSupportedDevices[i].desc.vendor)
688 			|| (fSupportedDevices[i].desc.product != 0
689 			&& device->ProductID() != fSupportedDevices[i].desc.product))
690 			continue;
691 
692 		if ((fSupportedDevices[i].desc.dev_class == 0
693 			|| device->Class() == fSupportedDevices[i].desc.dev_class)
694 			&& (fSupportedDevices[i].desc.dev_subclass == 0
695 			|| device->Subclass() == fSupportedDevices[i].desc.dev_subclass)
696 			&& (fSupportedDevices[i].desc.dev_protocol == 0
697 			|| device->Protocol() == fSupportedDevices[i].desc.dev_protocol)) {
698 			supported = true;
699 		}
700 
701 #ifdef __HAIKU__
702 		// we have to check all interfaces for matching class/subclass/protocol
703 		for (uint32 j = 0; !supported && j < device->CountConfigurations(); j++) {
704 			const BUSBConfiguration* cfg = device->ConfigurationAt(j);
705 			for (uint32 k = 0; !supported && k < cfg->CountInterfaces(); k++) {
706 				const BUSBInterface* intf = cfg->InterfaceAt(k);
707 				for (uint32 l = 0; !supported && l < intf->CountAlternates(); l++) {
708 					const BUSBInterface* alt = intf->AlternateAt(l);
709 					if ((fSupportedDevices[i].desc.dev_class == 0
710 						|| alt->Class() == fSupportedDevices[i].desc.dev_class)
711 						&& (fSupportedDevices[i].desc.dev_subclass == 0
712 						|| alt->Subclass() == fSupportedDevices[i].desc.dev_subclass)
713 						&& (fSupportedDevices[i].desc.dev_protocol == 0
714 						|| alt->Protocol() == fSupportedDevices[i].desc.dev_protocol)) {
715 						supported = true;
716 					}
717 				}
718 			}
719 		}
720 #endif
721 
722 		if (supported)
723 			return i;
724 	}
725 
726 	return ENODEV;
727 }
728 
729 
730 CamDevice *
Instantiate(CamRoster & roster,BUSBDevice * from)731 CamDeviceAddon::Instantiate(CamRoster &roster, BUSBDevice *from)
732 {
733 	return NULL;
734 }
735 
736 
737 void
SetSupportedDevices(const usb_webcam_support_descriptor * devs)738 CamDeviceAddon::SetSupportedDevices(const usb_webcam_support_descriptor *devs)
739 {
740 	fSupportedDevices = devs;
741 }
742