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