1 /*
2 * Copyright (C) 2002-2013 The DOSBox Team
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19
20 #include "dosbox.h"
21
22 #if C_DIRECTSERIAL
23
24 #include "serialport.h"
25 #include "directserial.h"
26 #include "misc_util.h"
27 #include "pic.h"
28
29 #include "libserial.h"
30
31 /* This is a serial passthrough class. Its amazingly simple to */
32 /* write now that the serial ports themselves were abstracted out */
33
CDirectSerial(Bitu id,CommandLine * cmd)34 CDirectSerial::CDirectSerial (Bitu id, CommandLine* cmd)
35 :CSerial (id, cmd) {
36 InstallationSuccessful = false;
37 comport = 0;
38
39 rx_retry = 0;
40 rx_retry_max = 0;
41
42 std::string tmpstring;
43 if(!cmd->FindStringBegin("realport:",tmpstring,false)) return;
44
45 LOG_MSG ("Serial%d: Opening %s", COMNUMBER, tmpstring.c_str());
46 if(!SERIAL_open(tmpstring.c_str(), &comport)) {
47 char errorbuffer[256];
48 SERIAL_getErrorString(errorbuffer, sizeof(errorbuffer));
49 LOG_MSG("Serial%d: Serial Port \"%s\" could not be opened.",
50 COMNUMBER, tmpstring.c_str());
51 LOG_MSG("%s",errorbuffer);
52 return;
53 }
54
55 #if SERIAL_DEBUG
56 dbgmsg_poll_block=false;
57 dbgmsg_rx_block=false;
58 #endif
59
60 // rxdelay: How many milliseconds to wait before causing an
61 // overflow when the application is unresponsive.
62 if(getBituSubstring("rxdelay:", &rx_retry_max, cmd)) {
63 if(!(rx_retry_max<=10000)) {
64 rx_retry_max=0;
65 }
66 }
67
68 CSerial::Init_Registers();
69 InstallationSuccessful = true;
70 rx_state = D_RX_IDLE;
71 setEvent(SERIAL_POLLING_EVENT, 1); // millisecond receive tick
72 }
73
~CDirectSerial()74 CDirectSerial::~CDirectSerial () {
75 if(comport) SERIAL_close(comport);
76 // We do not use own events so we don't have to clear them.
77 }
78
79 // CanReceive: true:UART part has room left
80 // doReceive: true:there was really a byte to receive
81 // rx_retry is incremented in polling events
82
83 // in POLLING_EVENT: always add new polling event
84 // D_RX_IDLE + CanReceive + doReceive -> D_RX_WAIT , add RX_EVENT
85 // D_RX_IDLE + CanReceive + not doReceive -> D_RX_IDLE
86 // D_RX_IDLE + not CanReceive -> D_RX_BLOCKED, add RX_EVENT
87
88 // D_RX_BLOCKED + CanReceive + doReceive -> D_RX_FASTWAIT, rem RX_EVENT
89 // rx_retry=0 , add RX_EVENT
90 // D_RX_BLOCKED + CanReceive + !doReceive -> D_RX_IDLE, rem RX_EVENT
91 // rx_retry=0
92 // D_RX_BLOCKED + !CanReceive + doReceive + retry < max -> D_RX_BLOCKED, rx_retry++
93 // D_RX_BLOCKED + !CanReceive + doReceive + retry >=max -> rx_retry=0
94
95 // to be continued...
96
handleUpperEvent(Bit16u type)97 void CDirectSerial::handleUpperEvent(Bit16u type) {
98 switch(type) {
99 case SERIAL_POLLING_EVENT: {
100 setEvent(SERIAL_POLLING_EVENT, 1.0f);
101 // update Modem input line states
102 switch(rx_state) {
103 case D_RX_IDLE:
104 if(CanReceiveByte()) {
105 if(doReceive()) {
106 // a byte was received
107 rx_state=D_RX_WAIT;
108 setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
109 } // else still idle
110 } else {
111 #if SERIAL_DEBUG
112 if(!dbgmsg_poll_block) {
113 log_ser(dbg_aux,"Directserial: block on polling.");
114 dbgmsg_poll_block=true;
115 }
116 #endif
117 rx_state=D_RX_BLOCKED;
118 // have both delays (1ms + bytetime)
119 setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
120 }
121 break;
122 case D_RX_BLOCKED:
123 // one timeout tick
124 if(!CanReceiveByte()) {
125 rx_retry++;
126 if(rx_retry>=rx_retry_max) {
127 // it has timed out:
128 rx_retry=0;
129 removeEvent(SERIAL_RX_EVENT);
130 if(doReceive()) {
131 // read away everything
132 // this will set overrun errors
133 while(doReceive());
134 rx_state=D_RX_WAIT;
135 setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
136 } else {
137 // much trouble about nothing
138 rx_state=D_RX_IDLE;
139 }
140 } // else wait further
141 } else {
142 // good: we can receive again
143 #if SERIAL_DEBUG
144 dbgmsg_poll_block=false;
145 dbgmsg_rx_block=false;
146 #endif
147 removeEvent(SERIAL_RX_EVENT);
148 rx_retry=0;
149 if(doReceive()) {
150 rx_state=D_RX_FASTWAIT;
151 setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
152 } else {
153 // much trouble about nothing
154 rx_state=D_RX_IDLE;
155 }
156 }
157 break;
158
159 case D_RX_WAIT:
160 case D_RX_FASTWAIT:
161 break;
162 }
163 updateMSR();
164 break;
165 }
166 case SERIAL_RX_EVENT: {
167 switch(rx_state) {
168 case D_RX_IDLE:
169 LOG_MSG("internal error in directserial");
170 break;
171
172 case D_RX_BLOCKED: // try to receive
173 case D_RX_WAIT:
174 case D_RX_FASTWAIT:
175 if(CanReceiveByte()) {
176 // just works or unblocked
177 rx_retry=0; // not waiting anymore
178 if(doReceive()) {
179 if(rx_state==D_RX_WAIT) setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
180 else {
181 // maybe unblocked
182 rx_state=D_RX_FASTWAIT;
183 setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
184 }
185 } else {
186 // didn't receive anything
187 rx_state=D_RX_IDLE;
188 }
189 } else {
190 // blocking now or still blocked
191 #if SERIAL_DEBUG
192 if(rx_state==D_RX_BLOCKED) {
193 if(!dbgmsg_rx_block) {
194 log_ser(dbg_aux,"Directserial: rx still blocked (retry=%d)",rx_retry);
195 dbgmsg_rx_block=true;
196 }
197 }
198
199
200
201
202
203
204 else log_ser(dbg_aux,"Directserial: block on continued rx (retry=%d).",rx_retry);
205 #endif
206 setEvent(SERIAL_RX_EVENT, bytetime*0.65f);
207 rx_state=D_RX_BLOCKED;
208 }
209
210 break;
211 }
212 updateMSR();
213 break;
214 }
215 case SERIAL_TX_EVENT: {
216 // Maybe echo cirquit works a bit better this way
217 if(rx_state==D_RX_IDLE && CanReceiveByte()) {
218 if(doReceive()) {
219 // a byte was received
220 rx_state=D_RX_WAIT;
221 setEvent(SERIAL_RX_EVENT, bytetime*0.9f);
222 }
223 }
224 ByteTransmitted();
225 updateMSR();
226 break;
227 }
228 case SERIAL_THR_EVENT: {
229 ByteTransmitting();
230 setEvent(SERIAL_TX_EVENT,bytetime*1.1f);
231 break;
232 }
233 }
234 }
235
doReceive()236 bool CDirectSerial::doReceive() {
237 int value = SERIAL_getextchar(comport);
238 if(value) {
239 receiveByteEx((Bit8u)(value&0xff),(Bit8u)((value&0xff00)>>8));
240 return true;
241 }
242 return false;
243 }
244
245 // updatePortConfig is called when emulated app changes the serial port
246 // parameters baudrate, stopbits, number of databits, parity.
updatePortConfig(Bit16u divider,Bit8u lcr)247 void CDirectSerial::updatePortConfig (Bit16u divider, Bit8u lcr) {
248 Bit8u parity = 0;
249
250 switch ((lcr & 0x38)>>3) {
251 case 0x1: parity='o'; break;
252 case 0x3: parity='e'; break;
253 case 0x5: parity='m'; break;
254 case 0x7: parity='s'; break;
255 default: parity='n'; break;
256 }
257
258 Bit8u bytelength = (lcr & 0x3)+5;
259
260 // baudrate
261 Bitu baudrate;
262 if(divider==0) baudrate=115200;
263 else baudrate = 115200 / divider;
264
265 // stopbits
266 Bit8u stopbits;
267 if (lcr & 0x4) {
268 if (bytelength == 5) stopbits = SERIAL_15STOP;
269 else stopbits = SERIAL_2STOP;
270 } else stopbits = SERIAL_1STOP;
271
272 if(!SERIAL_setCommParameters(comport, baudrate, parity, stopbits, bytelength)) {
273 #if SERIAL_DEBUG
274 log_ser(dbg_aux,"Serial port settings not supported by host." );
275 #endif
276 LOG_MSG ("Serial%d: Desired serial mode not supported (%d,%d,%c,%d)",
277 COMNUMBER, baudrate,bytelength,parity,stopbits);
278 }
279 CDirectSerial::setRTSDTR(getRTS(), getDTR());
280 }
281
updateMSR()282 void CDirectSerial::updateMSR () {
283 int new_status = SERIAL_getmodemstatus(comport);
284
285 setCTS(new_status&SERIAL_CTS? true:false);
286 setDSR(new_status&SERIAL_DSR? true:false);
287 setRI(new_status&SERIAL_RI? true:false);
288 setCD(new_status&SERIAL_CD? true:false);
289 }
290
transmitByte(Bit8u val,bool first)291 void CDirectSerial::transmitByte (Bit8u val, bool first) {
292 if(!SERIAL_sendchar(comport, val))
293 LOG_MSG("Serial%d: COM port error: write failed!", COMNUMBER);
294 if(first) setEvent(SERIAL_THR_EVENT, bytetime/8);
295 else setEvent(SERIAL_TX_EVENT, bytetime);
296 }
297
298
299 // setBreak(val) switches break on or off
setBreak(bool value)300 void CDirectSerial::setBreak (bool value) {
301 SERIAL_setBREAK(comport,value);
302 }
303
304 // updateModemControlLines(mcr) sets DTR and RTS.
setRTSDTR(bool rts,bool dtr)305 void CDirectSerial::setRTSDTR(bool rts, bool dtr) {
306 SERIAL_setRTS(comport,rts);
307 SERIAL_setDTR(comport,dtr);
308 }
309
setRTS(bool val)310 void CDirectSerial::setRTS(bool val) {
311 SERIAL_setRTS(comport,val);
312 }
313
setDTR(bool val)314 void CDirectSerial::setDTR(bool val) {
315 SERIAL_setDTR(comport,val);
316 }
317
318 #endif
319