1/******************************************************************************
2/
3/	File:			RadeonAddOn.cpp
4/
5/	Description:	ATI Radeon Video Capture Media AddOn for BeOS.
6/
7/	Copyright 2001, Carlos Hasan
8/
9*******************************************************************************/
10
11#include <support/Autolock.h>
12#include <media/MediaFormats.h>
13#include <Directory.h>
14#include <Entry.h>
15#include <Debug.h>
16#include <File.h>
17
18#include <stdio.h>
19#include <string.h>
20#include <unistd.h>
21
22#include <storage/FindDirectory.h>
23#include <String.h>
24
25#include "RadeonAddOn.h"
26#include "RadeonProducer.h"
27
28#define DPRINT(args) { PRINT(("\x1b[0;30;35m")); PRINT(args); PRINT(("\x1b[0;30;47m")); }
29
30CRadeonPlug::CRadeonPlug( CRadeonAddOn *aaddon, const BPath &adev_path, int aid )
31  : addon( aaddon ), dev_path( adev_path ), id( aid ), node( NULL )
32{
33	fFlavorInfo.name = const_cast<char *>("Radeon In");
34	fFlavorInfo.info = const_cast<char *>("Radeon Video In Media Node");
35	fFlavorInfo.kinds = B_BUFFER_PRODUCER | B_CONTROLLABLE | B_PHYSICAL_INPUT;
36	fFlavorInfo.flavor_flags = 0;
37	fFlavorInfo.internal_id = aid;
38	fFlavorInfo.possible_count = 1;
39
40	fFlavorInfo.in_format_count = 0;
41	fFlavorInfo.in_format_flags = 0;
42	fFlavorInfo.in_formats = NULL;
43
44	fFlavorInfo.out_format_count = 4;
45	//fFlavorInfo.out_format_count = 1;
46	fFlavorInfo.out_format_flags = 0;
47
48	fMediaFormat[0].type = B_MEDIA_RAW_VIDEO;
49	fMediaFormat[0].u.raw_video = media_raw_video_format::wildcard;
50	fMediaFormat[0].u.raw_video.interlace = 1;
51	fMediaFormat[0].u.raw_video.display.format = B_RGB32;
52
53	fMediaFormat[1].type = B_MEDIA_RAW_VIDEO;
54	fMediaFormat[1].u.raw_video = media_raw_video_format::wildcard;
55	fMediaFormat[1].u.raw_video.interlace = 1;
56	fMediaFormat[1].u.raw_video.display.format = B_RGB16;
57
58	fMediaFormat[2].type = B_MEDIA_RAW_VIDEO;
59	fMediaFormat[2].u.raw_video = media_raw_video_format::wildcard;
60	fMediaFormat[2].u.raw_video.interlace = 1;
61	fMediaFormat[2].u.raw_video.display.format = B_RGB15;
62
63	fMediaFormat[3].type = B_MEDIA_RAW_VIDEO;
64	fMediaFormat[3].u.raw_video = media_raw_video_format::wildcard;
65	fMediaFormat[3].u.raw_video.interlace = 1;
66	fMediaFormat[3].u.raw_video.display.format = B_YCbCr422;
67
68	/*fMediaFormat[0].type = B_MEDIA_RAW_VIDEO;
69	fMediaFormat[0].u.raw_video = media_raw_video_format::wildcard;
70	fMediaFormat[0].u.raw_video.interlace = 1;
71	fMediaFormat[0].u.raw_video.display.format = B_YCbCr422;*/
72
73	fFlavorInfo.out_formats = fMediaFormat;
74
75	readSettings();
76}
77
78BPath
79CRadeonPlug::getSettingsPath()
80{
81	BPath path;
82
83	if( find_directory( B_USER_CONFIG_DIRECTORY, &path ) != B_OK )
84		return BPath();
85
86	path.Append( "settings/Media/RadeonIn" );
87
88	create_directory( path.Path(), 755 );
89
90	BString id_string;
91
92	id_string << "settings" << id;
93
94	path.Append( id_string.String() );
95
96	return path;
97}
98
99void
100CRadeonPlug::writeSettings( BMessage *new_settings )
101{
102	BMessage cur_settings;
103
104	// if new_settings are provided, use them; else, ask node for settings
105	// (needed during shutdown where node cannot reply anymore)
106	if( new_settings != NULL ) {
107		cur_settings = *new_settings;
108
109	} else {
110		if( node == NULL )
111			return;
112
113		if( addon->GetConfigurationFor( node, &cur_settings ) != B_OK )
114			return;
115	}
116
117	BMallocIO old_settings_flat, new_settings_flat;
118
119	settings.Flatten( &old_settings_flat );
120	cur_settings.Flatten( &new_settings_flat );
121
122	if( old_settings_flat.BufferLength() == new_settings_flat.BufferLength() &&
123		memcmp( old_settings_flat.Buffer(), new_settings_flat.Buffer(), old_settings_flat.BufferLength() ) == 0 )
124		return;
125
126	settings = cur_settings;
127
128	BPath settings_path = getSettingsPath();
129
130	BFile file( settings_path.Path(), B_WRITE_ONLY | B_CREATE_FILE );
131
132	settings.Flatten( &file );
133}
134
135void
136CRadeonPlug::readSettings()
137{
138	BPath settings_path = getSettingsPath();
139
140	BFile file( settings_path.Path(), B_READ_ONLY );
141
142	settings.Unflatten( &file );
143}
144
145
146CRadeonAddOn::CRadeonAddOn(image_id imid)
147	: BMediaAddOn(imid),
148	settings_thread( -1 ), settings_thread_sem( -1 )
149{
150	DPRINT(("CRadeonAddOn::CRadeonAddOn()\n"));
151
152	fInitStatus = B_NO_INIT;
153
154	if( RecursiveScan( "/dev/video/radeon" ) != B_OK )
155		return;
156
157	if( (settings_thread_sem = create_sem( 0, "Radeon In settings" )) < 0 )
158		return;
159
160	if( INIT_BEN( plug_lock, "Radeon device list" ) < 0 )
161		return;
162
163	if( (settings_thread = spawn_thread(
164		settings_writer, "Radeon In settings", B_LOW_PRIORITY, this )) < 0 )
165		return;
166
167	resume_thread( settings_thread );
168
169	fInitStatus = B_OK;
170}
171
172CRadeonAddOn::~CRadeonAddOn()
173{
174	status_t dummy;
175
176	DPRINT(("CRadeonAddOn::~CRadeonAddOn()\n"));
177
178	release_sem( settings_thread_sem );
179	wait_for_thread( settings_thread, &dummy );
180
181	delete_sem( settings_thread_sem );
182
183	for( int32 i = 0; i < fDevices.CountItems(); ++i ) {
184		CRadeonPlug *plug = (CRadeonPlug *)fDevices.ItemAt(i);
185
186		delete plug;
187	}
188
189	DELETE_BEN( plug_lock );
190}
191
192
193status_t
194CRadeonAddOn::InitCheck(const char **out_failure_text)
195{
196	DPRINT(("CRadeonAddOn::InitCheck()\n"));
197
198	if (fInitStatus < B_OK) {
199		*out_failure_text = "Unknown error";
200		return fInitStatus;
201	}
202
203	return B_OK;
204}
205
206int32
207CRadeonAddOn::settings_writer( void *param )
208{
209	((CRadeonAddOn *)param)->settingsWriter();
210	return B_OK;
211}
212
213void
214CRadeonAddOn::writeSettings()
215{
216	ACQUIRE_BEN( plug_lock );
217
218	for( int32 i = 0; i < fDevices.CountItems(); ++i ) {
219		CRadeonPlug *plug = (CRadeonPlug *)fDevices.ItemAt(i);
220
221		plug->writeSettings( NULL );
222	}
223
224	RELEASE_BEN( plug_lock );
225}
226
227void
228CRadeonAddOn::settingsWriter()
229{
230	while( acquire_sem_etc( settings_thread_sem, 1, B_RELATIVE_TIMEOUT, 10000000 ) == B_TIMED_OUT ) {
231		writeSettings();
232	}
233}
234
235void
236CRadeonAddOn::UnregisterNode( BMediaNode *node, BMessage *settings )
237{
238	ACQUIRE_BEN( plug_lock );
239
240	for( int32 i = 0; i < fDevices.CountItems(); ++i ) {
241		CRadeonPlug *plug = (CRadeonPlug *)fDevices.ItemAt(i);
242
243		if( plug->getNode() == node ) {
244			// write last settings, so they don't get lost
245			plug->writeSettings( settings );
246			plug->setNode( NULL );
247			break;
248		}
249	}
250
251	RELEASE_BEN( plug_lock );
252}
253
254int32
255CRadeonAddOn::CountFlavors()
256{
257	DPRINT(("CRadeonAddOn::CountFlavors()\n"));
258
259	if (fInitStatus < B_OK)
260		return fInitStatus;
261
262	return fDevices.CountItems();
263}
264
265/*
266 * The pointer to the flavor received only needs to be valid between
267 * successive calls to BCRadeonAddOn::GetFlavorAt().
268 */
269status_t
270CRadeonAddOn::GetFlavorAt(int32 n, const flavor_info **out_info)
271{
272	DPRINT(("CRadeonAddOn::GetFlavorAt()\n"));
273
274	if (fInitStatus < B_OK)
275		return fInitStatus;
276
277	if (n < 0 || n >= fDevices.CountItems() )
278		return B_BAD_INDEX;
279
280	/* Return the flavor defined in the constructor */
281	*out_info = ((CRadeonPlug *)fDevices.ItemAt(n))->getFlavorInfo();
282	return B_OK;
283}
284
285BMediaNode *
286CRadeonAddOn::InstantiateNodeFor(
287		const flavor_info *info, BMessage *config, status_t *out_error)
288{
289	DPRINT(("CRadeonAddOn::InstantiateNodeFor()\n"));
290
291	CRadeonProducer *node;
292
293	if (fInitStatus < B_OK)
294		return NULL;
295
296	if (info->internal_id < 0 || info->internal_id >= fDevices.CountItems())
297		return NULL;
298
299	CRadeonPlug *plug = (CRadeonPlug *)fDevices.ItemAt( info->internal_id );
300
301	ACQUIRE_BEN( plug_lock );
302
303	if( plug->getNode() != NULL ) {
304		*out_error = B_BUSY;
305		node = NULL;
306
307	} else {
308		BMessage single_settings;
309
310		// under R5, configuration is always an empty message, so we need to
311		// get our own configuration
312		if( config == NULL || 1 )
313			config = plug->getSettings();
314
315		node = new CRadeonProducer( this, plug->getName(),
316			plug->getDeviceName(), info->internal_id, config );
317
318		if (node && (node->InitCheck() < B_OK)) {
319			*out_error = node->InitCheck();
320			delete node;
321			node = NULL;
322		}
323	}
324
325	plug->setNode( node );
326
327	RELEASE_BEN( plug_lock );
328
329	return node;
330}
331
332status_t CRadeonAddOn::GetConfigurationFor(
333	BMediaNode *your_node,
334	BMessage *into_message )
335{
336	port_id reply_port;
337	CRadeonProducer::configuration_msg msg;
338	status_t res;
339
340	reply_port = create_port( 1, "GetConfiguration Reply" );
341	if( reply_port < 0 )
342		return reply_port;
343
344	msg.reply_port = reply_port;
345
346	res = write_port_etc( your_node->ControlPort(), CRadeonProducer::C_GET_CONFIGURATION,
347		&msg, sizeof( msg ), B_TIMEOUT, 10000000 );
348	if( res == B_OK ) {
349		ssize_t reply_size;
350		CRadeonProducer::configuration_msg_reply *reply;
351		int32 code;
352
353		reply_size = port_buffer_size_etc( reply_port, B_TIMEOUT, 10000000 );
354		if( reply_size < 0 )
355			res = reply_size;
356		else {
357			reply = (CRadeonProducer::configuration_msg_reply *)malloc( reply_size );
358
359			res = read_port( reply_port, &code, reply, reply_size );
360			if( res == reply_size ) {
361				if( code != CRadeonProducer::C_GET_CONFIGURATION_REPLY )
362					res = B_ERROR;
363				else {
364					res = reply->res;
365
366					if( res == B_OK )
367						res = into_message->Unflatten( &reply->config );
368				}
369			}
370
371			free( reply );
372		}
373	}
374
375	delete_port( reply_port );
376
377	return res;
378}
379
380status_t
381CRadeonAddOn::RecursiveScan(const char* rootPath, BEntry *rootEntry = NULL)
382{
383	BDirectory root;
384
385	if( rootEntry != NULL )
386		root.SetTo( rootEntry );
387	else if( rootPath != NULL ) {
388		root.SetTo( rootPath );
389	} else {
390		PRINT(("Error in MultiAudioAddOn::RecursiveScan null params\n"));
391		return B_ERROR;
392	}
393
394	BEntry entry;
395	int cur_id = 0;
396
397	while( root.GetNextEntry( &entry ) > B_ERROR ) {
398		if(entry.IsDirectory()) {
399			RecursiveScan( rootPath, &entry );
400
401		} else {
402			BPath path;
403
404			entry.GetPath(&path);
405
406			CRadeon device( path.Path() );
407
408			if( device.InitCheck() != B_OK)
409				continue;
410
411			CVIPPort vip_port( device );
412
413			// if there is a Rage Theatre, then there should be Video-In
414			if( vip_port.InitCheck() == B_OK &&
415				((vip_port.FindVIPDevice( C_THEATER100_VIP_DEVICE_ID ) >= 0)
416					|| (vip_port.FindVIPDevice( C_THEATER200_VIP_DEVICE_ID ) >= 0)))
417			{
418				fDevices.AddItem( new CRadeonPlug( this, path, cur_id++ ));
419			}
420		}
421	}
422
423	return B_OK;
424}
425
426
427BMediaAddOn *
428make_media_addon(image_id imid)
429{
430	return new CRadeonAddOn(imid);
431}
432