Trivial cleanups to the OLE section.
[wine.git] / dlls / winedos / devices.c
blobfe58d6d940b770095a94a1e7e1404cf6659be26e
1 /*
2 * DOS devices
4 * Copyright 1999 Ove Kåven
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <stdlib.h>
22 #include <string.h>
23 #include "wine/winbase16.h"
24 #include "msdos.h"
25 #include "miscemu.h"
26 #include "dosexe.h"
27 #include "wine/debug.h"
29 #include "pshpack1.h"
31 typedef struct {
32 BYTE ljmp1;
33 RMCBPROC strategy;
34 BYTE ljmp2;
35 RMCBPROC interrupt;
36 } WINEDEV_THUNK;
38 typedef struct {
39 BYTE size; /* length of header + data */
40 BYTE unit; /* unit (block devices only) */
41 BYTE command;
42 WORD status;
43 BYTE reserved[8];
44 } REQUEST_HEADER;
46 typedef struct {
47 REQUEST_HEADER hdr;
48 BYTE media; /* media descriptor from BPB */
49 SEGPTR buffer;
50 WORD count; /* byte/sector count */
51 WORD sector; /* starting sector (block devices) */
52 DWORD volume; /* volume ID (block devices) */
53 } REQ_IO;
55 typedef struct {
56 REQUEST_HEADER hdr;
57 BYTE data;
58 } REQ_SAFEINPUT;
60 #include "poppack.h"
62 #define CON_BUFFER 128
64 enum strategy { SYSTEM_STRATEGY_NUL, SYSTEM_STRATEGY_CON, NB_SYSTEM_STRATEGIES };
66 static void *strategy_data[NB_SYSTEM_STRATEGIES];
68 #define NONEXT ((DWORD)-1)
70 #define ATTR_STDIN 0x0001
71 #define ATTR_STDOUT 0x0002
72 #define ATTR_NUL 0x0004
73 #define ATTR_CLOCK 0x0008
74 #define ATTR_FASTCON 0x0010
75 #define ATTR_RAW 0x0020
76 #define ATTR_NOTEOF 0x0040
77 #define ATTR_DEVICE 0x0080
78 #define ATTR_REMOVABLE 0x0800
79 #define ATTR_NONIBM 0x2000 /* block devices */
80 #define ATTR_UNTILBUSY 0x2000 /* char devices */
81 #define ATTR_IOCTL 0x4000
82 #define ATTR_CHAR 0x8000
84 #define CMD_INIT 0
85 #define CMD_MEDIACHECK 1 /* block devices */
86 #define CMD_BUILDBPB 2 /* block devices */
87 #define CMD_INIOCTL 3
88 #define CMD_INPUT 4 /* read data */
89 #define CMD_SAFEINPUT 5 /* "non-destructive input no wait", char devices */
90 #define CMD_INSTATUS 6 /* char devices */
91 #define CMD_INFLUSH 7 /* char devices */
92 #define CMD_OUTPUT 8 /* write data */
93 #define CMD_SAFEOUTPUT 9 /* write data with verify */
94 #define CMD_OUTSTATUS 10 /* char devices */
95 #define CMD_OUTFLUSH 11 /* char devices */
96 #define CMD_OUTIOCTL 12
97 #define CMD_DEVOPEN 13
98 #define CMD_DEVCLOSE 14
99 #define CMD_REMOVABLE 15 /* block devices */
100 #define CMD_UNTILBUSY 16 /* output until busy */
102 #define STAT_MASK 0x00FF
103 #define STAT_DONE 0x0100
104 #define STAT_BUSY 0x0200
105 #define STAT_ERROR 0x8000
107 #define LJMP 0xea
110 /* prototypes */
111 static void WINAPI nul_strategy(CONTEXT86*ctx);
112 static void WINAPI nul_interrupt(CONTEXT86*ctx);
113 static void WINAPI con_strategy(CONTEXT86*ctx);
114 static void WINAPI con_interrupt(CONTEXT86*ctx);
116 /* devices */
117 typedef struct
119 char name[8];
120 WORD attr;
121 RMCBPROC strategy;
122 RMCBPROC interrupt;
123 } WINEDEV;
125 static WINEDEV devs[] =
127 { "NUL ",
128 ATTR_CHAR|ATTR_NUL|ATTR_DEVICE,
129 nul_strategy, nul_interrupt },
131 { "CON ",
132 ATTR_CHAR|ATTR_STDIN|ATTR_STDOUT|ATTR_FASTCON|ATTR_NOTEOF|ATTR_DEVICE,
133 con_strategy, con_interrupt }
136 #define NR_DEVS (sizeof(devs)/sizeof(WINEDEV))
138 /* DOS data segment */
139 typedef struct
141 DOS_LISTOFLISTS lol;
142 DOS_DEVICE_HEADER dev[NR_DEVS-1];
143 WINEDEV_THUNK thunk[NR_DEVS];
144 REQ_IO req;
145 BYTE buffer[CON_BUFFER];
147 } DOS_DATASEG;
149 #define DOS_DATASEG_OFF(xxx) FIELD_OFFSET(DOS_DATASEG, xxx)
151 DWORD DOS_LOLSeg;
153 struct _DOS_LISTOFLISTS * DOSMEM_LOL()
155 return PTR_REAL_TO_LIN(HIWORD(DOS_LOLSeg),0);
159 /* the device implementations */
160 static void do_lret(CONTEXT86*ctx)
162 WORD *stack = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegSs, ctx->Esp);
164 ctx->Eip = *(stack++);
165 ctx->SegCs = *(stack++);
166 ctx->Esp += 2*sizeof(WORD);
169 static void do_strategy(CONTEXT86*ctx, int id, int extra)
171 REQUEST_HEADER *hdr = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegEs, ctx->Ebx);
172 void **hdr_ptr = strategy_data[id];
174 if (!hdr_ptr) {
175 hdr_ptr = calloc(1,sizeof(void *)+extra);
176 strategy_data[id] = hdr_ptr;
178 *hdr_ptr = hdr;
179 do_lret(ctx);
182 static REQUEST_HEADER * get_hdr(int id, void**extra)
184 void **hdr_ptr = strategy_data[id];
185 if (extra)
186 *extra = hdr_ptr ? (void*)(hdr_ptr+1) : (void *)NULL;
187 return hdr_ptr ? *hdr_ptr : (void *)NULL;
190 static void WINAPI nul_strategy(CONTEXT86*ctx)
192 do_strategy(ctx, SYSTEM_STRATEGY_NUL, 0);
195 static void WINAPI nul_interrupt(CONTEXT86*ctx)
197 REQUEST_HEADER *hdr = get_hdr(SYSTEM_STRATEGY_NUL, NULL);
198 /* eat everything and recycle nothing */
199 switch (hdr->command) {
200 case CMD_INPUT:
201 ((REQ_IO*)hdr)->count = 0;
202 hdr->status = STAT_DONE;
203 break;
204 case CMD_SAFEINPUT:
205 hdr->status = STAT_DONE|STAT_BUSY;
206 break;
207 default:
208 hdr->status = STAT_DONE;
210 do_lret(ctx);
213 static void WINAPI con_strategy(CONTEXT86*ctx)
215 do_strategy(ctx, SYSTEM_STRATEGY_CON, sizeof(int));
218 static void WINAPI con_interrupt(CONTEXT86*ctx)
220 int *scan;
221 REQUEST_HEADER *hdr = get_hdr(SYSTEM_STRATEGY_CON,(void **)&scan);
222 BIOSDATA *bios = BIOS_DATA;
223 WORD CurOfs = bios->NextKbdCharPtr;
224 DOS_LISTOFLISTS *lol = DOSMEM_LOL();
225 DOS_DATASEG *dataseg = (DOS_DATASEG *)lol;
226 BYTE *linebuffer = dataseg->buffer;
227 BYTE *curbuffer = (lol->offs_unread_CON) ?
228 (((BYTE*)dataseg) + lol->offs_unread_CON) : (BYTE*)NULL;
229 DOS_DEVICE_HEADER *con = dataseg->dev;
231 switch (hdr->command) {
232 case CMD_INPUT:
234 REQ_IO *io = (REQ_IO *)hdr;
235 WORD count = io->count, len = 0;
236 BYTE *buffer = CTX_SEG_OFF_TO_LIN(ctx,
237 SELECTOROF(io->buffer),
238 (DWORD)OFFSETOF(io->buffer));
240 hdr->status = STAT_BUSY;
241 /* first, check whether we already have data in line buffer */
242 if (curbuffer) {
243 /* yep, copy as much as we can */
244 BYTE data = 0;
245 while ((len<count) && (data != '\r')) {
246 data = *curbuffer++;
247 buffer[len++] = data;
249 if (data == '\r') {
250 /* line buffer emptied */
251 lol->offs_unread_CON = 0;
252 curbuffer = NULL;
253 /* if we're not in raw mode, call it a day */
254 if (!(con->attr & ATTR_RAW)) {
255 hdr->status = STAT_DONE;
256 io->count = len;
257 break;
259 } else {
260 /* still some data left */
261 lol->offs_unread_CON = curbuffer - (BYTE*)lol;
262 /* but buffer was filled, we're done */
263 hdr->status = STAT_DONE;
264 io->count = len;
265 break;
269 /* if we're in raw mode, we just need to fill the buffer */
270 if (con->attr & ATTR_RAW) {
271 while (len<count) {
272 WORD data;
274 /* do we have a waiting scancode? */
275 if (*scan) {
276 /* yes, store scancode in buffer */
277 buffer[len++] = *scan;
278 *scan = 0;
279 if (len==count) break;
282 /* check for new keyboard input */
283 while (CurOfs == bios->FirstKbdCharPtr) {
284 /* no input available yet, so wait... */
285 DOSVM_Wait( ctx );
287 /* read from keyboard queue (call int16?) */
288 data = ((WORD*)bios)[CurOfs];
289 CurOfs += 2;
290 if (CurOfs >= bios->KbdBufferEnd) CurOfs = bios->KbdBufferStart;
291 bios->NextKbdCharPtr = CurOfs;
292 /* if it's an extended key, save scancode */
293 if (LOBYTE(data) == 0) *scan = HIBYTE(data);
294 /* store ASCII char in buffer */
295 buffer[len++] = LOBYTE(data);
297 } else {
298 /* we're not in raw mode, so we need to do line input... */
299 while (TRUE) {
300 WORD data;
301 /* check for new keyboard input */
302 while (CurOfs == bios->FirstKbdCharPtr) {
303 /* no input available yet, so wait... */
304 DOSVM_Wait( ctx );
306 /* read from keyboard queue (call int16?) */
307 data = ((WORD*)bios)[CurOfs];
308 CurOfs += 2;
309 if (CurOfs >= bios->KbdBufferEnd) CurOfs = bios->KbdBufferStart;
310 bios->NextKbdCharPtr = CurOfs;
312 if (LOBYTE(data) == '\r') {
313 /* it's the return key, we're done */
314 linebuffer[len++] = LOBYTE(data);
315 break;
317 else if (LOBYTE(data) >= ' ') {
318 /* a character */
319 if ((len+1)<CON_BUFFER) {
320 linebuffer[len] = LOBYTE(data);
321 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &linebuffer[len++], 1, NULL, NULL);
323 /* else beep, but I don't like noise */
325 else switch (LOBYTE(data)) {
326 case '\b':
327 if (len>0) {
328 len--;
329 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "\b \b", 3, NULL, NULL);
331 break;
334 if (len > count) {
335 /* save rest of line for later */
336 lol->offs_unread_CON = linebuffer - (BYTE*)lol + count;
337 len = count;
339 memcpy(buffer, linebuffer, len);
341 hdr->status = STAT_DONE;
342 io->count = len;
344 break;
345 case CMD_SAFEINPUT:
346 if (curbuffer) {
347 /* some line input waiting */
348 hdr->status = STAT_DONE;
349 ((REQ_SAFEINPUT*)hdr)->data = *curbuffer;
351 else if (con->attr & ATTR_RAW) {
352 if (CurOfs == bios->FirstKbdCharPtr) {
353 /* no input */
354 hdr->status = STAT_DONE|STAT_BUSY;
355 } else {
356 /* some keyboard input waiting */
357 hdr->status = STAT_DONE;
358 ((REQ_SAFEINPUT*)hdr)->data = ((BYTE*)bios)[CurOfs];
360 } else {
361 /* no line input */
362 hdr->status = STAT_DONE|STAT_BUSY;
364 break;
365 case CMD_INSTATUS:
366 if (curbuffer) {
367 /* we have data */
368 hdr->status = STAT_DONE;
370 else if (con->attr & ATTR_RAW) {
371 if (CurOfs == bios->FirstKbdCharPtr) {
372 /* no input */
373 hdr->status = STAT_DONE|STAT_BUSY;
374 } else {
375 /* some keyboard input waiting */
376 hdr->status = STAT_DONE;
378 } else {
379 /* no line input */
380 hdr->status = STAT_DONE|STAT_BUSY;
383 break;
384 case CMD_INFLUSH:
385 /* flush line and keyboard queue */
386 lol->offs_unread_CON = 0;
387 bios->NextKbdCharPtr = bios->FirstKbdCharPtr;
388 break;
389 case CMD_OUTPUT:
390 case CMD_SAFEOUTPUT:
392 REQ_IO *io = (REQ_IO *)hdr;
393 BYTE *buffer = CTX_SEG_OFF_TO_LIN(ctx,
394 SELECTOROF(io->buffer),
395 (DWORD)OFFSETOF(io->buffer));
396 DWORD result = 0;
397 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer, io->count, &result, NULL);
398 io->count = result;
399 hdr->status = STAT_DONE;
401 break;
402 default:
403 hdr->status = STAT_DONE;
405 do_lret(ctx);
408 static void InitListOfLists(DOS_LISTOFLISTS *DOS_LOL)
411 Output of DOS 6.22:
413 0133:0020 6A 13-33 01 CC 00 33 01 59 00 j.3...3.Y.
414 0133:0030 70 00 00 00 72 02 00 02-6D 00 33 01 00 00 2E 05 p...r...m.3.....
415 0133:0040 00 00 FC 04 00 00 03 08-92 21 11 E0 04 80 C6 0D .........!......
416 0133:0050 CC 0D 4E 55 4C 20 20 20-20 20 00 00 00 00 00 00 ..NUL ......
417 0133:0060 00 4B BA C1 06 14 00 00-00 03 01 00 04 70 CE FF .K...........p..
418 0133:0070 FF 00 00 00 00 00 00 00-00 01 00 00 0D 05 00 00 ................
419 0133:0080 00 FF FF 00 00 00 00 FE-00 00 F8 03 FF 9F 70 02 ..............p.
420 0133:0090 D0 44 C8 FD D4 44 C8 FD-D4 44 C8 FD D0 44 C8 FD .D...D...D...D..
421 0133:00A0 D0 44 C8 FD D0 44 .D...D
423 DOS_LOL->CX_Int21_5e01 = 0x0;
424 DOS_LOL->LRU_count_FCB_cache = 0x0;
425 DOS_LOL->LRU_count_FCB_open = 0x0;
426 DOS_LOL->OEM_func_handler = -1; /* not available */
427 DOS_LOL->INT21_offset = 0x0;
428 DOS_LOL->sharing_retry_count = 3;
429 DOS_LOL->sharing_retry_delay = 1;
430 DOS_LOL->ptr_disk_buf = 0x0;
431 DOS_LOL->offs_unread_CON = 0x0;
432 DOS_LOL->seg_first_MCB = 0x0;
433 DOS_LOL->ptr_first_DPB = 0x0;
434 DOS_LOL->ptr_first_SysFileTable = 0x0;
435 DOS_LOL->ptr_clock_dev_hdr = 0x0;
436 DOS_LOL->ptr_CON_dev_hdr = 0x0;
437 DOS_LOL->max_byte_per_sec = 512;
438 DOS_LOL->ptr_disk_buf_info = 0x0;
439 DOS_LOL->ptr_array_CDS = 0x0;
440 DOS_LOL->ptr_sys_FCB = 0x0;
441 DOS_LOL->nr_protect_FCB = 0x0;
442 DOS_LOL->nr_block_dev = 0x0;
443 DOS_LOL->nr_avail_drive_letters = 26; /* A - Z */
444 DOS_LOL->nr_drives_JOINed = 0x0;
445 DOS_LOL->ptr_spec_prg_names = 0x0;
446 DOS_LOL->ptr_SETVER_prg_list = 0x0; /* no SETVER list */
447 DOS_LOL->DOS_HIGH_A20_func_offs = 0x0;
448 DOS_LOL->PSP_last_exec = 0x0;
449 DOS_LOL->BUFFERS_val = 99; /* maximum: 99 */
450 DOS_LOL->BUFFERS_nr_lookahead = 8; /* maximum: 8 */
451 DOS_LOL->boot_drive = 3; /* C: */
452 DOS_LOL->flag_DWORD_moves = 0x01; /* i386+ */
453 DOS_LOL->size_extended_mem = 0xf000; /* very high value */
456 void DOSDEV_InstallDOSDevices(void)
458 DOS_DATASEG *dataseg;
459 WORD seg;
460 WORD selector;
461 unsigned int n;
463 /* allocate DOS data segment or something */
464 dataseg = DOSVM_AllocDataUMB( sizeof(DOS_DATASEG), &seg, &selector );
466 DOS_LOLSeg = MAKESEGPTR( seg, 0 );
467 DOSMEM_LOL()->wine_rm_lol =
468 MAKESEGPTR( seg, FIELD_OFFSET(DOS_LISTOFLISTS, ptr_first_DPB) );
469 DOSMEM_LOL()->wine_pm_lol =
470 MAKESEGPTR( selector, FIELD_OFFSET(DOS_LISTOFLISTS, ptr_first_DPB) );
472 /* initialize the magnificent List Of Lists */
473 InitListOfLists(&dataseg->lol);
475 /* Set up first device (NUL) */
476 dataseg->lol.NUL_dev.next_dev = MAKESEGPTR(seg, DOS_DATASEG_OFF(dev[0]));
477 dataseg->lol.NUL_dev.attr = devs[0].attr;
478 dataseg->lol.NUL_dev.strategy = DOS_DATASEG_OFF(thunk[0].ljmp1);
479 dataseg->lol.NUL_dev.interrupt = DOS_DATASEG_OFF(thunk[0].ljmp2);
480 memcpy(dataseg->lol.NUL_dev.name, devs[0].name, 8);
482 /* Set up the remaining devices */
483 for (n = 1; n < NR_DEVS; n++)
485 dataseg->dev[n-1].next_dev = (n+1) == NR_DEVS ? NONEXT :
486 MAKESEGPTR(seg, DOS_DATASEG_OFF(dev[n]));
487 dataseg->dev[n-1].attr = devs[n].attr;
488 dataseg->dev[n-1].strategy = DOS_DATASEG_OFF(thunk[n].ljmp1);
489 dataseg->dev[n-1].interrupt = DOS_DATASEG_OFF(thunk[n].ljmp2);
490 memcpy(dataseg->dev[n-1].name, devs[n].name, 8);
493 /* Set up thunks */
494 for (n = 0; n < NR_DEVS; n++)
496 dataseg->thunk[n].ljmp1 = LJMP;
497 dataseg->thunk[n].strategy = (RMCBPROC)DPMI_AllocInternalRMCB(devs[n].strategy);
498 dataseg->thunk[n].ljmp2 = LJMP;
499 dataseg->thunk[n].interrupt = (RMCBPROC)DPMI_AllocInternalRMCB(devs[n].interrupt);
502 /* CON is device 1 */
503 dataseg->lol.ptr_CON_dev_hdr = MAKESEGPTR(seg, DOS_DATASEG_OFF(dev[0]));
506 DWORD DOSDEV_Console(void)
508 return DOSMEM_LOL()->ptr_CON_dev_hdr;
511 DWORD DOSDEV_FindCharDevice(char*name)
513 SEGPTR cur_ptr = MAKESEGPTR(HIWORD(DOS_LOLSeg), FIELD_OFFSET(DOS_LISTOFLISTS,NUL_dev));
514 DOS_DEVICE_HEADER *cur = PTR_REAL_TO_LIN(SELECTOROF(cur_ptr),OFFSETOF(cur_ptr));
515 char dname[8];
516 int cnt;
518 /* get first 8 characters */
519 strncpy(dname,name,8);
520 /* if less than 8 characters, pad with spaces */
521 for (cnt=0; cnt<8; cnt++)
522 if (!dname[cnt]) dname[cnt]=' ';
524 /* search for char devices with the right name */
525 while (cur &&
526 ((!(cur->attr & ATTR_CHAR)) ||
527 memcmp(cur->name,dname,8))) {
528 cur_ptr = cur->next_dev;
529 if (cur_ptr == NONEXT) cur=NULL;
530 else cur = PTR_REAL_TO_LIN(SELECTOROF(cur_ptr),OFFSETOF(cur_ptr));
532 return cur_ptr;
535 static void DOSDEV_DoReq(void*req, DWORD dev)
537 REQUEST_HEADER *hdr = (REQUEST_HEADER *)req;
538 DOS_DEVICE_HEADER *dhdr;
539 CONTEXT86 ctx;
540 char *phdr;
542 dhdr = PTR_REAL_TO_LIN(SELECTOROF(dev),OFFSETOF(dev));
543 phdr = ((char*)DOSMEM_LOL()) + DOS_DATASEG_OFF(req);
545 /* copy request to request scratch area */
546 memcpy(phdr, req, hdr->size);
548 /* prepare to call device driver */
549 memset(&ctx, 0, sizeof(ctx));
550 ctx.EFlags |= V86_FLAG;
552 /* ES:BX points to request for strategy routine */
553 ctx.SegEs = HIWORD(DOS_LOLSeg);
554 ctx.Ebx = DOS_DATASEG_OFF(req);
556 /* call strategy routine */
557 ctx.SegCs = SELECTOROF(dev);
558 ctx.Eip = dhdr->strategy;
559 DPMI_CallRMProc(&ctx, 0, 0, 0);
561 /* call interrupt routine */
562 ctx.SegCs = SELECTOROF(dev);
563 ctx.Eip = dhdr->interrupt;
564 DPMI_CallRMProc(&ctx, 0, 0, 0);
566 /* completed, copy request back */
567 memcpy(req, phdr, hdr->size);
569 if (hdr->status & STAT_ERROR) {
570 switch (hdr->status & STAT_MASK) {
571 case 0x0F: /* invalid disk change */
572 /* this error seems to fit the bill */
573 SetLastError(ER_NotSameDevice);
574 break;
575 default:
576 SetLastError((hdr->status & STAT_MASK) + 0x13);
577 break;
582 static int DOSDEV_IO(unsigned cmd, DWORD dev, DWORD buf, int buflen)
584 REQ_IO req;
586 req.hdr.size=sizeof(req);
587 req.hdr.unit=0; /* not dealing with block devices yet */
588 req.hdr.command=cmd;
589 req.hdr.status=STAT_BUSY;
590 req.media=0; /* not dealing with block devices yet */
591 req.buffer=buf;
592 req.count=buflen;
593 req.sector=0; /* block devices */
594 req.volume=0; /* block devices */
596 DOSDEV_DoReq(&req, dev);
598 return req.count;
601 int DOSDEV_Peek(DWORD dev, BYTE*data)
603 REQ_SAFEINPUT req;
605 req.hdr.size=sizeof(req);
606 req.hdr.unit=0; /* not dealing with block devices yet */
607 req.hdr.command=CMD_SAFEINPUT;
608 req.hdr.status=STAT_BUSY;
609 req.data=0;
611 DOSDEV_DoReq(&req, dev);
613 if (req.hdr.status & STAT_BUSY) return 0;
615 *data = req.data;
616 return 1;
619 int DOSDEV_Read(DWORD dev, DWORD buf, int buflen)
621 return DOSDEV_IO(CMD_INPUT, dev, buf, buflen);
624 int DOSDEV_Write(DWORD dev, DWORD buf, int buflen, int verify)
626 return DOSDEV_IO(verify?CMD_SAFEOUTPUT:CMD_OUTPUT, dev, buf, buflen);
629 int DOSDEV_IoctlRead(DWORD dev, DWORD buf, int buflen)
631 return DOSDEV_IO(CMD_INIOCTL, dev, buf, buflen);
634 int DOSDEV_IoctlWrite(DWORD dev, DWORD buf, int buflen)
636 return DOSDEV_IO(CMD_OUTIOCTL, dev, buf, buflen);