synaser.pas 64 KB

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