synaser.pas 78 KB

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