1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 2009 Melanie Rhianna Lewis                             |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.0 of the PHP license,       |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt.                                 |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Melanie Rhianna Lewis <cyberspice@php.net>                   |
16    +----------------------------------------------------------------------+
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include "php.h"
24 #include "php_dio_common.h"
25 
26 #ifndef ZEND_WIN32
27 #error ZEND_WIN32 not defined!
28 #endif
29 
30 /* {{{ dio_last_error_php_error
31  * Generates a PHP error message based upon the last Windows error.
32  */
dio_last_error_php_error(int level,char * message)33 static void dio_last_error_php_error(int level, char * message) {
34 	LPVOID msgbuf;
35 	DWORD  msgbuflen;
36 	char * errmsg;
37 	DWORD  err;
38 
39 #ifdef UNICODE
40 	DWORD  errmsglen;
41 #endif
42 
43 	err = GetLastError();
44 	msgbuflen = FormatMessage(
45 		FORMAT_MESSAGE_ALLOCATE_BUFFER|
46 		FORMAT_MESSAGE_FROM_SYSTEM|
47 		FORMAT_MESSAGE_IGNORE_INSERTS,
48 		NULL,
49 		err,
50 		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
51 		(LPTSTR)&msgbuf,
52 		0,
53 		NULL);
54 
55 #ifdef UNICODE
56 
57 	/* Get the length of the converted message */
58 	errmsglen = WideCharToMultibyte(
59 		CP_ACP,
60 		0
61 		(LPCWSTR)msgbuf,
62 		-1,
63 		(LPSTR)errmsg,
64 		0,
65 		NULL,
66 		NULL);
67 
68 	/* Allocate a buffer */
69 	errmsg = emalloc(errmsglen);
70 	if (!errmsg) {
71 		php_error_docref(NULL, E_ERROR, "Out of memory in dio_last_error_php_error()!");
72 		LocalFree(msgbuf);
73 		return;
74 	}
75 
76 	/* Convert the message */
77 	errmsglen = WideCharToMultibyte(
78 		CP_ACP,
79 		0
80 		(LPCWSTR)msgbuf,
81 		-1,
82 		(LPSTR)errmsg,
83 		errmsglen,
84 		NULL,
85 		NULL);
86 
87 #else
88 	errmsg = (char *)msgbuf;
89 #endif
90 
91 	php_error_docref(NULL, E_WARNING, "%s[ERROR %d] %s", message, err, errmsg);
92 
93 	LocalFree(msgbuf);
94 #ifdef UNICODE
95 	efree(errmsg);
96 #endif
97 }
98 
99 /* {{{ dio_data_rate_to_define
100  * Converts a numeric data rate to a termios define
101  */
dio_data_rate_to_define(long rate,DWORD * def)102 static int dio_data_rate_to_define(long rate, DWORD *def) {
103 	switch (rate) {
104 		case 75:
105 		case 110:
106 		case 134:
107 		case 150:
108 		case 300:
109 		case 600:
110 		case 1200:
111 		case 1800:
112 		case 2400:
113 		case 4800:
114 		case 7200:
115 		case 9600:
116 		case 14400:
117 		case 19200:
118 		case 38400:
119 		case 57600:
120 		case 115200:
121 		case 56000:
122 		case 128000:
123 		case 256000:
124 			break;
125 		default:
126 			return 0;
127 	}
128 
129 	*def = (DWORD)rate;
130 	return 1;
131 }
132 /* }}} */
133 
134 
135 /* {{{ dio_data_bits_to_define
136  * Converts a number of data bits to a termios define
137  */
dio_data_bits_to_define(int data_bits,DWORD * def)138 static int dio_data_bits_to_define(int data_bits, DWORD *def) {
139 	switch (data_bits) {
140 		case 8:
141 		case 7:
142 		case 6:
143 		case 5:
144 		case 4:
145 			break;
146 		default:
147 			return 0;
148 	}
149 
150 	*def = (DWORD)data_bits;
151 	return 1;
152 }
153 /* }}} */
154 
155 /* {{{ dio_stop_bits_to_define
156  * Converts a number of stop bits to a termios define
157  */
dio_stop_bits_to_define(int stop_bits,DWORD * def)158 static int dio_stop_bits_to_define(int stop_bits, DWORD *def) {
159 	DWORD val;
160 
161 	switch (stop_bits) {
162 		case 1:
163 			val = ONESTOPBIT;
164 			break;
165 		case 2:
166 			val = TWOSTOPBITS;
167 			break;
168 		case 3:
169 			val = ONE5STOPBITS;
170 			break;
171 		default:
172 			return 0;
173 	}
174 
175 	*def = val;
176 	return 1;
177 }
178 /* }}} */
179 
180 /* {{{ dio_parity_to_define
181  * Converts a parity type to a termios define
182  */
dio_parity_to_define(int parity,DWORD * def)183 static int dio_parity_to_define(int parity, DWORD *def) {
184 	DWORD val;
185 
186 	switch (parity) {
187 		case 0:
188 			val = NOPARITY;
189 			break;
190 		case 1:
191 			val = ODDPARITY;
192 			break;
193 		case 2:
194 			val = EVENPARITY;
195 			break;
196 		default:
197 			return 0;
198 	}
199 
200 	*def = val;
201 	return 1;
202 }
203 /* }}} */
204 
205 /* {{{ dio_create_stream_data
206  * Creates an initialised stream data structure.  Free with efree().
207  */
dio_create_stream_data(void)208 php_dio_stream_data * dio_create_stream_data(void) {
209 	php_dio_win32_stream_data * data = emalloc(sizeof(php_dio_win32_stream_data));
210 	memset(data, 0, sizeof(php_dio_win32_stream_data));
211 	dio_init_stream_data(&(data->common));
212 	data->handle = INVALID_HANDLE_VALUE;
213 	data->desired_access = 0;
214 	data->creation_disposition = 0;
215 	data->olddcb.DCBlength = sizeof(DCB);
216 
217 	return (php_dio_stream_data *)data;
218 }
219 /* }}} */
220 
221 /* {{{ dio_common_write
222  * Writes count chars from the buffer to the stream described by the stream data.
223  */
224 #if PHP_VERSION_ID < 70400
dio_common_write(php_dio_stream_data * data,const char * buf,size_t count)225 size_t dio_common_write(php_dio_stream_data *data, const char *buf, size_t count) {
226 #else
227 ssize_t dio_common_write(php_dio_stream_data *data, const char *buf, size_t count) {
228 #endif
229 	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
230 	DWORD total = 0;
231 
232 	if (WriteFile(wdata->handle, buf, (DWORD)count, &total, NULL)) {
233 		return (size_t)total;
234 	}
235 
236 #if PHP_VERSION_ID < 70400
237 	return 0;
238 #else
239 	return -1;
240 #endif
241 }
242 /* }}} */
243 
244 /* {{{ dio_buffer_read
245  * Reads any available chars from the canonical buffer.
246  */
247 static size_t dio_buffer_read(php_dio_win32_stream_data *wdata, const char *buf, size_t count) {
248 	php_dio_win32_canon_data *canon_data = wdata->canon_data;
249 	size_t total = 0;
250 
251 	/* Read always follows write.  I.e. if read ptr > write ptr buffer has
252 	   wrapped and so we need to copy two blocks of data. */
253 	if (canon_data->read_pos > canon_data->write_pos) {
254 
255 		/* Check we actually need to copy both blocks */
256 		if ((canon_data->size - canon_data->read_pos) > count) {
257 
258 			/* No we don't.  Just copy as much as we were asked for. */
259 			memcpy((char*)buf,
260 				   &(canon_data->buf[canon_data->read_pos]),
261 				   count);
262 			/* Update the read pointer. */
263 			canon_data->read_pos += count;
264 
265 			/* Return the amount read. */
266 			return count;
267 		} else {
268 
269 			/* We need to copy both blocks so copy data up to the end of
270 			   the buffer. */
271 			total = canon_data->size - canon_data->read_pos;
272 			memcpy((char*)buf,
273 				   &(canon_data->buf[canon_data->read_pos]),
274 				   total);
275 			canon_data->read_pos = 0;
276 			count -= total;
277 
278 			/* Now copy the data from the start of the buffer either up
279 			   count or the number of bytes in the buffer. */
280 
281 			if (canon_data->write_pos > count) {
282 				memcpy((char*)buf, canon_data->buf, count);
283 				canon_data->read_pos = count;
284 				total += count;
285 
286 				return total;
287 			} else {
288 				memcpy((char*)buf, canon_data->buf, canon_data->write_pos);
289 				canon_data->read_pos = canon_data->write_pos;
290 				total += canon_data->write_pos;
291 
292 				return total;
293 			}
294 		}
295 
296 	/* Else if write follows read.  This is a simpler case.  We just copy
297 	   either all the data buffered or count, which ever is smaller. */
298 	} else if (canon_data->write_pos > canon_data->read_pos) {
299 		if ((canon_data->write_pos - canon_data->read_pos) > count) {
300 			memcpy((char*)buf,
301 				   &(canon_data->buf[canon_data->read_pos]),
302 				   count);
303 			canon_data->read_pos += count;
304 
305 			return count;
306 		} else {
307 			total = canon_data->write_pos - canon_data->read_pos;
308 			memcpy((char*)buf,
309 				   &(canon_data->buf[canon_data->read_pos]),
310 				   total);
311 			canon_data->read_pos += total;
312 
313 			return total;
314 		}
315 	}
316 
317 	/* Else we need to read more data from the data port. */
318 	return 0;
319 }
320 
321 /* {{{ dio_com_read
322  * Read chars from the data port.
323  */
324 #if PHP_VERSION_ID < 70400
325 static size_t dio_com_read(php_dio_stream_data *data, const char *buf, size_t count) {
326 #else
327 static ssize_t dio_com_read(php_dio_stream_data *data, const char *buf, size_t count) {
328 #endif
329 	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
330 	DWORD err, total = 0;
331 
332 	if (ReadFile(wdata->handle, (void*)buf, (DWORD)count, &total, NULL)) {
333 
334 		if (total) {
335 			return (size_t)total;
336 		}
337 
338 		data->end_of_file = 1;
339 	}
340 
341 	if (!data->end_of_file) {
342 		err = GetLastError();
343 
344 		if (ERROR_HANDLE_EOF == err) {
345 			data->end_of_file = 1;
346 		}
347 	}
348 
349 #if PHP_VERSION_ID < 70400
350 	return 0;
351 #else
352 	return (data->end_of_file ? 0 : -1);
353 #endif
354 }
355 
356 /* {{{ dio_canonical_read
357  * Reads chars from the input stream until the internal buffer is full or a new
358  * line is reached.
359  */
360 #if PHP_VERSION_ID < 70400
361 static size_t dio_canonical_read(php_dio_win32_stream_data *wdata, const char *buf, size_t count) {
362 #else
363 static ssize_t dio_canonical_read(php_dio_win32_stream_data *wdata, const char *buf, size_t count) {
364 #endif
365 	php_dio_win32_canon_data *canon_data = wdata->canon_data;
366 	size_t total = 0;
367 	char ch;
368 
369 	/* See if there's any buffered data and copy it. */
370 	total = dio_buffer_read(wdata, buf, count);
371 	if (total) {
372 		return total;
373 	}
374 
375 	/* Need to read more data from the data port.  Buffer should be empty(er)
376 	   by now. */
377 	do {
378 		/* Is the buffer full? */
379 		if (((canon_data->write_pos + 1) % canon_data->size) ==
380 			canon_data->read_pos) {
381 			break;
382 		}
383 
384 		/* Read a byte from the input checking for EOF. */
385 		if (dio_com_read((php_dio_stream_data*)wdata, &ch, 1) < 1) {
386 			break;
387 		}
388 
389 		/* Translate CR to newlines (same as ICRNL in POSIX) */
390 		ch = (ch != '\r') ? ch : '\n';
391 
392 		/* We read a character!  So buffer it. */
393 		canon_data->buf[canon_data->write_pos++] = ch;
394 		if (canon_data->write_pos >= canon_data->size) {
395 			canon_data->write_pos = 0;
396 		}
397 
398 		/* End of line/input (^D)? */
399 	} while ((ch != '\n') && (ch != 0x04));
400 
401 	return dio_buffer_read(wdata, buf, count);
402 }
403 /* }}} */
404 
405 /* {{{ dio_common_read
406  * Reads count chars to the buffer to the stream described by the stream data.
407  */
408 #if PHP_VERSION_ID < 70400
409 size_t dio_common_read(php_dio_stream_data *data, const char *buf, size_t count) {
410 #else
411 ssize_t dio_common_read(php_dio_stream_data *data, const char *buf, size_t count) {
412 #endif
413 
414 	/* You ask for no bytes you'll get none :-) */
415 	if (!count) {
416 		return 0;
417 	}
418 
419 	if (data->canonical) {
420 		return dio_canonical_read((php_dio_win32_stream_data*)data, buf, count);
421 	} else {
422 		return dio_com_read(data, buf, count);
423 	}
424 }
425 /* }}} */
426 
427 /* {{{ php_dio_stream_data
428  * Closes the php_stream.
429  */
430 int dio_common_close(php_dio_stream_data *data) {
431 	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
432 
433 	if (data->canonical) {
434 		efree(wdata->canon_data);
435 	}
436 
437 	if (!CloseHandle(wdata->handle)) {
438 		return 0;
439 	}
440 
441 	return 1;
442 }
443 /* }}} */
444 
445 /* {{{ dio_common_set_option
446  * Sets/gets stream options
447  */
448 int dio_common_set_option(php_dio_stream_data *data, int option, int value, void *ptrparam) {
449 	COMMTIMEOUTS cto = { 0, 0, 0, 0, 0 };
450 	int old_is_blocking = 0;
451 
452 	/* Can't do timeouts or non blocking with raw windows streams :-( */
453 	if (DIO_STREAM_TYPE_SERIAL == data->stream_type) {
454 		switch (option) {
455 			case PHP_STREAM_OPTION_BLOCKING:
456 				old_is_blocking   = data->is_blocking;
457 				data->is_blocking = value ? 1 : 0;
458 
459 				/* Only change values if we need to change them. */
460 				if (data->is_blocking != old_is_blocking) {
461 					/* If we're not blocking but don't have a timeout
462 					   set to return immediately */
463 					if (!data->is_blocking && !data->has_timeout) {
464 						cto.ReadIntervalTimeout = MAXDWORD;
465 					}
466 
467 					/* If we have a timeout ignore the blocking and set
468 					   the total time in which to read the data */
469 					if (data->has_timeout) {
470 						cto.ReadIntervalTimeout = MAXDWORD;
471 						cto.ReadTotalTimeoutMultiplier  = MAXDWORD;
472 						cto.ReadTotalTimeoutConstant = (data->timeout_usec / 1000) +
473 							(data->timeout_sec * 1000);
474 					}
475 
476 					if (!SetCommTimeouts(((php_dio_win32_stream_data*)data)->handle, &cto)) {
477 						return PHP_STREAM_OPTION_RETURN_ERR;
478 					}
479 				}
480 				return old_is_blocking ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
481 
482 			case PHP_STREAM_OPTION_READ_TIMEOUT:
483 				if (ptrparam) {
484 					/* struct timeval is supported with PHP_WIN32 defined. */
485 					struct timeval *tv = (struct timeval*)ptrparam;
486 
487 					/* A timeout of zero seconds and zero microseconds disables
488 					   any existing timeout. */
489 					if (tv->tv_sec || tv->tv_usec) {
490 						data->timeout_sec = tv->tv_sec;
491 						data->timeout_usec = tv->tv_usec;
492 						data->has_timeout = -1;
493 
494 						cto.ReadIntervalTimeout = MAXDWORD;
495 						cto.ReadTotalTimeoutMultiplier  = MAXDWORD;
496 						cto.ReadTotalTimeoutConstant = (data->timeout_usec / 1000) +
497 							(data->timeout_sec * 1000);
498 					} else {
499 						data->timeout_sec = 0;
500 						data->timeout_usec = 0;
501 						data->has_timeout = 0;
502 						data->timed_out = 0;
503 
504 						/* If we're not blocking but don't have a timeout
505 						   set to return immediately */
506 						if (!data->is_blocking) {
507 							cto.ReadIntervalTimeout = MAXDWORD;
508 						}
509 					}
510 
511 					if (!SetCommTimeouts(((php_dio_win32_stream_data*)data)->handle, &cto)) {
512 						return PHP_STREAM_OPTION_RETURN_ERR;
513 					} else {
514 						return PHP_STREAM_OPTION_RETURN_OK;
515 					}
516 				} else {
517 					return PHP_STREAM_OPTION_RETURN_ERR;
518 				}
519 
520 			default:
521 				break;
522 		}
523 	}
524 
525 	return 1;
526 }
527 /* }}} */
528 
529 /* {{{ dio_raw_open_stream
530  * Opens the underlying stream.
531  */
532 int dio_raw_open_stream(const char *filename, const char *mode, php_dio_stream_data *data) {
533 	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
534 	DWORD err;
535 
536 	switch(*mode) {
537 		case 'r':
538 			wdata->creation_disposition = OPEN_EXISTING;
539 			break;
540 		case 'w':
541 			wdata->creation_disposition = TRUNCATE_EXISTING;
542 			break;
543 		case 'a':
544 			wdata->creation_disposition = OPEN_ALWAYS;
545 			break;
546 		case 'x':
547 			wdata->creation_disposition = CREATE_NEW;
548 			break;
549 	}
550 	mode ++;
551 
552 	if (*mode && (*mode != '+')) {
553 		mode++;
554 	}
555 
556 	if (*mode && (*mode == '+')) {
557 		wdata->desired_access = GENERIC_READ | GENERIC_WRITE;
558 	} else if (OPEN_EXISTING == wdata->creation_disposition) {
559 		wdata->desired_access = GENERIC_READ;
560 	} else {
561 		wdata->desired_access = GENERIC_WRITE;
562 	}
563 
564 	wdata->handle = CreateFile(filename, wdata->desired_access, 0,
565 			NULL, wdata->creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
566 	if (INVALID_HANDLE_VALUE == wdata->handle) {
567 		err = GetLastError();
568 		switch (err) {
569 			case ERROR_FILE_EXISTS:
570 				php_error_docref(NULL, E_WARNING, "File exists!");
571 				return 0;
572 
573 			case ERROR_FILE_NOT_FOUND:
574 				/* ERROR_FILE_NOT_FOUND with TRUNCATE_EXISTING means that
575 				 * the file doesn't exist so now try to create it. */
576 				if (TRUNCATE_EXISTING == wdata->creation_disposition) {
577 					php_error_docref(NULL, E_NOTICE, "File does not exist, creating new file!");
578 
579 					wdata->handle = CreateFile(filename, wdata->desired_access, 0,
580 								NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
581 					if (INVALID_HANDLE_VALUE == wdata->handle) {
582 						dio_last_error_php_error(E_WARNING, "CreateFile() failed:");
583 						return 0;
584 					}
585 				} else {
586 					php_error_docref(NULL, E_WARNING, "File not found!");
587 					return 0;
588 				}
589 				break;
590 
591 			default:
592 				dio_last_error_php_error(E_WARNING, "CreateFile() failed:");
593 				return 0;
594 		}
595 	}
596 
597 	/* If canonical allocate the canonical buffer. */
598 	if (data->canonical) {
599 		wdata->canon_data = emalloc(sizeof(php_dio_win32_canon_data));
600 		memset(wdata->canon_data, 0, sizeof(php_dio_win32_canon_data));
601 		wdata->canon_data->size = DIO_WIN32_CANON_BUF_SIZE;
602 	}
603 
604 	return 1;
605 }
606 /* }}} */
607 
608 /* {{{ dio_serial_init
609  * Initialises the serial port
610  */
611 static int dio_serial_init(php_dio_stream_data *data) {
612 	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
613 	DWORD rate_def, data_bits_def, stop_bits_def, parity_def;
614 	DCB dcb;
615 
616 	if (!dio_data_rate_to_define(data->data_rate, &rate_def)) {
617 		php_error_docref(NULL, E_WARNING, "invalid data_rate value (%d)", data->data_rate);
618 		return 0;
619 	}
620 
621 	if (!dio_data_bits_to_define(data->data_bits, &data_bits_def)) {
622 		php_error_docref(NULL, E_WARNING, "invalid data_bits value (%d)", data->data_bits);
623 		return 0;
624 	}
625 
626 	if (!dio_stop_bits_to_define(data->stop_bits, &stop_bits_def)) {
627 		php_error_docref(NULL, E_WARNING, "invalid stop_bits value (%d)", data->stop_bits);
628 		return 0;
629 	}
630 
631 	if (!dio_parity_to_define(data->parity, &parity_def)) {
632 		php_error_docref(NULL, E_WARNING, "invalid parity value (%d)", data->parity);
633 		return 0;
634 	}
635 
636 	if (!GetCommState(wdata->handle, &(wdata->olddcb))) {
637 		dio_last_error_php_error(E_WARNING, "GetCommState() failed:");
638 		return 0;
639 	}
640 
641 	/* Init the DCB structure */
642 	memset(&dcb, 0, sizeof(DCB));
643 	dcb.DCBlength = sizeof(DCB);
644 
645 	/* Set the communication parameters */
646 	dcb.fBinary  = 1;
647 	dcb.BaudRate = rate_def;
648 	dcb.ByteSize = (BYTE)data_bits_def;
649 	dcb.StopBits = (BYTE)stop_bits_def;
650 	dcb.Parity   = (BYTE)parity_def;
651 
652 	/* Set the control line parameters */
653 	dcb.fDtrControl       = DTR_CONTROL_DISABLE;
654 	dcb.fDsrSensitivity   = FALSE;
655 	dcb.fOutxDsrFlow      = FALSE;
656 	dcb.fTXContinueOnXoff = FALSE;
657 	dcb.fOutX             = FALSE;
658 	dcb.fInX              = FALSE;
659 	dcb.fErrorChar        = FALSE;
660 	dcb.fNull             = FALSE;
661 	dcb.fAbortOnError     = FALSE;
662 
663 	/* Hardware flow control */
664 	if (data->flow_control) {
665 		dcb.fOutxCtsFlow = TRUE;
666 		dcb.fRtsControl  = RTS_CONTROL_HANDSHAKE;
667 	} else {
668 		dcb.fOutxCtsFlow = FALSE;
669 		dcb.fRtsControl  = RTS_CONTROL_DISABLE;
670 	}
671 
672 	if (!SetCommState(wdata->handle, &dcb)) {
673 		dio_last_error_php_error(E_WARNING, "SetCommState() failed:");
674 		return 0;
675 	}
676 
677 	return 1;
678 }
679 /* }}} */
680 
681 
682 /* {{{ dio_serial_uninit
683  * Restores the serial settings back to their original state.
684  */
685 int dio_serial_uninit(php_dio_stream_data *data) {
686 	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
687 
688 	if (!SetCommState(wdata->handle, &(wdata->olddcb))) {
689 		return 0;
690 	}
691 
692 	return 1;
693 }
694 /* }}} */
695 
696 /* {{{ dio_serial_flush
697  * Purges the serial buffers of data.
698  */
699 int dio_serial_purge(php_dio_stream_data *data) {
700 	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
701 	BOOL ret;
702 
703 	/* Purge the canonical buffer if required */
704 	if (data->canonical && ((wdata->desired_access & GENERIC_READ) == GENERIC_READ)) {
705 		wdata->canon_data->read_pos  = 0;
706 		wdata->canon_data->write_pos = 0;
707 	}
708 
709 	/* Purge the com port */
710 	if ((wdata->desired_access & (GENERIC_READ|GENERIC_WRITE)) == (GENERIC_READ|GENERIC_WRITE)) {
711 		ret = PurgeComm(wdata->handle, PURGE_RXCLEAR|PURGE_TXCLEAR);
712 	} else if ((wdata->desired_access & GENERIC_WRITE) == GENERIC_WRITE) {
713 		ret = PurgeComm(wdata->handle, PURGE_TXCLEAR);
714 	} else if ((wdata->desired_access & GENERIC_READ) == GENERIC_READ) {
715 		ret = PurgeComm(wdata->handle, PURGE_RXCLEAR);
716 	}
717 
718 	return ret;
719 }
720 /* }}} */
721 
722 /* {{{ dio_serial_open_stream
723  * Opens the underlying stream.
724  */
725 int dio_serial_open_stream(const char *filename, const char *mode, php_dio_stream_data *data) {
726 	php_dio_win32_stream_data *wdata = (php_dio_win32_stream_data*)data;
727 	COMMTIMEOUTS cto = { 0, 0, 0, 0, 0 };
728 
729 	php_error_docref(NULL, E_NOTICE, "Opening \"%s\" as a serial port (mode=\"%s\").", filename, mode);
730 
731 	if (*mode != 'r') {
732 		php_error_docref(NULL, E_WARNING, "You must open serial ports in read or read/write mode!");
733 		return 0;
734 	}
735 
736 	if (!dio_raw_open_stream(filename, mode, data)) {
737 		return 0;
738 	}
739 
740 	if (!GetCommTimeouts(wdata->handle, &(wdata->oldcto))) {
741 		dio_last_error_php_error(E_WARNING, "GetCommTimeouts() failed (Not a comm port?):");
742 		CloseHandle(wdata->handle);
743 		return 0;
744 	}
745 
746 	/* If we're not blocking but don't have a timeout
747 	   set to return immediately */
748 	if (!data->is_blocking && !data->has_timeout) {
749 		cto.ReadIntervalTimeout = MAXDWORD;
750 	}
751 
752 	/* If we have a timeout ignore the blocking and set
753 	   the total time in which to read the data */
754 	if (data->has_timeout) {
755 		cto.ReadIntervalTimeout = MAXDWORD;
756 		cto.ReadTotalTimeoutMultiplier  = MAXDWORD;
757 		cto.ReadTotalTimeoutConstant = (data->timeout_usec / 1000) +
758 			(data->timeout_sec * 1000);
759 	}
760 
761 	if (!SetCommTimeouts(wdata->handle, &cto)) {
762 		dio_last_error_php_error(E_WARNING, "SetCommTimeouts() failed:");
763 		CloseHandle(wdata->handle);
764 		return 0;
765 	}
766 
767 	if (!dio_serial_init(data)) {
768 		CloseHandle(wdata->handle);
769 		return 0;
770 	}
771 
772 	return 1;
773 }
774 /* }}} */
775 
776 /*
777  * Local variables:
778  * c-basic-offset: 4
779  * tab-width: 4
780  * End:
781  * vim600: fdm=marker
782  * vim: sw=4 ts=4 noet
783  */
784