1 /* pro_serial.c: serial port
2 
3    Copyright (c) 1997-2003, Tarik Isani (xhomer@isani.org)
4 
5    This file is part of Xhomer.
6 
7    Xhomer is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License version 2
9    as published by the Free Software Foundation.
10 
11    Xhomer is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with Xhomer; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 
21 
22 /* TBD:
23 	-return line parameters:
24 		break
25 		parity error
26 	-unsupported baud rates: 2000, 3600, 7200
27 	-unsupported stop bits: 1.5
28 	-parity errors are ignored
29 	-only sends momentary break instead of holding break
30 	-implement WDEPTH in way that works for parallel port
31 */
32 
33 #ifdef PRO
34 #include "pdp11_defs.h"
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <termios.h>
38 #include <sys/ioctl.h>
39 #ifdef __hpux__
40 #include <sys/modem.h>
41 #endif
42 
43 #define PRO_SER_RSIZE	16	/* number of characters to read at once */
44 
45 
46 /* Serial code entry points */
47 
48 struct sercall pro_serial = {&pro_serial_get, &pro_serial_put,
49 	                     &pro_serial_ctrl_get, &pro_serial_ctrl_put,
50 	                     &pro_serial_reset, &pro_serial_exit};
51 
52 int			pro_lp_workaround = 0;				/* workaround for Linux /dev/lp driver */
53 char			*pro_serial_devname[4];				/* serial device names */
54 
55 LOCAL	unsigned char	pro_ser_rbuf[PRO_SER_NUMDEV][PRO_SER_RSIZE];	/* read buffer */
56 LOCAL	int		pro_ser_level[PRO_SER_NUMDEV] = {0, 0, 0};	/* fullness of read buffer */
57 LOCAL	int		pro_ser_xfer[PRO_SER_NUMDEV] = {0, 0, 0};	/* amount read */
58 LOCAL	int		pro_ser_cnt[PRO_SER_NUMDEV] = {0, 0, 0};
59 
60 LOCAL	int		pro_ser_sfd[PRO_SER_NUMDEV] = {-1, -1, -1};
61 
62 LOCAL	int		pro_ser_mlines_save[PRO_SER_NUMDEV];		/* modemlines to be restored on exit */
63 struct	termios		pro_ser_par_save[PRO_SER_NUMDEV];		/* parameters to be restored on exit */
64 
65 
66 /* Get character from serial port */
67 
pro_serial_get(int dev)68 int pro_serial_get (int dev)
69 {
70 int	schar, sstat;
71 
72 
73 	if (pro_ser_cnt[dev]++ >= (PRO_SER_RSIZE-1))
74 	{
75 	  if (pro_ser_sfd[dev] != -1)
76 	  {
77 	    sstat = (int)read(pro_ser_sfd[dev], &pro_ser_rbuf[dev], PRO_SER_RSIZE);
78 
79 	    if (sstat > 0)
80 	    {
81 	      pro_ser_xfer[dev] = sstat;
82 	      pro_ser_level[dev] = 0;
83 	    }
84 	  }
85 
86 	  pro_ser_cnt[dev] = 0;
87 	}
88 
89 	/* Return character if read buffer not empty */
90 
91 	if (pro_ser_level[dev] == pro_ser_xfer[dev])
92 	  schar = PRO_NOCHAR;
93 	else
94 	  schar = (int)pro_ser_rbuf[dev][pro_ser_level[dev]++];
95 
96 /* XXX
97 if (schar != PRO_NOCHAR)
98   printf("R%d %d\r\n",dev, schar);
99   printf("%c",c);
100 */
101 
102 	return schar;
103 }
104 
105 
106 /* Send character to serial port */
107 
pro_serial_put(int dev,int schar)108 int pro_serial_put (int dev, int schar)
109 {
110 int		stat, bufdepth;
111 unsigned char	c;
112 
113 
114 	if (pro_ser_sfd[dev] != -1)
115 	{
116 	  c = (unsigned char)(schar);
117 
118 	  /* Check number of characters in system write queue, and stall
119 	     if limit has been reached */
120 
121 	  /* The system write buffer depth is currently limited to three characters.
122 	     Some serial devices don't support the ioctl needed to perform write
123 	     buffer occupancy checking.  If "lp_workaround" is turned on in the
124 	     config file, then the full depth of the host operating system's write
125 	     buffer is used.  (This is needed for Linux's /dev/lp, for example.)
126 	     This is also the case if ioctl support is not detected at compile-time.
127 	     (HPUX, for example)
128 
129 	     There are serial port performance issues with P/OS if write buffer
130 	     occupancy checking is turned off. */
131 
132 #ifdef TIOCOUTQ
133 	  if (pro_lp_workaround != 1)
134 	    ioctl(pro_ser_sfd[dev], TIOCOUTQ, &bufdepth);
135 	  else
136 #else
137 #warning TIOCOUTQ is not available.  Writes to serial ports not optimized!
138 #endif
139 	    bufdepth = 0;	/* lp_workaround: always allow a write to be attempted */
140 
141 	  if (bufdepth > PRO_SERIAL_WDEPTH)
142 	    stat = PRO_FAIL;
143 	  else
144 	    switch (write(pro_ser_sfd[dev], &c, 1))
145 	    {
146 	      case -1:
147 	        stat = PRO_FAIL;
148 	        break;
149 
150 	      default:
151 	        stat = PRO_SUCCESS;
152 	    }
153 	}
154 	else
155 	  stat = PRO_SUCCESS;
156 
157 /* XXX
158 printf("W%d %d\r\n",dev, schar);
159 */
160 
161 	return stat;
162 }
163 
164 
165 /* Return serial line parameters */
166 
pro_serial_ctrl_get(int dev,struct serctrl * sctrl)167 void pro_serial_ctrl_get(int dev, struct serctrl *sctrl)
168 {
169 int	mlines;		/* modem lines */
170 
171 
172 	if (pro_ser_sfd[dev] != -1)
173 	{
174 	  ioctl(pro_ser_sfd[dev], TIOCMGET, &mlines);
175 
176 	  sctrl->dsr = ((mlines&TIOCM_DSR) ? 1:0);
177 	  sctrl->cts = ((mlines&TIOCM_CTS) ? 1:0);
178 	  sctrl->ri = ((mlines&TIOCM_RI) ? 1:0);
179 	  sctrl->cd = ((mlines&TIOCM_CD) ? 1:0);
180 
181 	  /* XXX unimplemented */
182 
183 	  sctrl->perror = 0;
184 	  sctrl->ibrk = 0;
185 	}
186 }
187 
188 
189 /* Set serial line parameters */
190 
pro_serial_ctrl_put(int dev,struct serctrl * sctrl)191 void pro_serial_ctrl_put(int dev, struct serctrl *sctrl)
192 {
193 const tcflag_t cs[] = {CS5, CS6, CS7, CS8};
194 
195 /* XXX 1.5 stop bits is implemented as 1 */
196 
197 const tcflag_t stop[] = {0, 0, 0, CSTOPB};
198 
199 const tcflag_t parity[] = {PARODD, 0};
200 
201 const tcflag_t penable[] = {0, PARENB};
202 
203 /* XXX 2000, 3600 and 7200 baud are implemented as 0 */
204 
205 const speed_t baud[] = {B50, B75, B110, B134, B150, B300, B600, B1200,
206 	                B1800, B0, B2400, B0, B4800, B0, B9600, B19200};
207 
208 int	mlines;		/* modem lines */
209 struct	termios	serpar;	/* serial port parameters */
210 
211 
212 	if (pro_ser_sfd[dev] != -1)
213 	{
214 	  memset(&serpar, 0, sizeof(serpar));
215 
216 	  mlines = 0;
217 
218 	  serpar.c_iflag = IGNPAR | IGNBRK; /* XXX parity errors, break ignored */
219 	  serpar.c_oflag = 0;
220 	  serpar.c_cflag = CREAD | CLOCAL;
221 	  serpar.c_lflag = 0;
222 
223 	  serpar.c_cflag |= cs[(sctrl->cs)&03];
224 	  serpar.c_cflag |= stop[(sctrl->stop)&03];
225 	  serpar.c_cflag |= parity[(sctrl->parity)&01];
226 	  serpar.c_cflag |= penable[(sctrl->penable)&01];
227 
228 	  cfsetispeed(&serpar, baud[(sctrl->ibaud)&017]);
229 	  cfsetospeed(&serpar, baud[(sctrl->obaud)&017]);
230 
231 	  tcsetattr(pro_ser_sfd[dev], TCSANOW, &serpar);
232 
233 	  /* Set modem control lines */
234 
235 	  mlines |= ((sctrl->dtr) ? TIOCM_DTR:0);
236 	  mlines |= ((sctrl->rts) ? TIOCM_RTS:0);
237 
238 	  ioctl(pro_ser_sfd[dev], TIOCMSET, &mlines);
239 
240 	  /* Check if a break should be sent */
241 
242 	  if ((sctrl->obrk) == 1)
243 	    tcsendbreak(pro_ser_sfd[dev], 0);
244 	}
245 }
246 
247 
248 /* Open serial port */
249 
pro_serial_reset(int dev,int portnum)250 void pro_serial_reset (int dev, int portnum)
251 {
252 	if (pro_ser_sfd[dev] == -1)
253 	{
254 	  pro_ser_sfd[dev] = open(pro_serial_devname[portnum], O_RDWR|O_NONBLOCK);
255 
256 	  if (pro_ser_sfd[dev] == -1)
257 	  {
258 	    perror("Serial port failure");
259 	  }
260 	  else
261 	  {
262 	    /* Save serial parameters for exit */
263 
264 	    tcgetattr(pro_ser_sfd[dev], &pro_ser_par_save[dev]);
265 	    ioctl(pro_ser_sfd[dev], TIOCMGET, &pro_ser_mlines_save[dev]);
266 	  }
267 	}
268 }
269 
270 
271 /* Close serial port */
272 
pro_serial_exit(int dev)273 void pro_serial_exit (int dev)
274 {
275 	if (pro_ser_sfd[dev] != -1)
276 	{
277 	  /* Restore serial parameters */
278 
279 	  tcsetattr(pro_ser_sfd[dev], TCSANOW, &pro_ser_par_save[dev]);
280 	  ioctl(pro_ser_sfd[dev], TIOCMSET, &pro_ser_mlines_save[dev]);
281 
282 	  close(pro_ser_sfd[dev]);
283 	  pro_ser_sfd[dev] = -1;
284 	}
285 }
286 #endif
287