1 //---------------------------------------------------------------------------
2 // Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 // IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
18 // OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 // OTHER DEALINGS IN THE SOFTWARE.
21 //
22 // Except as contained in this notice, the name of Dallas Semiconductor
23 // shall not be used except as stated in the Dallas Semiconductor
24 // Branding Policy.
25 //---------------------------------------------------------------------------
26 //
27 //  linuxlnk.C - COM functions required by MLANLL.C, MLANTRNU, MLANNETU.C and
28 //           MLanFile.C for MLANU to communicate with the DS2480 based
29 //           Universal Serial Adapter 'U'.  Fill in the platform specific code.
30 //
31 //  Version: 1.02
32 //
33 //  History: 1.00 -> 1.01  Added function msDelay.
34 //
35 //           1.01 -> 1.02  Changed to generic OpenCOM/CloseCOM for easier
36 //                         use with other platforms.
37 //
38 
39 //--------------------------------------------------------------------------
40 // Copyright (C) 1998 Andrea Chambers and University of Newcastle upon Tyne,
41 // All Rights Reserved.
42 //--------------------------------------------------------------------------
43 //
44 // Permission is hereby granted, free of charge, to any person obtaining a
45 // copy of this software and associated documentation files (the "Software"),
46 // to deal in the Software without restriction, including without limitation
47 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
48 // and/or sell copies of the Software, and to permit persons to whom the
49 // Software is furnished to do so, subject to the following conditions:
50 //
51 // The above copyright notice and this permission notice shall be included
52 // in all copies or substantial portions of the Software.
53 //
54 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
55 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
56 // MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
57 // IN NO EVENT SHALL THE UNIVERSITY OF NEWCASTLE UPON TYNE OR ANDREA CHAMBERS
58 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
59 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
60 // THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
61 //---------------------------------------------------------------------------
62 //
63 //  LinuxLNK.C - COM functions required by MLANLLU.C, MLANTRNU.C, MLANNETU.C
64 //             and MLanFile.C for MLANU to communicate with the DS2480 based
65 //             Universal Serial Adapter 'U'.  Platform specific code.
66 //
67 //  Version: 2.01
68 //  History: 1.02 -> 1.03  modifications by David Smiczek
69 //                         Changed to use generic OpenCOM/CloseCOM
70 //                         Pass port name to OpenCOM instead of hard coded
71 //                         Changed msDelay to handle long delays
72 //                         Reformatted to look like 'TODO.C'
73 //                         Added #include "ds2480.h" to use constants.
74 //                         Added function SetBaudCOM()
75 //                         Added function msGettick()
76 //                         Removed delay from WriteCOM(), used tcdrain()
77 //                         Added wait for byte available with timeout using
78 //                          select() in ReadCOM()
79 //
80 //           1.03 -> 2.00  Support for multiple ports. Include "ownet.h". Use
81 //                         'uchar'.  Reorder functions. Provide correct
82 //                         return values to OpenCOM.  Replace 'makeraw' call.
83 //                         Should now be POSIX.
84 //           2.00 -> 2.01  Added support for owError library.
85 //
86 
87 #include <unistd.h>
88 #include <sys/types.h>
89 #include <sys/stat.h>
90 #include <fcntl.h>
91 #include <sys/ioctl.h>
92 #include <time.h>
93 #include <termios.h>
94 #include <errno.h>
95 #include <sys/time.h>
96 #include <sys/file.h>
97 
98 #include "ds2480.h"
99 #include "ownet.h"
100 
101 /* exportable functions */
102 SMALLINT  OpenCOM(int, char*);
103 SMALLINT  WriteCOM(int, int, uchar*);
104 void      CloseCOM(int);
105 void      FlushCOM(int);
106 int       ReadCOM(int, int, uchar*);
107 void      BreakCOM(int);
108 void      SetBaudCOM(int, uchar);
109 void      msDelay(int);
110 long      msGettick(void);
111 
112 // LinuxLNK global
113 int fd[MAX_PORTNUM];
114 struct termios origterm;
115 
116 //---------------------------------------------------------------------------
117 // Attempt to open a com port.
118 // Set the starting baud rate to 9600.
119 //
120 // 'portnum'   - number 0 to MAX_PORTNUM-1.  This number provided will
121 //               be used to indicate the port number desired when calling
122 //               all other functions in this library.
123 //
124 // 'port_zstr' - zero terminate port name.  For this platform
125 //               use format COMX where X is the port number.
126 //
127 //
128 // Returns: TRUE(1)  - success, COM port opened
129 //          FALSE(0) - failure, could not open specified port
130 //
OpenCOM(int portnum,char * port_zstr)131 SMALLINT OpenCOM(int portnum, char *port_zstr)
132 {
133    struct termios t;               // see man termios - declared as above
134    int rc;
135 
136    fd[portnum] = open(port_zstr, O_RDWR|O_NONBLOCK);
137    if (fd[portnum]<0)
138    {
139       OWERROR(OWERROR_GET_SYSTEM_RESOURCE_FAILED);
140       return FALSE;  // changed (2.00), used to return fd;
141    }
142 
143    /* Lock the device */
144    if(flock(fd[portnum], LOCK_EX|LOCK_NB))
145      {
146         OWERROR(OWERROR_GET_SYSTEM_RESOURCE_FAILED);
147         return FALSE;
148      }
149 
150    rc = tcgetattr (fd[portnum], &t);
151    if (rc < 0)
152    {
153       int tmp;
154       tmp = errno;
155       close(fd[portnum]);
156       errno = tmp;
157       OWERROR(OWERROR_SYSTEM_RESOURCE_INIT_FAILED);
158       return FALSE; // changed (2.00), used to return rc;
159    }
160 
161    cfsetospeed(&t, B9600);
162    cfsetispeed (&t, B9600);
163 
164    // Get terminal parameters. (2.00) removed raw
165    tcgetattr(fd[portnum],&t);
166    // Save original settings.
167    origterm = t;
168 
169    // Set to non-canonical mode, and no RTS/CTS handshaking
170    t.c_iflag &= ~(BRKINT|ICRNL|IGNCR|INLCR|INPCK|ISTRIP|IXON|IXOFF|PARMRK);
171    t.c_iflag |= IGNBRK|IGNPAR;
172    t.c_oflag &= ~(OPOST);
173    t.c_cflag &= ~(CRTSCTS|CSIZE|HUPCL|PARENB);
174    t.c_cflag |= (CLOCAL|CS8|CREAD);
175    t.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|IEXTEN|ISIG);
176    t.c_cc[VMIN] = 0;
177    t.c_cc[VTIME] = 3;
178 
179    rc = tcsetattr(fd[portnum], TCSAFLUSH, &t);
180    tcflush(fd[portnum],TCIOFLUSH);
181 
182    if (rc < 0)
183    {
184       int tmp;
185       tmp = errno;
186       close(fd[portnum]);
187       errno = tmp;
188       OWERROR(OWERROR_SYSTEM_RESOURCE_INIT_FAILED);
189       return FALSE; // changed (2.00), used to return rc;
190    }
191 
192    return TRUE; // changed (2.00), used to return fd;
193 }
194 
195 
196 //---------------------------------------------------------------------------
197 // Closes the connection to the port.
198 //
199 // 'portnum'  - number 0 to MAX_PORTNUM-1.  This number was provided to
200 //              OpenCOM to indicate the port number.
201 //
CloseCOM(int portnum)202 void CloseCOM(int portnum)
203 {
204    // restore tty settings
205    tcsetattr(fd[portnum], TCSAFLUSH, &origterm);
206    FlushCOM(portnum);
207    close(fd[portnum]);
208 }
209 
210 
211 //--------------------------------------------------------------------------
212 // Write an array of bytes to the COM port, verify that it was
213 // sent out.  Assume that baud rate has been set.
214 //
215 // 'portnum'   - number 0 to MAX_PORTNUM-1.  This number provided will
216 //               be used to indicate the port number desired when calling
217 //               all other functions in this library.
218 // Returns 1 for success and 0 for failure
219 //
WriteCOM(int portnum,int outlen,uchar * outbuf)220 SMALLINT WriteCOM(int portnum, int outlen, uchar *outbuf)
221 {
222    long count = outlen;
223    int i = write(fd[portnum], outbuf, outlen);
224 
225    tcdrain(fd[portnum]);
226    return (i == count);
227 }
228 
229 
230 //--------------------------------------------------------------------------
231 // Read an array of bytes to the COM port, verify that it was
232 // sent out.  Assume that baud rate has been set.
233 //
234 // 'portnum'  - number 0 to MAX_PORTNUM-1.  This number was provided to
235 //              OpenCOM to indicate the port number.
236 // 'outlen'   - number of bytes to write to COM port
237 // 'outbuf'   - pointer ot an array of bytes to write
238 //
239 // Returns:  Number of bytes actually read
240 //
ReadCOM(int portnum,int inlen,uchar * inbuf)241 int ReadCOM(int portnum, int inlen, uchar *inbuf)
242 {
243    fd_set         filedescr;
244    struct timeval tval;
245    int            cnt;
246    int            sel;
247    int            msec = 0;
248    extern int     global_msec,
249                   global_msec_max;
250 
251 
252    // loop to wait until each byte is available and read it
253    for (cnt = 0; cnt < inlen; cnt++)
254    {
255       if( msec < global_msec )
256          msec = global_msec;
257 
258       // set a descriptor to wait for a character available
259       FD_ZERO(&filedescr);
260       FD_SET(fd[portnum],&filedescr);
261       // set initial timeout to global_msec ms
262       tval.tv_sec = 1;
263       tval.tv_usec = 1000 * msec;
264 
265       // if byte available read or return bytes read
266       sel = select(fd[portnum]+1,&filedescr,NULL,NULL,&tval);
267       if ( sel > 0 )
268       {
269          if (read(fd[portnum],&inbuf[cnt],1) != 1)
270          {
271             /* Received something, return it */
272             return cnt;
273          }
274       } else if( sel == 0 ) {
275          /* We timed out waiting for a character, so increase the limit */
276          global_msec++;
277          cnt = -1;
278 
279 #ifdef DEBUG_USERIAL
280          fprintf(stderr, "Increasing delay to %ld\n", global_msec * 1000 );
281 #endif /* DEBUG_USERIAL */
282 
283          /* don't go too high, just return an error (0) */
284          if( global_msec > global_msec_max )
285            return 0;
286       } else {
287          return cnt;
288       } /* select if */
289    } /* count loop */
290 
291    // success, so return desired length
292    return inlen;
293 }
294 
295 
296 //---------------------------------------------------------------------------
297 //  Description:
298 //     flush the rx and tx buffers
299 //
300 // 'portnum'  - number 0 to MAX_PORTNUM-1.  This number was provided to
301 //              OpenCOM to indicate the port number.
302 //
FlushCOM(int portnum)303 void FlushCOM(int portnum)
304 {
305    tcflush(fd[portnum], TCIOFLUSH);
306 }
307 
308 
309 //--------------------------------------------------------------------------
310 //  Description:
311 //     Send a break on the com port for at least 2 ms
312 //
313 // 'portnum'  - number 0 to MAX_PORTNUM-1.  This number was provided to
314 //              OpenCOM to indicate the port number.
315 //
BreakCOM(int portnum)316 void BreakCOM(int portnum)
317 {
318    int duration = 0;                       // see man termios break may be
319    tcsendbreak(fd[portnum], duration);     // too long
320 }
321 
322 
323 //--------------------------------------------------------------------------
324 // Set the baud rate on the com port.
325 //
326 // 'portnum'   - number 0 to MAX_PORTNUM-1.  This number was provided to
327 //               OpenCOM to indicate the port number.
328 // 'new_baud'  - new baud rate defined as
329 // PARMSET_9600     0x00
330 // PARMSET_19200    0x02
331 // PARMSET_57600    0x04
332 // PARMSET_115200   0x06
333 //
SetBaudCOM(int portnum,uchar new_baud)334 void SetBaudCOM(int portnum, uchar new_baud)
335 {
336    struct termios t;
337    int rc;
338    speed_t baud=B9600;
339 
340    // read the attribute structure
341    rc = tcgetattr(fd[portnum], &t);
342    if (rc < 0)
343    {
344       close(fd[portnum]);
345       return;
346    }
347 
348    // convert parameter to linux baud rate
349    switch(new_baud)
350    {
351       case PARMSET_9600:
352          baud = B9600;
353          break;
354       case PARMSET_19200:
355          baud = B19200;
356          break;
357       case PARMSET_57600:
358          baud = B57600;
359          break;
360       case PARMSET_115200:
361          baud = B115200;
362          break;
363    }
364 
365    // set baud in structure
366    cfsetospeed(&t, baud);
367    cfsetispeed(&t, baud);
368 
369    // change baud on port
370    rc = tcsetattr(fd[portnum], TCSAFLUSH, &t);
371    if (rc < 0)
372       close(fd[portnum]);
373 }
374 
375 
376 //--------------------------------------------------------------------------
377 // Get the current millisecond tick count.  Does not have to represent
378 // an actual time, it just needs to be an incrementing timer.
379 //
msGettick(void)380 long msGettick(void)
381 {
382    struct timezone tmzone;
383    struct timeval  tmval;
384    long ms;
385 
386    gettimeofday(&tmval,&tmzone);
387    ms = (tmval.tv_sec & 0xFFFF) * 1000 + tmval.tv_usec / 1000;
388    return ms;
389 }
390 
391 
392 //--------------------------------------------------------------------------
393 //  Description:
394 //     Delay for at least 'len' ms
395 //
msDelay(int len)396 void msDelay(int len)
397 {
398    struct timespec s;              // Set aside memory space on the stack
399 
400    s.tv_sec = len / 1000;
401    s.tv_nsec = (len - (s.tv_sec * 1000)) * 1000000;
402    nanosleep(&s, NULL);
403 }
404 
405