xref: /haiku/src/add-ons/media/media-add-ons/radeon/RadeonAddOn.cpp (revision 863634b83f627a5950315df1added5f754d42c23)
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 
CRadeonPlug(CRadeonAddOn * aaddon,const BPath & adev_path,int aid)30 CRadeonPlug::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 
78 BPath
getSettingsPath()79 CRadeonPlug::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 
99 void
writeSettings(BMessage * new_settings)100 CRadeonPlug::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 
135 void
readSettings()136 CRadeonPlug::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 
CRadeonAddOn(image_id imid)146 CRadeonAddOn::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 
~CRadeonAddOn()172 CRadeonAddOn::~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 
193 status_t
InitCheck(const char ** out_failure_text)194 CRadeonAddOn::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 
206 int32
settings_writer(void * param)207 CRadeonAddOn::settings_writer( void *param )
208 {
209 	((CRadeonAddOn *)param)->settingsWriter();
210 	return B_OK;
211 }
212 
213 void
writeSettings()214 CRadeonAddOn::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 
227 void
settingsWriter()228 CRadeonAddOn::settingsWriter()
229 {
230 	while( acquire_sem_etc( settings_thread_sem, 1, B_RELATIVE_TIMEOUT, 10000000 ) == B_TIMED_OUT ) {
231 		writeSettings();
232 	}
233 }
234 
235 void
UnregisterNode(BMediaNode * node,BMessage * settings)236 CRadeonAddOn::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 
254 int32
CountFlavors()255 CRadeonAddOn::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  */
269 status_t
GetFlavorAt(int32 n,const flavor_info ** out_info)270 CRadeonAddOn::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 
285 BMediaNode *
InstantiateNodeFor(const flavor_info * info,BMessage * config,status_t * out_error)286 CRadeonAddOn::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 
GetConfigurationFor(BMediaNode * your_node,BMessage * into_message)332 status_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 
380 status_t
RecursiveScan(const char * rootPath,BEntry * rootEntry=NULL)381 CRadeonAddOn::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 
427 BMediaAddOn *
make_media_addon(image_id imid)428 make_media_addon(image_id imid)
429 {
430 	return new CRadeonAddOn(imid);
431 }
432