synaser.pas 67 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357
  1. {==============================================================================|
  2. | Project : Ararat Synapse | 007.006.000 |
  3. |==============================================================================|
  4. | Content: Serial port support |
  5. |==============================================================================|
  6. | Copyright (c)2001-2015, Lukas Gebauer |
  7. | All rights reserved. |
  8. | |
  9. | Redistribution and use in source and binary forms, with or without |
  10. | modification, are permitted provided that the following conditions are met: |
  11. | |
  12. | Redistributions of source code must retain the above copyright notice, this |
  13. | list of conditions and the following disclaimer. |
  14. | |
  15. | Redistributions in binary form must reproduce the above copyright notice, |
  16. | this list of conditions and the following disclaimer in the documentation |
  17. | and/or other materials provided with the distribution. |
  18. | |
  19. | Neither the name of Lukas Gebauer nor the names of its contributors may |
  20. | be used to endorse or promote products derived from this software without |
  21. | specific prior written permission. |
  22. | |
  23. | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
  24. | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
  25. | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
  26. | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR |
  27. | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
  28. | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
  29. | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
  30. | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
  31. | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
  32. | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
  33. | DAMAGE. |
  34. |==============================================================================|
  35. | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
  36. | Portions created by Lukas Gebauer are Copyright (c)2001-2015. |
  37. | All Rights Reserved. |
  38. |==============================================================================|
  39. | Contributor(s): |
  40. | (c)2002, Hans-Georg Joepgen (cpom Comport Ownership Manager and bugfixes) |
  41. |==============================================================================|
  42. | History: see HISTORY.HTM from distribution package |
  43. | (Found at URL: http://www.ararat.cz/synapse/) |
  44. |==============================================================================}
  45. {: @abstract(Serial port communication library)
  46. This unit contains a class that implements serial port communication
  47. for Windows, Linux, Unix or MacOSx. This class provides numerous methods with
  48. same name and functionality as methods of the Ararat Synapse TCP/IP library.
  49. The following is a small example how establish a connection by modem (in this
  50. case with my USB modem):
  51. @longcode(#
  52. ser:=TBlockSerial.Create;
  53. try
  54. ser.Connect('COM3');
  55. ser.config(460800,8,'N',0,false,true);
  56. ser.ATCommand('AT');
  57. if (ser.LastError <> 0) or (not ser.ATResult) then
  58. Exit;
  59. ser.ATConnect('ATDT+420971200111');
  60. if (ser.LastError <> 0) or (not ser.ATResult) then
  61. Exit;
  62. // you are now connected to a modem at +420971200111
  63. // you can transmit or receive data now
  64. finally
  65. ser.free;
  66. end;
  67. #)
  68. }
  69. //old Delphi does not have MSWINDOWS define.
  70. {$IFDEF WIN32}
  71. {$IFNDEF MSWINDOWS}
  72. {$DEFINE MSWINDOWS}
  73. {$ENDIF}
  74. {$ENDIF}
  75. //Kylix does not known UNIX define
  76. {$IFDEF LINUX}
  77. {$IFNDEF UNIX}
  78. {$DEFINE UNIX}
  79. {$ENDIF}
  80. {$ENDIF}
  81. {$IFDEF FPC}
  82. {$MODE DELPHI}
  83. {$IFDEF MSWINDOWS}
  84. {$ASMMODE intel}
  85. {$ENDIF}
  86. {define working mode w/o LIBC for fpc}
  87. {$DEFINE NO_LIBC}
  88. {$ENDIF}
  89. {$Q-}
  90. {$H+}
  91. {$M+}
  92. unit synaser;
  93. interface
  94. uses
  95. {$IFNDEF MSWINDOWS}
  96. {$IFNDEF NO_LIBC}
  97. Libc,
  98. KernelIoctl,
  99. {$ELSE}
  100. termio, baseunix, unix,
  101. {$ENDIF}
  102. {$IFNDEF FPC}
  103. Types,
  104. {$ENDIF}
  105. {$ELSE}
  106. Windows, registry,
  107. {$IFDEF FPC}
  108. winver,
  109. {$ENDIF}
  110. {$ENDIF}
  111. synafpc,
  112. Classes, SysUtils, synautil;
  113. const
  114. CR = #$0d;
  115. LF = #$0a;
  116. CRLF = CR + LF;
  117. cSerialChunk = 8192;
  118. LockfileDirectory = '/var/lock'; {HGJ}
  119. PortIsClosed = -1; {HGJ}
  120. ErrAlreadyOwned = 9991; {HGJ}
  121. ErrAlreadyInUse = 9992; {HGJ}
  122. ErrWrongParameter = 9993; {HGJ}
  123. ErrPortNotOpen = 9994; {HGJ}
  124. ErrNoDeviceAnswer = 9995; {HGJ}
  125. ErrMaxBuffer = 9996;
  126. ErrTimeout = 9997;
  127. ErrNotRead = 9998;
  128. ErrFrame = 9999;
  129. ErrOverrun = 10000;
  130. ErrRxOver = 10001;
  131. ErrRxParity = 10002;
  132. ErrTxFull = 10003;
  133. dcb_Binary = $00000001;
  134. dcb_ParityCheck = $00000002;
  135. dcb_OutxCtsFlow = $00000004;
  136. dcb_OutxDsrFlow = $00000008;
  137. dcb_DtrControlMask = $00000030;
  138. dcb_DtrControlDisable = $00000000;
  139. dcb_DtrControlEnable = $00000010;
  140. dcb_DtrControlHandshake = $00000020;
  141. dcb_DsrSensivity = $00000040;
  142. dcb_TXContinueOnXoff = $00000080;
  143. dcb_OutX = $00000100;
  144. dcb_InX = $00000200;
  145. dcb_ErrorChar = $00000400;
  146. dcb_NullStrip = $00000800;
  147. dcb_RtsControlMask = $00003000;
  148. dcb_RtsControlDisable = $00000000;
  149. dcb_RtsControlEnable = $00001000;
  150. dcb_RtsControlHandshake = $00002000;
  151. dcb_RtsControlToggle = $00003000;
  152. dcb_AbortOnError = $00004000;
  153. dcb_Reserveds = $FFFF8000;
  154. {:stopbit value for 1 stopbit}
  155. SB1 = 0;
  156. {:stopbit value for 1.5 stopbit}
  157. SB1andHalf = 1;
  158. {:stopbit value for 2 stopbits}
  159. SB2 = 2;
  160. {$IFNDEF MSWINDOWS}
  161. const
  162. INVALID_HANDLE_VALUE = THandle(-1);
  163. CS7fix = $0000020;
  164. type
  165. TDCB = record
  166. DCBlength: DWORD;
  167. BaudRate: DWORD;
  168. Flags: Longint;
  169. wReserved: Word;
  170. XonLim: Word;
  171. XoffLim: Word;
  172. ByteSize: Byte;
  173. Parity: Byte;
  174. StopBits: Byte;
  175. XonChar: CHAR;
  176. XoffChar: CHAR;
  177. ErrorChar: CHAR;
  178. EofChar: CHAR;
  179. EvtChar: CHAR;
  180. wReserved1: Word;
  181. end;
  182. PDCB = ^TDCB;
  183. const
  184. {$IFDEF UNIX}
  185. {$IFDEF BSD}
  186. MaxRates = 18; //MAC
  187. {$ELSE}
  188. MaxRates = 30; //UNIX
  189. {$ENDIF}
  190. {$ELSE}
  191. MaxRates = 19; //WIN
  192. {$ENDIF}
  193. Rates: array[0..MaxRates, 0..1] of cardinal =
  194. (
  195. (0, B0),
  196. (50, B50),
  197. (75, B75),
  198. (110, B110),
  199. (134, B134),
  200. (150, B150),
  201. (200, B200),
  202. (300, B300),
  203. (600, B600),
  204. (1200, B1200),
  205. (1800, B1800),
  206. (2400, B2400),
  207. (4800, B4800),
  208. (9600, B9600),
  209. (19200, B19200),
  210. (38400, B38400),
  211. (57600, B57600),
  212. (115200, B115200),
  213. (230400, B230400)
  214. {$IFNDEF BSD}
  215. ,(460800, B460800)
  216. {$IFDEF UNIX}
  217. ,(500000, B500000),
  218. (576000, B576000),
  219. (921600, B921600),
  220. (1000000, B1000000),
  221. (1152000, B1152000),
  222. (1500000, B1500000),
  223. (2000000, B2000000),
  224. (2500000, B2500000),
  225. (3000000, B3000000),
  226. (3500000, B3500000),
  227. (4000000, B4000000)
  228. {$ENDIF}
  229. {$ENDIF}
  230. );
  231. {$ENDIF}
  232. {$IFDEF BSD}
  233. const // From fcntl.h
  234. O_SYNC = $0080; { synchronous writes }
  235. {$ENDIF}
  236. const
  237. sOK = 0;
  238. sErr = integer(-1);
  239. type
  240. {:Possible status event types for @link(THookSerialStatus)}
  241. THookSerialReason = (
  242. HR_SerialClose,
  243. HR_Connect,
  244. HR_CanRead,
  245. HR_CanWrite,
  246. HR_ReadCount,
  247. HR_WriteCount,
  248. HR_Wait
  249. );
  250. {:procedural prototype for status event hooking}
  251. THookSerialStatus = procedure(Sender: TObject; Reason: THookSerialReason;
  252. const Value: string) of object;
  253. {:@abstract(Exception type for SynaSer errors)}
  254. ESynaSerError = class(Exception)
  255. public
  256. ErrorCode: integer;
  257. ErrorMessage: string;
  258. end;
  259. {:@abstract(Main class implementing all communication routines)}
  260. TBlockSerial = class(TObject)
  261. protected
  262. FOnStatus: THookSerialStatus;
  263. Fhandle: THandle;
  264. FTag: integer;
  265. FDevice: string;
  266. FLastError: integer;
  267. FLastErrorDesc: string;
  268. FBuffer: AnsiString;
  269. FRaiseExcept: boolean;
  270. FRecvBuffer: integer;
  271. FSendBuffer: integer;
  272. FModemWord: integer;
  273. FRTSToggle: Boolean;
  274. FDeadlockTimeout: integer;
  275. FInstanceActive: boolean; {HGJ}
  276. FTestDSR: Boolean;
  277. FTestCTS: Boolean;
  278. FLastCR: Boolean;
  279. FLastLF: Boolean;
  280. FMaxLineLength: Integer;
  281. FLinuxLock: Boolean;
  282. FMaxSendBandwidth: Integer;
  283. FNextSend: LongWord;
  284. FMaxRecvBandwidth: Integer;
  285. FNextRecv: LongWord;
  286. FConvertLineEnd: Boolean;
  287. FATResult: Boolean;
  288. FAtTimeout: integer;
  289. FInterPacketTimeout: Boolean;
  290. FComNr: integer;
  291. {$IFDEF MSWINDOWS}
  292. FPortAddr: Word;
  293. function CanEvent(Event: dword; Timeout: integer): boolean;
  294. procedure DecodeCommError(Error: DWord); virtual;
  295. {$IFDEF WIN32}
  296. function GetPortAddr: Word; virtual;
  297. function ReadTxEmpty(PortAddr: Word): Boolean; virtual;
  298. {$ENDIF}
  299. {$ENDIF}
  300. procedure SetSizeRecvBuffer(size: integer); virtual;
  301. function GetDSR: Boolean; virtual;
  302. procedure SetDTRF(Value: Boolean); virtual;
  303. function GetCTS: Boolean; virtual;
  304. procedure SetRTSF(Value: Boolean); virtual;
  305. function GetCarrier: Boolean; virtual;
  306. function GetRing: Boolean; virtual;
  307. procedure DoStatus(Reason: THookSerialReason; const Value: string); virtual;
  308. procedure GetComNr(Value: string); virtual;
  309. function PreTestFailing: boolean; virtual;{HGJ}
  310. function TestCtrlLine: Boolean; virtual;
  311. {$IFDEF UNIX}
  312. procedure DcbToTermios(const dcb: TDCB; var term: termios); virtual;
  313. procedure TermiosToDcb(const term: termios; var dcb: TDCB); virtual;
  314. function ReadLockfile: integer; virtual;
  315. function LockfileName: String; virtual;
  316. procedure CreateLockfile(PidNr: integer); virtual;
  317. {$ENDIF}
  318. procedure LimitBandwidth(Length: Integer; MaxB: integer; var Next: LongWord); virtual;
  319. procedure SetBandwidth(Value: Integer); virtual;
  320. public
  321. {: data Control Block with communication parameters. Usable only when you
  322. need to call API directly.}
  323. DCB: Tdcb;
  324. {$IFDEF UNIX}
  325. TermiosStruc: termios;
  326. {$ENDIF}
  327. {:Object constructor.}
  328. constructor Create;
  329. {:Object destructor.}
  330. destructor Destroy; override;
  331. {:Returns a string containing the version number of the library.}
  332. class function GetVersion: string; virtual;
  333. {:Destroy handle in use. It close connection to serial port.}
  334. procedure CloseSocket; virtual;
  335. {:Reconfigure communication parameters on the fly. You must be connected to
  336. port before!
  337. @param(baud Define connection speed. Baud rate can be from 50 to 4000000
  338. bits per second. (it depends on your hardware!))
  339. @param(bits Number of bits in communication.)
  340. @param(parity Define communication parity (N - None, O - Odd, E - Even, M - Mark or S - Space).)
  341. @param(stop Define number of stopbits. Use constants @link(SB1),
  342. @link(SB1andHalf) and @link(SB2).)
  343. @param(softflow Enable XON/XOFF handshake.)
  344. @param(hardflow Enable CTS/RTS handshake.)}
  345. procedure Config(baud, bits: integer; parity: char; stop: integer;
  346. softflow, hardflow: boolean); virtual;
  347. {:Connects to the port indicated by comport. Comport can be used in Windows
  348. style (COM2), or in Linux style (/dev/ttyS1). When you use windows style
  349. in Linux, then it will be converted to Linux name. And vice versa! However
  350. you can specify any device name! (other device names then standart is not
  351. converted!)
  352. After successfull connection the DTR signal is set (if you not set hardware
  353. handshake, then the RTS signal is set, too!)
  354. Connection parameters is predefined by your system configuration. If you
  355. need use another parameters, then you can use Config method after.
  356. Notes:
  357. - Remember, the commonly used serial Laplink cable does not support
  358. hardware handshake.
  359. - Before setting any handshake you must be sure that it is supported by
  360. your hardware.
  361. - Some serial devices are slow. In some cases you must wait up to a few
  362. seconds after connection for the device to respond.
  363. - when you connect to a modem device, then is best to test it by an empty
  364. AT command. (call ATCommand('AT'))}
  365. procedure Connect(comport: string); virtual;
  366. {:Set communication parameters from the DCB structure (the DCB structure is
  367. simulated under Linux).}
  368. procedure SetCommState; virtual;
  369. {:Read communication parameters into the DCB structure (DCB structure is
  370. simulated under Linux).}
  371. procedure GetCommState; virtual;
  372. {:Sends Length bytes of data from Buffer through the connected port.}
  373. function SendBuffer(buffer: pointer; length: integer): integer; virtual;
  374. {:One data BYTE is sent.}
  375. procedure SendByte(data: byte); virtual;
  376. {:Send the string in the data parameter. No terminator is appended by this
  377. method. If you need to send a string with CR/LF terminator, you must append
  378. the CR/LF characters to the data string!
  379. Since no terminator is appended, you can use this function for sending
  380. binary data too.}
  381. procedure SendString(data: AnsiString); virtual;
  382. {:send four bytes as integer.}
  383. procedure SendInteger(Data: integer); virtual;
  384. {:send data as one block. Each block begins with integer value with Length
  385. of block.}
  386. procedure SendBlock(const Data: AnsiString); virtual;
  387. {:send content of stream from current position}
  388. procedure SendStreamRaw(const Stream: TStream); virtual;
  389. {:send content of stream as block. see @link(SendBlock)}
  390. procedure SendStream(const Stream: TStream); virtual;
  391. {:send content of stream as block, but this is compatioble with Indy library.
  392. (it have swapped lenght of block). See @link(SendStream)}
  393. procedure SendStreamIndy(const Stream: TStream); virtual;
  394. {:Waits until the allocated buffer is filled by received data. Returns number
  395. of data bytes received, which equals to the Length value under normal
  396. operation. If it is not equal, the communication channel is possibly broken.
  397. This method not using any internal buffering, like all others receiving
  398. methods. You cannot freely combine this method with all others receiving
  399. methods!}
  400. function RecvBuffer(buffer: pointer; length: integer): integer; virtual;
  401. {:Method waits until data is received. If no data is received within
  402. the Timeout (in milliseconds) period, @link(LastError) is set to
  403. @link(ErrTimeout). This method is used to read any amount of data
  404. (e. g. 1MB), and may be freely combined with all receviving methods what
  405. have Timeout parameter, like the @link(RecvString), @link(RecvByte) or
  406. @link(RecvTerminated) methods.}
  407. function RecvBufferEx(buffer: pointer; length: integer; timeout: integer): integer; virtual;
  408. {:It is like recvBufferEx, but data is readed to dynamicly allocated binary
  409. string.}
  410. function RecvBufferStr(Length: Integer; Timeout: Integer): AnsiString; virtual;
  411. {:Read all available data and return it in the function result string. This
  412. function may be combined with @link(RecvString), @link(RecvByte) or related
  413. methods.}
  414. function RecvPacket(Timeout: Integer): AnsiString; virtual;
  415. {:Waits until one data byte is received which is returned as the function
  416. result. If no data is received within the Timeout (in milliseconds) period,
  417. @link(LastError) is set to @link(ErrTimeout).}
  418. function RecvByte(timeout: integer): byte; virtual;
  419. {:This method waits until a terminated data string is received. This string
  420. is terminated by the Terminator string. The resulting string is returned
  421. without this termination string! If no data is received within the Timeout
  422. (in milliseconds) period, @link(LastError) is set to @link(ErrTimeout).}
  423. function RecvTerminated(Timeout: Integer; const Terminator: AnsiString): AnsiString; virtual;
  424. {:This method waits until a terminated data string is received. The string
  425. is terminated by a CR/LF sequence. The resulting string is returned without
  426. the terminator (CR/LF)! If no data is received within the Timeout (in
  427. milliseconds) period, @link(LastError) is set to @link(ErrTimeout).
  428. If @link(ConvertLineEnd) is used, then the CR/LF sequence may not be exactly
  429. CR/LF. See the description of @link(ConvertLineEnd).
  430. This method serves for line protocol implementation and uses its own
  431. buffers to maximize performance. Therefore do NOT use this method with the
  432. @link(RecvBuffer) method to receive data as it may cause data loss.}
  433. function Recvstring(timeout: integer): AnsiString; virtual;
  434. {:Waits until four data bytes are received which is returned as the function
  435. integer result. If no data is received within the Timeout (in milliseconds) period,
  436. @link(LastError) is set to @link(ErrTimeout).}
  437. function RecvInteger(Timeout: Integer): Integer; virtual;
  438. {:Waits until one data block is received. See @link(sendblock). If no data
  439. is received within the Timeout (in milliseconds) period, @link(LastError)
  440. is set to @link(ErrTimeout).}
  441. function RecvBlock(Timeout: Integer): AnsiString; virtual;
  442. {:Receive all data to stream, until some error occured. (for example timeout)}
  443. procedure RecvStreamRaw(const Stream: TStream; Timeout: Integer); virtual;
  444. {:receive requested count of bytes to stream}
  445. procedure RecvStreamSize(const Stream: TStream; Timeout: Integer; Size: Integer); virtual;
  446. {:receive block of data to stream. (Data can be sended by @link(sendstream)}
  447. procedure RecvStream(const Stream: TStream; Timeout: Integer); virtual;
  448. {:receive block of data to stream. (Data can be sended by @link(sendstreamIndy)}
  449. procedure RecvStreamIndy(const Stream: TStream; Timeout: Integer); virtual;
  450. {:Returns the number of received bytes waiting for reading. 0 is returned
  451. when there is no data waiting.}
  452. function WaitingData: integer; virtual;
  453. {:Same as @link(WaitingData), but in respect to data in the internal
  454. @link(LineBuffer).}
  455. function WaitingDataEx: integer; virtual;
  456. {:Returns the number of bytes waiting to be sent in the output buffer.
  457. 0 is returned when the output buffer is empty.}
  458. function SendingData: integer; virtual;
  459. {:Enable or disable RTS driven communication (half-duplex). It can be used
  460. to communicate with RS485 converters, or other special equipment. If you
  461. enable this feature, the system automatically controls the RTS signal.
  462. Notes:
  463. - On Windows NT (or higher) ir RTS signal driven by system driver.
  464. - On Win9x family is used special code for waiting until last byte is
  465. sended from your UART.
  466. - On Linux you must have kernel 2.1 or higher!}
  467. procedure EnableRTSToggle(value: boolean); virtual;
  468. {:Waits until all data to is sent and buffers are emptied.
  469. Warning: On Windows systems is this method returns when all buffers are
  470. flushed to the serial port controller, before the last byte is sent!}
  471. procedure Flush; virtual;
  472. {:Unconditionally empty all buffers. It is good when you need to interrupt
  473. communication and for cleanups.}
  474. procedure Purge; virtual;
  475. {:Returns @True, if you can from read any data from the port. Status is
  476. tested for a period of time given by the Timeout parameter (in milliseconds).
  477. If the value of the Timeout parameter is 0, the status is tested only once
  478. and the function returns immediately. If the value of the Timeout parameter
  479. is set to -1, the function returns only after it detects data on the port
  480. (this may cause the process to hang).}
  481. function CanRead(Timeout: integer): boolean; virtual;
  482. {:Returns @True, if you can write any data to the port (this function is not
  483. sending the contents of the buffer). Status is tested for a period of time
  484. given by the Timeout parameter (in milliseconds). If the value of
  485. the Timeout parameter is 0, the status is tested only once and the function
  486. returns immediately. If the value of the Timeout parameter is set to -1,
  487. the function returns only after it detects that it can write data to
  488. the port (this may cause the process to hang).}
  489. function CanWrite(Timeout: integer): boolean; virtual;
  490. {:Same as @link(CanRead), but the test is against data in the internal
  491. @link(LineBuffer) too.}
  492. function CanReadEx(Timeout: integer): boolean; virtual;
  493. {:Returns the status word of the modem. Decoding the status word could yield
  494. the status of carrier detect signaland other signals. This method is used
  495. internally by the modem status reading properties. You usually do not need
  496. to call this method directly.}
  497. function ModemStatus: integer; virtual;
  498. {:Send a break signal to the communication device for Duration milliseconds.}
  499. procedure SetBreak(Duration: integer); virtual;
  500. {:This function is designed to send AT commands to the modem. The AT command
  501. is sent in the Value parameter and the response is returned in the function
  502. return value (may contain multiple lines!).
  503. If the AT command is processed successfully (modem returns OK), then the
  504. @link(ATResult) property is set to True.
  505. This function is designed only for AT commands that return OK or ERROR
  506. response! To call connection commands the @link(ATConnect) method.
  507. Remember, when you connect to a modem device, it is in AT command mode.
  508. Now you can send AT commands to the modem. If you need to transfer data to
  509. the modem on the other side of the line, you must first switch to data mode
  510. using the @link(ATConnect) method.}
  511. function ATCommand(value: AnsiString): AnsiString; virtual;
  512. {:This function is used to send connect type AT commands to the modem. It is
  513. for commands to switch to connected state. (ATD, ATA, ATO,...)
  514. It sends the AT command in the Value parameter and returns the modem's
  515. response (may be multiple lines - usually with connection parameters info).
  516. If the AT command is processed successfully (the modem returns CONNECT),
  517. then the ATResult property is set to @True.
  518. This function is designed only for AT commands which respond by CONNECT,
  519. BUSY, NO DIALTONE NO CARRIER or ERROR. For other AT commands use the
  520. @link(ATCommand) method.
  521. The connect timeout is 90*@link(ATTimeout). If this command is successful
  522. (@link(ATresult) is @true), then the modem is in data state. When you now
  523. send or receive some data, it is not to or from your modem, but from the
  524. modem on other side of the line. Now you can transfer your data.
  525. If the connection attempt failed (@link(ATResult) is @False), then the
  526. modem is still in AT command mode.}
  527. function ATConnect(value: AnsiString): AnsiString; virtual;
  528. {:If you "manually" call API functions, forward their return code in
  529. the SerialResult parameter to this function, which evaluates it and sets
  530. @link(LastError) and @link(LastErrorDesc).}
  531. function SerialCheck(SerialResult: integer): integer; virtual;
  532. {:If @link(Lasterror) is not 0 and exceptions are enabled, then this procedure
  533. raises an exception. This method is used internally. You may need it only
  534. in special cases.}
  535. procedure ExceptCheck; virtual;
  536. {:Set Synaser to error state with ErrNumber code. Usually used by internal
  537. routines.}
  538. procedure SetSynaError(ErrNumber: integer); virtual;
  539. {:Raise Synaser error with ErrNumber code. Usually used by internal routines.}
  540. procedure RaiseSynaError(ErrNumber: integer); virtual;
  541. {$IFDEF UNIX}
  542. function cpomComportAccessible: boolean; virtual;{HGJ}
  543. procedure cpomReleaseComport; virtual; {HGJ}
  544. {$ENDIF}
  545. {:True device name of currently used port}
  546. property Device: string read FDevice;
  547. {:Error code of last operation. Value is defined by the host operating
  548. system, but value 0 is always OK.}
  549. property LastError: integer read FLastError;
  550. {:Human readable description of LastError code.}
  551. property LastErrorDesc: string read FLastErrorDesc;
  552. {:Indicates if the last @link(ATCommand) or @link(ATConnect) method was successful}
  553. property ATResult: Boolean read FATResult;
  554. {:Read the value of the RTS signal.}
  555. property RTS: Boolean write SetRTSF;
  556. {:Indicates the presence of the CTS signal}
  557. property CTS: boolean read GetCTS;
  558. {:Use this property to set the value of the DTR signal.}
  559. property DTR: Boolean write SetDTRF;
  560. {:Exposes the status of the DSR signal.}
  561. property DSR: boolean read GetDSR;
  562. {:Indicates the presence of the Carrier signal}
  563. property Carrier: boolean read GetCarrier;
  564. {:Reflects the status of the Ring signal.}
  565. property Ring: boolean read GetRing;
  566. {:indicates if this instance of SynaSer is active. (Connected to some port)}
  567. property InstanceActive: boolean read FInstanceActive; {HGJ}
  568. {:Defines maximum bandwidth for all sending operations in bytes per second.
  569. If this value is set to 0 (default), bandwidth limitation is not used.}
  570. property MaxSendBandwidth: Integer read FMaxSendBandwidth Write FMaxSendBandwidth;
  571. {:Defines maximum bandwidth for all receiving operations in bytes per second.
  572. If this value is set to 0 (default), bandwidth limitation is not used.}
  573. property MaxRecvBandwidth: Integer read FMaxRecvBandwidth Write FMaxRecvBandwidth;
  574. {:Defines maximum bandwidth for all sending and receiving operations
  575. in bytes per second. If this value is set to 0 (default), bandwidth
  576. limitation is not used.}
  577. property MaxBandwidth: Integer Write SetBandwidth;
  578. {:Size of the Windows internal receive buffer. Default value is usually
  579. 4096 bytes. Note: Valid only in Windows versions!}
  580. property SizeRecvBuffer: integer read FRecvBuffer write SetSizeRecvBuffer;
  581. published
  582. {:Returns the descriptive text associated with ErrorCode. You need this
  583. method only in special cases. Description of LastError is now accessible
  584. through the LastErrorDesc property.}
  585. class function GetErrorDesc(ErrorCode: integer): string;
  586. {:Freely usable property}
  587. property Tag: integer read FTag write FTag;
  588. {:Contains the handle of the open communication port.
  589. You may need this value to directly call communication functions outside
  590. SynaSer.}
  591. property Handle: THandle read Fhandle write FHandle;
  592. {:Internally used read buffer.}
  593. property LineBuffer: AnsiString read FBuffer write FBuffer;
  594. {:If @true, communication errors raise exceptions. If @false (default), only
  595. the @link(LastError) value is set.}
  596. property RaiseExcept: boolean read FRaiseExcept write FRaiseExcept;
  597. {:This event is triggered when the communication status changes. It can be
  598. used to monitor communication status.}
  599. property OnStatus: THookSerialStatus read FOnStatus write FOnStatus;
  600. {:If you set this property to @true, then the value of the DSR signal
  601. is tested before every data transfer. It can be used to detect the presence
  602. of a communications device.}
  603. property TestDSR: boolean read FTestDSR write FTestDSR;
  604. {:If you set this property to @true, then the value of the CTS signal
  605. is tested before every data transfer. It can be used to detect the presence
  606. of a communications device. Warning: This property cannot be used if you
  607. need hardware handshake!}
  608. property TestCTS: boolean read FTestCTS write FTestCTS;
  609. {:Use this property you to limit the maximum size of LineBuffer
  610. (as a protection against unlimited memory allocation for LineBuffer).
  611. Default value is 0 - no limit.}
  612. property MaxLineLength: Integer read FMaxLineLength Write FMaxLineLength;
  613. {:This timeout value is used as deadlock protection when trying to send data
  614. to (or receive data from) a device that stopped communicating during data
  615. transmission (e.g. by physically disconnecting the device).
  616. The timeout value is in milliseconds. The default value is 30,000 (30 seconds).}
  617. property DeadlockTimeout: Integer read FDeadlockTimeout Write FDeadlockTimeout;
  618. {:If set to @true (default value), port locking is enabled (under Linux only).
  619. WARNING: To use this feature, the application must run by a user with full
  620. permission to the /var/lock directory!}
  621. property LinuxLock: Boolean read FLinuxLock write FLinuxLock;
  622. {:Indicates if non-standard line terminators should be converted to a CR/LF pair
  623. (standard DOS line terminator). If @TRUE, line terminators CR, single LF
  624. or LF/CR are converted to CR/LF. Defaults to @FALSE.
  625. This property has effect only on the behavior of the RecvString method.}
  626. property ConvertLineEnd: Boolean read FConvertLineEnd Write FConvertLineEnd;
  627. {:Timeout for AT modem based operations}
  628. property AtTimeout: integer read FAtTimeout Write FAtTimeout;
  629. {:If @true (default), then all timeouts is timeout between two characters.
  630. If @False, then timeout is overall for whoole reading operation.}
  631. property InterPacketTimeout: Boolean read FInterPacketTimeout Write FInterPacketTimeout;
  632. end;
  633. {:Returns list of existing computer serial ports. Working properly only in Windows!}
  634. function GetSerialPortNames: string;
  635. implementation
  636. constructor TBlockSerial.Create;
  637. begin
  638. inherited create;
  639. FRaiseExcept := false;
  640. FHandle := INVALID_HANDLE_VALUE;
  641. FDevice := '';
  642. FComNr:= PortIsClosed; {HGJ}
  643. FInstanceActive:= false; {HGJ}
  644. Fbuffer := '';
  645. FRTSToggle := False;
  646. FMaxLineLength := 0;
  647. FTestDSR := False;
  648. FTestCTS := False;
  649. FDeadlockTimeout := 30000;
  650. FLinuxLock := True;
  651. FMaxSendBandwidth := 0;
  652. FNextSend := 0;
  653. FMaxRecvBandwidth := 0;
  654. FNextRecv := 0;
  655. FConvertLineEnd := False;
  656. SetSynaError(sOK);
  657. FRecvBuffer := 4096;
  658. FLastCR := False;
  659. FLastLF := False;
  660. FAtTimeout := 1000;
  661. FInterPacketTimeout := True;
  662. end;
  663. destructor TBlockSerial.Destroy;
  664. begin
  665. CloseSocket;
  666. inherited destroy;
  667. end;
  668. class function TBlockSerial.GetVersion: string;
  669. begin
  670. Result := 'SynaSer 7.6.0';
  671. end;
  672. procedure TBlockSerial.CloseSocket;
  673. begin
  674. if Fhandle <> INVALID_HANDLE_VALUE then
  675. begin
  676. Purge;
  677. RTS := False;
  678. DTR := False;
  679. FileClose(FHandle);
  680. end;
  681. if InstanceActive then
  682. begin
  683. {$IFDEF UNIX}
  684. if FLinuxLock then
  685. cpomReleaseComport;
  686. {$ENDIF}
  687. FInstanceActive:= false
  688. end;
  689. Fhandle := INVALID_HANDLE_VALUE;
  690. FComNr:= PortIsClosed;
  691. SetSynaError(sOK);
  692. DoStatus(HR_SerialClose, FDevice);
  693. end;
  694. {$IFDEF WIN32}
  695. function TBlockSerial.GetPortAddr: Word;
  696. begin
  697. Result := 0;
  698. if Win32Platform <> VER_PLATFORM_WIN32_NT then
  699. begin
  700. EscapeCommFunction(FHandle, 10);
  701. asm
  702. MOV @Result, DX;
  703. end;
  704. end;
  705. end;
  706. function TBlockSerial.ReadTxEmpty(PortAddr: Word): Boolean;
  707. begin
  708. Result := True;
  709. if Win32Platform <> VER_PLATFORM_WIN32_NT then
  710. begin
  711. asm
  712. MOV DX, PortAddr;
  713. ADD DX, 5;
  714. IN AL, DX;
  715. AND AL, $40;
  716. JZ @K;
  717. MOV AL,1;
  718. @K: MOV @Result, AL;
  719. end;
  720. end;
  721. end;
  722. {$ENDIF}
  723. procedure TBlockSerial.GetComNr(Value: string);
  724. begin
  725. FComNr := PortIsClosed;
  726. if pos('COM', uppercase(Value)) = 1 then
  727. FComNr := StrToIntdef(copy(Value, 4, Length(Value) - 3), PortIsClosed + 1) - 1;
  728. if pos('/DEV/TTYS', uppercase(Value)) = 1 then
  729. FComNr := StrToIntdef(copy(Value, 10, Length(Value) - 9), PortIsClosed - 1);
  730. end;
  731. procedure TBlockSerial.SetBandwidth(Value: Integer);
  732. begin
  733. MaxSendBandwidth := Value;
  734. MaxRecvBandwidth := Value;
  735. end;
  736. procedure TBlockSerial.LimitBandwidth(Length: Integer; MaxB: integer; var Next: LongWord);
  737. var
  738. x: LongWord;
  739. y: LongWord;
  740. begin
  741. if MaxB > 0 then
  742. begin
  743. y := GetTick;
  744. if Next > y then
  745. begin
  746. x := Next - y;
  747. if x > 0 then
  748. begin
  749. DoStatus(HR_Wait, IntToStr(x));
  750. sleep(x);
  751. end;
  752. end;
  753. Next := GetTick + Trunc((Length / MaxB) * 1000);
  754. end;
  755. end;
  756. procedure TBlockSerial.Config(baud, bits: integer; parity: char; stop: integer;
  757. softflow, hardflow: boolean);
  758. begin
  759. FillChar(dcb, SizeOf(dcb), 0);
  760. GetCommState;
  761. dcb.DCBlength := SizeOf(dcb);
  762. dcb.BaudRate := baud;
  763. dcb.ByteSize := bits;
  764. case parity of
  765. 'N', 'n': dcb.parity := 0;
  766. 'O', 'o': dcb.parity := 1;
  767. 'E', 'e': dcb.parity := 2;
  768. 'M', 'm': dcb.parity := 3;
  769. 'S', 's': dcb.parity := 4;
  770. end;
  771. dcb.StopBits := stop;
  772. dcb.XonChar := #17;
  773. dcb.XoffChar := #19;
  774. dcb.XonLim := FRecvBuffer div 4;
  775. dcb.XoffLim := FRecvBuffer div 4;
  776. dcb.Flags := dcb_Binary;
  777. if softflow then
  778. dcb.Flags := dcb.Flags or dcb_OutX or dcb_InX;
  779. if hardflow then
  780. dcb.Flags := dcb.Flags or dcb_OutxCtsFlow or dcb_RtsControlHandshake
  781. else
  782. dcb.Flags := dcb.Flags or dcb_RtsControlEnable;
  783. dcb.Flags := dcb.Flags or dcb_DtrControlEnable;
  784. if dcb.Parity > 0 then
  785. dcb.Flags := dcb.Flags or dcb_ParityCheck;
  786. SetCommState;
  787. end;
  788. procedure TBlockSerial.Connect(comport: string);
  789. {$IFDEF MSWINDOWS}
  790. var
  791. CommTimeouts: TCommTimeouts;
  792. {$ENDIF}
  793. begin
  794. // Is this TBlockSerial Instance already busy?
  795. if InstanceActive then {HGJ}
  796. begin {HGJ}
  797. RaiseSynaError(ErrAlreadyInUse);
  798. Exit; {HGJ}
  799. end; {HGJ}
  800. FBuffer := '';
  801. FDevice := comport;
  802. GetComNr(comport);
  803. {$IFDEF MSWINDOWS}
  804. SetLastError (sOK);
  805. {$ELSE}
  806. {$IFNDEF FPC}
  807. SetLastError (sOK);
  808. {$ELSE}
  809. fpSetErrno(sOK);
  810. {$ENDIF}
  811. {$ENDIF}
  812. {$IFNDEF MSWINDOWS}
  813. if FComNr <> PortIsClosed then
  814. FDevice := '/dev/ttyS' + IntToStr(FComNr);
  815. // Comport already owned by another process? {HGJ}
  816. if FLinuxLock then
  817. if not cpomComportAccessible then
  818. begin
  819. RaiseSynaError(ErrAlreadyOwned);
  820. Exit;
  821. end;
  822. {$IFNDEF FPC}
  823. FHandle := THandle(Libc.open(pchar(FDevice), O_RDWR or O_SYNC));
  824. {$ELSE}
  825. FHandle := THandle(fpOpen(FDevice, O_RDWR or O_SYNC));
  826. {$ENDIF}
  827. if FHandle = INVALID_HANDLE_VALUE then //because THandle is not integer on all platforms!
  828. SerialCheck(-1)
  829. else
  830. SerialCheck(0);
  831. {$IFDEF UNIX}
  832. if FLastError <> sOK then
  833. if FLinuxLock then
  834. cpomReleaseComport;
  835. {$ENDIF}
  836. ExceptCheck;
  837. if FLastError <> sOK then
  838. Exit;
  839. {$ELSE}
  840. if FComNr <> PortIsClosed then
  841. FDevice := '\\.\COM' + IntToStr(FComNr + 1);
  842. FHandle := THandle(CreateFile(PChar(FDevice), GENERIC_READ or GENERIC_WRITE,
  843. 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, 0));
  844. if FHandle = INVALID_HANDLE_VALUE then //because THandle is not integer on all platforms!
  845. SerialCheck(-1)
  846. else
  847. SerialCheck(0);
  848. ExceptCheck;
  849. if FLastError <> sOK then
  850. Exit;
  851. SetCommMask(FHandle, 0);
  852. SetupComm(Fhandle, FRecvBuffer, 0);
  853. CommTimeOuts.ReadIntervalTimeout := MAXWORD;
  854. CommTimeOuts.ReadTotalTimeoutMultiplier := 0;
  855. CommTimeOuts.ReadTotalTimeoutConstant := 0;
  856. CommTimeOuts.WriteTotalTimeoutMultiplier := 0;
  857. CommTimeOuts.WriteTotalTimeoutConstant := 0;
  858. SetCommTimeOuts(FHandle, CommTimeOuts);
  859. {$IFDEF WIN32}
  860. FPortAddr := GetPortAddr;
  861. {$ENDIF}
  862. {$ENDIF}
  863. SetSynaError(sOK);
  864. if not TestCtrlLine then {HGJ}
  865. begin
  866. SetSynaError(ErrNoDeviceAnswer);
  867. FileClose(FHandle); {HGJ}
  868. {$IFDEF UNIX}
  869. if FLinuxLock then
  870. cpomReleaseComport; {HGJ}
  871. {$ENDIF} {HGJ}
  872. Fhandle := INVALID_HANDLE_VALUE; {HGJ}
  873. FComNr:= PortIsClosed; {HGJ}
  874. end
  875. else
  876. begin
  877. FInstanceActive:= True;
  878. RTS := True;
  879. DTR := True;
  880. Purge;
  881. end;
  882. ExceptCheck;
  883. DoStatus(HR_Connect, FDevice);
  884. end;
  885. function TBlockSerial.SendBuffer(buffer: pointer; length: integer): integer;
  886. {$IFDEF MSWINDOWS}
  887. var
  888. Overlapped: TOverlapped;
  889. x, y, Err: DWord;
  890. {$ENDIF}
  891. begin
  892. Result := 0;
  893. if PreTestFailing then {HGJ}
  894. Exit; {HGJ}
  895. LimitBandwidth(Length, FMaxSendBandwidth, FNextsend);
  896. if FRTSToggle then
  897. begin
  898. Flush;
  899. RTS := True;
  900. end;
  901. {$IFNDEF MSWINDOWS}
  902. result := FileWrite(Fhandle, Buffer^, Length);
  903. serialcheck(result);
  904. {$ELSE}
  905. FillChar(Overlapped, Sizeof(Overlapped), 0);
  906. SetSynaError(sOK);
  907. y := 0;
  908. if not WriteFile(FHandle, Buffer^, Length, DWord(Result), @Overlapped) then
  909. y := GetLastError;
  910. if y = ERROR_IO_PENDING then
  911. begin
  912. x := WaitForSingleObject(FHandle, FDeadlockTimeout);
  913. if x = WAIT_TIMEOUT then
  914. begin
  915. PurgeComm(FHandle, PURGE_TXABORT);
  916. SetSynaError(ErrTimeout);
  917. end;
  918. GetOverlappedResult(FHandle, Overlapped, Dword(Result), False);
  919. end
  920. else
  921. SetSynaError(y);
  922. err := 0;
  923. ClearCommError(FHandle, err, nil);
  924. if err <> 0 then
  925. DecodeCommError(err);
  926. {$ENDIF}
  927. if FRTSToggle then
  928. begin
  929. Flush;
  930. CanWrite(255);
  931. RTS := False;
  932. end;
  933. ExceptCheck;
  934. DoStatus(HR_WriteCount, IntToStr(Result));
  935. end;
  936. procedure TBlockSerial.SendByte(data: byte);
  937. begin
  938. SendBuffer(@Data, 1);
  939. end;
  940. procedure TBlockSerial.SendString(data: AnsiString);
  941. begin
  942. SendBuffer(Pointer(Data), Length(Data));
  943. end;
  944. procedure TBlockSerial.SendInteger(Data: integer);
  945. begin
  946. SendBuffer(@data, SizeOf(Data));
  947. end;
  948. procedure TBlockSerial.SendBlock(const Data: AnsiString);
  949. begin
  950. SendInteger(Length(data));
  951. SendString(Data);
  952. end;
  953. procedure TBlockSerial.SendStreamRaw(const Stream: TStream);
  954. var
  955. si: integer;
  956. x, y, yr: integer;
  957. s: AnsiString;
  958. begin
  959. si := Stream.Size - Stream.Position;
  960. x := 0;
  961. while x < si do
  962. begin
  963. y := si - x;
  964. if y > cSerialChunk then
  965. y := cSerialChunk;
  966. Setlength(s, y);
  967. yr := Stream.read(PAnsiChar(s)^, y);
  968. if yr > 0 then
  969. begin
  970. SetLength(s, yr);
  971. SendString(s);
  972. Inc(x, yr);
  973. end
  974. else
  975. break;
  976. end;
  977. end;
  978. procedure TBlockSerial.SendStreamIndy(const Stream: TStream);
  979. var
  980. si: integer;
  981. begin
  982. si := Stream.Size - Stream.Position;
  983. si := Swapbytes(si);
  984. SendInteger(si);
  985. SendStreamRaw(Stream);
  986. end;
  987. procedure TBlockSerial.SendStream(const Stream: TStream);
  988. var
  989. si: integer;
  990. begin
  991. si := Stream.Size - Stream.Position;
  992. SendInteger(si);
  993. SendStreamRaw(Stream);
  994. end;
  995. function TBlockSerial.RecvBuffer(buffer: pointer; length: integer): integer;
  996. {$IFNDEF MSWINDOWS}
  997. begin
  998. Result := 0;
  999. if PreTestFailing then {HGJ}
  1000. Exit; {HGJ}
  1001. LimitBandwidth(Length, FMaxRecvBandwidth, FNextRecv);
  1002. result := FileRead(FHandle, Buffer^, length);
  1003. serialcheck(result);
  1004. {$ELSE}
  1005. var
  1006. Overlapped: TOverlapped;
  1007. x, y, Err: DWord;
  1008. begin
  1009. Result := 0;
  1010. if PreTestFailing then {HGJ}
  1011. Exit; {HGJ}
  1012. LimitBandwidth(Length, FMaxRecvBandwidth, FNextRecv);
  1013. FillChar(Overlapped, Sizeof(Overlapped), 0);
  1014. SetSynaError(sOK);
  1015. y := 0;
  1016. if not ReadFile(FHandle, Buffer^, length, Dword(Result), @Overlapped) then
  1017. y := GetLastError;
  1018. if y = ERROR_IO_PENDING then
  1019. begin
  1020. x := WaitForSingleObject(FHandle, FDeadlockTimeout);
  1021. if x = WAIT_TIMEOUT then
  1022. begin
  1023. PurgeComm(FHandle, PURGE_RXABORT);
  1024. SetSynaError(ErrTimeout);
  1025. end;
  1026. GetOverlappedResult(FHandle, Overlapped, Dword(Result), False);
  1027. end
  1028. else
  1029. SetSynaError(y);
  1030. err := 0;
  1031. ClearCommError(FHandle, err, nil);
  1032. if err <> 0 then
  1033. DecodeCommError(err);
  1034. {$ENDIF}
  1035. ExceptCheck;
  1036. DoStatus(HR_ReadCount, IntToStr(Result));
  1037. end;
  1038. function TBlockSerial.RecvBufferEx(buffer: pointer; length: integer; timeout: integer): integer;
  1039. var
  1040. s: AnsiString;
  1041. rl, l: integer;
  1042. ti: LongWord;
  1043. begin
  1044. Result := 0;
  1045. if PreTestFailing then {HGJ}
  1046. Exit; {HGJ}
  1047. SetSynaError(sOK);
  1048. rl := 0;
  1049. repeat
  1050. ti := GetTick;
  1051. s := RecvPacket(Timeout);
  1052. l := System.Length(s);
  1053. if (rl + l) > Length then
  1054. l := Length - rl;
  1055. Move(Pointer(s)^, IncPoint(Buffer, rl)^, l);
  1056. rl := rl + l;
  1057. if FLastError <> sOK then
  1058. Break;
  1059. if rl >= Length then
  1060. Break;
  1061. if not FInterPacketTimeout then
  1062. begin
  1063. Timeout := Timeout - integer(TickDelta(ti, GetTick));
  1064. if Timeout <= 0 then
  1065. begin
  1066. SetSynaError(ErrTimeout);
  1067. Break;
  1068. end;
  1069. end;
  1070. until False;
  1071. delete(s, 1, l);
  1072. FBuffer := s;
  1073. Result := rl;
  1074. end;
  1075. function TBlockSerial.RecvBufferStr(Length: Integer; Timeout: Integer): AnsiString;
  1076. var
  1077. x: integer;
  1078. begin
  1079. Result := '';
  1080. if PreTestFailing then {HGJ}
  1081. Exit; {HGJ}
  1082. SetSynaError(sOK);
  1083. if Length > 0 then
  1084. begin
  1085. Setlength(Result, Length);
  1086. x := RecvBufferEx(PAnsiChar(Result), Length , Timeout);
  1087. if FLastError = sOK then
  1088. SetLength(Result, x)
  1089. else
  1090. Result := '';
  1091. end;
  1092. end;
  1093. function TBlockSerial.RecvPacket(Timeout: Integer): AnsiString;
  1094. var
  1095. x: integer;
  1096. begin
  1097. Result := '';
  1098. if PreTestFailing then {HGJ}
  1099. Exit; {HGJ}
  1100. SetSynaError(sOK);
  1101. if FBuffer <> '' then
  1102. begin
  1103. Result := FBuffer;
  1104. FBuffer := '';
  1105. end
  1106. else
  1107. begin
  1108. //not drain CPU on large downloads...
  1109. Sleep(0);
  1110. x := WaitingData;
  1111. if x > 0 then
  1112. begin
  1113. SetLength(Result, x);
  1114. x := RecvBuffer(Pointer(Result), x);
  1115. if x >= 0 then
  1116. SetLength(Result, x);
  1117. end
  1118. else
  1119. begin
  1120. if CanRead(Timeout) then
  1121. begin
  1122. x := WaitingData;
  1123. if x = 0 then
  1124. SetSynaError(ErrTimeout);
  1125. if x > 0 then
  1126. begin
  1127. SetLength(Result, x);
  1128. x := RecvBuffer(Pointer(Result), x);
  1129. if x >= 0 then
  1130. SetLength(Result, x);
  1131. end;
  1132. end
  1133. else
  1134. SetSynaError(ErrTimeout);
  1135. end;
  1136. end;
  1137. ExceptCheck;
  1138. end;
  1139. function TBlockSerial.RecvByte(timeout: integer): byte;
  1140. begin
  1141. Result := 0;
  1142. if PreTestFailing then {HGJ}
  1143. Exit; {HGJ}
  1144. SetSynaError(sOK);
  1145. if FBuffer = '' then
  1146. FBuffer := RecvPacket(Timeout);
  1147. if (FLastError = sOK) and (FBuffer <> '') then
  1148. begin
  1149. Result := Ord(FBuffer[1]);
  1150. System.Delete(FBuffer, 1, 1);
  1151. end;
  1152. ExceptCheck;
  1153. end;
  1154. function TBlockSerial.RecvTerminated(Timeout: Integer; const Terminator: AnsiString): AnsiString;
  1155. var
  1156. x: Integer;
  1157. s: AnsiString;
  1158. l: Integer;
  1159. CorCRLF: Boolean;
  1160. t: ansistring;
  1161. tl: integer;
  1162. ti: LongWord;
  1163. begin
  1164. Result := '';
  1165. if PreTestFailing then {HGJ}
  1166. Exit; {HGJ}
  1167. SetSynaError(sOK);
  1168. l := system.Length(Terminator);
  1169. if l = 0 then
  1170. Exit;
  1171. tl := l;
  1172. CorCRLF := FConvertLineEnd and (Terminator = CRLF);
  1173. s := '';
  1174. x := 0;
  1175. repeat
  1176. ti := GetTick;
  1177. //get rest of FBuffer or incomming new data...
  1178. s := s + RecvPacket(Timeout);
  1179. if FLastError <> sOK then
  1180. Break;
  1181. x := 0;
  1182. if Length(s) > 0 then
  1183. if CorCRLF then
  1184. begin
  1185. if FLastCR and (s[1] = LF) then
  1186. Delete(s, 1, 1);
  1187. if FLastLF and (s[1] = CR) then
  1188. Delete(s, 1, 1);
  1189. FLastCR := False;
  1190. FLastLF := False;
  1191. t := '';
  1192. x := PosCRLF(s, t);
  1193. tl := system.Length(t);
  1194. if t = CR then
  1195. FLastCR := True;
  1196. if t = LF then
  1197. FLastLF := True;
  1198. end
  1199. else
  1200. begin
  1201. x := pos(Terminator, s);
  1202. tl := l;
  1203. end;
  1204. if (FMaxLineLength <> 0) and (system.Length(s) > FMaxLineLength) then
  1205. begin
  1206. SetSynaError(ErrMaxBuffer);
  1207. Break;
  1208. end;
  1209. if x > 0 then
  1210. Break;
  1211. if not FInterPacketTimeout then
  1212. begin
  1213. Timeout := Timeout - integer(TickDelta(ti, GetTick));
  1214. if Timeout <= 0 then
  1215. begin
  1216. SetSynaError(ErrTimeout);
  1217. Break;
  1218. end;
  1219. end;
  1220. until False;
  1221. if x > 0 then
  1222. begin
  1223. Result := Copy(s, 1, x - 1);
  1224. System.Delete(s, 1, x + tl - 1);
  1225. end;
  1226. FBuffer := s;
  1227. ExceptCheck;
  1228. end;
  1229. function TBlockSerial.RecvString(Timeout: Integer): AnsiString;
  1230. var
  1231. s: AnsiString;
  1232. begin
  1233. Result := '';
  1234. s := RecvTerminated(Timeout, #13 + #10);
  1235. if FLastError = sOK then
  1236. Result := s;
  1237. end;
  1238. function TBlockSerial.RecvInteger(Timeout: Integer): Integer;
  1239. var
  1240. s: AnsiString;
  1241. begin
  1242. Result := 0;
  1243. s := RecvBufferStr(4, Timeout);
  1244. if FLastError = 0 then
  1245. Result := (ord(s[1]) + ord(s[2]) * 256) + (ord(s[3]) + ord(s[4]) * 256) * 65536;
  1246. end;
  1247. function TBlockSerial.RecvBlock(Timeout: Integer): AnsiString;
  1248. var
  1249. x: integer;
  1250. begin
  1251. Result := '';
  1252. x := RecvInteger(Timeout);
  1253. if FLastError = 0 then
  1254. Result := RecvBufferStr(x, Timeout);
  1255. end;
  1256. procedure TBlockSerial.RecvStreamRaw(const Stream: TStream; Timeout: Integer);
  1257. var
  1258. s: AnsiString;
  1259. begin
  1260. repeat
  1261. s := RecvPacket(Timeout);
  1262. if FLastError = 0 then
  1263. WriteStrToStream(Stream, s);
  1264. until FLastError <> 0;
  1265. end;
  1266. procedure TBlockSerial.RecvStreamSize(const Stream: TStream; Timeout: Integer; Size: Integer);
  1267. var
  1268. s: AnsiString;
  1269. n: integer;
  1270. begin
  1271. for n := 1 to (Size div cSerialChunk) do
  1272. begin
  1273. s := RecvBufferStr(cSerialChunk, Timeout);
  1274. if FLastError <> 0 then
  1275. Exit;
  1276. Stream.Write(PAnsichar(s)^, cSerialChunk);
  1277. end;
  1278. n := Size mod cSerialChunk;
  1279. if n > 0 then
  1280. begin
  1281. s := RecvBufferStr(n, Timeout);
  1282. if FLastError <> 0 then
  1283. Exit;
  1284. Stream.Write(PAnsichar(s)^, n);
  1285. end;
  1286. end;
  1287. procedure TBlockSerial.RecvStreamIndy(const Stream: TStream; Timeout: Integer);
  1288. var
  1289. x: integer;
  1290. begin
  1291. x := RecvInteger(Timeout);
  1292. x := SwapBytes(x);
  1293. if FLastError = 0 then
  1294. RecvStreamSize(Stream, Timeout, x);
  1295. end;
  1296. procedure TBlockSerial.RecvStream(const Stream: TStream; Timeout: Integer);
  1297. var
  1298. x: integer;
  1299. begin
  1300. x := RecvInteger(Timeout);
  1301. if FLastError = 0 then
  1302. RecvStreamSize(Stream, Timeout, x);
  1303. end;
  1304. {$IFNDEF MSWINDOWS}
  1305. function TBlockSerial.WaitingData: integer;
  1306. begin
  1307. {$IFNDEF FPC}
  1308. serialcheck(ioctl(FHandle, FIONREAD, @result));
  1309. {$ELSE}
  1310. serialcheck(fpIoctl(FHandle, FIONREAD, @result));
  1311. {$ENDIF}
  1312. if FLastError <> 0 then
  1313. Result := 0;
  1314. ExceptCheck;
  1315. end;
  1316. {$ELSE}
  1317. function TBlockSerial.WaitingData: integer;
  1318. var
  1319. stat: TComStat;
  1320. err: DWORD;
  1321. begin
  1322. err := 0;
  1323. if ClearCommError(FHandle, err, @stat) then
  1324. begin
  1325. SetSynaError(sOK);
  1326. Result := stat.cbInQue;
  1327. end
  1328. else
  1329. begin
  1330. SerialCheck(sErr);
  1331. Result := 0;
  1332. end;
  1333. ExceptCheck;
  1334. end;
  1335. {$ENDIF}
  1336. function TBlockSerial.WaitingDataEx: integer;
  1337. begin
  1338. if FBuffer <> '' then
  1339. Result := Length(FBuffer)
  1340. else
  1341. Result := Waitingdata;
  1342. end;
  1343. {$IFNDEF MSWINDOWS}
  1344. function TBlockSerial.SendingData: integer;
  1345. begin
  1346. SetSynaError(sOK);
  1347. Result := 0;
  1348. end;
  1349. {$ELSE}
  1350. function TBlockSerial.SendingData: integer;
  1351. var
  1352. stat: TComStat;
  1353. err: DWORD;
  1354. begin
  1355. SetSynaError(sOK);
  1356. err := 0;
  1357. if not ClearCommError(FHandle, err, @stat) then
  1358. serialcheck(sErr);
  1359. ExceptCheck;
  1360. result := stat.cbOutQue;
  1361. end;
  1362. {$ENDIF}
  1363. {$IFNDEF MSWINDOWS}
  1364. procedure TBlockSerial.DcbToTermios(const dcb: TDCB; var term: termios);
  1365. var
  1366. n: integer;
  1367. x: cardinal;
  1368. begin
  1369. //others
  1370. cfmakeraw(term);
  1371. term.c_cflag := term.c_cflag or CREAD;
  1372. term.c_cflag := term.c_cflag or CLOCAL;
  1373. term.c_cflag := term.c_cflag or HUPCL;
  1374. //hardware handshake
  1375. if (dcb.flags and dcb_RtsControlHandshake) > 0 then
  1376. term.c_cflag := term.c_cflag or CRTSCTS
  1377. else
  1378. term.c_cflag := term.c_cflag and (not CRTSCTS);
  1379. //software handshake
  1380. if (dcb.flags and dcb_OutX) > 0 then
  1381. term.c_iflag := term.c_iflag or IXON or IXOFF or IXANY
  1382. else
  1383. term.c_iflag := term.c_iflag and (not (IXON or IXOFF or IXANY));
  1384. //size of byte
  1385. term.c_cflag := term.c_cflag and (not CSIZE);
  1386. case dcb.bytesize of
  1387. 5:
  1388. term.c_cflag := term.c_cflag or CS5;
  1389. 6:
  1390. term.c_cflag := term.c_cflag or CS6;
  1391. 7:
  1392. {$IFDEF FPC}
  1393. term.c_cflag := term.c_cflag or CS7;
  1394. {$ELSE}
  1395. term.c_cflag := term.c_cflag or CS7fix;
  1396. {$ENDIF}
  1397. 8:
  1398. term.c_cflag := term.c_cflag or CS8;
  1399. end;
  1400. //parity
  1401. if (dcb.flags and dcb_ParityCheck) > 0 then
  1402. term.c_cflag := term.c_cflag or PARENB
  1403. else
  1404. term.c_cflag := term.c_cflag and (not PARENB);
  1405. case dcb.parity of
  1406. 1: //'O'
  1407. term.c_cflag := term.c_cflag or PARODD;
  1408. 2: //'E'
  1409. term.c_cflag := term.c_cflag and (not PARODD);
  1410. end;
  1411. //stop bits
  1412. if dcb.stopbits > 0 then
  1413. term.c_cflag := term.c_cflag or CSTOPB
  1414. else
  1415. term.c_cflag := term.c_cflag and (not CSTOPB);
  1416. //set baudrate;
  1417. x := 0;
  1418. for n := 0 to Maxrates do
  1419. if rates[n, 0] = dcb.BaudRate then
  1420. begin
  1421. x := rates[n, 1];
  1422. break;
  1423. end;
  1424. cfsetospeed(term, x);
  1425. cfsetispeed(term, x);
  1426. end;
  1427. procedure TBlockSerial.TermiosToDcb(const term: termios; var dcb: TDCB);
  1428. var
  1429. n: integer;
  1430. x: cardinal;
  1431. begin
  1432. //set baudrate;
  1433. dcb.baudrate := 0;
  1434. {$IFDEF FPC}
  1435. //why FPC not have cfgetospeed???
  1436. x := term.c_oflag and $0F;
  1437. {$ELSE}
  1438. x := cfgetospeed(term);
  1439. {$ENDIF}
  1440. for n := 0 to Maxrates do
  1441. if rates[n, 1] = x then
  1442. begin
  1443. dcb.baudrate := rates[n, 0];
  1444. break;
  1445. end;
  1446. //hardware handshake
  1447. if (term.c_cflag and CRTSCTS) > 0 then
  1448. dcb.flags := dcb.flags or dcb_RtsControlHandshake or dcb_OutxCtsFlow
  1449. else
  1450. dcb.flags := dcb.flags and (not (dcb_RtsControlHandshake or dcb_OutxCtsFlow));
  1451. //software handshake
  1452. if (term.c_cflag and IXOFF) > 0 then
  1453. dcb.flags := dcb.flags or dcb_OutX or dcb_InX
  1454. else
  1455. dcb.flags := dcb.flags and (not (dcb_OutX or dcb_InX));
  1456. //size of byte
  1457. case term.c_cflag and CSIZE of
  1458. CS5:
  1459. dcb.bytesize := 5;
  1460. CS6:
  1461. dcb.bytesize := 6;
  1462. CS7fix:
  1463. dcb.bytesize := 7;
  1464. CS8:
  1465. dcb.bytesize := 8;
  1466. end;
  1467. //parity
  1468. if (term.c_cflag and PARENB) > 0 then
  1469. dcb.flags := dcb.flags or dcb_ParityCheck
  1470. else
  1471. dcb.flags := dcb.flags and (not dcb_ParityCheck);
  1472. dcb.parity := 0;
  1473. if (term.c_cflag and PARODD) > 0 then
  1474. dcb.parity := 1
  1475. else
  1476. dcb.parity := 2;
  1477. //stop bits
  1478. if (term.c_cflag and CSTOPB) > 0 then
  1479. dcb.stopbits := 2
  1480. else
  1481. dcb.stopbits := 0;
  1482. end;
  1483. {$ENDIF}
  1484. {$IFNDEF MSWINDOWS}
  1485. procedure TBlockSerial.SetCommState;
  1486. begin
  1487. DcbToTermios(dcb, termiosstruc);
  1488. SerialCheck(tcsetattr(FHandle, TCSANOW, termiosstruc));
  1489. ExceptCheck;
  1490. end;
  1491. {$ELSE}
  1492. procedure TBlockSerial.SetCommState;
  1493. begin
  1494. SetSynaError(sOK);
  1495. if not windows.SetCommState(Fhandle, dcb) then
  1496. SerialCheck(sErr);
  1497. ExceptCheck;
  1498. end;
  1499. {$ENDIF}
  1500. {$IFNDEF MSWINDOWS}
  1501. procedure TBlockSerial.GetCommState;
  1502. begin
  1503. SerialCheck(tcgetattr(FHandle, termiosstruc));
  1504. ExceptCheck;
  1505. TermiostoDCB(termiosstruc, dcb);
  1506. end;
  1507. {$ELSE}
  1508. procedure TBlockSerial.GetCommState;
  1509. begin
  1510. SetSynaError(sOK);
  1511. if not windows.GetCommState(Fhandle, dcb) then
  1512. SerialCheck(sErr);
  1513. ExceptCheck;
  1514. end;
  1515. {$ENDIF}
  1516. procedure TBlockSerial.SetSizeRecvBuffer(size: integer);
  1517. begin
  1518. {$IFDEF MSWINDOWS}
  1519. SetupComm(Fhandle, size, 0);
  1520. GetCommState;
  1521. dcb.XonLim := size div 4;
  1522. dcb.XoffLim := size div 4;
  1523. SetCommState;
  1524. {$ENDIF}
  1525. FRecvBuffer := size;
  1526. end;
  1527. function TBlockSerial.GetDSR: Boolean;
  1528. begin
  1529. ModemStatus;
  1530. {$IFNDEF MSWINDOWS}
  1531. Result := (FModemWord and TIOCM_DSR) > 0;
  1532. {$ELSE}
  1533. Result := (FModemWord and MS_DSR_ON) > 0;
  1534. {$ENDIF}
  1535. end;
  1536. procedure TBlockSerial.SetDTRF(Value: Boolean);
  1537. begin
  1538. {$IFNDEF MSWINDOWS}
  1539. ModemStatus;
  1540. if Value then
  1541. FModemWord := FModemWord or TIOCM_DTR
  1542. else
  1543. FModemWord := FModemWord and not TIOCM_DTR;
  1544. {$IFNDEF FPC}
  1545. ioctl(FHandle, TIOCMSET, @FModemWord);
  1546. {$ELSE}
  1547. fpioctl(FHandle, TIOCMSET, @FModemWord);
  1548. {$ENDIF}
  1549. {$ELSE}
  1550. if Value then
  1551. EscapeCommFunction(FHandle, SETDTR)
  1552. else
  1553. EscapeCommFunction(FHandle, CLRDTR);
  1554. {$ENDIF}
  1555. end;
  1556. function TBlockSerial.GetCTS: Boolean;
  1557. begin
  1558. ModemStatus;
  1559. {$IFNDEF MSWINDOWS}
  1560. Result := (FModemWord and TIOCM_CTS) > 0;
  1561. {$ELSE}
  1562. Result := (FModemWord and MS_CTS_ON) > 0;
  1563. {$ENDIF}
  1564. end;
  1565. procedure TBlockSerial.SetRTSF(Value: Boolean);
  1566. begin
  1567. {$IFNDEF MSWINDOWS}
  1568. ModemStatus;
  1569. if Value then
  1570. FModemWord := FModemWord or TIOCM_RTS
  1571. else
  1572. FModemWord := FModemWord and not TIOCM_RTS;
  1573. {$IFNDEF FPC}
  1574. ioctl(FHandle, TIOCMSET, @FModemWord);
  1575. {$ELSE}
  1576. fpioctl(FHandle, TIOCMSET, @FModemWord);
  1577. {$ENDIF}
  1578. {$ELSE}
  1579. if Value then
  1580. EscapeCommFunction(FHandle, SETRTS)
  1581. else
  1582. EscapeCommFunction(FHandle, CLRRTS);
  1583. {$ENDIF}
  1584. end;
  1585. function TBlockSerial.GetCarrier: Boolean;
  1586. begin
  1587. ModemStatus;
  1588. {$IFNDEF MSWINDOWS}
  1589. Result := (FModemWord and TIOCM_CAR) > 0;
  1590. {$ELSE}
  1591. Result := (FModemWord and MS_RLSD_ON) > 0;
  1592. {$ENDIF}
  1593. end;
  1594. function TBlockSerial.GetRing: Boolean;
  1595. begin
  1596. ModemStatus;
  1597. {$IFNDEF MSWINDOWS}
  1598. Result := (FModemWord and TIOCM_RNG) > 0;
  1599. {$ELSE}
  1600. Result := (FModemWord and MS_RING_ON) > 0;
  1601. {$ENDIF}
  1602. end;
  1603. {$IFDEF MSWINDOWS}
  1604. function TBlockSerial.CanEvent(Event: dword; Timeout: integer): boolean;
  1605. var
  1606. ex: DWord;
  1607. y: Integer;
  1608. Overlapped: TOverlapped;
  1609. begin
  1610. FillChar(Overlapped, Sizeof(Overlapped), 0);
  1611. Overlapped.hEvent := CreateEvent(nil, True, False, nil);
  1612. try
  1613. SetCommMask(FHandle, Event);
  1614. SetSynaError(sOK);
  1615. if (Event = EV_RXCHAR) and (Waitingdata > 0) then
  1616. Result := True
  1617. else
  1618. begin
  1619. y := 0;
  1620. ex := 0;
  1621. if not WaitCommEvent(FHandle, ex, @Overlapped) then
  1622. y := GetLastError;
  1623. if y = ERROR_IO_PENDING then
  1624. begin
  1625. //timedout
  1626. WaitForSingleObject(Overlapped.hEvent, Timeout);
  1627. SetCommMask(FHandle, 0);
  1628. GetOverlappedResult(FHandle, Overlapped, DWord(y), True);
  1629. end;
  1630. Result := (ex and Event) = Event;
  1631. end;
  1632. finally
  1633. SetCommMask(FHandle, 0);
  1634. CloseHandle(Overlapped.hEvent);
  1635. end;
  1636. end;
  1637. {$ENDIF}
  1638. {$IFNDEF MSWINDOWS}
  1639. function TBlockSerial.CanRead(Timeout: integer): boolean;
  1640. var
  1641. FDSet: TFDSet;
  1642. TimeVal: PTimeVal;
  1643. TimeV: TTimeVal;
  1644. x: Integer;
  1645. begin
  1646. TimeV.tv_usec := (Timeout mod 1000) * 1000;
  1647. TimeV.tv_sec := Timeout div 1000;
  1648. TimeVal := @TimeV;
  1649. if Timeout = -1 then
  1650. TimeVal := nil;
  1651. {$IFNDEF FPC}
  1652. FD_ZERO(FDSet);
  1653. FD_SET(FHandle, FDSet);
  1654. x := Select(FHandle + 1, @FDSet, nil, nil, TimeVal);
  1655. {$ELSE}
  1656. fpFD_ZERO(FDSet);
  1657. fpFD_SET(FHandle, FDSet);
  1658. x := fpSelect(FHandle + 1, @FDSet, nil, nil, TimeVal);
  1659. {$ENDIF}
  1660. SerialCheck(x);
  1661. if FLastError <> sOK then
  1662. x := 0;
  1663. Result := x > 0;
  1664. ExceptCheck;
  1665. if Result then
  1666. DoStatus(HR_CanRead, '');
  1667. end;
  1668. {$ELSE}
  1669. function TBlockSerial.CanRead(Timeout: integer): boolean;
  1670. begin
  1671. Result := WaitingData > 0;
  1672. if not Result then
  1673. Result := CanEvent(EV_RXCHAR, Timeout) or (WaitingData > 0);
  1674. //check WaitingData again due some broken virtual ports
  1675. if Result then
  1676. DoStatus(HR_CanRead, '');
  1677. end;
  1678. {$ENDIF}
  1679. {$IFNDEF MSWINDOWS}
  1680. function TBlockSerial.CanWrite(Timeout: integer): boolean;
  1681. var
  1682. FDSet: TFDSet;
  1683. TimeVal: PTimeVal;
  1684. TimeV: TTimeVal;
  1685. x: Integer;
  1686. begin
  1687. TimeV.tv_usec := (Timeout mod 1000) * 1000;
  1688. TimeV.tv_sec := Timeout div 1000;
  1689. TimeVal := @TimeV;
  1690. if Timeout = -1 then
  1691. TimeVal := nil;
  1692. {$IFNDEF FPC}
  1693. FD_ZERO(FDSet);
  1694. FD_SET(FHandle, FDSet);
  1695. x := Select(FHandle + 1, nil, @FDSet, nil, TimeVal);
  1696. {$ELSE}
  1697. fpFD_ZERO(FDSet);
  1698. fpFD_SET(FHandle, FDSet);
  1699. x := fpSelect(FHandle + 1, nil, @FDSet, nil, TimeVal);
  1700. {$ENDIF}
  1701. SerialCheck(x);
  1702. if FLastError <> sOK then
  1703. x := 0;
  1704. Result := x > 0;
  1705. ExceptCheck;
  1706. if Result then
  1707. DoStatus(HR_CanWrite, '');
  1708. end;
  1709. {$ELSE}
  1710. function TBlockSerial.CanWrite(Timeout: integer): boolean;
  1711. var
  1712. t: LongWord;
  1713. begin
  1714. Result := SendingData = 0;
  1715. if not Result then
  1716. Result := CanEvent(EV_TXEMPTY, Timeout);
  1717. {$IFDEF WIN32}
  1718. if Result and (Win32Platform <> VER_PLATFORM_WIN32_NT) then
  1719. begin
  1720. t := GetTick;
  1721. while not ReadTxEmpty(FPortAddr) do
  1722. begin
  1723. if TickDelta(t, GetTick) > 255 then
  1724. Break;
  1725. Sleep(0);
  1726. end;
  1727. end;
  1728. {$ENDIF}
  1729. if Result then
  1730. DoStatus(HR_CanWrite, '');
  1731. end;
  1732. {$ENDIF}
  1733. function TBlockSerial.CanReadEx(Timeout: integer): boolean;
  1734. begin
  1735. if Fbuffer <> '' then
  1736. Result := True
  1737. else
  1738. Result := CanRead(Timeout);
  1739. end;
  1740. procedure TBlockSerial.EnableRTSToggle(Value: boolean);
  1741. begin
  1742. SetSynaError(sOK);
  1743. {$IFNDEF MSWINDOWS}
  1744. FRTSToggle := Value;
  1745. if Value then
  1746. RTS:=False;
  1747. {$ELSE}
  1748. if Win32Platform = VER_PLATFORM_WIN32_NT then
  1749. begin
  1750. GetCommState;
  1751. if value then
  1752. dcb.Flags := dcb.Flags or dcb_RtsControlToggle
  1753. else
  1754. dcb.flags := dcb.flags and (not dcb_RtsControlToggle);
  1755. SetCommState;
  1756. end
  1757. else
  1758. begin
  1759. FRTSToggle := Value;
  1760. if Value then
  1761. RTS:=False;
  1762. end;
  1763. {$ENDIF}
  1764. end;
  1765. procedure TBlockSerial.Flush;
  1766. begin
  1767. {$IFNDEF MSWINDOWS}
  1768. SerialCheck(tcdrain(FHandle));
  1769. {$ELSE}
  1770. SetSynaError(sOK);
  1771. if not Flushfilebuffers(FHandle) then
  1772. SerialCheck(sErr);
  1773. {$ENDIF}
  1774. ExceptCheck;
  1775. end;
  1776. {$IFNDEF MSWINDOWS}
  1777. procedure TBlockSerial.Purge;
  1778. begin
  1779. {$IFNDEF FPC}
  1780. SerialCheck(ioctl(FHandle, TCFLSH, TCIOFLUSH));
  1781. {$ELSE}
  1782. {$IFDEF DARWIN}
  1783. SerialCheck(fpioctl(FHandle, TCIOflush, Pointer(PtrInt(TCIOFLUSH))));
  1784. {$ELSE}
  1785. SerialCheck(fpioctl(FHandle, {$IFDEF FreeBSD}TCIOFLUSH{$ELSE}TCFLSH{$ENDIF}, Pointer(PtrInt(TCIOFLUSH))));
  1786. {$ENDIF}
  1787. {$ENDIF}
  1788. FBuffer := '';
  1789. ExceptCheck;
  1790. end;
  1791. {$ELSE}
  1792. procedure TBlockSerial.Purge;
  1793. var
  1794. x: integer;
  1795. begin
  1796. SetSynaError(sOK);
  1797. x := PURGE_TXABORT or PURGE_TXCLEAR or PURGE_RXABORT or PURGE_RXCLEAR;
  1798. if not PurgeComm(FHandle, x) then
  1799. SerialCheck(sErr);
  1800. FBuffer := '';
  1801. ExceptCheck;
  1802. end;
  1803. {$ENDIF}
  1804. function TBlockSerial.ModemStatus: integer;
  1805. begin
  1806. Result := 0;
  1807. {$IFNDEF MSWINDOWS}
  1808. {$IFNDEF FPC}
  1809. SerialCheck(ioctl(FHandle, TIOCMGET, @Result));
  1810. {$ELSE}
  1811. SerialCheck(fpioctl(FHandle, TIOCMGET, @Result));
  1812. {$ENDIF}
  1813. {$ELSE}
  1814. SetSynaError(sOK);
  1815. if not GetCommModemStatus(FHandle, dword(Result)) then
  1816. SerialCheck(sErr);
  1817. {$ENDIF}
  1818. ExceptCheck;
  1819. FModemWord := Result;
  1820. end;
  1821. procedure TBlockSerial.SetBreak(Duration: integer);
  1822. begin
  1823. {$IFNDEF MSWINDOWS}
  1824. SerialCheck(tcsendbreak(FHandle, Duration));
  1825. {$ELSE}
  1826. SetCommBreak(FHandle);
  1827. Sleep(Duration);
  1828. SetSynaError(sOK);
  1829. if not ClearCommBreak(FHandle) then
  1830. SerialCheck(sErr);
  1831. {$ENDIF}
  1832. end;
  1833. {$IFDEF MSWINDOWS}
  1834. procedure TBlockSerial.DecodeCommError(Error: DWord);
  1835. begin
  1836. if (Error and DWord(CE_FRAME)) > 1 then
  1837. FLastError := ErrFrame;
  1838. if (Error and DWord(CE_OVERRUN)) > 1 then
  1839. FLastError := ErrOverrun;
  1840. if (Error and DWord(CE_RXOVER)) > 1 then
  1841. FLastError := ErrRxOver;
  1842. if (Error and DWord(CE_RXPARITY)) > 1 then
  1843. FLastError := ErrRxParity;
  1844. if (Error and DWord(CE_TXFULL)) > 1 then
  1845. FLastError := ErrTxFull;
  1846. end;
  1847. {$ENDIF}
  1848. //HGJ
  1849. function TBlockSerial.PreTestFailing: Boolean;
  1850. begin
  1851. if not FInstanceActive then
  1852. begin
  1853. RaiseSynaError(ErrPortNotOpen);
  1854. result:= true;
  1855. Exit;
  1856. end;
  1857. Result := not TestCtrlLine;
  1858. if result then
  1859. RaiseSynaError(ErrNoDeviceAnswer)
  1860. end;
  1861. function TBlockSerial.TestCtrlLine: Boolean;
  1862. begin
  1863. result := ((not FTestDSR) or DSR) and ((not FTestCTS) or CTS);
  1864. end;
  1865. function TBlockSerial.ATCommand(value: AnsiString): AnsiString;
  1866. var
  1867. s: AnsiString;
  1868. ConvSave: Boolean;
  1869. begin
  1870. result := '';
  1871. FAtResult := False;
  1872. ConvSave := FConvertLineEnd;
  1873. try
  1874. FConvertLineEnd := True;
  1875. SendString(value + #$0D);
  1876. repeat
  1877. s := RecvString(FAtTimeout);
  1878. if s <> Value then
  1879. result := result + s + CRLF;
  1880. if s = 'OK' then
  1881. begin
  1882. FAtResult := True;
  1883. break;
  1884. end;
  1885. if s = 'ERROR' then
  1886. break;
  1887. until FLastError <> sOK;
  1888. finally
  1889. FConvertLineEnd := Convsave;
  1890. end;
  1891. end;
  1892. function TBlockSerial.ATConnect(value: AnsiString): AnsiString;
  1893. var
  1894. s: AnsiString;
  1895. ConvSave: Boolean;
  1896. begin
  1897. result := '';
  1898. FAtResult := False;
  1899. ConvSave := FConvertLineEnd;
  1900. try
  1901. FConvertLineEnd := True;
  1902. SendString(value + #$0D);
  1903. repeat
  1904. s := RecvString(90 * FAtTimeout);
  1905. if s <> Value then
  1906. result := result + s + CRLF;
  1907. if s = 'NO CARRIER' then
  1908. break;
  1909. if s = 'ERROR' then
  1910. break;
  1911. if s = 'BUSY' then
  1912. break;
  1913. if s = 'NO DIALTONE' then
  1914. break;
  1915. if Pos('CONNECT', s) = 1 then
  1916. begin
  1917. FAtResult := True;
  1918. break;
  1919. end;
  1920. until FLastError <> sOK;
  1921. finally
  1922. FConvertLineEnd := Convsave;
  1923. end;
  1924. end;
  1925. function TBlockSerial.SerialCheck(SerialResult: integer): integer;
  1926. begin
  1927. if SerialResult = integer(INVALID_HANDLE_VALUE) then
  1928. {$IFDEF MSWINDOWS}
  1929. result := GetLastError
  1930. {$ELSE}
  1931. {$IFNDEF FPC}
  1932. result := GetLastError
  1933. {$ELSE}
  1934. result := fpGetErrno
  1935. {$ENDIF}
  1936. {$ENDIF}
  1937. else
  1938. result := sOK;
  1939. FLastError := result;
  1940. FLastErrorDesc := GetErrorDesc(FLastError);
  1941. end;
  1942. procedure TBlockSerial.ExceptCheck;
  1943. var
  1944. e: ESynaSerError;
  1945. s: string;
  1946. begin
  1947. if FRaiseExcept and (FLastError <> sOK) then
  1948. begin
  1949. s := GetErrorDesc(FLastError);
  1950. e := ESynaSerError.CreateFmt('Communication error %d: %s', [FLastError, s]);
  1951. e.ErrorCode := FLastError;
  1952. e.ErrorMessage := s;
  1953. raise e;
  1954. end;
  1955. end;
  1956. procedure TBlockSerial.SetSynaError(ErrNumber: integer);
  1957. begin
  1958. FLastError := ErrNumber;
  1959. FLastErrorDesc := GetErrorDesc(FLastError);
  1960. end;
  1961. procedure TBlockSerial.RaiseSynaError(ErrNumber: integer);
  1962. begin
  1963. SetSynaError(ErrNumber);
  1964. ExceptCheck;
  1965. end;
  1966. procedure TBlockSerial.DoStatus(Reason: THookSerialReason; const Value: string);
  1967. begin
  1968. if assigned(OnStatus) then
  1969. OnStatus(Self, Reason, Value);
  1970. end;
  1971. {======================================================================}
  1972. class function TBlockSerial.GetErrorDesc(ErrorCode: integer): string;
  1973. begin
  1974. Result:= '';
  1975. case ErrorCode of
  1976. sOK: Result := 'OK';
  1977. ErrAlreadyOwned: Result := 'Port owned by other process';{HGJ}
  1978. ErrAlreadyInUse: Result := 'Instance already in use'; {HGJ}
  1979. ErrWrongParameter: Result := 'Wrong parameter at call'; {HGJ}
  1980. ErrPortNotOpen: Result := 'Instance not yet connected'; {HGJ}
  1981. ErrNoDeviceAnswer: Result := 'No device answer detected'; {HGJ}
  1982. ErrMaxBuffer: Result := 'Maximal buffer length exceeded';
  1983. ErrTimeout: Result := 'Timeout during operation';
  1984. ErrNotRead: Result := 'Reading of data failed';
  1985. ErrFrame: Result := 'Receive framing error';
  1986. ErrOverrun: Result := 'Receive Overrun Error';
  1987. ErrRxOver: Result := 'Receive Queue overflow';
  1988. ErrRxParity: Result := 'Receive Parity Error';
  1989. ErrTxFull: Result := 'Tranceive Queue is full';
  1990. end;
  1991. if Result = '' then
  1992. begin
  1993. Result := SysErrorMessage(ErrorCode);
  1994. end;
  1995. end;
  1996. {---------- cpom Comport Ownership Manager Routines -------------
  1997. by Hans-Georg Joepgen of Stuttgart, Germany.
  1998. Copyright (c) 2002, by Hans-Georg Joepgen
  1999. Stefan Krauss of Stuttgart, Germany, contributed literature and Internet
  2000. research results, invaluable advice and excellent answers to the Comport
  2001. Ownership Manager.
  2002. }
  2003. {$IFDEF UNIX}
  2004. function TBlockSerial.LockfileName: String;
  2005. var
  2006. s: string;
  2007. begin
  2008. s := SeparateRight(FDevice, '/dev/');
  2009. result := LockfileDirectory + '/LCK..' + s;
  2010. end;
  2011. procedure TBlockSerial.CreateLockfile(PidNr: integer);
  2012. var
  2013. f: TextFile;
  2014. s: string;
  2015. begin
  2016. // Create content for file
  2017. s := IntToStr(PidNr);
  2018. while length(s) < 10 do
  2019. s := ' ' + s;
  2020. // Create file
  2021. try
  2022. AssignFile(f, LockfileName);
  2023. try
  2024. Rewrite(f);
  2025. writeln(f, s);
  2026. finally
  2027. CloseFile(f);
  2028. end;
  2029. // Allow all users to enjoy the benefits of cpom
  2030. s := 'chmod a+rw ' + LockfileName;
  2031. {$IFNDEF FPC}
  2032. FileSetReadOnly( LockfileName, False ) ;
  2033. // Libc.system(pchar(s));
  2034. {$ELSE}
  2035. fpSystem(s);
  2036. {$ENDIF}
  2037. except
  2038. // not raise exception, if you not have write permission for lock.
  2039. on Exception do
  2040. ;
  2041. end;
  2042. end;
  2043. function TBlockSerial.ReadLockfile: integer;
  2044. {Returns PID from Lockfile. Lockfile must exist.}
  2045. var
  2046. f: TextFile;
  2047. s: string;
  2048. begin
  2049. AssignFile(f, LockfileName);
  2050. Reset(f);
  2051. try
  2052. readln(f, s);
  2053. finally
  2054. CloseFile(f);
  2055. end;
  2056. Result := StrToIntDef(s, -1)
  2057. end;
  2058. function TBlockSerial.cpomComportAccessible: boolean;
  2059. var
  2060. MyPid: integer;
  2061. Filename: string;
  2062. begin
  2063. Filename := LockfileName;
  2064. {$IFNDEF FPC}
  2065. MyPid := Libc.getpid;
  2066. {$ELSE}
  2067. MyPid := fpGetPid;
  2068. {$ENDIF}
  2069. // Make sure, the Lock Files Directory exists. We need it.
  2070. if not DirectoryExists(LockfileDirectory) then
  2071. CreateDir(LockfileDirectory);
  2072. // Check the Lockfile
  2073. if not FileExists (Filename) then
  2074. begin // comport is not locked. Lock it for us.
  2075. CreateLockfile(MyPid);
  2076. result := true;
  2077. exit; // done.
  2078. end;
  2079. // Is port owned by orphan? Then it's time for error recovery.
  2080. //FPC forgot to add getsid.. :-(
  2081. {$IFNDEF FPC}
  2082. if Libc.getsid(ReadLockfile) = -1 then
  2083. begin // Lockfile was left from former desaster
  2084. DeleteFile(Filename); // error recovery
  2085. CreateLockfile(MyPid);
  2086. result := true;
  2087. exit;
  2088. end;
  2089. {$ENDIF}
  2090. result := false // Sorry, port is owned by living PID and locked
  2091. end;
  2092. procedure TBlockSerial.cpomReleaseComport;
  2093. begin
  2094. DeleteFile(LockfileName);
  2095. end;
  2096. {$ENDIF}
  2097. {----------------------------------------------------------------}
  2098. {$IFDEF MSWINDOWS}
  2099. function GetSerialPortNames: string;
  2100. var
  2101. reg: TRegistry;
  2102. l, v: TStringList;
  2103. n: integer;
  2104. begin
  2105. l := TStringList.Create;
  2106. v := TStringList.Create;
  2107. reg := TRegistry.Create;
  2108. try
  2109. {$IFNDEF VER100}
  2110. {$IFNDEF VER120}
  2111. reg.Access := KEY_READ;
  2112. {$ENDIF}
  2113. {$ENDIF}
  2114. reg.RootKey := HKEY_LOCAL_MACHINE;
  2115. reg.OpenKey('\HARDWARE\DEVICEMAP\SERIALCOMM', false);
  2116. reg.GetValueNames(l);
  2117. for n := 0 to l.Count - 1 do
  2118. v.Add(reg.ReadString(l[n]));
  2119. Result := v.CommaText;
  2120. finally
  2121. reg.Free;
  2122. l.Free;
  2123. v.Free;
  2124. end;
  2125. end;
  2126. {$ENDIF}
  2127. {$IFNDEF MSWINDOWS}
  2128. function GetSerialPortNames: string;
  2129. var
  2130. sr : TSearchRec;
  2131. begin
  2132. Result := '';
  2133. if FindFirst('/dev/ttyS*', $FFFFFFFF, sr) = 0 then
  2134. repeat
  2135. if (sr.Attr and $FFFFFFFF) = Sr.Attr then
  2136. begin
  2137. if Result <> '' then
  2138. Result := Result + ',';
  2139. Result := Result + '/dev/' + sr.Name;
  2140. end;
  2141. until FindNext(sr) <> 0;
  2142. FindClose(sr);
  2143. if FindFirst('/dev/ttyUSB*', $FFFFFFFF, sr) = 0 then begin
  2144. repeat
  2145. if (sr.Attr and $FFFFFFFF) = Sr.Attr then begin
  2146. if Result <> '' then Result := Result + ',';
  2147. Result := Result + '/dev/' + sr.Name;
  2148. end;
  2149. until FindNext(sr) <> 0;
  2150. end;
  2151. FindClose(sr);
  2152. if FindFirst('/dev/ttyAM*', $FFFFFFFF, sr) = 0 then begin
  2153. repeat
  2154. if (sr.Attr and $FFFFFFFF) = Sr.Attr then begin
  2155. if Result <> '' then Result := Result + ',';
  2156. Result := Result + '/dev/' + sr.Name;
  2157. end;
  2158. until FindNext(sr) <> 0;
  2159. end;
  2160. FindClose(sr);
  2161. end;
  2162. {$ENDIF}
  2163. end.