]> git.sesse.net Git - vlc/blob - src/interface/intf_eject.c
* ALL: the first libvlc commit.
[vlc] / src / interface / intf_eject.c
1 /*****************************************************************************
2  * intf_eject.c: CD/DVD-ROM ejection handling functions
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: intf_eject.c,v 1.13 2002/06/01 12:32:01 sam Exp $
6  *
7  * Author: Julien Blache <jb@technologeek.org> for the Linux part
8  *               with code taken from the Linux "eject" command
9  *         Jon Lech Johansen <jon-vl@nanocrew.net> for Darwin
10  *         Xavier Marchesini <xav@alarue.net> for Win32
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 #include <vlc/vlc.h>
28
29 #include <stdio.h>
30 #include <stdlib.h>
31
32 #ifdef HAVE_UNISTD_H
33 #    include <unistd.h>
34 #endif
35
36 #include <string.h>
37
38 #ifdef HAVE_FCNTL_H
39 #   include <fcntl.h>
40 #endif
41
42 #ifdef HAVE_DVD_H
43 #   include <dvd.h>
44 #endif
45
46 #if defined(SYS_LINUX) && defined(HAVE_LINUX_VERSION_H)
47 #   include <linux/version.h>
48     /* handy macro found in 2.1 kernels, but not in older ones */
49 #   ifndef KERNEL_VERSION
50 #       define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
51 #   endif
52
53 #   include <sys/types.h>
54 #   include <sys/stat.h>
55 #   include <sys/ioctl.h>
56
57 #   include <sys/ioctl.h>
58 #   include <sys/mount.h>
59
60 #   include <linux/cdrom.h>
61 #   if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
62 #       include <linux/ucdrom.h>
63 #   endif
64
65 #   include <scsi/scsi.h>
66 #   include <scsi/sg.h>
67 #   include <scsi/scsi_ioctl.h>
68 #endif
69
70 #ifdef WIN32 
71 #   include <windows.h>
72 #   include <stdio.h>
73 #   include <winioctl.h>
74 #   include <ctype.h>
75 #   include <tchar.h>
76
77 /* define the structures to eject under Win95/98/Me */
78
79 #if !defined (VWIN32_DIOC_DOS_IOCTL)
80 #define VWIN32_DIOC_DOS_IOCTL      1
81
82 typedef struct _DIOC_REGISTERS {
83     DWORD reg_EBX;
84     DWORD reg_EDX;
85     DWORD reg_ECX;
86     DWORD reg_EAX;
87     DWORD reg_EDI;
88     DWORD reg_ESI;
89     DWORD reg_Flags;
90 } DIOC_REGISTERS, *PDIOC_REGISTERS;
91 #endif    /* VWIN32_DIOC_DOS_IOCTL */
92
93 #endif
94
95
96 /*****************************************************************************
97  * Local prototypes
98  *****************************************************************************/
99 #if defined(SYS_LINUX) && defined(HAVE_LINUX_VERSION_H)
100 static int EjectSCSI ( int i_fd );
101 #endif
102
103 /*****************************************************************************
104  * intf_Eject: eject the CDRom
105  *****************************************************************************
106  * returns 0 on success
107  * returns 1 on failure
108  * returns -1 if not implemented
109  *****************************************************************************/
110 int intf_Eject( vlc_object_t *p_this, const char *psz_device )
111 {
112     int i_ret;
113
114 #ifdef SYS_DARWIN
115     FILE *p_eject;
116     char *psz_disk;
117     char sz_cmd[32];
118
119     /*
120      * The only way to cleanly unmount the disc under MacOS X
121      * is to use the 'disktool' command line utility. It uses
122      * the non-public Disk Arbitration API, which can not be
123      * used by Cocoa or Carbon applications. 
124      */
125
126     if( ( psz_disk = (char *)strstr( psz_device, "disk" ) ) != NULL &&
127         strlen( psz_disk ) > 4 )
128     {
129 #define EJECT_CMD "/usr/sbin/disktool -e %s 0"
130         snprintf( sz_cmd, sizeof(sz_cmd), EJECT_CMD, psz_disk );
131 #undef EJECT_CMD
132
133         if( ( p_eject = popen( sz_cmd, "r" ) ) != NULL )
134         {
135             char psz_result[0x200];
136             i_ret = fread( psz_result, 1, sizeof(psz_result) - 1, p_eject );
137
138             if( i_ret == 0 && ferror( p_eject ) != 0 )
139             {
140                 pclose( p_eject );
141                 return VLC_EGENERIC;
142             }
143
144             pclose( p_eject );
145
146             psz_result[ i_ret ] = 0;
147
148             if( strstr( psz_result, "Disk Ejected" ) != NULL )
149             {
150                 return VLC_SUCCESS;
151             }
152         }
153     }
154
155     return 1;
156
157 #elif defined(WIN32) 
158     
159     HANDLE h_drive ;
160     TCHAR  psz_drive_id[8] ;
161     DWORD  dw_access_flags = GENERIC_READ ;
162     DWORD  dw_result ;
163     LPTSTR psz_volume_format = TEXT("\\\\.\\%s") ;
164     BYTE   by_drive ;
165     DIOC_REGISTERS regs = {0} ;
166     
167     /* Win2K ejection code */
168     if ( GetVersion() < 0x80000000 )
169     {
170         intf_WarnMsg (3, "intf: win2k ejecting procedure launched") ;
171         
172         wsprintf(psz_drive_id, psz_volume_format, psz_device) ;
173          
174         intf_WarnMsg(3, "intf: Ejecting drive %s", psz_drive_id) ;
175         
176         /* Create the file handle */ 
177         h_drive = CreateFile(  psz_drive_id, 
178                                dw_access_flags, 
179                                FILE_SHARE_READ | FILE_SHARE_WRITE,
180                                NULL,
181                                OPEN_EXISTING,
182                                0,
183                                NULL );
184
185         if (h_drive == INVALID_HANDLE_VALUE )
186         {
187             intf_ErrMsg ("intf error: (Win32) couldn't create handle for device %s", psz_device) ;
188         }
189
190         i_ret = DeviceIoControl ( h_drive, 
191                                  IOCTL_STORAGE_EJECT_MEDIA,
192                                  NULL, 0,
193                                  NULL, 0, 
194                                  &dw_result, 
195                                  NULL);
196         return (i_ret) ;
197     }
198     else        /* Win95/98/ME */
199     {
200         /* Create the handle to VWIN32 */
201         h_drive = CreateFile ("\\\\.\\vwin32", 0, 0, NULL, 0,
202                               FILE_FLAG_DELETE_ON_CLOSE, NULL ) ;
203         
204         /* Convert logical disk name to DOS-like disk name */
205         by_drive = (toupper (*psz_device) - 'A') + 1;
206
207         /* Let's eject now : Int 21H function 440DH minor code 49h*/
208         regs.reg_EAX = 0x440D ;                        
209         regs.reg_EBX = by_drive ;
210         regs.reg_ECX = MAKEWORD(0x49 , 0x08) ;        // minor code
211
212         i_ret = DeviceIoControl (h_drive, VWIN32_DIOC_DOS_IOCTL,
213                                  &regs, sizeof(regs), &regs, sizeof(regs),
214                                  &dw_result, 0) ;
215
216         CloseHandle (h_drive) ;
217         return (i_ret) ;
218     }
219 #else   /* WIN32 */
220     
221     int i_fd;
222
223     /* This code could be extended to support CD/DVD-ROM chargers */
224
225     i_fd = open( psz_device, O_RDONLY | O_NONBLOCK );
226    
227     if( i_fd == -1 )
228     {
229         msg_Err( p_this, "could not open device %s", psz_device );
230         return VLC_EGENERIC;
231     }
232
233 #if defined(SYS_LINUX) && defined(HAVE_LINUX_VERSION_H)
234     /* Try a simple ATAPI eject */
235     i_ret = ioctl( i_fd, CDROMEJECT, 0 );
236
237     if( i_ret != 0 )
238     {
239         i_ret = EjectSCSI( i_fd );
240     }
241
242     if( i_ret != 0 )
243     {
244         msg_Err( p_this, "could not eject %s", psz_device );
245     }
246
247 #elif defined (HAVE_DVD_H)
248     i_ret = ioctl( i_fd, CDROMEJECT, 0 );
249
250 #else
251     msg_Warn( p_this, "CD-Rom ejection unsupported on this platform" );
252     i_ret = -1;
253
254 #endif
255     close( i_fd );
256
257     return i_ret;
258 #endif
259 }
260
261 /* The following functions are local */
262
263 #if defined(SYS_LINUX) && defined(HAVE_LINUX_VERSION_H)
264 /*****************************************************************************
265  * Eject using SCSI commands. Return 0 if successful
266  *****************************************************************************/
267 static int EjectSCSI( int i_fd )
268 {
269     int i_status;
270
271     struct sdata
272     {
273         int  inlen;
274         int  outlen;
275         char cmd[256];
276     } scsi_cmd;
277
278     scsi_cmd.inlen  = 0;
279     scsi_cmd.outlen = 0;
280     scsi_cmd.cmd[0] = ALLOW_MEDIUM_REMOVAL;
281     scsi_cmd.cmd[1] = 0;
282     scsi_cmd.cmd[2] = 0;
283     scsi_cmd.cmd[3] = 0;
284     scsi_cmd.cmd[4] = 0;
285     scsi_cmd.cmd[5] = 0;
286     i_status = ioctl( i_fd, SCSI_IOCTL_SEND_COMMAND, (void *)&scsi_cmd );
287     if( i_status != 0 )
288     {
289         return VLC_EGENERIC;
290     }
291
292     scsi_cmd.inlen  = 0;
293     scsi_cmd.outlen = 0;
294     scsi_cmd.cmd[0] = START_STOP;
295     scsi_cmd.cmd[1] = 0;
296     scsi_cmd.cmd[2] = 0;
297     scsi_cmd.cmd[3] = 0;
298     scsi_cmd.cmd[4] = 1;
299     scsi_cmd.cmd[5] = 0;
300     i_status = ioctl( i_fd, SCSI_IOCTL_SEND_COMMAND, (void *)&scsi_cmd );
301     if( i_status != 0 )
302     {
303         return VLC_EGENERIC;
304     }
305   
306     scsi_cmd.inlen  = 0;
307     scsi_cmd.outlen = 0;
308     scsi_cmd.cmd[0] = START_STOP;
309     scsi_cmd.cmd[1] = 0;
310     scsi_cmd.cmd[2] = 0;
311     scsi_cmd.cmd[3] = 0;
312     scsi_cmd.cmd[4] = 2;
313     scsi_cmd.cmd[5] = 0;
314     i_status = ioctl( i_fd, SCSI_IOCTL_SEND_COMMAND, (void *)&scsi_cmd );
315     if( i_status != 0 )
316     {
317         return VLC_EGENERIC;
318     }
319   
320     /* Force kernel to reread partition table when new disc inserted */
321     i_status = ioctl( i_fd, BLKRRPART );
322   
323     return i_status;
324 }
325 #endif
326