wineps.drv: Slightly simplify a PPD parser code snippet.
[wine] / dlls / krnl386.exe16 / int2f.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3  * DOS interrupt 2fh handler
4  *
5  *  Cdrom - device driver emulation - Audio features.
6  *     Copyright (c) 1998 Petr Tomasek <tomasek@etf.cuni.cz>
7  *     Copyright (c) 1999,2002 Eric Pouech
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25
26 #include <string.h>
27
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
30 #include "ntstatus.h"
31 #define WIN32_NO_STATUS
32 #include "wine/winbase16.h"
33 #include "wine/debug.h"
34 #include "winternl.h"
35 #include "winioctl.h"
36 #include "ntddstor.h"
37 #include "ntddcdrm.h"
38 #include "dosexe.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(int);
41
42 /* base KERNEL ordinal number for VxDs */
43 #define VXD_BASE 900
44
45 typedef struct
46 {
47     DOS_DEVICE_HEADER hdr;
48
49     WORD  reserved;             /* must be 0 */
50     BYTE  drive;                /* drive letter (0=A:, 1=B:, ...) */
51     BYTE  units;                /* number of supported units */
52 } CDROM_DEVICE_HEADER;
53
54 typedef struct
55 {
56     CDROM_DEVICE_HEADER hdr;
57     WINEDEV_THUNK thunk;
58
59     WORD  cdrom_segment;        /* Real mode segment for CDROM_HEAP */
60     WORD  cdrom_selector;       /* Protected mode selector for CDROM_HEAP */
61 } CDROM_HEAP;
62
63 static void do_int2f_16( CONTEXT *context );
64 static void MSCDEX_Handler( CONTEXT *context );
65
66 /**********************************************************************
67  *          DOSVM_Int2fHandler
68  *
69  * Handler for int 2fh (multiplex).
70  */
71 void WINAPI DOSVM_Int2fHandler( CONTEXT *context )
72 {
73     TRACE("Subfunction 0x%X\n", AX_reg(context));
74
75     switch(AH_reg(context))
76     {
77     case 0x10:
78         SET_AL( context, 0xff ); /* share is installed */
79         break;
80
81     case 0x11:  /* Network Redirector / IFSFUNC */
82         switch (LOBYTE(context->Eax))
83         {
84         case 0x00:  /* Install check */
85             /* not installed */
86             break;
87         case 0x80:  /* Enhanced services - Install check */
88             /* not installed */
89             break;
90         default:
91            INT_BARF( context, 0x2f );
92             break;
93         }
94         break;
95
96     case 0x12:
97         switch (LOBYTE(context->Eax))
98         {
99         case 0x2e: /* get or set DOS error table address */
100             switch (DL_reg(context))
101             {
102             /* Four tables: even commands are 'get', odd are 'set' */
103             /* DOS 5.0+ ignores "set" commands */
104             case 0x01:
105             case 0x03:
106             case 0x05:
107             case 0x07:
108             case 0x09:
109                 break;
110             /* Instead of having a message table in DOS-space, */
111             /* we can use a special case for MS-DOS to force   */
112             /* the secondary interface.                               */
113             case 0x00:
114             case 0x02:
115             case 0x04:
116             case 0x06:
117                 context->SegEs = 0x0001;
118                 SET_DI( context, 0x0000 );
119                 break;
120             case 0x08:
121                 FIXME("No real-mode handler for errors yet! (bye!)\n");
122                 break;
123             default:
124                 INT_BARF(context, 0x2f);
125             }
126             break;
127         default:
128            INT_BARF(context, 0x2f);
129         }
130         break;
131
132     case 0x15: /* mscdex */
133         MSCDEX_Handler(context);
134         break;
135
136     case 0x16:
137         do_int2f_16( context );
138         break;
139
140     case 0x1a: /* ANSI.SYS / AVATAR.SYS Install Check */
141         /* Not supported yet, do nothing */
142         break;
143
144     case 0x43:
145 #if 1
146        switch (LOBYTE(context->Eax))
147        {
148        case 0x00:   /* XMS v2+ installation check */
149            WARN("XMS is not fully implemented\n");
150            SET_AL( context, 0x80 );
151            break;
152        case 0x10:   /* XMS v2+ get driver address */
153        {
154             context->SegEs = DOSVM_dpmi_segments->xms_seg;
155             SET_BX( context, 0 );
156             break;
157        }
158        default:
159            INT_BARF( context, 0x2f );
160        }
161 #else
162        FIXME("check for XMS (not supported)\n");
163        SET_AL( context, 0x42 ); /* != 0x80 */
164 #endif
165        break;
166
167     case 0x45:
168        switch (LOBYTE(context->Eax))
169        {
170        case 0x00:
171        case 0x01:
172        case 0x02:
173        case 0x03:
174        case 0x04:
175        case 0x05:
176        case 0x06:
177        case 0x07:
178        case 0x08:
179            /* Microsoft Profiler - not installed */
180            break;
181        default:
182             INT_BARF( context, 0x2f );
183        }
184        break;
185
186     case 0x4a:
187         switch(LOBYTE(context->Eax))
188         {
189        case 0x10:  /* smartdrv */
190            break;  /* not installed */
191         case 0x11:  /* dblspace */
192             break;  /* not installed */
193         case 0x12:  /* realtime compression interface */
194             break;  /* not installed */
195         case 0x32:  /* patch IO.SYS (???) */
196             break;  /* we have no IO.SYS, so we can't patch it :-/ */
197         default:
198             INT_BARF( context, 0x2f );
199         }
200         break;
201     case 0x4b:
202        switch(LOBYTE(context->Eax))
203        {
204        case 0x01:
205        case 0x02:
206        case 0x03:
207        case 0x04:
208        case 0x05:
209            FIXME("Task Switcher - not implemented\n");
210            break;
211        default:
212            INT_BARF( context, 0x2f );
213        }
214        break;
215     case 0x56:  /* INTERLNK */
216        switch(LOBYTE(context->Eax))
217        {
218        case 0x01:  /* check if redirected drive */
219            SET_AL( context, 0 ); /* not redirected */
220            break;
221        default:
222            INT_BARF( context, 0x2f );
223        }
224        break;
225     case 0x7a:  /* NOVELL NetWare */
226         switch (LOBYTE(context->Eax))
227         {
228        case 0x0:  /* Low-level Netware installation check AL=0 not installed.*/
229             SET_AL( context, 0 );
230             break;
231         case 0x20:  /* Get VLM Call Address */
232             /* return nothing -> NetWare not installed */
233             break;
234         default:
235            INT_BARF( context, 0x2f );
236             break;
237         }
238         break;
239     case 0xb7:  /* append */
240         SET_AL( context, 0 ); /* not installed */
241         break;
242     case 0xb8:  /* network */
243         switch (LOBYTE(context->Eax))
244         {
245         case 0x00:  /* Install check */
246             /* not installed */
247             break;
248         default:
249            INT_BARF( context, 0x2f );
250             break;
251         }
252         break;
253     case 0xbc:
254         if (AL_reg(context) == 0x00 && BX_reg(context) == 0x3f3f) {
255             /* MVSOUND.SYS - Install check: not installed */
256         } else { 
257             INT_BARF( context, 0x2f );
258         }
259        break;
260     case 0xbd:  /* some Novell network install check ??? */
261         SET_AX( context, 0xa5a5 ); /* pretend to have Novell IPX installed */
262        break;
263     case 0xbf:  /* REDIRIFS.EXE */
264         switch (LOBYTE(context->Eax))
265         {
266         case 0x00:  /* Install check */
267             /* not installed */
268             break;
269         default:
270            INT_BARF( context, 0x2f );
271             break;
272         }
273         break;
274     case 0xd2:
275        switch(LOBYTE(context->Eax))
276        {
277        case 0x01: /* Quarterdeck RPCI - QEMM/QRAM - PCL-838.EXE functions */
278            if(BX_reg(context) == 0x5145 && CX_reg(context) == 0x4D4D
279              && DX_reg(context) == 0x3432)
280                TRACE("Check for QEMM v5.0+ (not installed)\n");
281            break;
282        default:
283            INT_BARF( context, 0x2f );
284            break;
285        }
286        break;
287     case 0xd7:  /* Banyan Vines */
288         switch (LOBYTE(context->Eax))
289         {
290         case 0x01:  /* Install check - Get Int Number */
291             /* not installed */
292             break;
293         default:
294            INT_BARF( context, 0x2f );
295             break;
296         }
297         break;
298     case 0xde:
299        switch(LOBYTE(context->Eax))
300        {
301        case 0x01:   /* Quarterdeck QDPMI.SYS - DESQview */
302            if(BX_reg(context) == 0x4450 && CX_reg(context) == 0x4d49
303              && DX_reg(context) == 0x8f4f)
304                TRACE("Check for QDPMI.SYS (not installed)\n");
305            break;
306        default:
307            INT_BARF( context, 0x2f );
308            break;
309        }
310        break;
311     case 0xfa:  /* Watcom debugger check, returns 0x666 if installed */
312         break;
313     default:
314         INT_BARF( context, 0x2f );
315         break;
316     }
317 }
318
319
320 /**********************************************************************
321  *         do_int2f_16
322  */
323 static void do_int2f_16( CONTEXT *context )
324 {
325     DWORD addr;
326
327     switch(LOBYTE(context->Eax))
328     {
329     case 0x00:  /* Windows enhanced mode installation check */
330         SET_AX( context, LOWORD(GetVersion16()) );
331         break;
332
333     case 0x0a:  /* Get Windows version and type */
334         SET_AX( context, 0 );
335         SET_BX( context, (LOWORD(GetVersion16()) << 8) | (LOWORD(GetVersion16()) >> 8) );
336         SET_CX( context, 3 );
337         break;
338
339     case 0x0b:  /* Identify Windows-aware TSRs */
340         /* we don't have any pre-Windows TSRs */
341         break;
342
343     case 0x11:  /* Get Shell Parameters - (SHELL= in CONFIG.SYS) */
344         /* We can mock this up. But not today... */
345         FIXME("Get Shell Parameters\n");
346         break;
347
348     case 0x80:  /* Release time-slice */
349        /* Linux sched_yield() still keeps burning CPU cycles
350         * if the current process is the only one in highest priority list
351         * (as Linux will immediately return to this process to waste
352         * more CPU cycles), so sched_yield() is essentially useless for us
353         * (poor API, if you ask me: its return code should indicate
354         * whether other processes did run in between, in order for us
355         * to be able to decide whether to do an additional Sleep() or not...)
356         * Thus we better unconditionally use a well-balanced Sleep()
357         * instead to really make sure the process calling int 0x2f/0x1680
358         * *doesn't* use 100% CPU...
359         */
360        Sleep(55); /* just wait 55ms (one "timer tick") for now. */
361        SET_AL( context, 0 );
362         break;
363
364     case 0x81: /* Begin critical section.  */
365         /* FIXME? */
366         break;
367
368     case 0x82: /* End critical section.  */
369         /* FIXME? */
370         break;
371
372     case 0x83:  /* Return Current Virtual Machine ID */
373         /* Virtual Machines are usually created/destroyed when Windows runs
374          * DOS programs. Since we never do, we are always in the System VM.
375          * According to Ralf Brown's Interrupt List, never return 0. But it
376          * seems to work okay (returning 0), just to be sure we return 1.
377          */
378        SET_BX( context, 1 ); /* VM 1 is probably the System VM */
379        break;
380
381     case 0x84:  /* Get device API entry point */
382         {
383             HMODULE16 mod = GetModuleHandle16("kernel");
384             addr = (DWORD)GetProcAddress16( mod, (LPCSTR)(VXD_BASE + BX_reg(context)) );
385             if (!addr)  /* not supported */
386                 ERR("Accessing unknown VxD %04x - Expect a failure now.\n", BX_reg(context) );
387             context->SegEs = SELECTOROF(addr);
388             SET_DI( context, OFFSETOF(addr) );
389         }
390        break;
391
392     case 0x86:  /* DPMI detect mode */
393         SET_AX( context, 0 );  /* Running under DPMI */
394         break;
395
396     case 0x87: /* DPMI installation check */
397         {
398            SYSTEM_INFO si;
399            GetSystemInfo(&si);
400            SET_AX( context, 0x0000 ); /* DPMI Installed */
401             SET_BX( context, 0x0001 ); /* 32bits available */
402             SET_CL( context, si.wProcessorLevel );
403             SET_DX( context, 0x005a ); /* DPMI major/minor 0.90 */
404             SET_SI( context, 0 );      /* # of para. of DOS extended private data */
405             context->SegEs = DOSVM_dpmi_segments->dpmi_seg;
406             SET_DI( context, 0 );      /* ES:DI is DPMI switch entry point */
407             break;
408         }
409     case 0x8a:  /* DPMI get vendor-specific API entry point. */
410        /* The 1.0 specs say this should work with all 0.9 hosts.  */
411        break;
412
413     default:
414         INT_BARF( context, 0x2f );
415     }
416 }
417
418 /* FIXME: this macro may have to be changed on architectures where <size> reads/writes
419  * must be <size> aligned
420  * <size> could be WORD, DWORD...
421  * in this case, we would need two macros, one for read, the other one for write
422  * Note: PTR_AT can be used as an l-value
423  */
424 #define        PTR_AT(_ptr, _ofs, _typ)        (*((_typ*)(((char*)_ptr)+(_ofs))))
425
426 /* Use #if 1 if you want full int 2f debug... normal users can leave it at 0 */
427 #if 0
428 /**********************************************************************
429  *         MSCDEX_Dump                                 [internal]
430  *
431  * Dumps mscdex requests to int debug channel.
432  */
433 static void    MSCDEX_Dump(char* pfx, BYTE* req, int dorealmode)
434 {
435     int        i;
436     BYTE       buf[2048];
437     BYTE*      ptr;
438     BYTE*      ios;
439
440     ptr = buf;
441     ptr += sprintf(ptr, "%s\tCommand => ", pfx);
442     for (i = 0; i < req[0]; i++) {
443        ptr += sprintf(ptr, "%02x ", req[i]);
444     }
445
446     switch (req[2]) {
447     case 3:
448     case 12:
449        ptr += sprintf(ptr, "\n\t\t\t\tIO_struct => ");
450        ios = (dorealmode) ? PTR_REAL_TO_LIN( PTR_AT(req, 16, WORD), PTR_AT(req, 14, WORD)) :
451                              MapSL(MAKESEGPTR(PTR_AT(req, 16, WORD), PTR_AT(req, 14, WORD)));
452
453        for (i = 0; i < PTR_AT(req, 18, WORD); i++) {
454            ptr += sprintf(ptr, "%02x ", ios[i]);
455            if ((i & 0x1F) == 0x1F) {
456                *ptr++ = '\n';
457                *ptr = 0;
458            }
459        }
460        break;
461     }
462     TRACE("%s\n", buf);
463 }
464 #else
465 #define MSCDEX_Dump(pfx, req, drm)
466 #endif
467
468 #define CDFRAMES_PERSEC                 75
469 #define CDFRAMES_PERMIN                 (CDFRAMES_PERSEC * 60)
470 #define FRAME_OF_ADDR(a)        ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
471 #define FRAME_OF_TOC(toc, idx)  FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
472 #define CTRL_OF_TOC(toc, idx)   (((toc).TrackData[idx - (toc).FirstTrack].Control << 4) | \
473                                   (toc).TrackData[idx - (toc).FirstTrack].Adr)
474
475 static void    MSCDEX_StoreMSF(DWORD frame, BYTE* val)
476 {
477     val[3] = 0;        /* zero */
478     val[2] = frame / CDFRAMES_PERMIN; /* minutes */
479     val[1] = (frame / CDFRAMES_PERSEC) % 60; /* seconds */
480     val[0] = frame % CDFRAMES_PERSEC; /* frames */
481 }
482
483 static int is_cdrom( int drive)
484 {
485     char root[] = "A:\\";
486     root[0] += drive;
487     return (GetDriveTypeA(root) == DRIVE_CDROM);
488 }
489
490 /***********************************************************************
491  *           CDROM_FillHeap
492  *
493  * Initialize CDROM heap.
494  *
495  */
496 static void CDROM_FillHeap( CDROM_HEAP *heap )
497 {
498     int drive, count;
499
500     /* Count the number of contiguous CDROM drives
501      */
502     for (drive = count = 0; drive < 26; drive++) {
503         if (is_cdrom(drive)) {
504             while (is_cdrom(drive + count)) count++;
505             break;
506         }
507     }
508     TRACE("Installation check: %d cdroms, starting at %d\n", count, drive);
509     heap->hdr.drive = (drive < 26) ? drive : 0;
510     heap->hdr.units = count;
511     heap->hdr.reserved = 0;
512 }
513
514 /**********************************************************************
515  *         CDROM_GetHeap
516  *
517  * Get pointer for CDROM heap (CDROM_HEAP).
518  * Creates and initializes heap on first call.
519  */
520 static CDROM_HEAP *CDROM_GetHeap( void )
521 {
522     static CDROM_HEAP *heap_pointer = NULL;
523
524     if ( !heap_pointer )
525     {
526         WORD heap_segment;
527         WORD heap_selector;
528
529         /* allocate a new DOS data segment */
530         heap_pointer = DOSVM_AllocDataUMB( sizeof(CDROM_HEAP),
531                                            &heap_segment,
532                                            &heap_selector );
533
534         heap_pointer->cdrom_segment  = heap_segment;
535         heap_pointer->cdrom_selector = heap_selector;
536         CDROM_FillHeap( heap_pointer );
537     }
538
539     return heap_pointer;
540 }
541
542 static void MSCDEX_Request(BYTE *driver_request, BOOL dorealmode)
543 {
544     BYTE*       io_stru;
545     BYTE        Error = 255; /* No Error */
546     char        devName[] = "\\\\.\\@:";
547     HANDLE      h;
548     CDROM_TOC                   toc;
549     CDROM_SUB_Q_DATA_FORMAT     fmt;
550     SUB_Q_CHANNEL_DATA          data;
551     DWORD                       br;
552     DWORD                       present = TRUE;
553
554     /* FIXME
555      * the following tests are wrong because lots of functions don't require the
556      * tray to be closed with a CD inside
557      */
558     TRACE("CDROM device driver -> command <%d>\n", driver_request[2]);
559
560     MSCDEX_Dump("Beg", driver_request, dorealmode);
561
562     /* set status to 0 */
563     PTR_AT(driver_request, 3, WORD) = 0;
564     devName[4] = 'A' + CDROM_GetHeap()->hdr.drive + driver_request[1];
565     h = CreateFileA(devName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
566     if (h == INVALID_HANDLE_VALUE) {
567         WARN("Couldn't open cdrom handle\n");
568         driver_request[4] |= 0x80;
569         driver_request[3] = 1;  /* unknown unit */
570         return;
571     }
572
573     fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
574     if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, NULL) ||
575         !DeviceIoControl(h, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
576                          &data, sizeof(data), &br, NULL)) {
577         if (GetLastError() == STATUS_NO_MEDIA_IN_DEVICE)
578         {
579             if (driver_request[2] != 6 && driver_request[2] != 15)
580             {
581                 driver_request[4] |= 0x80;
582                 driver_request[3] = 2; /* drive not ready */
583                 CloseHandle(h);
584                 return;
585             }
586             present = FALSE;
587         }
588         else
589         {
590             driver_request[4] |= 0x80;
591             driver_request[3] = 1;     /* unknown unit */
592             CloseHandle(h);
593             return;
594         }
595     }
596
597     switch (driver_request[2]) {
598     case 3:
599         io_stru = (dorealmode) ?
600             PTR_REAL_TO_LIN( PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD) ) :
601             MapSL( MAKESEGPTR(PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD)));
602
603         TRACE(" --> IOCTL INPUT <%d>\n", io_stru[0]);
604         switch (io_stru[0]) {
605 #if 0
606         case 0: /* Get device Header */
607         {
608             static  LPSTR ptr = 0;
609             if (ptr == 0)   {
610                 ptr = SEGPTR_ALLOC(22);
611                 PTR_AT(ptr,  0, DWORD) = ~1;        /* Next Device Driver */
612                 PTR_AT(ptr,  4,  WORD) = 0xC800;    /* Device attributes  */
613                 PTR_AT(ptr,  6,  WORD) = 0x1234;    /* Pointer to device strategy routine: FIXME */
614                 PTR_AT(ptr,  8,  WORD) = 0x3142;    /* Pointer to device interrupt routine: FIXME */
615                 PTR_AT(ptr, 10,  char) = 'W';       /* 8-byte character device name field */
616                 PTR_AT(ptr, 11,  char) = 'I';
617                 PTR_AT(ptr, 12,  char) = 'N';
618                 PTR_AT(ptr, 13,  char) = 'E';
619                 PTR_AT(ptr, 14,  char) = '_';
620                 PTR_AT(ptr, 15,  char) = 'C';
621                 PTR_AT(ptr, 16,  char) = 'D';
622                 PTR_AT(ptr, 17,  char) = '_';
623                 PTR_AT(ptr, 18,  WORD) = 0;         /* Reserved (must be zero) */
624                 PTR_AT(ptr, 20,  BYTE) = 0;         /* Drive letter (must be zero) */
625                 PTR_AT(ptr, 21,  BYTE) = 1;         /* Number of units supported (one or more) FIXME*/
626             }
627             PTR_AT(io_stru, DWORD,  0) = SEGPTR_GET(ptr);
628         }
629         break;
630 #endif
631
632         case 1: /* location of head */
633             switch (io_stru[1]) {
634             case 0:
635                 PTR_AT(io_stru, 2, DWORD) =
636                     FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
637                 break;
638             case 1:
639                 MSCDEX_StoreMSF(FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress),
640                                 io_stru + 2);
641                 break;
642             default:
643                 ERR("CD-ROM driver: unsupported addressing mode !!\n");
644                 Error = 0x0c;
645             }
646             TRACE(" ----> HEAD LOCATION <%d>\n", PTR_AT(io_stru, 2, DWORD));
647             break;
648
649         case 4: /* Audio channel info */
650             io_stru[1] = 0;
651             io_stru[2] = 0xff;
652             io_stru[3] = 1;
653             io_stru[4] = 0xff;
654             io_stru[5] = 2;
655             io_stru[6] = 0;
656             io_stru[7] = 3;
657             io_stru[8] = 0;
658             TRACE(" ----> AUDIO CHANNEL INFO\n");
659             break;
660
661         case 6: /* device status */
662             PTR_AT(io_stru, 1, DWORD) = 0x00000290;
663             /* 290 =>
664              * 1        Supports HSG and Red Book addressing modes
665              * 0        Supports audio channel manipulation
666              *
667              * 1        Supports prefetching requests
668              * 0        Reserved
669              * 0        No interleaving
670              * 1        Data read and plays audio/video tracks
671              *
672              * 0        Read only
673              * 0        Supports only cooked reading
674              * 0        Door locked
675              * 0        see below (Door closed/opened)
676              */
677             if (!present) PTR_AT(io_stru, 1, DWORD) |= 1;
678             TRACE(" ----> DEVICE STATUS <0x%08x>\n", PTR_AT(io_stru, 1, DWORD));
679             break;
680
681         case 8: /* Volume size */
682             PTR_AT(io_stru, 1, DWORD) = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
683                 FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
684             TRACE(" ----> VOLUME SIZE <%d>\n", PTR_AT(io_stru, 1, DWORD));
685             break;
686
687         case 9: /* media changed ? */
688             /* answers don't know... -1/1 for yes/no would be better */
689             io_stru[1] = 0; /* FIXME? 1? */
690             TRACE(" ----> MEDIA CHANGED <%d>\n", io_stru[1]);
691             break;
692
693         case 10: /* audio disk info */
694             io_stru[1] = toc.FirstTrack; /* starting track of the disc */
695             io_stru[2] = toc.LastTrack;  /* ending track */
696             MSCDEX_StoreMSF(FRAME_OF_TOC(toc, toc.LastTrack + 1) -
697                             FRAME_OF_TOC(toc, toc.FirstTrack) - 1, io_stru + 3);
698
699             TRACE(" ----> AUDIO DISK INFO <%d-%d/%08x>\n",
700                   io_stru[1], io_stru[2], PTR_AT(io_stru, 3, DWORD));
701             break;
702
703         case 11: /* audio track info */
704             if (io_stru[1] >= toc.FirstTrack && io_stru[1] <= toc.LastTrack) {
705                 MSCDEX_StoreMSF(FRAME_OF_TOC(toc, io_stru[1]), io_stru + 2);
706                 /* starting point if the track */
707                 io_stru[6] = CTRL_OF_TOC(toc, io_stru[1]);
708             } else {
709                 PTR_AT(io_stru, 2, DWORD) = 0;
710                 io_stru[6] = 0;
711             }
712             TRACE(" ----> AUDIO TRACK INFO[%d] = [%08x:%d]\n",
713                   io_stru[1], PTR_AT(io_stru, 2, DWORD), io_stru[6]);
714             break;
715
716         case 12: /* get Q-Channel info */
717             io_stru[1] = CTRL_OF_TOC(toc, data.CurrentPosition.TrackNumber);
718             io_stru[2] = data.CurrentPosition.TrackNumber;
719             io_stru[3] = 0; /* FIXME ?? */
720
721             /* why the heck did MS use another format for 0MSF information... sigh */
722             {
723                 BYTE    bTmp[4];
724
725                 MSCDEX_StoreMSF(FRAME_OF_ADDR(data.CurrentPosition.TrackRelativeAddress), bTmp);
726                 io_stru[ 4] = bTmp[2];
727                 io_stru[ 5] = bTmp[1];
728                 io_stru[ 6] = bTmp[0];
729                 io_stru[ 7] = 0;
730
731                 MSCDEX_StoreMSF(FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress), bTmp);
732                 io_stru[ 8] = bTmp[2];
733                 io_stru[ 9] = bTmp[1];
734                 io_stru[10] = bTmp[0];
735                 io_stru[11] = 0;
736             }
737             TRACE("Q-Channel info: Ctrl/adr=%02x TNO=%02x X=%02x rtt=%02x:%02x:%02x rtd=%02x:%02x:%02x (cf=%08x, tp=%08x)\n",
738                   io_stru[ 1], io_stru[ 2], io_stru[ 3],
739                   io_stru[ 4], io_stru[ 5], io_stru[ 6],
740                   io_stru[ 8], io_stru[ 9], io_stru[10],
741                   FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress),
742                   FRAME_OF_TOC(toc, data.CurrentPosition.TrackNumber));
743             break;
744
745         case 15: /* Audio status info */
746             /* !!!! FIXME FIXME FIXME !! */
747             PTR_AT(io_stru, 1,  WORD) = 2 | ((data.CurrentPosition.Header.AudioStatus == AUDIO_STATUS_PAUSED) ? 1 : 0);
748             if (!present) {
749                 PTR_AT(io_stru, 3, DWORD) = 0;
750                 PTR_AT(io_stru, 7, DWORD) = 0;
751             } else {
752                 PTR_AT(io_stru, 3, DWORD) = FRAME_OF_TOC(toc, toc.FirstTrack);
753                 PTR_AT(io_stru, 7, DWORD) = FRAME_OF_TOC(toc, toc.LastTrack + 1);
754             }
755             TRACE("Audio status info: status=%04x startLoc=%d endLoc=%d\n",
756                   PTR_AT(io_stru, 1, WORD), PTR_AT(io_stru, 3, DWORD), PTR_AT(io_stru, 7, DWORD));
757             break;
758
759         default:
760             FIXME("IOCTL INPUT: Unimplemented <%d>!!\n", io_stru[0]);
761             Error = 0x0c;
762             break;
763         }
764         break;
765
766     case 12:
767         io_stru = (dorealmode) ?
768             PTR_REAL_TO_LIN( PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD)) :
769             MapSL( MAKESEGPTR(PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD)));
770
771         TRACE(" --> IOCTL OUTPUT <%d>\n", io_stru[0]);
772         switch (io_stru[0]) {
773         case 0: /* eject */
774             DeviceIoControl(h, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &br, NULL);
775             TRACE(" ----> EJECT\n");
776             break;
777         case 2: /* reset drive */
778             DeviceIoControl(h, IOCTL_STORAGE_RESET_DEVICE, NULL, 0, NULL, 0, &br, NULL);
779             TRACE(" ----> RESET\n");
780             break;
781         case 3: /* Audio Channel Control */
782             FIXME(" ----> AUDIO CHANNEL CONTROL (NIY)\n");
783             break;
784         case 5: /* close tray */
785             DeviceIoControl(h, IOCTL_STORAGE_LOAD_MEDIA, NULL, 0, NULL, 0, &br, NULL);
786             TRACE(" ----> CLOSE TRAY\n");
787             break;
788         default:
789             FIXME(" IOCTL OUTPUT: Unimplemented <%d>!!\n", io_stru[0]);
790             Error = 0x0c;
791             break;
792         }
793         break;
794
795     case 128: /* read long */
796     {
797         LPVOID              dst = MapSL(MAKESEGPTR(PTR_AT(driver_request, 16, WORD),
798                                                    PTR_AT(driver_request, 14, WORD)));
799         DWORD               at = PTR_AT(driver_request, 20, DWORD);
800         WORD                num = PTR_AT(driver_request, 18, WORD);
801         RAW_READ_INFO       rri;
802
803         switch (driver_request[13]) {
804         case 1: /* Red book addressing mode = 0:m:s:f */
805             /* FIXME : frame <=> msf conversion routines could be shared
806              * between mscdex and mcicda
807              */
808             at = LOBYTE(HIWORD(at)) * CDFRAMES_PERMIN +
809                 HIBYTE(LOWORD(at)) * CDFRAMES_PERSEC +
810                 LOBYTE(LOWORD(at));
811             /* fall through */
812         case 0: /* HSG addressing mode */
813             switch (PTR_AT(driver_request, 24, BYTE))
814             {
815             case 0: /* cooked */
816                 ReadFile(h, dst, num * 2048, &br, NULL);
817                 break;
818             case 1:
819                 /* FIXME: computation is wrong */
820                 rri.DiskOffset.u.HighPart = 0;
821                 rri.DiskOffset.u.LowPart = at << 11;
822                 rri.TrackMode = YellowMode2;
823                 rri.SectorCount = num;
824                 DeviceIoControl(h, IOCTL_CDROM_RAW_READ, &rri, sizeof(rri),
825                                 dst, num * 2352, &br, NULL);
826                 break;
827             default:
828                 ERR("Unsupported read mode !!\n");
829                 Error = 0x0c;
830                 break;
831             }
832             break;
833         default:
834             ERR("Unsupported address mode !!\n");
835             Error = 0x0c;
836             break;
837         }
838     }
839     break;
840
841     case 131: /* seek */
842     {
843         DWORD                       at;
844         CDROM_SEEK_AUDIO_MSF        seek;
845
846         at = PTR_AT(driver_request, 20, DWORD);
847
848         TRACE(" --> SEEK AUDIO mode :<0x%02X>, [%d]\n", driver_request[13], at);
849
850         switch (driver_request[13]) {
851         case 1: /* Red book addressing mode = 0:m:s:f */
852             /* FIXME : frame <=> msf conversion routines could be shared
853              * between mscdex and mcicda
854              */
855             at = LOBYTE(HIWORD(at)) * CDFRAMES_PERMIN +
856                 HIBYTE(LOWORD(at)) * CDFRAMES_PERSEC +
857                 LOBYTE(LOWORD(at));
858             /* fall through */
859         case 0: /* HSG addressing mode */
860             seek.M = at / CDFRAMES_PERMIN;
861             seek.S = (at / CDFRAMES_PERSEC) % 60;
862             seek.F = at % CDFRAMES_PERSEC;
863             DeviceIoControl(h, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
864                             NULL, 0, &br, NULL);
865             break;
866         default:
867             ERR("Unsupported address mode !!\n");
868             Error = 0x0c;
869             break;
870         }
871     }
872     break;
873
874     case 132: /* play */
875     {
876         DWORD                       beg, end;
877         CDROM_PLAY_AUDIO_MSF        play;
878
879         beg = end = PTR_AT(driver_request, 14, DWORD);
880         end += PTR_AT(driver_request, 18, DWORD);
881
882         TRACE(" --> PLAY AUDIO mode :<0x%02X>, [%d-%d]\n", driver_request[13], beg, end);
883
884         switch (driver_request[13]) {
885         case 1:
886             /* Red book addressing mode = 0:m:s:f */
887             /* FIXME : frame <=> msf conversion routines could be shared
888              * between mscdex and mcicda
889              */
890             beg = LOBYTE(LOWORD(beg)) * CDFRAMES_PERMIN +
891                 HIBYTE(LOWORD(beg)) * CDFRAMES_PERSEC +
892                 LOBYTE(HIWORD(beg));
893             end = LOBYTE(LOWORD(end)) * CDFRAMES_PERMIN +
894                 HIBYTE(LOWORD(end)) * CDFRAMES_PERSEC +
895                 LOBYTE(HIWORD(end));
896             /* fall through */
897         case 0: /* HSG addressing mode */
898             play.StartingM = beg / CDFRAMES_PERMIN;
899             play.StartingS = (beg / CDFRAMES_PERSEC) % 60;
900             play.StartingF = beg % CDFRAMES_PERSEC;
901             play.EndingM   = end / CDFRAMES_PERMIN;
902             play.EndingS   = (end / CDFRAMES_PERSEC) % 60;
903             play.EndingF   = end % CDFRAMES_PERSEC;
904             DeviceIoControl(h, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
905                             NULL, 0, &br, NULL);
906             break;
907         default:
908             ERR("Unsupported address mode !!\n");
909             Error = 0x0c;
910             break;
911         }
912     }
913     break;
914
915     case 133:
916         if (data.CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) {
917             DeviceIoControl(h, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL);
918             TRACE(" --> STOP AUDIO (Paused)\n");
919         } else {
920             DeviceIoControl(h, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL);
921             TRACE(" --> STOP AUDIO (Stopped)\n");
922         }
923         break;
924
925     case 136:
926         TRACE(" --> RESUME AUDIO\n");
927         DeviceIoControl(h, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL);
928         break;
929
930     default:
931         FIXME(" ioctl unimplemented <%d>\n", driver_request[2]);
932         Error = 0x0c;
933     }
934
935     /* setting error codes if any */
936     if (Error < 255) {
937         driver_request[4] |= 0x80;
938         driver_request[3] = Error;
939     }
940
941     CloseHandle(h);
942     /* setting status bits
943      * 3 == playing && done
944      * 1 == done
945      */
946     driver_request[4] |=
947         (data.CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) ? 3 : 1;
948
949     MSCDEX_Dump("End", driver_request, dorealmode);
950 }
951
952 static void MSCDEX_Handler(CONTEXT* context)
953 {
954     int        drive, count;
955     char*      p;
956
957     switch (LOBYTE(context->Eax)) {
958     case 0x00: /* Installation check */
959        /* Count the number of contiguous CDROM drives
960         */
961        for (drive = count = 0; drive < 26; drive++) {
962            if (is_cdrom(drive)) {
963                while (is_cdrom(drive + count)) count++;
964                break;
965            }
966        }
967        TRACE("Installation check: %d cdroms, starting at %d\n", count, drive);
968        SET_BX( context, count );
969        SET_CX( context, (drive < 26) ? drive : 0 );
970        break;
971
972     case 0x01: /* get drive device list */
973        {
974            CDROM_HEAP*          cdrom_heap = CDROM_GetHeap();
975            CDROM_DEVICE_HEADER* dev = &cdrom_heap->hdr;
976            SEGPTR ptr_dev = ISV86(context)
977                ? MAKESEGPTR( cdrom_heap->cdrom_segment,
978                              FIELD_OFFSET(CDROM_HEAP, hdr) )
979                : MAKESEGPTR( cdrom_heap->cdrom_selector,
980                              FIELD_OFFSET(CDROM_HEAP, hdr) );
981
982            p = CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx);
983            for (drive = 0; drive < dev->units; drive++) {
984                *p = drive; /* subunit */
985                ++p;
986                *(DWORD*)p = ptr_dev;
987                p += sizeof(DWORD);
988            }
989            TRACE("Get drive device list\n");
990        }
991        break;
992
993     case 0x0B: /* drive check */
994        SET_AX( context, is_cdrom(CX_reg(context)) );
995        SET_BX( context, 0xADAD );
996        break;
997
998     case 0x0C: /* get version */
999        SET_BX( context, 0x020a );
1000        TRACE("Version number => %04x\n", BX_reg(context));
1001        break;
1002
1003     case 0x0D: /* get drive letters */
1004        p = CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx);
1005        memset(p, 0, 26);
1006        for (drive = 0; drive < 26; drive++) {
1007            if (is_cdrom(drive)) *p++ = drive;
1008        }
1009        TRACE("Get drive letters\n");
1010        break;
1011
1012     case 0x10: /* direct driver access */
1013        {
1014            BYTE*       driver_request;
1015            CDROM_HEAP* cdrom_heap = CDROM_GetHeap();
1016
1017            if (!is_cdrom(CX_reg(context))) {
1018                WARN("Request made doesn't match a CD ROM drive (%d)\n", CX_reg(context));
1019                SET_CFLAG( context );
1020                SET_AX( context, 0x000f ); /* invalid drive */
1021                return;
1022            }
1023
1024            driver_request = CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx);
1025
1026            if (!driver_request) {
1027                /* FIXME - to be deleted ?? */
1028                ERR("ES:BX==0 ! SEGFAULT ?\n");
1029                ERR("-->BX=0x%04x, ES=0x%04x, DS=0x%04x, CX=0x%04x\n",
1030                    BX_reg(context), context->SegEs, context->SegDs, CX_reg(context));
1031                driver_request[4] |= 0x80;
1032                driver_request[3] = 5;  /* bad request length */
1033                return;
1034            }
1035
1036            driver_request[1] = CX_reg(context) - cdrom_heap->hdr.drive;
1037            MSCDEX_Request(driver_request, ISV86(context));
1038        }
1039        break;
1040     default:
1041        FIXME("Unimplemented MSCDEX function 0x%02X.\n", LOBYTE(context->Eax));
1042        break;
1043     }
1044 }
1045
1046 /* prototypes */
1047 static void WINAPI cdrom_strategy(CONTEXT*ctx);
1048 static void WINAPI cdrom_interrupt(CONTEXT*ctx);
1049
1050 /* device info */
1051 static const WINEDEV cdromdev =
1052 {
1053     "WINE_CD_",
1054     ATTR_CHAR|ATTR_REMOVABLE|ATTR_IOCTL,
1055     cdrom_strategy, cdrom_interrupt
1056 };
1057
1058 static REQUEST_HEADER *cdrom_driver_request;
1059
1060 /* Return to caller */
1061 static void do_lret(CONTEXT*ctx)
1062 {
1063     WORD *stack = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegSs, ctx->Esp);
1064
1065     ctx->Eip   = *(stack++);
1066     ctx->SegCs = *(stack++);
1067     ctx->Esp  += 2*sizeof(WORD);
1068 }
1069
1070 static void WINAPI cdrom_strategy(CONTEXT*ctx)
1071 {
1072     cdrom_driver_request = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegEs, ctx->Ebx);
1073     do_lret( ctx );
1074 }
1075
1076 static void WINAPI cdrom_interrupt(CONTEXT*ctx)
1077 {
1078     if (cdrom_driver_request->unit > CDROM_GetHeap()->hdr.units)
1079         cdrom_driver_request->status = STAT_ERROR | 1; /* unknown unit */
1080     else
1081         MSCDEX_Request((BYTE*)cdrom_driver_request, ISV86(ctx));
1082
1083     do_lret( ctx );
1084 }
1085
1086 /**********************************************************************
1087  *         MSCDEX_InstallCDROM  [internal]
1088  *
1089  * Install the CDROM driver into the DOS device driver chain.
1090  */
1091 void MSCDEX_InstallCDROM(void)
1092 {
1093     CDROM_HEAP *cdrom_heap = CDROM_GetHeap();
1094
1095     DOSDEV_SetupDevice( &cdromdev,
1096                         cdrom_heap->cdrom_segment,
1097                         FIELD_OFFSET(CDROM_HEAP, hdr),
1098                         FIELD_OFFSET(CDROM_HEAP, thunk) );
1099 }