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