source: trunk/csctapi/io_serial.c@ 1252

Last change on this file since 1252 was 1252, checked in by dingo35, 11 years ago

New SetBaudrate in ifd_phoenix.c eliminates lot of stuff

File size: 16.5 KB
Line 
1 /*
2 io_serial.c
3 Serial port input/output functions
4
5 This file is part of the Unix driver for Towitoko smartcard readers
6 Copyright (C) 2000 2001 Carlos Prados <cprados@yahoo.com>
7
8 This version is modified by doz21 to work in a special manner ;)
9
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2 of the License, or (at your option) any later version.
14
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
19
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23*/
24
25#include "defines.h"
26#include "../globals.h"
27#include <stdio.h>
28#include <string.h>
29#include <stdlib.h>
30#include <errno.h>
31#ifdef OS_HPUX
32#include <sys/modem.h>
33#endif
34#include <termios.h>
35#include <unistd.h>
36#include <sys/stat.h>
37#include <fcntl.h>
38#ifdef HAVE_POLL
39#include <sys/poll.h>
40#else
41#include <sys/signal.h>
42#include <sys/types.h>
43#endif
44#include <sys/time.h>
45#include <sys/ioctl.h>
46#include <time.h>
47#include "io_serial.h"
48#include "mc_global.h"
49
50#ifdef OS_LINUX
51#include <linux/serial.h>
52#endif
53
54#define IO_SERIAL_FILENAME_LENGTH 32
55
56/*
57 * Internal functions declaration
58 */
59
60static int IO_Serial_Bitrate(int bitrate);
61
62static bool IO_Serial_WaitToRead (unsigned delay_ms, unsigned timeout_ms);
63
64static bool IO_Serial_WaitToWrite (unsigned delay_ms, unsigned timeout_ms);
65
66static bool IO_Serial_InitPnP (IO_Serial * io);
67
68static void IO_Serial_Clear (IO_Serial * io);
69
70static int _in_echo_read = 0;
71int io_serial_need_dummy_char = 0;
72
73extern int fdmc;
74
75#if defined(TUXBOX) && defined(PPC)
76void IO_Serial_Ioctl_Lock(int flag)
77{
78 extern int *oscam_sem;
79 if ((reader[ridx].typ != R_DB2COM1) && (reader[ridx].typ != R_DB2COM2)) return;
80 if (!flag)
81 *oscam_sem=0;
82 else while (*oscam_sem!=reader[ridx].typ)
83 {
84 while (*oscam_sem)
85 usleep((reader[ridx].typ)*2000); //FIXME is this right ?!?!
86 *oscam_sem=reader[ridx].typ;
87 usleep(1000);
88 }
89}
90
91static bool IO_Serial_DTR_RTS_dbox2(int mcport, int dtr, int set)
92{
93 int rc;
94 unsigned short msr;
95 unsigned int mbit;
96 unsigned short rts_bits[2]={ 0x10, 0x800};
97 unsigned short dtr_bits[2]={0x100, 0};
98
99#ifdef DEBUG_IO
100printf("IO: multicam.o %s %s\n", dtr ? "dtr" : "rts", set ? "set" : "clear"); fflush(stdout);
101#endif
102 if ((rc=ioctl(fdmc, GET_PCDAT, &msr))>=0)
103 {
104 if (dtr) // DTR
105 {
106 if (dtr_bits[mcport])
107 {
108 if (set)
109 msr&=(unsigned short)(~dtr_bits[mcport]);
110 else
111 msr|=dtr_bits[mcport];
112 rc=ioctl(fdmc, SET_PCDAT, &msr);
113 }
114 else
115 rc=0; // Dummy, can't handle using multicam.o
116 }
117 else // RTS
118 {
119 if (set)
120 msr&=(unsigned short)(~rts_bits[mcport]);
121 else
122 msr|=rts_bits[mcport];
123 rc=ioctl(fdmc, SET_PCDAT, &msr);
124 }
125 }
126 return((rc<0) ? FALSE : TRUE);
127}
128#endif
129
130bool IO_Serial_DTR_RTS(int dtr, int set)
131{
132 unsigned int msr;
133 unsigned int mbit;
134
135#if defined(TUXBOX) && defined(PPC)
136 if ((reader[ridx].typ == R_DB2COM1) || (reader[ridx].typ == R_DB2COM2))
137 return(IO_Serial_DTR_RTS_dbox2(reader[ridx].typ == R_DB2COM2, dtr, set));
138#endif
139
140 mbit=(dtr) ? TIOCM_DTR : TIOCM_RTS;
141#if defined(TIOCMBIS) && defined(TIOBMBIC)
142 if (ioctl (reader[ridx].handle, set ? TIOCMBIS : TIOCMBIC, &mbit) < 0)
143 return FALSE;
144#else
145 if (ioctl(reader[ridx].handle, TIOCMGET, &msr) < 0)
146 return FALSE;
147 if (set)
148 msr|=mbit;
149 else
150 msr&=~mbit;
151 return((ioctl(reader[ridx].handle, TIOCMSET, &msr)<0) ? FALSE : TRUE);
152#endif
153}
154
155/*
156 * Public functions definition
157 */
158
159IO_Serial * IO_Serial_New (int mhz, int cardmhz)
160{
161 IO_Serial *io;
162
163 io = (IO_Serial *) malloc (sizeof (IO_Serial));
164
165 if (io != NULL)
166 IO_Serial_Clear (io);
167
168 return io;
169}
170
171bool IO_Serial_Init (IO_Serial * io, int reader_type)
172{
173 if (reader[ridx].typ != R_INTERNAL)
174 IO_Serial_InitPnP (io);
175
176 if(reader[ridx].typ!=R_INTERNAL)
177 IO_Serial_Flush();
178
179 return TRUE;
180}
181
182bool IO_Serial_SetBitrate (unsigned long bitrate, struct termios * tio)
183{
184 /* Set the bitrate */
185#ifdef OS_LINUX
186 if (reader[ridx].mhz == reader[ridx].cardmhz)
187#endif
188 { //no overcloking
189 cfsetospeed(tio, IO_Serial_Bitrate(bitrate));
190 cfsetispeed(tio, IO_Serial_Bitrate(bitrate));
191 cs_debug("standard baudrate: cardmhz=%d mhz=%d -> effective baudrate %lu", reader[ridx].cardmhz, reader[ridx].mhz, bitrate);
192 }
193#ifdef OS_LINUX
194 else { //over or underclocking
195 /* these structures are only available on linux as fas as we know so limit this code to OS_LINUX */
196 struct serial_struct nuts;
197 ioctl(reader[ridx].handle, TIOCGSERIAL, &nuts);
198 int custom_baud = bitrate * reader[ridx].mhz / reader[ridx].cardmhz;
199 nuts.custom_divisor = (nuts.baud_base + (custom_baud/2))/ custom_baud;
200 cs_debug("custom baudrate: cardmhz=%d mhz=%d custom_baud=%d baud_base=%d divisor=%d -> effective baudrate %d",
201 reader[ridx].cardmhz, reader[ridx].mhz, custom_baud, nuts.baud_base, nuts.custom_divisor, nuts.baud_base/nuts.custom_divisor);
202 nuts.flags &= ~ASYNC_SPD_MASK;
203 nuts.flags |= ASYNC_SPD_CUST;
204 ioctl(reader[ridx].handle, TIOCSSERIAL, &nuts);
205 cfsetospeed(tio, IO_Serial_Bitrate(38400));
206 cfsetispeed(tio, IO_Serial_Bitrate(38400));
207 }
208#endif
209 return TRUE;
210}
211
212bool IO_Serial_SetParams (unsigned long bitrate, unsigned bits, int parity, unsigned stopbits, int dtr, int rts)
213{
214 struct termios newtio;
215
216 if(reader[ridx].typ == R_INTERNAL)
217 return FALSE;
218
219 // printf("IO: Setting properties: reader_type%d, %ld bps; %d bits/byte; %s parity; %d stopbits; dtr=%d; rts=%d\n", reader[ridx].typ, bitrate, bits, parity == PARITY_EVEN ? "Even" : parity == PARITY_ODD ? "Odd" : "None", stopbits, dtr, rts);
220 memset (&newtio, 0, sizeof (newtio));
221
222 if (!IO_Serial_SetBitrate (bitrate, & newtio))
223 return FALSE;
224
225 /* Set the character size */
226 switch (bits)
227 {
228 case 5:
229 newtio.c_cflag |= CS5;
230 break;
231
232 case 6:
233 newtio.c_cflag |= CS6;
234 break;
235
236 case 7:
237 newtio.c_cflag |= CS7;
238 break;
239
240 case 8:
241 newtio.c_cflag |= CS8;
242 break;
243 }
244
245 /* Set the parity */
246 switch (parity)
247 {
248 case PARITY_ODD:
249 newtio.c_cflag |= PARENB;
250 newtio.c_cflag |= PARODD;
251 break;
252
253 case PARITY_EVEN:
254 newtio.c_cflag |= PARENB;
255 newtio.c_cflag &= ~PARODD;
256 break;
257
258 case PARITY_NONE:
259 newtio.c_cflag &= ~PARENB;
260 break;
261 }
262
263 /* Set the number of stop bits */
264 switch (stopbits)
265 {
266 case 1:
267 newtio.c_cflag &= (~CSTOPB);
268 break;
269 case 2:
270 newtio.c_cflag |= CSTOPB;
271 break;
272 }
273
274 /* Selects raw (non-canonical) input and output */
275 newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
276 newtio.c_oflag &= ~OPOST;
277#if 1
278 newtio.c_iflag |= IGNPAR;
279 /* Ignore parity errors!!! Windows driver does so why shouldn't I? */
280#endif
281 /* Enable receiber, hang on close, ignore control line */
282 newtio.c_cflag |= CREAD | HUPCL | CLOCAL;
283
284 /* Read 1 byte minimun, no timeout specified */
285 newtio.c_cc[VMIN] = 1;
286 newtio.c_cc[VTIME] = 0;
287
288 if (!IO_Serial_SetProperties(newtio))
289 return FALSE;
290
291 reader[ridx].baudrate = bitrate;
292
293 IO_Serial_Ioctl_Lock(1);
294 IO_Serial_DTR_RTS(0, rts == IO_SERIAL_HIGH);
295 IO_Serial_DTR_RTS(1, dtr == IO_SERIAL_HIGH);
296 IO_Serial_Ioctl_Lock(0);
297
298 return TRUE;
299}
300
301bool IO_Serial_SetProperties (struct termios newtio)
302{
303 if(reader[ridx].typ == R_INTERNAL)
304 return FALSE;
305
306 if (tcsetattr (reader[ridx].handle, TCSANOW, &newtio) < 0)
307 return FALSE;
308// tcflush(reader[ridx].handle, TCIOFLUSH);
309// if (tcsetattr (reader[ridx].handle, TCSAFLUSH, &newtio) < 0)
310// return FALSE;
311
312 unsigned int mctl;
313 if (ioctl (reader[ridx].handle, TIOCMGET, &mctl) < 0)
314 return FALSE;
315
316#ifdef DEBUG_IO
317 printf("IO: Setting properties\n");
318#endif
319
320 return TRUE;
321}
322
323int IO_Serial_SetParity (BYTE parity)
324{
325 if(reader[ridx].typ == R_INTERNAL)
326 return TRUE;
327
328 if ((parity != PARITY_EVEN) && (parity != PARITY_ODD) && (parity != PARITY_NONE))
329 return FALSE;
330
331 struct termios tio;
332 int current_parity;
333 // Get current parity
334 if (tcgetattr (reader[ridx].handle, &tio) != 0)
335 return FALSE;
336
337 if (((tio.c_cflag) & PARENB) == PARENB)
338 {
339 if (((tio.c_cflag) & PARODD) == PARODD)
340 current_parity = PARITY_ODD;
341 else
342 current_parity = PARITY_EVEN;
343 }
344 else
345 {
346 current_parity = PARITY_NONE;
347 }
348
349#ifdef DEBUG_IFD
350 printf ("IFD: Setting parity from %s to %s\n",
351 current_parity == PARITY_ODD ? "Odd" :
352 current_parity == PARITY_NONE ? "None" :
353 current_parity == PARITY_EVEN ? "Even" : "Invalid",
354 parity == PARITY_ODD ? "Odd" :
355 parity == PARITY_NONE ? "None" :
356 parity == PARITY_EVEN ? "Even" : "Invalid");
357#endif
358
359 if (current_parity != parity)
360 {
361
362 // Set the parity
363 switch (parity)
364 {
365 case PARITY_ODD:
366 tio.c_cflag |= PARENB;
367 tio.c_cflag |= PARODD;
368 break;
369
370 case PARITY_EVEN:
371 tio.c_cflag |= PARENB;
372 tio.c_cflag &= ~PARODD;
373 break;
374
375 case PARITY_NONE:
376 tio.c_cflag &= ~PARENB;
377 break;
378 }
379 if (!IO_Serial_SetProperties (tio))
380 return FALSE;
381 }
382
383 return TRUE;
384}
385
386void IO_Serial_Flush ()
387{
388 BYTE b;
389 while(IO_Serial_Read(1000, 1, &b));
390}
391
392
393void IO_Serial_GetPnPId (IO_Serial * io, BYTE * pnp_id, unsigned *length)
394{
395 (*length) = io->PnP_id_size;
396 memcpy (pnp_id, io->PnP_id, io->PnP_id_size);
397}
398
399bool IO_Serial_Read (unsigned timeout, unsigned size, BYTE * data)
400{
401 BYTE c;
402 uint count = 0;
403#ifdef SH4
404 bool readed;
405 struct timeval tv, tv_spent;
406#endif
407
408 if((reader[ridx].typ != R_INTERNAL) && (wr>0))
409 {
410 BYTE buf[256];
411 int n = wr;
412 wr = 0;
413
414 if(!IO_Serial_Read (timeout, n, buf))
415 {
416 return FALSE;
417 }
418 }
419
420#ifdef DEBUG_IO
421 printf ("IO: Receiving: ");
422 fflush (stdout);
423#endif
424 for (count = 0; count < size * (_in_echo_read ? (1+io_serial_need_dummy_char) : 1); count++)
425 {
426#ifdef SH4
427 gettimeofday(&tv,0);
428 memcpy(&tv_spent,&tv,sizeof(struct timeval));
429 readed=FALSE;
430 while( (((tv_spent.tv_sec-tv.tv_sec)*1000) + ((tv_spent.tv_usec-tv.tv_usec)/1000L))<timeout )
431 {
432 if (read (reader[ridx].handle, &c, 1) == 1)
433 {
434 readed=TRUE;
435 break;
436 }
437 gettimeofday(&tv_spent,0);
438 }
439 if(!readed) return FALSE;
440
441 data[_in_echo_read ? count/(1+io_serial_need_dummy_char) : count] = c;
442#ifdef DEBUG_IO
443 printf ("%X ", c);
444 fflush (stdout);
445#endif
446#else
447 if (IO_Serial_WaitToRead (0, timeout))
448 {
449 if (read (reader[ridx].handle, &c, 1) != 1)
450 {
451#ifdef DEBUG_IO
452 printf ("ERROR\n");
453 fflush (stdout);
454#endif
455 return FALSE;
456 }
457 data[_in_echo_read ? count/(1+io_serial_need_dummy_char) : count] = c;
458
459#ifdef DEBUG_IO
460 printf ("%X ", c);
461 fflush (stdout);
462#endif
463 }
464 else
465 {
466#ifdef DEBUG_IO
467 printf ("TIMEOUT\n");
468 fflush (stdout);
469#endif
470 tcflush (reader[ridx].handle, TCIFLUSH);
471 return FALSE;
472 }
473#endif
474 }
475
476 _in_echo_read = 0;
477
478#ifdef DEBUG_IO
479 printf ("\n");
480 fflush (stdout);
481#endif
482
483 return TRUE;
484}
485
486
487
488
489bool IO_Serial_Write (unsigned delay, unsigned size, BYTE * data)
490{
491 unsigned count, to_send, i_w;
492 BYTE data_w[512];
493#ifdef DEBUG_IO
494 unsigned i;
495
496 printf ("IO: Sending: ");
497 fflush (stdout);
498#endif
499 /* Discard input data from previous commands */
500// tcflush (reader[ridx].handle, TCIFLUSH);
501
502 for (count = 0; count < size; count += to_send)
503 {
504// if(reader[ridx].typ == R_INTERNAL)
505// to_send = 1;
506// else
507 to_send = (delay? 1: size);
508
509 if (IO_Serial_WaitToWrite (delay, 1000))
510 {
511 for (i_w=0; i_w < to_send; i_w++) {
512 data_w [(1+io_serial_need_dummy_char)*i_w] = data [count + i_w];
513 if (io_serial_need_dummy_char) {
514 data_w [2*i_w+1] = 0x00;
515 }
516 }
517 unsigned int u = write (reader[ridx].handle, data_w, (1+io_serial_need_dummy_char)*to_send);
518 _in_echo_read = 1;
519 if (u != (1+io_serial_need_dummy_char)*to_send)
520 {
521#ifdef DEBUG_IO
522 printf ("ERROR\n");
523 fflush (stdout);
524#endif
525 if(reader[ridx].typ != R_INTERNAL)
526 wr += u;
527 return FALSE;
528 }
529
530 if(reader[ridx].typ != R_INTERNAL)
531 wr += to_send;
532
533#ifdef DEBUG_IO
534 for (i=0; i<(1+io_serial_need_dummy_char)*to_send; i++)
535 printf ("%X ", data_w[count + i]);
536 fflush (stdout);
537#endif
538 }
539 else
540 {
541#ifdef DEBUG_IO
542 printf ("TIMEOUT\n");
543 fflush (stdout);
544#endif
545// tcflush (reader[ridx].handle, TCIFLUSH);
546 return FALSE;
547 }
548 }
549
550#ifdef DEBUG_IO
551 printf ("\n");
552 fflush (stdout);
553#endif
554
555 return TRUE;
556}
557
558bool IO_Serial_Close (IO_Serial * io)
559{
560
561#ifdef DEBUG_IO
562 printf ("IO: Clossing serial port %s\n", reader[ridx].device);
563#endif
564
565#if defined(TUXBOX) && defined(PPC)
566 close(fdmc);
567#endif
568 if (close (reader[ridx].handle) != 0)
569 return FALSE;
570
571 IO_Serial_Clear (io);
572
573 return TRUE;
574}
575
576/*
577 * Internal functions definition
578 */
579
580static int IO_Serial_Bitrate(int bitrate)
581{
582#ifdef B230400
583 if ((bitrate)>=230400) return B230400;
584#endif
585#ifdef B115200
586 if ((bitrate)>=115200) return B115200;
587#endif
588#ifdef B57600
589 if ((bitrate)>=57600) return B57600;
590#endif
591#ifdef B38400
592 if ((bitrate)>=38400) return B38400;
593#endif
594#ifdef B19200
595 if ((bitrate)>=19200) return B19200;
596#endif
597#ifdef B9600
598 if ((bitrate)>=9600) return B9600;
599#endif
600#ifdef B4800
601 if ((bitrate)>=4800) return B4800;
602#endif
603#ifdef B2400
604 if ((bitrate)>=2400) return B2400;
605#endif
606#ifdef B1800
607 if ((bitrate)>=1800) return B1800;
608#endif
609#ifdef B1200
610 if ((bitrate)>=1200) return B1200;
611#endif
612#ifdef B600
613 if ((bitrate)>=600) return B600;
614#endif
615#ifdef B300
616 if ((bitrate)>=300) return B300;
617#endif
618#ifdef B200
619 if ((bitrate)>=200) return B200;
620#endif
621#ifdef B150
622 if ((bitrate)>=150) return B150;
623#endif
624#ifdef B134
625 if ((bitrate)>=134) return B134;
626#endif
627#ifdef B110
628 if ((bitrate)>=110) return B110;
629#endif
630#ifdef B75
631 if ((bitrate)>=75) return B75;
632#endif
633#ifdef B50
634 if ((bitrate)>=50) return B50;
635#endif
636#ifdef B0
637 if ((bitrate)>=0) return B0;
638#endif
639 return 0; /* Should never get here */
640}
641
642static bool IO_Serial_WaitToRead (unsigned delay_ms, unsigned timeout_ms)
643{
644 fd_set rfds;
645 fd_set erfds;
646 struct timeval tv;
647 int select_ret;
648 int in_fd;
649
650 if (delay_ms > 0)
651 {
652#ifdef HAVE_NANOSLEEP
653 struct timespec req_ts;
654
655 req_ts.tv_sec = delay_ms / 1000;
656 req_ts.tv_nsec = (delay_ms % 1000) * 1000000L;
657 nanosleep (&req_ts, NULL);
658#else
659 usleep (delay_ms * 1000L);
660#endif
661 }
662
663 in_fd=reader[ridx].handle;
664
665 FD_ZERO(&rfds);
666 FD_SET(in_fd, &rfds);
667
668 FD_ZERO(&erfds);
669 FD_SET(in_fd, &erfds);
670
671 tv.tv_sec = timeout_ms/1000;
672 tv.tv_usec = (timeout_ms % 1000) * 1000L;
673 select_ret = select(in_fd+1, &rfds, NULL, &erfds, &tv);
674 if(select_ret==-1)
675 {
676 printf("select_ret=%i\n" , select_ret);
677 printf("errno =%d\n", errno);
678 fflush(stdout);
679 return (FALSE);
680 }
681
682 if (FD_ISSET(in_fd, &erfds))
683 {
684 printf("fd is in error fds\n");
685 printf("errno =%d\n", errno);
686 fflush(stdout);
687 return (FALSE);
688 }
689
690 return(FD_ISSET(in_fd,&rfds));
691}
692
693static bool IO_Serial_WaitToWrite (unsigned delay_ms, unsigned timeout_ms)
694{
695 fd_set wfds;
696 fd_set ewfds;
697 struct timeval tv;
698 int select_ret;
699 int out_fd;
700
701#ifdef SCI_DEV
702 if(reader[ridx].typ == R_INTERNAL)
703 return TRUE;
704#endif
705
706 if (delay_ms > 0)
707 {
708#ifdef HAVE_NANOSLEEP
709 struct timespec req_ts;
710
711 req_ts.tv_sec = delay_ms / 1000;
712 req_ts.tv_nsec = (delay_ms % 1000) * 1000000L;
713 nanosleep (&req_ts, NULL);
714#else
715 usleep (delay_ms * 1000L);
716#endif
717 }
718
719 out_fd=reader[ridx].handle;
720
721 FD_ZERO(&wfds);
722 FD_SET(out_fd, &wfds);
723
724 FD_ZERO(&ewfds);
725 FD_SET(out_fd, &ewfds);
726
727 tv.tv_sec = timeout_ms/1000L;
728 tv.tv_usec = (timeout_ms % 1000) * 1000L;
729
730 select_ret = select(out_fd+1, NULL, &wfds, &ewfds, &tv);
731
732 if(select_ret==-1)
733 {
734 printf("select_ret=%d\n" , select_ret);
735 printf("errno =%d\n", errno);
736 fflush(stdout);
737 return (FALSE);
738 }
739
740 if (FD_ISSET(out_fd, &ewfds))
741 {
742 printf("fd is in ewfds\n");
743 printf("errno =%d\n", errno);
744 fflush(stdout);
745 return (FALSE);
746 }
747
748 return(FD_ISSET(out_fd,&wfds));
749
750}
751
752static void IO_Serial_Clear (IO_Serial * io)
753{
754 memset (io->PnP_id, 0, IO_SERIAL_PNPID_SIZE);
755 io->PnP_id_size = 0;
756 wr = 0;
757 //modifyable properties:
758 io->input_bitrate = 0;
759 io->output_bitrate = 0;
760 io->bits = 0;
761 io->stopbits = 0;
762 io->parity = 0;
763 io->dtr = 0;
764 io->rts = 0;
765}
766
767static bool IO_Serial_InitPnP (IO_Serial * io)
768{
769 int i = 0;
770 if (!IO_Serial_SetParams (1200, 7, PARITY_NONE, 1, IO_SERIAL_HIGH, IO_SERIAL_LOW))
771 return FALSE;
772
773 while ((i < IO_SERIAL_PNPID_SIZE) && IO_Serial_Read (200, 1, &(io->PnP_id[i])))
774 i++;
775
776 io->PnP_id_size = i;
777 return TRUE;
778}
779
Note: See TracBrowser for help on using the repository browser.