1 /*
2  * port.c - All port related vrflash functions Copyright (C) 2001 Jeff
3  * Carneal
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */
19 
20 #include "config.h"
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <sys/time.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 
28 #ifdef HAVE_TERMIO_H
29 #include <termio.h>
30 #else
31 #include <termios.h>
32 #endif
33 
34 #include <string.h>
35 #include <ctype.h>
36 #ifdef HAVE_LOW_LATENCY
37 #include <linux/serial.h>
38 #endif
39 #include "main.h"
40 #include "port.h"
41 #include "xmodem.h"
42 #include "vrerror.h"
43 
44 const char *prompt1 = "PMON> ";
45 const char *prompt2 = "PMram>";	// used on all 16MB ram models before 2002/10/23
46 
47 extern int do_portlog;
48 
49 #ifdef HAVE_LOW_LATENCY
50 int
port_lowlatency(char * filestr,int fd)51 port_lowlatency(char *filestr, int fd)
52 {
53     struct serial_struct serinfo;
54 
55 	if (ioctl (fd, TIOCGSERIAL, &serinfo) < 0)
56     {
57 		vr_warning ("Warning:  Cannot get linux serial options");
58         return fd;
59     }
60 	serinfo.flags |= ASYNC_LOW_LATENCY;
61 	if (ioctl (fd, TIOCSSERIAL, &serinfo) < 0)
62     {
63 		vr_warning ("Warning:  Cannot set linux serial options");
64         return fd;
65     }
66 
67 	/*
68 	 * Major hack.  For some reason the LOW_LATENCY flag
69 	 * will not set on an open port.  You have to close
70 	 * and reopen port to make it work.  I looked
71 	 * for a TIOC ioctl flag that would override this
72 	 * but couldn't find it.  Let me know if you know
73 	 * of one, cuz I hate this code
74 	 */
75 	close (fd);
76 	if ((fd = open (filestr, O_RDWR | O_NOCTTY | O_NDELAY)) < 0)
77 		return -1;
78 
79 	if (ioctl (fd, TIOCGSERIAL, &serinfo) < 0)
80     {
81 		vr_warning ("Warning:  Cannot get linux serial options");
82         return fd;
83     }
84 	serinfo.flags |= ASYNC_LOW_LATENCY;
85 	if (ioctl (fd, TIOCSSERIAL, &serinfo) < 0)
86     {
87 		vr_warning ("Warning:  Cannot set linux serial options");
88         return fd;
89     }
90     return fd;
91 }
92 #endif
93 
94 /*
95  * Returns the file descriptor on success or -1 on error.
96  */
97 int
port_open(char * filestr)98 port_open (char *filestr)
99 {
100 	int fd;
101 
102 	if ((fd = open (filestr, O_RDWR | O_NOCTTY | O_NDELAY)) < 0)
103 		return -1;
104 
105 #ifdef HAVE_LOW_LATENCY
106     if ((fd=port_lowlatency(filestr, fd)) < 0)
107         return -1;
108 #endif
109 
110 	fcntl (fd, F_SETFL, 0);
111 	return (fd);
112 }
113 
114 void
port_init(int fd)115 port_init (int fd)
116 {
117 	struct termios options;
118 
119 	/*
120 	 * Get the port options
121 	 */
122 	if (tcgetattr (fd, &options) < 0)
123 		vr_error ("Error:  Cannot get serial options");
124 
125 	/*
126 	 * Set to 115200 bps
127 	 */
128 	cfsetispeed (&options, B115200);
129 	cfsetospeed (&options, B115200);
130 
131 	/*
132 	 * Set to 8N1
133 	 */
134 	options.c_cflag |= (CLOCAL | CREAD);
135 	options.c_cflag &= ~PARENB;
136 	options.c_cflag &= ~CSTOPB;
137 	options.c_cflag &= ~CSIZE;
138 	options.c_cflag |= CS8;
139 
140 	/*
141 	 * Hardware FC
142 	 */
143 #ifdef HAVE_CRTSCTS
144 	options.c_cflag |= CRTSCTS;
145 #elif HAVE_NEWRTSCTS
146 	options.c_cflag |= CNEW_RTSCTS;
147 #endif
148 	options.c_iflag &= ~(IXON | IXOFF | IXANY);
149 
150 	/*
151 	 * Raw inputing.  Disable local echo,
152 	 * canonical input and signals
153 	 */
154 	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
155 
156 	/*
157 	 * Use parity checking
158 	 */
159 	options.c_iflag |= (INPCK | ISTRIP);
160 
161 	/*
162 	 * Raw output
163 	 */
164 	options.c_oflag &= ~OPOST;
165 
166 	/*
167 	 * Set 5s timeout value
168 	 */
169 	options.c_cc[VMIN] = 0;
170 	options.c_cc[VTIME] = TIMEOUT;
171 	/*
172 	 * options.c_cc[VEOF] = 1; options.c_cc[VEOL] = 0; * no wait *
173 	 * serinfo.flags |= ASYNC_LOW_LATENCY;
174 	 */
175 
176 	/*
177 	 * Set the new options for the port...
178 	 */
179 
180 	if (tcsetattr (fd, TCSANOW, &options) < 0)
181 		vr_error ("Error:  Cannot set serial options");
182 
183 }
184 
185 void
port_send(int fd,char * str,int len,int log)186 port_send (int fd, char *str, int len, int log)
187 {
188 	int wlen = 0;
189 	int ret = 0;
190 	int i;
191 	fd_set fds;
192 	struct timeval tv;
193 
194 	for (i = 0; i < RETRIES; i++)
195 	{
196 		FD_ZERO (&fds);
197 		FD_SET (fd, &fds);
198 		tv.tv_sec = TIMEOUT;
199 		tv.tv_usec = 0;
200 		if ((ret = select (fd + 1, NULL, &fds, NULL, &tv)) > 0)
201 		{
202 			if ((wlen = write (fd, str, len)) < 0)
203 			{
204 				vr_error ("Error:  port_send unable to write to serial port");
205 			}
206 			else if (wlen < len)
207 			{
208 				vr_error ("Error:  port_send to write full buffer to serial port");
209 			}
210 			else
211 			{
212 				if (do_portlog && log)
213 				{
214 					port_log (str, len);
215 				}
216 				return;
217 			}
218 		}
219 	}
220 	vr_error ("Error:  port_send timeout writing to port");
221 }
222 
223 
224 int
port_readline(int fd,char * buf)225 port_readline (int fd, char *buf)
226 {
227 	char inchar;
228 	int i = 0;
229 	int index = 0;
230 	int ret = 0;
231 	fd_set fds;
232 	struct timeval tv;
233 
234 	memset (buf, 0, BUFSIZE);
235 
236 	for (i = 0; i < RETRIES; i++)
237 	{
238 		FD_ZERO (&fds);
239 		FD_SET (fd, &fds);
240 		tv.tv_sec = TIMEOUT;
241 		tv.tv_usec = 0;
242 		if ((ret = select (fd + 1, &fds, NULL, NULL, &tv)) > 0)
243 		{
244 			while (read (fd, (void *) &inchar, 1) >= 0)
245 			{
246 				/*
247 				 * Skip '\n' and '\n' at beginning
248 				 * ie, blank lines
249 				 */
250 				if ((!index)
251 				    && ((inchar == '\n') || (inchar == '\r')))
252 				{
253 					continue;
254 				}
255 				if (strstr (buf, prompt1)
256 				    || strstr (buf, prompt2)
257 				    || (inchar == '\n') || (inchar == '\r'))
258 				{
259 					if (do_portlog)
260 					{
261 						port_log (buf, strlen (buf));
262 					}
263 					return 0;
264 				}
265 				buf[index] = inchar;
266 				index++;
267 				if (index >= BUFSIZE)
268 				{
269 					vr_error ("Error:  port_readline line too long");
270 				}
271 			}
272 			return -1;
273 		}
274 	}
275 	return -1;
276 }
277 
278 void
port_readflush(int fd)279 port_readflush (int fd)
280 {
281 	char buf;
282 	char logbuf[BUFSIZE];
283 	fd_set fds;
284 	int i = 0;
285 	struct timeval tv;
286 
287 	memset (logbuf, 0, BUFSIZE);
288 
289 	do
290 	{
291 		FD_ZERO (&fds);
292 		FD_SET (fd, &fds);
293 		/*
294 		 * Short timeout since we're
295 		 * flushing not reading
296 		 */
297 		tv.tv_sec = 0;
298 		tv.tv_usec = UTIMEOUT;
299 		if (read (fd, (void *) &buf, 1) < 0)
300 		{
301 			vr_error ("Error:  port_readflush failed to read byte");
302 		}
303 		if ((i < BUFSIZE) && (buf != '\r'))
304 		{
305 			logbuf[i] = buf;
306 			i++;
307 		}
308 	}
309 	while (select (fd + 1, &fds, NULL, NULL, &tv));
310 	if (do_portlog)
311 	{
312 		port_log (logbuf, strlen (logbuf));
313 	}
314 }
315 
316 void
port_flush(int fd)317 port_flush (int fd)
318 {
319 	/*
320 	 * char buf[3]; snprintf(buf, 3, "%c\r", EOF); buf[2]=0; port_send(fd,
321 	 * buf);
322 	 */
323 	port_send (fd, "\r\r", 2, PORTLOG);
324 	port_readflush (fd);
325 	port_send (fd, "\r", 1, PORTLOG);
326 }
327 
328 void
port_getpmon(int fd,char * buf)329 port_getpmon (int fd, char *buf)
330 {
331 	fprintf (stderr, "Looking for PMON prompt...");
332 	port_flush (fd);
333 	if (port_readline (fd, buf) < 0)
334 		vr_error ("Error: port_getpmon timed out reading from port");
335 	fprintf (stderr, "done\n");
336 }
337 
338 void
port_log(char * str,int len)339 port_log (char *str, int len)
340 {
341 	int fd;
342 	int i;
343 	int index = 0;
344 	char logbuf[BUFSIZE];
345 
346 	memset (logbuf, 0, BUFSIZE);
347 	for (i = 0; (i < len) && (i < BUFSIZE); i++)
348 	{
349 		if (isprint (str[i]) || (str[i] == '\n'))
350 		{
351 			logbuf[index] = str[i];
352 			index++;
353 		}
354 	}
355 	if (!index)
356 		return;
357 
358 	if (logbuf[index] != '\n')
359 		logbuf[index] = '\n';
360 	fd = open (LOGFILE, O_WRONLY | O_APPEND | O_NDELAY, 0);
361 	if (fd)
362 	{
363 		write (fd, &logbuf, strlen (logbuf));
364 		close (fd);
365 	}
366 }
367