hal.c 10.6 KB
Newer Older
1
/*****************************************************************************
2
 * hal.c :  HAL interface module
3
 *****************************************************************************
4
 * Copyright (C) 2004 the VideoLAN team
5
 * Copyright © 2006-2007 Rafaël Carré
dionoea's avatar
dionoea committed
6
 * $Id$
7
 *
8
 * Authors: Clément Stenac <zorglub@videolan.org>
9
 *          Rafaël Carré <funman at videolanorg>
10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 25
 *****************************************************************************/

26 27 28 29
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

30
#include <vlc_common.h>
31
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
32
#include <vlc_playlist.h>
33
#include <vlc_services_discovery.h>
34

zorglub's avatar
zorglub committed
35
#include <vlc_network.h>
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

#include <errno.h>                                                 /* ENOMEM */

#ifdef HAVE_UNISTD_H
#    include <unistd.h>
#endif
#ifdef HAVE_SYS_TIME_H
#    include <sys/time.h>
#endif

#include <hal/libhal.h>

#define MAX_LINE_LENGTH 256

/*****************************************************************************
51
 * Local prototypes
52
 *****************************************************************************/
53

54 55 56
/* store relation between item id and udi for ejection */
struct udi_input_id_t
{
57 58
    char            *psz_udi;
    input_item_t    *p_item;
59 60
};

61 62
struct services_discovery_sys_t
{
63
    vlc_thread_t            thread;
64
    LibHalContext           *p_ctx;
Rafaël Carré's avatar
typo  
Rafaël Carré committed
65
    DBusConnection          *p_connection;
66 67
    int                     i_devices_number;
    struct udi_input_id_t   **pp_devices;
68
};
69
static void *Run ( void * );
70 71
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );
72

73 74 75
/* HAL callbacks */
void DeviceAdded( LibHalContext *p_ctx, const char *psz_udi );
void DeviceRemoved( LibHalContext *p_ctx, const char *psz_udi );
76

77 78 79
/* to retrieve p_sd in HAL callbacks */
services_discovery_t        *p_sd_global;

80 81 82
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
83 84 85 86
vlc_module_begin ()
    set_description( N_("HAL devices detection") )
    set_category( CAT_PLAYLIST )
    set_subcategory( SUBCAT_PLAYLIST_SD )
87

88 89
    set_capability( "services_discovery", 0 )
    set_callbacks( Open, Close )
90

91
vlc_module_end ()
92 93 94 95 96 97 98


/*****************************************************************************
 * Open: initialize and create stuff
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
zorglub's avatar
zorglub committed
99 100 101
    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
    services_discovery_sys_t *p_sys  = malloc(
                                    sizeof( services_discovery_sys_t ) );
Rafaël Carré's avatar
Rafaël Carré committed
102 103
    if( !p_sys )
        return VLC_ENOMEM;
104

105
    DBusError           dbus_error;
zorglub's avatar
zorglub committed
106
    DBusConnection      *p_connection;
107 108 109 110

    p_sd_global = p_sd;
    p_sys->i_devices_number = 0;
    p_sys->pp_devices = NULL;
111

zorglub's avatar
zorglub committed
112
    p_sd->p_sys  = p_sys;
113

114 115
    dbus_error_init( &dbus_error );

zorglub's avatar
zorglub committed
116 117 118
    p_sys->p_ctx = libhal_ctx_new();
    if( !p_sys->p_ctx )
    {
119
        msg_Err( p_sd, "unable to create HAL context") ;
zorglub's avatar
zorglub committed
120 121 122 123 124 125
        free( p_sys );
        return VLC_EGENERIC;
    }
    p_connection = dbus_bus_get( DBUS_BUS_SYSTEM, &dbus_error );
    if( dbus_error_is_set( &dbus_error ) )
    {
126
        msg_Err( p_sd, "unable to connect to DBUS: %s", dbus_error.message );
zorglub's avatar
zorglub committed
127 128 129 130 131
        dbus_error_free( &dbus_error );
        free( p_sys );
        return VLC_EGENERIC;
    }
    libhal_ctx_set_dbus_connection( p_sys->p_ctx, p_connection );
132
    p_sys->p_connection = p_connection;
zorglub's avatar
zorglub committed
133
    if( !libhal_ctx_init( p_sys->p_ctx, &dbus_error ) )
134
    {
135
        msg_Err( p_sd, "hal not available : %s", dbus_error.message );
136
        goto error;
137 138
    }

139 140 141 142
    if( !libhal_ctx_set_device_added( p_sys->p_ctx, DeviceAdded ) ||
            !libhal_ctx_set_device_removed( p_sys->p_ctx, DeviceRemoved ) )
    {
        msg_Err( p_sd, "unable to add callback" );
143
        goto error;
144
    }
145

146 147 148
    if( vlc_clone( &p_sys->thread, Run, p_this, VLC_THREAD_PRIORITY_LOW ) )
        goto error;

149
    return VLC_SUCCESS;
150 151 152 153
error:
    dbus_error_free( &dbus_error );
    free( p_sys );
    return VLC_EGENERIC;
154 155 156 157 158 159 160
}

/*****************************************************************************
 * Close:
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
{
161 162
    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
    services_discovery_sys_t *p_sys  = p_sd->p_sys;
163

164 165 166
    /*vlc_cancel( p_sys->thread );*/
    vlc_object_kill( p_sd );
    vlc_join( p_sys->thread, NULL );
167
    dbus_connection_unref( p_sys->p_connection );
168 169 170
    struct udi_input_id_t *p_udi_entry;

    while( p_sys->i_devices_number > 0 )
171
    {
172
        p_udi_entry = p_sys->pp_devices[0];
ivoire's avatar
ivoire committed
173
        free( p_udi_entry->psz_udi );
174 175
        TAB_REMOVE( p_sys->i_devices_number, p_sys->pp_devices,
                p_sys->pp_devices[0] );
ivoire's avatar
ivoire committed
176
        free( p_udi_entry );
177 178
    }
    p_sys->pp_devices = NULL;
179

180
    free( p_sys );
181 182
}

183 184
static void AddItem( services_discovery_t *p_sd, input_item_t * p_input,
                    const char* psz_device )
185 186
{
    services_discovery_sys_t *p_sys  = p_sd->p_sys;
187
    services_discovery_AddItem( p_sd, p_input, NULL /* no category */ );
188 189 190 191 192

    struct udi_input_id_t *p_udi_entry;
    p_udi_entry = malloc( sizeof( struct udi_input_id_t ) );
    if( !p_udi_entry )
        return;
Rafaël Carré's avatar
Rafaël Carré committed
193 194
    p_udi_entry->psz_udi = strdup( psz_device );
    if( !p_udi_entry->psz_udi )
Rafaël Carré's avatar
Rafaël Carré committed
195 196
    {
        free( p_udi_entry );
Rafaël Carré's avatar
Rafaël Carré committed
197
        return;
Rafaël Carré's avatar
Rafaël Carré committed
198
    }
Rafaël Carré's avatar
Rafaël Carré committed
199

Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
200
    vlc_gc_incref( p_input );
201
    p_udi_entry->p_item = p_input;
202
    TAB_APPEND( p_sys->i_devices_number, p_sys->pp_devices, p_udi_entry );
203 204
}

205
static void AddDvd( services_discovery_t *p_sd, const char *psz_device )
206 207 208 209
{
    char *psz_name;
    char *psz_uri;
    char *psz_blockdevice;
210
    input_item_t        *p_input;
211

212 213 214 215
    psz_name = libhal_device_get_property_string( p_sd->p_sys->p_ctx,
                                        psz_device, "volume.label", NULL );
    psz_blockdevice = libhal_device_get_property_string( p_sd->p_sys->p_ctx,
                                        psz_device, "block.device", NULL );
216

217 218
    if( asprintf( &psz_uri, "dvd://%s", psz_blockdevice ) == -1 )
        return;
219
    /* Create the playlist item here */
220
    p_input = input_item_New( p_sd, psz_uri, psz_name );
221
    free( psz_uri );
222
    if( !p_input )
223 224 225
    {
        return;
    }
226

227
    AddItem( p_sd, p_input, psz_device );
228

Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
229
    vlc_gc_decref( p_input );
230 231
}

232
static void DelItem( services_discovery_t *p_sd, const char* psz_udi )
233
{
234 235
    services_discovery_sys_t    *p_sys  = p_sd->p_sys;

236
    int i;
237
    for( i = 0; i < p_sys->i_devices_number; i++ )
238
    { /*  looks for a matching udi */
239
        if( strcmp( psz_udi, p_sys->pp_devices[i]->psz_udi ) == 0 )
240
        { /* delete the corresponding item */    
241
            services_discovery_RemoveItem( p_sd, p_sys->pp_devices[i]->p_item );
Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
242
            vlc_gc_decref( p_sys->pp_devices[i]->p_item );
ivoire's avatar
ivoire committed
243
            free( p_sys->pp_devices[i]->psz_udi );
244 245
            TAB_REMOVE( p_sys->i_devices_number, p_sys->pp_devices,
                    p_sys->pp_devices[i] );
246 247
        }
    }
248
}
zorglub's avatar
zorglub committed
249

250
static void AddCdda( services_discovery_t *p_sd, const char *psz_device )
251 252 253
{
    char *psz_uri;
    char *psz_blockdevice;
254
    input_item_t     *p_input;
255

256 257
    psz_blockdevice = libhal_device_get_property_string( p_sd->p_sys->p_ctx,
                                            psz_device, "block.device", NULL );
258

259 260
    if( asprintf( &psz_uri, "cdda://%s", psz_blockdevice ) == -1 )
        return;
261
    /* Create the item here */
262
    p_input = input_item_New( p_sd, psz_uri, "Audio CD" );
263
    free( psz_uri );
264
    if( !p_input )
265
        return;
266

267
    AddItem( p_sd, p_input, psz_device );
268

Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
269
    vlc_gc_decref( p_input );
270 271
}

272
static void ParseDevice( services_discovery_t *p_sd, const char *psz_device )
273 274
{
    char *psz_disc_type;
zorglub's avatar
zorglub committed
275
    services_discovery_sys_t    *p_sys  = p_sd->p_sys;
276

277
    if( !libhal_device_property_exists( p_sys->p_ctx, psz_device,
278
                                       "volume.disc.type", NULL ) )
279 280 281 282 283 284 285
        return;

    psz_disc_type = libhal_device_get_property_string( p_sys->p_ctx,
                                                    psz_device,
                                                    "volume.disc.type",
                                                    NULL );
    if( !strncmp( psz_disc_type, "dvd_r", 5 ) )
286
    {
287 288 289 290 291
        if (libhal_device_get_property_bool( p_sys->p_ctx, psz_device,
                                     "volume.disc.is_videodvd", NULL ) )
        AddDvd( p_sd, psz_device );
    }
    else if( !strncmp( psz_disc_type, "cd_r", 4 ) )
292
    {
293 294 295
        if( libhal_device_get_property_bool( p_sys->p_ctx, psz_device,
                                     "volume.disc.has_audio" , NULL ) )
            AddCdda( p_sd, psz_device );
296 297 298 299 300 301
    }
}

/*****************************************************************************
 * Run: main HAL thread
 *****************************************************************************/
Rémi Denis-Courmont's avatar
typo  
Rémi Denis-Courmont committed
302
static void *Run( void *data )
303
{
304 305
    services_discovery_t     *p_sd  = data;
    services_discovery_sys_t *p_sys = p_sd->p_sys;
306
    char **devices;
307
    int i, i_devices;
308
    int canc = vlc_savecancel();
309 310

    /* parse existing devices first */
311
    if( ( devices = libhal_get_all_devices( p_sys->p_ctx, &i_devices, NULL ) ) )
312 313 314
    {
        for( i = 0; i < i_devices; i++ )
        {
zorglub's avatar
zorglub committed
315
            ParseDevice( p_sd, devices[ i ] );
316 317
            libhal_free_string( devices[ i ] );
        }
ivoire's avatar
ivoire committed
318
        free( devices );
319
    }
320 321 322

    /* FIXME: Totally lame. There are DBus watch functions to do this properly.
     * -- Courmisch, 28/08/2008 */
323
    while( vlc_object_alive (p_sd) )
324
    {
325 326 327
        /* look for events on the bus, blocking 1 second */
        dbus_connection_read_write_dispatch( p_sys->p_connection, 1000 );
        /* HAL 0.5.8.1 can use libhal_ctx_get_dbus_connection(p_sys->p_ctx) */
328
    }
329
    vlc_restorecancel (canc);
330
    return NULL;
331 332 333 334
}

void DeviceAdded( LibHalContext *p_ctx, const char *psz_udi )
{
335 336
    VLC_UNUSED(p_ctx);
    ParseDevice( p_sd_global, psz_udi );
337
}
338

339 340
void DeviceRemoved( LibHalContext *p_ctx, const char *psz_udi )
{
341 342
    VLC_UNUSED(p_ctx);
    DelItem( p_sd_global, psz_udi );
343
}