1 /* JTAG GNU/Linux parport device io
2
3 Copyright (C) 2004 Andrew Rogers
4 Additions for Byte Blaster Cable (C) 2005-2011 Uwe Bonnes
5 bon@elektron.ikp.physik.tu-darmstadt.de
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
21 Changes:
22 Dmitry Teytelman [dimtey@gmail.com] 14 Jun 2006 [applied 13 Aug 2006]:
23 Code cleanup for clean -Wall compile.
24 Changes to support new IOBase interface.
25 Support for byte counting and progress bar.
26 */
27
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <fcntl.h>
32 #include <string.h>
33
34 #ifdef __linux__
35 // Default parport device
36 #ifndef PPDEV
37 # define PPDEV "/dev/parport0"
38 #endif
39
40 #include <sys/ioctl.h>
41 # include <linux/parport.h>
42 # include <linux/ppdev.h>
43 #include <errno.h>
44
45 #elif defined (__FreeBSD__)
46 // Default parport device
47 #ifndef PPDEV
48 # define PPDEV "/dev/parport0"
49 #include <errno.h>
50 #endif
51
52 #include <sys/ioctl.h>
53 # include <dev/ppbus/ppi.h>
54 # include <dev/ppbus/ppbconf.h>
55
56 # define PARPORT_CONTROL_STROBE STROBE
57 # define PARPORT_CONTROL_AUTOFD AUTOFEED
58 # define PARPORT_CONTROL_INIT INIT
59 # define PARPORT_CONTROL_SELECT SELECTIN
60 /* DLC 5 Schematics:
61 http://www.xilinx.com/itp/xilinx4/data/docs/pac/appendixb.html
62 Pin Connectes for a 25 pin parallel port connector
63 http://www.kabelfaq.de/-> parallel
64 Pin 15 is nERR*/
65 # define PARPORT_STATUS_ERROR nFAULT
66 /* PIN 13 is SELECT (Printer is online)*/
67 # define PARPORT_STATUS_SELECT SELECT
68 /* PIN 12 is PE */
69 # define PARPORT_STATUS_PAPEROUT PERROR
70 /* PIN 10 is nACK */
71 # define PARPORT_STATUS_ACK nACK
72 /* PIN 11 is nBusy */
73 # define PARPORT_STATUS_BUSY nBUSY
74
75 #elif defined (__WIN32__)
76 // Default parport device
77 #ifndef PPDEV
78 # define PPDEV "\\\\.\\$VDMLPT1"
79 #endif
80 #include <windows.h>
81 #include <ddk/ntddpar.h>
82 #include "par_nt.h"
83
84 /*FIXME: These defines fit numerically, but not logically*/
85 # define PARPORT_CONTROL_STROBE PARALLEL_INIT
86 # define PARPORT_CONTROL_AUTOFD PARALLEL_AUTOFEED
87 # define PARPORT_CONTROL_INIT PARALLEL_PAPER_EMPTY
88 # define PARPORT_CONTROL_SELECT PARALLEL_OFF_LINE
89
90 # define PARPORT_STATUS_ERROR PARALLEL_OFF_LINE
91 # define PARPORT_STATUS_SELECT PARALLEL_POWER_OFF
92 # define PARPORT_STATUS_PAPEROUT PARALLEL_NOT_CONNECTED
93 # define PARPORT_STATUS_ACK PARALLEL_BUSY
94 # define PARPORT_STATUS_BUSY PARALLEL_SELECTED
95 #endif
96
97 #include <sys/time.h>
98 #include <unistd.h>
99
100 #include "ioparport.h"
101 #include "debug.h"
102
103 #define NO_CABLE 0
104 #define IS_PCIII 1
105 #define IS_BBLST 2
106
107 #define BIT_MASK(b) (1<<(b))
108
109 /* Attention: PARPORT_STATUS_BUSY reflects the inverted input */
110 /* Attention: PARPORT_CONTROL_AUTOFD write zero to output */
111
112 /* Altera Byteblaster Definitions */
113 #define BBLST_DEF_BYTE 0
114 #define BBLST_ENABLE_N PARPORT_CONTROL_AUTOFD /* Base + 2, Inv */
115 #define BBLST_TCK_VALUE BIT_MASK(0) /* Base */
116 #define BBLST_TMS_VALUE BIT_MASK(1) /* Base */
117 #define BBLST_TDI_VALUE BIT_MASK(6) /* Base */
118 #define BBLST_RESET_VALUE BIT_MASK(3) /* Base, Inv by Open
119 Collector Transistor */
120 #define BBLST_TDO_MASK PARPORT_STATUS_BUSY /* Base + 1, Input */
121 #define BBLST_LB_IN_MASK PARPORT_STATUS_PAPEROUT /* Base + 1, Input */
122 #define BBLST_LB_OUT_VALUE BIT_MASK(7) /* Base */
123 #define BBLST_ACK_OUT_VALUE BIT_MASK(5)
124 #define BBLST_ACK_IN_MASK PARPORT_STATUS_ACK
125
126 /* Xilinx Parallel Cable III Definitions */
127 #define PCIII_PROG_EN_N BIT_MASK(4)
128 #define PCIII_DEF_BYTE PCIII_PROG_EN_N
129 #define PCIII_TCK_VALUE BIT_MASK(1) /* Base */
130 #define PCIII_TMS_VALUE BIT_MASK(2) /* Base */
131 #define PCIII_TDI_VALUE BIT_MASK(0) /* Base */
132 #define PCIII_TDO_MASK PARPORT_STATUS_SELECT
133 #define PCIII_CHECK_OUT BIT_MASK(6)
134 #define PCIII_CHECK_IN1 PARPORT_STATUS_BUSY
135 #define PCIII_CHECK_IN2 PARPORT_STATUS_PAPEROUT
136
137
138 using namespace std;
139
detectcable(void)140 int IOParport::detectcable(void)
141 {
142 unsigned char data=0, status, control;
143
144
145 write_data(fd, data);
146 read_status(fd, &status);
147 read_control(fd, &control);
148 if ((status == 0) || (status == 0xff))
149 {
150 fprintf(stderr,"IOParport::detectcable status 0x%02x control %02x"
151 " Check system driver setup\n",
152 status, control);
153 return NO_CABLE;
154 }
155 /* Error_n should is hardwired to ground on a byteblaster cable*/
156 if (!(status & PARPORT_STATUS_ERROR))
157 {
158 if (debug & HW_DETAILS)
159 fprintf(stderr,"Trying Byteblaster\n");
160 /* D5/ACK and D7/PE should be connected*/
161 if (( (data & BBLST_LB_OUT_VALUE) && !(status & BBLST_LB_IN_MASK)) ||
162 (!(data & BBLST_LB_OUT_VALUE) && (status & BBLST_LB_IN_MASK)))
163 { /* The difference is in D7/PE if the card is unpowered*/
164 if(( (data & BBLST_ACK_OUT_VALUE) && (status & BBLST_ACK_IN_MASK))||
165 (!(data & BBLST_ACK_OUT_VALUE) && !(status & BBLST_ACK_IN_MASK)))
166 {
167 fprintf(stderr,"Unpowered Byteblaster cable\n");
168 return NO_CABLE;
169 }
170 /*We have an unpowered Xilinx cable if D6/Busy/PE are connected */
171 else if
172 ( ( (data & PCIII_CHECK_OUT) && !(status & PCIII_CHECK_IN1))||
173 (!(data & PCIII_CHECK_OUT) && (status & PCIII_CHECK_IN1))||
174 ( (data & PCIII_CHECK_OUT) && (status & PCIII_CHECK_IN2))||
175 (!(data & PCIII_CHECK_OUT) && !(status & PCIII_CHECK_IN2))) {
176 fprintf(stderr,"Unpowered Parallel Cable III cable\n");
177 return NO_CABLE;
178 }
179 else {
180 fprintf(stderr,"No dongle found\n");
181 return NO_CABLE;
182 }
183 }
184 /* now try all 4 permuttation */
185 data = (data & BBLST_LB_OUT_VALUE) ? (data & ~BBLST_LB_OUT_VALUE) :
186 (data | BBLST_LB_OUT_VALUE);
187 write_data(fd, data);
188 read_status(fd, &status);
189 if (( (data & BBLST_LB_OUT_VALUE) && !(status & BBLST_LB_IN_MASK)) ||
190 (!(data & BBLST_LB_OUT_VALUE) && (status & BBLST_LB_IN_MASK)) ||
191 ( (data & BBLST_ACK_OUT_VALUE) && !(status & BBLST_ACK_IN_MASK))||
192 (!(data & BBLST_ACK_OUT_VALUE) && (status & BBLST_ACK_IN_MASK)))
193 {
194 fprintf(stderr,"Missing reaction for Altera cable(1)\n");
195 return NO_CABLE;
196 }
197 data = (data & BBLST_ACK_OUT_VALUE) ? (data & ~BBLST_ACK_OUT_VALUE) :
198 (data | BBLST_ACK_OUT_VALUE);
199 write_data(fd, data);
200 read_status(fd, &status);
201 if (( (data & BBLST_LB_OUT_VALUE) && !(status & BBLST_LB_IN_MASK)) ||
202 (!(data & BBLST_LB_OUT_VALUE) && (status & BBLST_LB_IN_MASK)) ||
203 ( (data & BBLST_ACK_OUT_VALUE) && !(status & BBLST_ACK_IN_MASK))||
204 (!(data & BBLST_ACK_OUT_VALUE) && (status & BBLST_ACK_IN_MASK)))
205 {
206 fprintf(stderr,"Missing reaction for Altera cable(2)\n");
207 return NO_CABLE;
208 }
209 data = (data & BBLST_LB_OUT_VALUE) ? (data & ~BBLST_LB_OUT_VALUE) :
210 (data | BBLST_LB_OUT_VALUE);
211 write_data(fd, data);
212 read_status(fd, &status);
213 if (( (data & BBLST_LB_OUT_VALUE) && !(status & BBLST_LB_IN_MASK)) ||
214 (!(data & BBLST_LB_OUT_VALUE) && (status & BBLST_LB_IN_MASK)) ||
215 ( (data & BBLST_ACK_OUT_VALUE) && !(status & BBLST_ACK_IN_MASK))||
216 (!(data & BBLST_ACK_OUT_VALUE) && (status & BBLST_ACK_IN_MASK)))
217 {
218 fprintf(stderr,"Missing reaction for Altera cable(3)\n");
219 return NO_CABLE;
220 }
221 data = (data & BBLST_ACK_OUT_VALUE) ? (data & ~BBLST_ACK_OUT_VALUE) :
222 (data | BBLST_ACK_OUT_VALUE);
223 write_data(fd, data);
224 read_status(fd, &status);
225 if (( (data & BBLST_LB_OUT_VALUE) && !(status & BBLST_LB_IN_MASK)) ||
226 (!(data & BBLST_LB_OUT_VALUE) && (status & BBLST_LB_IN_MASK)) ||
227 ( (data & BBLST_ACK_OUT_VALUE) && !(status & BBLST_ACK_IN_MASK))||
228 (!(data & BBLST_ACK_OUT_VALUE) && (status & BBLST_ACK_IN_MASK)))
229 {
230 fprintf(stderr,"Missing reaction for Altera cable(4)\n");
231 return NO_CABLE;
232 }
233 fprintf(stderr,"Found ByteBlaster Cable\n");
234 def_byte = BBLST_DEF_BYTE;
235 tdi_value = BBLST_TDI_VALUE;
236 tms_value = BBLST_TMS_VALUE;
237 tck_value = BBLST_TCK_VALUE;
238 tdo_mask = BBLST_TDO_MASK;
239 tdo_inv = 1;
240 read_control(fd, &control);
241 control |= BBLST_ENABLE_N;
242 write_control(fd, control);
243 return IS_BBLST;
244 }
245 else { /*Probably Xilinx cable */
246 /* Check for D6/BUSY/PE Connection and for D4/Select Feedback */
247 if ( ( (data & PCIII_CHECK_OUT) && (status & PCIII_CHECK_IN1))||
248 (!(data & PCIII_CHECK_OUT) && !(status & PCIII_CHECK_IN1))||
249 ( (data & PCIII_CHECK_OUT) && !(status & PCIII_CHECK_IN2))||
250 (!(data & PCIII_CHECK_OUT) && (status & PCIII_CHECK_IN2)))
251 {
252 fprintf(stderr,"No dongle found\n");
253 return NO_CABLE;
254 }
255
256 /* 20100708: This check will only work with U1 on the DLC(clone)
257 * from a high drive family like LVC and a not so strong driver at the end
258 * of the JTAG chain, like an XCF0x.
259 * E.G. Digilent S3 drives TDO-A with an LVC, while the
260 * original DLC5 only has an HC125, and so the HC125 can not drive the line
261 * low consitstantly
262 *
263 * So disable this check
264
265 if ((status & PCIII_TDO_MASK) && (!(data & PCIII_PROG_EN_N))) {
266 fprintf(stderr,"Missing power for Parallel Cable III\n");
267 return NO_CABLE;}
268 */
269 data = (data & PCIII_CHECK_OUT) ? (data & ~PCIII_CHECK_OUT) :
270 (data |PCIII_CHECK_OUT);
271 write_data(fd, data);
272 read_status(fd, &status);
273 if ( ( (data & PCIII_CHECK_OUT) && (status & PCIII_CHECK_IN1))||
274 (!(data & PCIII_CHECK_OUT) && !(status & PCIII_CHECK_IN1))||
275 ( (data & PCIII_CHECK_OUT) && !(status & PCIII_CHECK_IN2)) ||
276 (!(data & PCIII_CHECK_OUT) && (status & PCIII_CHECK_IN2)))
277 {
278 fprintf(stderr,"Missing reaction on XILINX Cable(1)\n");
279 return NO_CABLE;
280 }
281 data = (data & PCIII_CHECK_OUT) ? (data & ~PCIII_CHECK_OUT) :
282 (data | PCIII_CHECK_OUT);
283 write_data(fd, data);
284 read_status(fd, &status);
285 if ( ( (data & PCIII_CHECK_OUT) && (status & PCIII_CHECK_IN1))||
286 (!(data & PCIII_CHECK_OUT) && !(status & PCIII_CHECK_IN1))||
287 ( (data & PCIII_CHECK_OUT) && !(status & PCIII_CHECK_IN2))||
288 (!(data & PCIII_CHECK_OUT) && (status & PCIII_CHECK_IN2)))
289 {
290 fprintf(stderr,"Missing reaction on XILINX Cable(2)\n");
291 return NO_CABLE;
292 }
293 fprintf(stderr,"Found Xilinx Parallel Cable III\n");
294 def_byte = PCIII_DEF_BYTE;
295 tdi_value = PCIII_TDI_VALUE;
296 tms_value = PCIII_TMS_VALUE;
297 tck_value = PCIII_TCK_VALUE;
298 tdo_mask = PCIII_TDO_MASK;
299 tdo_inv = 0;
300 return IS_PCIII;
301 }
302 }
303
IOParport()304 IOParport::IOParport() : IOBase(), total(0), debug(0)
305 {
306 }
307
Init(struct cable_t * cable,const char * dev,unsigned int freq)308 int IOParport::Init(struct cable_t *cable, const char *dev, unsigned int freq)
309 {
310 int res;
311
312 // Try to obtain device from environment or use default if not given
313 if(!dev) {
314 if(!(dev = getenv("XCPORT"))) dev = PPDEV;
315 }
316
317 #if defined (__linux__) || defined(__FreeBSD__)
318 // Try to open parport device
319 if((fd = open(dev, O_RDWR)) == -1)
320 #elif defined(__WIN32__)
321 if ((fd = (int)CreateFile(dev, GENERIC_READ | GENERIC_WRITE,
322 0, NULL, OPEN_EXISTING, 0, NULL))
323 == (int)INVALID_HANDLE_VALUE)
324 #endif
325 {
326 fprintf(stderr,"Could not access parallel device '%s': %s\n",
327 dev, strerror(errno));
328 return -1;
329 }
330
331 #if defined (__linux__)
332 // Lock port
333 res = ioctl(fd, PPCLAIM);
334 if(res)
335 {
336 fprintf(stderr, "Port %s already in use\n", dev);
337 return res;
338 }
339
340 // Switch to compatibility mode
341 int const mode = IEEE1284_MODE_COMPAT;
342 res = ioctl(fd, PPNEGOT, &mode);
343 if(res) {
344 fprintf(stderr,"IEEE1284 compatibility not available on dev %s\n", dev);
345 return res;
346 }
347 #endif
348 cabletype = detectcable();
349 if(cabletype == NO_CABLE)
350 {
351 fprintf(stderr, "No adapter found\n");
352 return 1;
353 }
354 return 0;
355 }
356
txrx(bool tms,bool tdi)357 bool IOParport::txrx(bool tms, bool tdi)
358 {
359 unsigned char ret;
360 bool retval;
361 unsigned char data=def_byte; // D4 pin5 TDI enable
362 if(tdi)data|=tdi_value; // D0 pin2
363 if(tms)data|=tms_value; // D2 pin4
364 write_data(fd, data);
365 data|=tck_value; // clk high D1 pin3
366 write_data(fd, data);
367 total++;
368 read_status(fd, &ret);
369 //data=data^2; // clk low
370 //write_data(fd, data);
371 //read_status(fd, &ret);
372 retval = (ret&tdo_mask)?!tdo_inv:tdo_inv;
373 if (debug & HW_FUNCTIONS)
374 fprintf(stderr,"IOParport::txrx tms %s tdi %s tdo %s \n",
375 (tms)?"true ":"false", (tdi)?"true ":"false",
376 (retval)?"true ":"false");
377 return retval;
378
379 }
380
tx(bool tms,bool tdi)381 void IOParport::tx(bool tms, bool tdi)
382 {
383 unsigned char data=def_byte; // D4 pin5 TDI enable
384 if (debug & HW_FUNCTIONS)
385 fprintf(stderr,"tx tms %s tdi %s\n",(tms)?"true ":"false",
386 (tdi)?"true ":"false");
387 if(tdi)data|=tdi_value; // D0 pin2
388 if(tms)data|=tms_value; // D2 pin4
389 write_data(fd, data);
390 //delay(2);
391 data|=tck_value; // clk high
392 total++;
393 write_data(fd, data);
394 //delay(2);
395 //data=data^2; // clk low
396 //write_data(fd, data);
397 //delay(2);
398 }
399
tx_tdi_byte(unsigned char tdi_byte)400 void IOParport::tx_tdi_byte(unsigned char tdi_byte)
401 {
402 int k;
403
404 for (k = 0; k < 8; k++)
405 tx(false, (tdi_byte>>k)&1);
406 }
407
txrx_block(const unsigned char * tdi,unsigned char * tdo,int length,bool last)408 void IOParport::txrx_block(const unsigned char *tdi, unsigned char *tdo,
409 int length, bool last)
410 {
411 int i=0;
412 int j=0;
413 unsigned char tdo_byte=0;
414 unsigned char tdi_byte;
415 unsigned char data=def_byte;
416 if (tdi)
417 tdi_byte = tdi[j];
418
419 while(i<length-1){
420 tdo_byte=tdo_byte+(txrx(false, (tdi_byte&1)==1)<<(i%8));
421 if (tdi)
422 tdi_byte=tdi_byte>>1;
423 i++;
424 if((i%8)==0){ // Next byte
425 if(tdo)
426 tdo[j]=tdo_byte; // Save the TDO byte
427 tdo_byte=0;
428 j++;
429 if (tdi)
430 tdi_byte=tdi[j]; // Get the next TDI byte
431 }
432 };
433 tdo_byte=tdo_byte+(txrx(last, (tdi_byte&1)==1)<<(i%8));
434 if(tdo)
435 tdo[j]=tdo_byte;
436 write_data(fd, data); /* Make sure, TCK is low */
437 return;
438 }
439
tx_tms(unsigned char * pat,int length,int force)440 void IOParport::tx_tms(unsigned char *pat, int length, int force)
441 {
442 int i;
443 unsigned char tms;
444 unsigned char data=def_byte;
445 for (i = 0; i < length; i++)
446 {
447 if ((i & 0x7) == 0)
448 tms = pat[i>>3];
449 tx((tms & 0x01), true);
450 tms = tms >> 1;
451 }
452 write_data(fd, data); /* Make sure, TCK is low */
453 }
454
~IOParport()455 IOParport::~IOParport()
456 {
457 if (cabletype == IS_BBLST)
458 {
459 unsigned char control;
460 read_control(fd, &control);
461 control &= ~BBLST_ENABLE_N;
462 write_control(fd, control);
463 }
464 #ifdef __linux__
465 ioctl (fd, PPRELEASE);
466 close (fd);
467 #elif defined(__FreeBSD__)
468 close (fd);
469 #elif defined(__WIN32__)
470 CloseHandle((HANDLE)(fd));
471 #endif
472 if (verbose) fprintf(stderr, "Total bytes sent: %d\n", total>>3);
473 }
474 #define XC3S_OK 0
475 #define XC3S_EIO 1
476 #define XC3S_ENIMPL 2
477
write_data(int fd,unsigned char data)478 int IOParport::write_data(int fd, unsigned char data)
479 {
480 int status;
481 #ifdef __linux__
482 status = ioctl(fd, PPWDATA, &data);
483 return status == 0 ? XC3S_OK : -XC3S_EIO;
484 #elif defined (__FreeBSD__)
485 status = ioctl(fd, PPISDATA, &data);
486 return status == 0 ? XC3S_OK : -XC3S_EIO;
487 #elif defined(__WIN32__)
488 DWORD dummy;
489 status = DeviceIoControl((HANDLE)(fd), NT_IOCTL_DATA, &data, sizeof(data),
490 NULL, 0, (LPDWORD)&dummy, NULL);
491 return status != 0 ? XC3S_OK : -XC3S_EIO;
492 #else
493 return -XC3S_ENIMPL;
494 #endif
495 }
496
497
write_control(int fd,unsigned char control)498 int IOParport::write_control(int fd, unsigned char control)
499 {
500 int status;
501 #ifdef __linux__
502 status = ioctl(fd, PPWCONTROL, &control);
503 return status == 0 ? XC3S_OK : -XC3S_EIO;
504 #elif defined (__FreeBSD__)
505 status = ioctl(fd, PPISCTRL, &control);
506 return status == 0 ? XC3S_OK : -XC3S_EIO;
507 #elif defined(__WIN32__)
508 DWORD dummyc;
509 DWORD dummy;
510 /*FIXME: hamlib used much more compicated expression*/
511 status = DeviceIoControl((HANDLE)(fd),NT_IOCTL_CONTROL, &control,
512 sizeof(control), &dummyc, sizeof(dummyc),
513 (LPDWORD)&dummy, NULL);
514 return status != 0 ? XC3S_OK : -XC3S_EIO;
515 #else
516 return -XC3S_ENIMPL;
517 #endif
518 }
519
read_control(int fd,unsigned char * control)520 int IOParport::read_control(int fd, unsigned char *control)
521 {
522 int status;
523 #ifdef __linux
524 status = ioctl(fd, PPRCONTROL, control);
525 return status == 0 ? XC3S_OK : -XC3S_EIO;
526 #elif defined (__FreeBSD__)
527 status = ioctl(fd, PPIGCTRL, control);
528 return status == 0 ? XC3S_OK : -XC3S_EIO;
529 #elif defined (__WIN32__)
530 char ret;
531 DWORD dummy;
532 status = DeviceIoControl((HANDLE)(fd), NT_IOCTL_CONTROL, NULL, 0, &ret,
533 sizeof(ret), (LPDWORD)&dummy, NULL);
534 *control = ret ^ S1284_INVERTED;
535 return status == 0 ? XC3S_OK : -XC3S_EIO;
536 #else
537 return -XC3S_ENIMPL;
538 #endif
539 }
540
read_status(int fd,unsigned char * status)541 int IOParport::read_status(int fd, unsigned char *status)
542 {
543 int ret;
544 #ifdef __linux__
545 ret = ioctl(fd, PPRSTATUS, status);
546 return ret == 0 ? XC3S_OK : -XC3S_EIO;
547 #elif defined (__FreeBSD__)
548 ret = ioctl(fd, PPIGSTATUS, status);
549 return ret == 0 ? XC3S_OK : -XC3S_EIO;
550 #elif defined (__WIN32__)
551 unsigned char res;
552 DWORD dummy;
553 ret = DeviceIoControl((HANDLE)(fd), NT_IOCTL_STATUS, NULL, 0, &res,
554 sizeof(res), (LPDWORD)&dummy, NULL);
555 *status = res ;
556 return ret == 0 ? XC3S_OK : -XC3S_EIO;
557 #else
558 return -XC3S_ENIMPL;
559 #endif
560 }
561
562
563