From c2abde102926e1527f22cb366ab8f6b4afe80e65 Mon Sep 17 00:00:00 2001 From: Bill Bonney Date: Fri, 28 Jun 2013 10:36:28 -0700 Subject: [PATCH] adds missing files --- libs/serialport/apmserial.pri | 59 + .../doc/images/blockingmaster-example.png | Bin 0 -> 4656 bytes .../doc/images/blockingslave-example.png | Bin 0 -> 4784 bytes .../doc/images/cenumerator-example.png | Bin 0 -> 3532 bytes .../doc/images/enumerator-example.png | Bin 0 -> 18510 bytes .../doc/images/terminal-example.png | Bin 0 -> 10918 bytes libs/serialport/doc/qtserialport.qdocconf | 37 + libs/serialport/doc/src/index.qdoc | 65 + .../doc/src/qtserialport-module.qdoc | 46 + libs/serialport/doc/style/style.css | 137 ++ libs/serialport/qserialport.cpp | 1167 ++++++++++++++ libs/serialport/qserialport.h | 257 ++++ libs/serialport/qserialport_p.h | 89 ++ libs/serialport/qserialport_symbian.cpp | 645 ++++++++ libs/serialport/qserialport_symbian_p.h | 118 ++ libs/serialport/qserialport_unix.cpp | 1343 +++++++++++++++++ libs/serialport/qserialport_unix_p.h | 156 ++ libs/serialport/qserialport_win.cpp | 1091 +++++++++++++ libs/serialport/qserialport_win_p.h | 162 ++ libs/serialport/qserialport_wince.cpp | 509 +++++++ libs/serialport/qserialportglobal.h | 67 + libs/serialport/qserialportinfo.cpp | 259 ++++ libs/serialport/qserialportinfo.h | 98 ++ libs/serialport/qserialportinfo_mac.cpp | 272 ++++ libs/serialport/qserialportinfo_p.h | 85 ++ libs/serialport/qserialportinfo_symbian.cpp | 249 +++ libs/serialport/qserialportinfo_unix.cpp | 261 ++++ libs/serialport/qserialportinfo_win.cpp | 233 +++ libs/serialport/qserialportinfo_wince.cpp | 134 ++ libs/serialport/qt4support/install-helper.pri | 43 + libs/serialport/qt4support/qringbuffer_p.h | 451 ++++++ .../qt4support/qwineventnotifier_p.h | 94 ++ libs/serialport/qt4support/serialport.prf | 27 + libs/serialport/qttylocker_unix.cpp | 188 +++ libs/serialport/qttylocker_unix_p.h | 59 + libs/serialport/serialport-lib.pri | 87 ++ libs/serialport/serialport.pro | 17 + qgroundcontrol.pro | 41 +- src/comm/SerialLink.cc | 1102 ++++---------- src/comm/SerialLink.h | 55 +- src/ui/SerialConfigurationWindow.cc | 2 +- 41 files changed, 8874 insertions(+), 831 deletions(-) create mode 100644 libs/serialport/apmserial.pri create mode 100644 libs/serialport/doc/images/blockingmaster-example.png create mode 100644 libs/serialport/doc/images/blockingslave-example.png create mode 100644 libs/serialport/doc/images/cenumerator-example.png create mode 100644 libs/serialport/doc/images/enumerator-example.png create mode 100644 libs/serialport/doc/images/terminal-example.png create mode 100644 libs/serialport/doc/qtserialport.qdocconf create mode 100644 libs/serialport/doc/src/index.qdoc create mode 100644 libs/serialport/doc/src/qtserialport-module.qdoc create mode 100644 libs/serialport/doc/style/style.css create mode 100644 libs/serialport/qserialport.cpp create mode 100644 libs/serialport/qserialport.h create mode 100644 libs/serialport/qserialport_p.h create mode 100644 libs/serialport/qserialport_symbian.cpp create mode 100644 libs/serialport/qserialport_symbian_p.h create mode 100644 libs/serialport/qserialport_unix.cpp create mode 100644 libs/serialport/qserialport_unix_p.h create mode 100644 libs/serialport/qserialport_win.cpp create mode 100644 libs/serialport/qserialport_win_p.h create mode 100644 libs/serialport/qserialport_wince.cpp create mode 100644 libs/serialport/qserialportglobal.h create mode 100644 libs/serialport/qserialportinfo.cpp create mode 100644 libs/serialport/qserialportinfo.h create mode 100644 libs/serialport/qserialportinfo_mac.cpp create mode 100644 libs/serialport/qserialportinfo_p.h create mode 100644 libs/serialport/qserialportinfo_symbian.cpp create mode 100644 libs/serialport/qserialportinfo_unix.cpp create mode 100644 libs/serialport/qserialportinfo_win.cpp create mode 100644 libs/serialport/qserialportinfo_wince.cpp create mode 100644 libs/serialport/qt4support/install-helper.pri create mode 100644 libs/serialport/qt4support/qringbuffer_p.h create mode 100644 libs/serialport/qt4support/qwineventnotifier_p.h create mode 100644 libs/serialport/qt4support/serialport.prf create mode 100644 libs/serialport/qttylocker_unix.cpp create mode 100644 libs/serialport/qttylocker_unix_p.h create mode 100644 libs/serialport/serialport-lib.pri create mode 100644 libs/serialport/serialport.pro diff --git a/libs/serialport/apmserial.pri b/libs/serialport/apmserial.pri new file mode 100644 index 000000000..738c48294 --- /dev/null +++ b/libs/serialport/apmserial.pri @@ -0,0 +1,59 @@ + +INCLUDEPATH += $$PWD + +unix { + CONFIG += link_pkgconfig + + packagesExist(libudev) { + DEFINES += HAVE_LIBUDEV + PKGCONFIG += libudev + } +} + +PUBLIC_HEADERS += \ + $$PWD/qserialportglobal.h \ + $$PWD/qserialport.h \ + $$PWD/qserialportinfo.h + +PRIVATE_HEADERS += \ + $$PWD/qserialport_p.h \ + $$PWD/qserialportinfo_p.h + +SOURCES += \ + $$PWD/qserialport.cpp \ + $$PWD/qserialportinfo.cpp + +win32 { + PRIVATE_HEADERS += \ + $$PWD/qserialport_win_p.h + + SOURCES += \ + $$PWD/qserialport_win.cpp \ + $$PWD/qserialportinfo_win.cpp + + LIBS += -lsetupapi -ladvapi32 + +} + +unix:!symbian { + PRIVATE_HEADERS += \ + $$PWD/qttylocker_unix_p.h \ + $$PWD/qserialport_unix_p.h + + SOURCES += \ + $$PWD/qttylocker_unix.cpp \ + $$PWD/qserialport_unix.cpp \ + $$PWD/qserialportinfo_unix.cpp + + macx { + SOURCES += $$PWD/qserialportinfo_mac.cpp + + LIBS += -framework IOKit -framework CoreFoundation + } else { + linux*:contains( DEFINES, HAVE_LIBUDEV ) { + LIBS += -ludev + } + } +} + +HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS diff --git a/libs/serialport/doc/images/blockingmaster-example.png b/libs/serialport/doc/images/blockingmaster-example.png new file mode 100644 index 0000000000000000000000000000000000000000..44aa5ecfa894a78074ce7e89a331d0006be6d834 GIT binary patch literal 4656 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 P38en$v2y9d=YjtMeIisb literal 0 HcmV?d00001 diff --git a/libs/serialport/doc/images/cenumerator-example.png b/libs/serialport/doc/images/cenumerator-example.png new file mode 100644 index 0000000000000000000000000000000000000000..03792e71dbe3c6f291c4c6c0533f7e30286dccb6 GIT binary patch literal 3532 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 z zboJoxTGFO+7)X`{XNxpD_G|odNF9<7jm}kLm-v0RVD|jP=HX~V%`_Sw{RfRP!+>^6 z1a1(Yo zY{5pY1Z@{RMc=cfx~NDY2gNN(R@Nu^$9kGzEv~#^3E3zSu*sC_>+VT+nKUmSo`h#W z;KlL?9zC2XP0w&?f34e*yjBlmG>}f?cIKYPs2dVk8aGMFgdwkH{!Nklk0bqKtUOSK zhlb1zeKX*K0%LD{Buu=FK1(lF#-^DVHWU;wdj9TG0rd@-0?On`Kg^K?oPR6dpd12S zV0w}{D!U{ps;Y=zoXX29__hIQxX?@;j&>d{lNf=0b>^ozS$gGUk*2ksLZh?vaFn=T zWB^2tHNn6>1}0zr@9hc5z3z4BhBSB>{ueFXUYLIB&y^SQw74!QjpX?B!{kKm!Fa!? zGNkb7Nl3qw^Mo)e@{0=6kg4~>;023ahxoXUqT;n>UN|AdjFif5R5dKe&QEbNGUX2R zQ!^@ZhkCwzN(>`B{M@@?^`5`fnu*UlMPb9I+vp`^}SL^ zM~CH0pbC~3`q5An&h=`^d8L~v+mi?NW`eAB>+vxCyh9~JnkQmqE9ITKtANV&(Icq)v;w|+I^1|;?<#>%$2j`Lx75y9n5UL0s~$N*_yolCqu znt{&wLn(f-(c%qRshRS{&U*hR8Ql4>%|fG>v0$TrU!gpyitSvfI@rHfoQdDKVP-p- zlk!jYqby6{9pV?SS_K3WNd0e583nPdSYTrV^V(gBi?hB-{u6QBd)mx@cP;Azk*IKC zAoZ~{;~#4WN?$qL=rBnOacPU9^kz9NKE6A#;r{Wp*c=v9nX@`e>NjBkD;f)1w=u9> z6Ob(6w1&ffwd)LWds4-iT+oAOJD>|gP6Wz|9(da0mB6eFrP6EsjR+(Ii`_+n-qG-~ z^1S?+qNTgrg|X!H)eD1XcQ0z&rM~KA;r<{)k{}ueJ{U`z+#;yf-PJ2hlAnu_EiatP zv5_X4;6VGi$;>>}4sq<=v z;B6m(3L0J?PnLW{46Af0iQ8%ZN~|ihk&AGjoFqOZj!&!zfK z4j`p9ACE8=4!~m>j*Od@>~-qGI@lQbPz0iN0Nh0Fhz*fTj3d6fR(pv=kQT$NSh*yS z`btUC7_p6qg5s(isFw7jq;|%dUcPWD<|F9^eVrAr-LB^#93-CJ(>jp;* zcLsqx3-!O|M_^=Hz19z4_|+p08{Lt?QIn;YG8lbSqfi}fdcGss3w?xxLxZfCbFL2` zuXv}^8=2X`UQ@n=!JsTG&n_~L90>OBhd#2yu|f=u#f+f6!0{;CzCB8Q-FRb~FRXBQ zOw4?-U0IPh5YtZHg-vf+Tb@yzG-bNP)60TjiKiQ@VN22DGJ&}Q%eCZaTPvQN;P*x( z2!b7s<&;`Gg$6K$j=GjAf5tNtC#d4v(AIK77CsC@cXo@x^QpL_cWBT%lK@d#ph+~S z&6UpKgv^3yWY8*}-dKoG=pJ5{@qE5=wMvgj9}n#eLqU^vKK9E)W__z6ac#DQFY#}WP|Pn>~7Nz z-)+hKM7Oo7K-D597RMCb(<15ndb8uy&}OH+C_ycAnJ{6@rJlx}?EKS*c1uaCmo$X0 z7`|3&s7UZxNuwF7f8!kEz%^eLr?RAm;y}7Ntkgyyc^HW{2(*RQghxV8_r(!M>6bu_ zX&XQ94oqP{QK9#*Vv8!bE5Zumkw2H;*(A>EO)~`TC}#}VH;eUiCrtcqRveI(68pCT zgiPkOf^B|!_`;8#t#lc%lTw=BjZgJqpV8?!o11FzFq!DF8@vUsb@Gni?qGpQ`x^k^ z-&LlLHsrVkBqD*0Ja_KMEN4bt6r!LcQ zEO=oO3Tm>&QW|yuh=^j5?L|vabao*1Ugq<;~1RZS--Y zsZ`e^j1|n->C0^l(^{cGjXqEKzOer2vcv9sn;TaU{da+5phqqy&tqhZkGp@P+aDyT zksSd0Cq_!UMXr9CUq-gzmGlJrHGMym|K`m8IO1By^qorT*`~p9ncpGwBwejv z%1q*GK9mLe?JXv3In^geks(SHyQMBD$(gJheE#hSsPE$b1|c zAIdhEyJTQqz?*|7sk9J-q0)9*X(a+?Nq8_`o8JdbEte}n?dyCPi3t^#Z`7d6mFBah zU6Cn-4zf0qFP5lQ*Lg67y=pd*BbL)+&}50K7G*Au?}GG2-8Yxmf?gd{-qWPfIMBRnngR!=AoqtvXj^;@^vw~*kwuE!_-dItslbG@Bfj)7kcOdQo+~o=_Fevd ztRjMB`CZwRIVaG;XBI07m~J=uD6*P$AM8M;y&V@(=oDjQOz01^1qC%e(z-uo? zan%KS(9B^fS0dak8>C+rK1>oNZY<K7d`hbA zH8wawHmXnaexaXlvAX=&s`(vJsBCAl0aq4%+|bybp>46l{Os^xz{1erZpuv-u zfO9BD10J%rim5*XP7(xe&|q;!sTCbYI*}Rvp8LoFoIyqMLU;65`yIW=u=guxD<4Z= z`i9;f9g8gxtbvP2hFz&q*d>adM-8fbGvy_6*Xgcwm zzAXk4HO6Kko`~t;$Z16=l8w``OUF69E$G*@_+{LNM9P5cjimTy93A2Kp=tp5Di=eTjc-6`+O5 zXo+poF-K(J1WGG|k!D5-PnK`czbcamm-`BoYD33iDsPj7qKM$jK=$psu@0b^2_4H& zsz9Mbz}aSYv@HS`Mv9x3*ZeLoHLFI}T3%69wk9OeWu(xHzydZ*X-tDLM&S@P#AR@Y zEVPa`crdfhnkUgxyD zDd+W=OL)pkG4f*ybiI4w0b7)y!^!oh$~GQcTlXopo7I@b3Kf6A{!?bH2zXZ3%(yj! z$xqZ59wLMF(!!hCi4qEwkiDnqBZAqsey(6{AotVX=~{E$>-33+8NSPJ*Zn!Et7D4V z2rgGUBYXmE!ASKsP#`SyeAum;q0$DdaKY{E0r_G+j|=kOC<3BJtJ4A{Kimy11!$Df zePI7)-d9lY7CGHuF0zWp^KqRImsFC^lI}wOT^Pe1kz+|S4!|XrYl+Hg9w9}{OKu~UKkhTTIIBnQ1otV6Is)YKC`YmRL@((|4O3iA zlC}%4*S4&0L=U8~hV5y6bFc_KM)=<$i?SSkq+hi!UGK-tkR9@uvx}ryv=|m&X4M>!=v5PEn z4tEk}my_K58-liokmR9$2-P60cvtCX%P+L_nR|=;z`1aSbB77JjXExrWf_2MURd;2 z(ouPQ+hE!sv@s-embzfH4Wg0Oo7#0_kD)ay~oTf7zf0*5F*(97JQ?DOc*>09M-3_{0x)Mo#Kk!l;Qn^v9uN$=XR1wM;Z)N4F^4 zV6j=BW-TsJGE{chm{p|<+z;D z8D4;&P!0U2@}U8f0K5)$%9^|%)5Uf=128&V;(!>_m~2MaI5BSkz8vq5wD@3e?dwr@-lJ^tv zlp^F6OHn`sldy}OAbFfqm>eVD@vvCWe8Av$#15y5f795ECB=u{cq2rBxR)Tqvpz&( zMs>)F8~L(5oKRkXRH_7qAU3f73w+GLs#$M<5&$9&<#}{4;B_B!MF4d7R@vy$lTmJT zq1E=e1k$FlEuG5wep<3oM(k-#>+n+|%JO#@l#f{8^?*}p0QUqfM3~E*TUlPxk}_h} zXaRhtI0DB2Cr%3kPFWAY=z{vV=xkOeZ168m6rp-*_wyg`w7xqq`gPcPwaezK;CvV< zJ(BOO*(WqQmOq9^`iUeiH8z~} zI%HM5tIrkj5iXp7Hm22vfle0&2Wco!{&+sDYm)=Y4Ez-agiph%!wTwZNI;Yh*ww{- zBwLqmZ8cdHloBuO_A;r{WUKH*`CWPiqM#u|!~p0J zvYn3hVDX~pe2>^XM5aX?LJ>ke67!oa1jTu%&X8M&_X0xPb0TE zfnU>Ln7CMRFNt8|Ys#Nt@1dU9^JA@0t8Nfv*<;>WNdlC~cN+iR6X(}SgJPDcB?pfj z5cpn|-1FJneC*)%T$<6xXNtG`KlWqTz59M67%OoiP1(u=of57;?dx6}UJzvv6sU5G zST>Lyjj7dQn}(7|btO|zrJ&qU|1%V^K$kfvHLz-?bgcqc;sw?IQwn=Gc9)x!AlTu; z*wln}%NBa$==q75QN;i%o`jyBsXFbn3zXKNDYN~8ZeXgmPNue} zg)*~O&AZbvK%SrJ_x8QT;A8>mY!I}uuO+r~k%r!W4)KD^70_7d#egDI$dGj^KWYM^ zhXX((;n-3Qw*5D~O>455d#za=FJoH|2?J}~_45rn!5 z+!3VMC<$dbCU7N|eJ0_|Q7V#<=4+hK&tWtbgmyI8V|mMEs9w*Bl9j=33v>xfXEi9? z7axtW=HhGi9J_8nfWNff4rGact#P4gGOZGzt(Xz_&=)}mOb9J+{6{E`Ai`9B(N3}% zH6GM3;QgLP`(dQg^%1UAeEQB^(y^b1jL-$M!EYK5f;`Zaq10>yKXKtu07Yn6x0D9T z!@s)A(wg%@*ARLM5(ZoU9&s$5(Epn$_@g7?I}Ox*5g}*-z%i~7=zL$lL2qS)5_kAH zdWi7dU81cL!WzxLGd%{nc3^Y@)Q1C6A$#d+-y!sH?zGV8t#bl7C&(wWj|BM=XYmw; zaTy8;@`?R=_6v*!;cRY-Vgu58ImwsJKCPuE`1}PGjfdZbQzHVrW<#_ahT^4vGSJ@B zbA8^(J`(+OT^j44S#o8ipmuLZewPD&;b4FU6wm0uU{J|QMZgJgx8W7n*=_sT;6PwH zoOEdqdCq1kJ?hK8;!yXKbn`CEPn3M%sadeqB`LcwRlqlQ6$F~bBD_&W_rWK2fA z4@t`sFh|f+h~}WJrFHlj4|WxLYzFste~tV}G0n=|+eyOYT

G3(^%Oz|&=_ijAPE?j>k-tOvhe~W;v@N3pRP5d#z#-)~uX)JavDY6bkof&xY(g{7a0ON$q67o|i*O-Ys>5M(JheV= zogUord~18+&(q?qQdAa+W&e5$JxE~tVLET7mFj6>$QOzK0@halFR&(BP2)5XHEX;8 zOvOx|i8#G2coYLs@nETN>+?Y6IE*k;xlx0^g6h{vxFUhUn@?v>rbRE1n{`Ec%UV32 z)j0%I-tf@#{Qd_Qn`s2pVZN{ZIGNNl7hW1xfIU$U#<2Vc_lwDW72-#fBvGjAi@@bb z13)ZMky7LMmS|Ux3-p+5)2ksrjT%2?{LD}qzqT5s48w~6^$RNZ2jZ+E(hbCbmged) z0vpJi?Ly{cD0~%^Q>k$;abe9y4tDqVkMM%Jf}vSc4)lA@aWBUm2384rltl*SK}#{R zRJh{fZumM*uPE6b1Xw&;hb2gIJqmAfPM)bJIOq-+L9w=kE^GhLkh1CfqBDWOsJ?LU zb>Cy#WA^ly0r_z4cF%E_V|*y_CGOd8qs0Btl+ZQSBx*Whg+%5R?I0Bpvw+xM?m1pn zpp*=yxY&v|P`OrrBv^{^O9P)>8P4!@OBx)JWY`qYh^FhY!rb@{I}Y*FQ_MJd=b6by zGX)({GVia1n%isGO-~}D2>;;r!C$QD=uJKF)Rm>f{=>Qd$xnOIL5tn<09S?o7dF@4 z5R`5XN%Q%^C3$WWkd)KJ=vNn1KU|ma2_XvOJx9UI@Sy3;{8s0qL01($$iMs#!21D+ zw26D^2GG&~bVBJXSLY3WA(5aRMfU~{0-k!#GpcDwM!kTz~4Oo8TSsR`K#P@O4hQ>Q0@D2X|pB{X3ikqGR7-pgUjx@~6Bs+M#(mWjuY zpSzRHg7yn1A&rF-kGB7e34hS4nMiTxm|Xskio^7x1BOtdZgH8ZiAu*03u8VbLun`0 zekAZ!xd7?-_JjRwlZS}P7H$2KyN3NCAz$uvCiiIWkJDb^igt~(sgS|mjAF#>EA16iC z;lplxCZ%s*)X5kJE$x}?t;b94^*!6Cm*6Qx;lDcLeEyK`uP8qa57c13r9~{6D^Jo) z7XQMFhg*>HV+`6VL!86-Xma1w=e?i5U{+YO`==QSgpb!>SWTZT=oqODK%p01#fdgxQ`xg^rGqG<~*-xfxFh52@1I$FIrw z#1v8&y6OP6?Hm0B8kTJg0%=#t**{%2gV3|Q=9B~?Ngt1CTo`9u4nZF zXU>Yal<~PUrF#%IB(Jb~ZIS1%2K(iQ&ufTK^Y!elj6*Q}zKASi!G0l4RQuy9?9P$x zc4bV?83GG;AaUPmhn5Cxj0NSK-Q3o6G}bYXL8~D=xGwKz^~%(dpUwYsWjzO!Zxlute_137tn}EvW!8qB1{Zq8Y&=;k_%Tym!$Qp}lP@4ED&pGK>Ka>H# zXEy#4GhonH7+;>i`*V)@5AV@&!~|N#X6!gnW`zA^G-dj^;xEAncv*Fzxty3PtB471 z|MQ1WelVa;jGU!8ZlWgf@@ATs8zBBFOcPG0&I)bP=P8?02-+xXXQD$}Qti7iv9Nq~ z{3-p$?3y@8)aoMfKRle#y|9)t(ljZw{()BoRt3Xq*DV10kl20|WdtcG0Z`CD7>yb> z>PRgG%8&{RSVNbtxD=hsk*FpkAye_=j2*0>F9z%zz#wctu!!>#wPFL#5nF4TM7Brf z#x~B#@;ZDoiHm1H@dNX~VWlZ&Hu(a8s3% z5XGj#(a(f1Ihsbm^mUUCr8dMWLufq*xL8jHkzp8P+_A#H;lH3@qjkC>P#X#3b={D} zw9<5_9rn3x(A@TIB=`i%Ji^f|`;OsV@#E(fga~z4rCFVxtg-~#2O2;2LT)JYlBC$X zkE)u&tN?u|qmPnf#zmkKRgXl2zOrRH`Y}fBHz+7V_0E((h3@2kJfmUZ*h-Vk6j$_@ zg2o4l=vpJHcq0%z^Z~c8(Oez8qDy*Uh$+p#be;n4ejX=J_Qu$k9`amFdDx~_{4`JA znYF8c?V9)KX@vjZNo73n5B~H<7ozp+M)?0Wc?1ev+i)D&KyXZ zDj17iJ`T{TArR*>1%z|j_2c?upv_Q_Y^(JI@r9A6;0^Xn~lz}IZ6%oKbm z3pP`iVrX#vE(oWrs3fW~^Sy`;8?nwHHzi?sq_pBa;&N{~`o}`uM9gYqBQ_ z?~&ukW{&Nyl%_x&5wNe!gBIcrdU}_ze3`?5`NsUUh%uG#n#+Wr>PLt&N?yZ`&!YH- z<=&YG33OVdyx?5=om$Jqpks^LtNlJcnVfR3WV0*r^@P|$-V@xQIvMh35kZ4tWmh_y zn0XN;zlniI>UZ?OR*FHx+~px$C_LGp(7B$*w zScFp=BVy$(W+MAHzpJW`FsxEp}h4W*$uky(821-&R`QAmp9}M}!gi5Ry zRq%1pA)$niGcg>lMav{Y-%RHZhs(Gqb~ z50Nxug^ojraq5#m`oV=1b86p?;IupMmrV$$lj1vf8n1K{Vq6M-hmgy%%l@A-&O92* zHICzqu``xTWUONuJ7tihG-J&bH-@4jd%UD<*=J}hb45ncwUaFO$}&RNGL|G$l3P>u z?#PnL7Gh?k?mOLcI>))^{qOtF`#$ISJC_V{RenSH6Y-{sd_r3u38-pmr9n|+*tUfyWa8d+B~)K zBQ45X#J&)vLfWuqEUu}XDU&P5P9LP^J}*WdP}Jzzera-Tk^RmCkw7wgP1ytz2=L;F z>|%Rk7x%kb)}#yZfD8y<9WYz5o4^8TjujqIaySu6^g7MIQ^K$)1xJ_X`>8T@Oq#SD z!Qr(%bf@^b?DWi{1@3J5-S#XE^2~^VQvX%n0f0%(I#;A_)!=Te*85%B^Q#1P^q68& zh5iGvm~3MtSc(93B%PDUkq+<|n=2&Asb+s{JY74*x~s!NpskYp!xgDSTR@fF?_~Lw zyuE>M@SVl@)&;9faaB;qiP9%w0s@SK$2;MtCvc)?vpConP(paptpitPu>6xa!$#qp z_l@SrWwa*EU%HV}RFCO7MNtle?@yl=lvmC~uBKUOX^55Iw1oP#hg23Fxk7i!NXFkd zGm%_^RV`Haz3O%m)u1i0CIl`x>j_)t1CCdzsL-rj1;nK(XcrjS%C@OOMXbT85^>4y zefghso08dir7AdG0XdFV&h2Wy$T@NPv_$=H$R5JE?%C} zRei=Z7SRhG-Ewg9UtBHCPbr%hK*5OoygjdeE9?+5i#eZxcM}-1YaQC`2lM;j8ZFnc z+XQ)E@ftZ&+$K*>vJbTAy8U&~sSYp&b$C%3K4&apG3O%HAa6LU1nrMC5F4DEc@q}GfvYKX z3e75OKA=~tFTVjpvE_FW;t`>1#{RNR{ss=ev1ZQjG-rKQhh_n1pdfH9~ zKBc(Lgi{otd3AqS_DDlPg-Z7*(Uw@eB4A6g3#sHJShf_F;G)8|Y#zK-{wM7C5gx7G z>V5=s*j~|m#YjgRFxwk5AxRETA#rkh#ki^9Gp=J%;5Gw3kVK3H`MIOTJ?;!WG>4hKW&w z|Mhs=%hYOq@xxn94Wi%s{IHH3*e0Q#6l&;x}V!!F( z$R-cdvn$K8hyHom+dbO65jsWdj%i~=h5j0K;q!Fz%c4HYRKsqTe=+ z^i(k-Nn0n$!qq8CG1X?ERKi^8sO(W3#hBQ( z?+Iay#B3THs*`h`jR@dM%ak=8GzuIkJKOBfyENV$05*5=x)9Ak1P!pNPzWajC4@zU zqcN6xsib{1e%4ooN&spknNg_j>l^n`WYGqmoXhlx^vj!=N>m?f!Gto?#|ABmqTu(UveP^Z6&z{3xdG2jXnHO%2o z6H|nVicWXNY!ny;@7Hxl`wV2-<*FwzXVUJ}${qCXZ@l;@6(%pF@Y1tv{mFN2F;$2p xwp6Sb{uk5Zc6R^( literal 0 HcmV?d00001 diff --git a/libs/serialport/doc/images/terminal-example.png b/libs/serialport/doc/images/terminal-example.png new file mode 100644 index 0000000000000000000000000000000000000000..28aa366709c8e54968f54a75d4c3569e98c0cdc5 GIT binary patch literal 10918 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 +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page qtserialport-index.html + \title Qt Serial Port + \brief Provides API to make the serial programming simple and portable. + + Qt Serial Port provides the basic functionality, which includes + configuring, I/O operations, get and set control signals of the + RS-232 pinouts. + + The following are not supported by this module: + \list + \li Terminal features such as echo, control CR/LF, and so on. + \li Text mode. + \li Configuring timeouts and delays while reading. + \li Tracking and notification when the state of RS-232 pinout signals change. + \endlist + + To use these classes in your application, use the following include + statement: + + \code + #include + \endcode + + To link against the module, add this line to your \l qmake \c + .pro file: + + \code + QT += serialport + \endcode + + \section1 Related information + \list + \li \l{Qt Serial Port C++ Classes}{C++ Classes} + \li \l{Qt Serial Port Examples}{Examples} + \endlist + +*/ diff --git a/libs/serialport/doc/src/qtserialport-module.qdoc b/libs/serialport/doc/src/qtserialport-module.qdoc new file mode 100644 index 000000000..b0fc506e5 --- /dev/null +++ b/libs/serialport/doc/src/qtserialport-module.qdoc @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2011 - 2012 Denis Shienkov +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \module QtSerialPort + \title Qt Serial Port C++ Classes + \brief List of C++ classes that enable access to a serial port. + + To use these classes in your application, use the following include + statement: + + \code + #include + \endcode + + To link against the module, add this line to your \l qmake \c + .pro file: + + \code + QT += serialport + \endcode +*/ diff --git a/libs/serialport/doc/style/style.css b/libs/serialport/doc/style/style.css new file mode 100644 index 000000000..df84049f1 --- /dev/null +++ b/libs/serialport/doc/style/style.css @@ -0,0 +1,137 @@ +a:link, a:visited { + color: #00732F; + text-decoration: none; + font-weight: bold; +} + +body { + font: normal 400 14px/1.2 Arial; + margin-top: 85px; +} + +h1 { + margin: 0; +} + +h2 { + font: 500 20px/1.2 Arial; +} + +h3.fn, span.fn { + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + background-color: #F6F6F6; + border-width: 1px; + border-style: solid; + border-color: #E6E6E6; + word-spacing: 3px; + padding: 3px 5px; +} + +table, pre { + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + background-color: #F6F6F6; + border: 1px solid #E6E6E6; + border-collapse: separate; + font-size: 12px; + line-height: 1.2; + margin-bottom: 25px; + margin-left: 15px; +} + +table td { + padding: 3px 15px 3px 20px; +} + +table tr.even { + background-color: white; + color: #66666E; +} + +table tr.odd { + background-color: #F6F6F6; + color: #66666E; +} + +li { + margin-bottom: 10px; + padding-left: 12px; +} + +.cpp { + display: block; + margin: 10; + overflow: hidden; + overflow-x: hidden; + overflow-y: hidden; + padding: 20px 0 20px 0; +} + +.footer { + margin-top: 50px; +} + +.memItemLeft { + padding-right: 3px; +} + +.memItemRight { + padding: 3px 15px 3px 0; +} + +.qml { + display: block; + margin: 10; + overflow: hidden; + overflow-x: hidden; + overflow-y: hidden; + padding: 20px 0 20px 0; +} + +.qmldefault { + padding-left: 5px; + float: right; + color: red; +} + +.qmlreadonly { + padding-left: 5px; + float: right; + color: #254117; +} + +.rightAlign { + padding: 3px 5px 3px 10px; + text-align: right; +} + +.title { + background-color: white; + color: #44A51C; + font-family: Verdana; + font-size: 35px; + font-weight: normal; + left: 0; + padding-bottom: 5px; + padding-left: 16px; + padding-top: 20px; + position: absolute; + right: 0; + top: 0; +} + +.toc { + float: right; + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + background-color: #F6F6F6; + border: 1px solid #DDD; + margin: 0 20px 10px 10px; + padding: 20px 15px 20px 20px; + height: auto; + width: 200px; +} diff --git a/libs/serialport/qserialport.cpp b/libs/serialport/qserialport.cpp new file mode 100644 index 000000000..96f06d4bd --- /dev/null +++ b/libs/serialport/qserialport.cpp @@ -0,0 +1,1167 @@ +/**************************************************************************** +** +** Copyright (C) 2011-2012 Denis Shienkov +** Copyright (C) 2011 Sergey Belyashov +** Copyright (C) 2012 Laszlo Papp +** Copyright (C) 2012 Andre Hartmann +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialport.h" +#include "qserialportinfo.h" + +#ifdef Q_OS_WIN +#include "qserialport_win_p.h" +#elif defined (Q_OS_SYMBIAN) +#include "qserialport_symbian_p.h" +#elif defined (Q_OS_UNIX) +#include "qserialport_unix_p.h" +#else +#error Unsupported OS +#endif + +#ifndef SERIALPORT_BUFFERSIZE +# define SERIALPORT_BUFFERSIZE 16384 +#endif + +QT_BEGIN_NAMESPACE + +QSerialPortPrivateData::QSerialPortPrivateData(QSerialPort *q) + : readBufferMaxSize(0) + , readBuffer(SERIALPORT_BUFFERSIZE) + , writeBuffer(SERIALPORT_BUFFERSIZE) + , error(QSerialPort::NoError) + , inputBaudRate(0) + , outputBaudRate(0) + , dataBits(QSerialPort::UnknownDataBits) + , parity(QSerialPort::UnknownParity) + , stopBits(QSerialPort::UnknownStopBits) + , flow(QSerialPort::UnknownFlowControl) + , policy(QSerialPort::IgnorePolicy) + , settingsRestoredOnClose(true) + , q_ptr(q) +{ +} + +int QSerialPortPrivateData::timeoutValue(int msecs, int elapsed) +{ + if (msecs == -1) + return msecs; + msecs -= elapsed; + return qMax(msecs, 0); +} + +/*! + \class QSerialPort + + \brief Provides functions to access serial ports. + + \reentrant + \ingroup serialport-main + \inmodule QtSerialPort + \since 5.1 + + You can get information about the available serial ports using the + QSerialPortInfo helper class, which allows an enumeration of all the serial + ports in the system. This is useful to obtain the correct name of the + serial port you want to use. You can pass an object + of the helper class as an argument to the setPort() or setPortName() + methods to assign the desired serial device. + + After setting the port, you can open it in read-only (r/o), write-only + (w/o), or read-write (r/w) mode using the open() method. + + \note The serial port is always opened with exclusive access + (that is, no other process or thread can access an already opened serial port). + + Having successfully opened, QSerialPort tries to determine the current + configuration of the port and initializes itself. You can reconfigure the + port to the desired setting using the setBaudRate(), setDataBits(), + setParity(), setStopBits(), and setFlowControl() methods. + + The status of the control pinout signals is determined with the + isDataTerminalReady(), isRequestToSend, and pinoutSignals() methods. To + change the control line status, use the setDataTerminalReady(), and + setRequestToSend() methods. + + Once you know that the ports are ready to read or write, you can + use the read() or write() methods. Alternatively the + readLine() and readAll() convenience methods can also be invoked. + If not all the data is read at once, the remaining data will + be available for later as new incoming data is appended to the + QSerialPort's internal read buffer. You can limit the size of the read + buffer using setReadBufferSize(). + + Use the close() method to close the port and cancel the I/O operations. + + See the following example: + + \code + int numRead = 0, numReadTotal = 0; + char buffer[50]; + + forever { + numRead = serial.read(buffer, 50); + + // Do whatever with the array + + numReadTotal += numRead; + if (numRead == 0 && !serial.waitForReadyRead()) + break; + } + \endcode + + If \l{QIODevice::}{waitForReadyRead()} returns false, the + connection has been closed or an error has occurred. + + Programming with a blocking serial port is radically different from + programming with a non-blocking serial port. A blocking serial port + does not require an event loop and typically leads to simpler code. + However, in a GUI application, blocking serial port should only be + used in non-GUI threads, to avoid freezing the user interface. + + For more details about these approaches, refer to the + \l {Examples}{example} applications. + + The QSerialPort class can also be used with QTextStream and QDataStream's + stream operators (operator<<() and operator>>()). There is one issue to be + aware of, though: make sure that enough data is available before attempting + to read by using the operator>>() overloaded operator. + + \sa QSerialPortInfo +*/ + +/*! + \enum QSerialPort::Direction + + This enum describes the possible directions of the data transmission. + + \note This enumeration is used for setting the baud rate of the device + separately for each direction on some operating systems (for example, + POSIX-like). + + \value Input Input direction. + \value Output Output direction. + \value AllDirections Simultaneously in two directions. +*/ + +/*! + \enum QSerialPort::BaudRate + + This enum describes the baud rate which the communication device operates + with. Note: only the most common standard baud rates are listed in this + enum. + + \value Baud1200 1200 baud. + \value Baud2400 2400 baud. + \value Baud4800 4800 baud. + \value Baud9600 9600 baud. + \value Baud19200 19200 baud. + \value Baud38400 38400 baud. + \value Baud57600 57600 baud. + \value Baud115200 115200 baud. + \value UnknownBaud Unknown baud. + + \sa QSerialPort::baudRate +*/ + +/*! + \enum QSerialPort::DataBits + + This enum describes the number of data bits used. + + \value Data5 Five bits. + \value Data6 Six bits. + \value Data7 Seven bits + \value Data8 Eight bits. + \value UnknownDataBits Unknown number of bits. + + \sa QSerialPort::dataBits +*/ + +/*! + \enum QSerialPort::Parity + + This enum describes the parity scheme used. + + \value NoParity No parity. + \value EvenParity Even parity. + \value OddParity Odd parity. + \value SpaceParity Space parity. + \value MarkParity Mark parity. + \value UnknownParity Unknown parity. + + \sa QSerialPort::parity +*/ + +/*! + \enum QSerialPort::StopBits + + This enum describes the number of stop bits used. + + \value OneStop 1 stop bit. + \value OneAndHalfStop 1.5 stop bits. + \value TwoStop 2 stop bits. + \value UnknownStopBits Unknown number of stop bit. + + \sa QSerialPort::stopBits +*/ + +/*! + \enum QSerialPort::FlowControl + + This enum describes the flow control used. + + \value NoFlowControl No flow control. + \value HardwareControl Hardware flow control (RTS/CTS). + \value SoftwareControl Software flow control (XON/XOFF). + \value UnknownFlowControl Unknown flow control. + + \sa QSerialPort::flowControl +*/ + +/*! + \enum QSerialPort::PinoutSignal + + This enum describes the possible RS-232 pinout signals. + + \value NoSignal No line active + \value TransmittedDataSignal TxD (Transmitted Data). + \value ReceivedDataSignal RxD (Received Data). + \value DataTerminalReadySignal DTR (Data Terminal Ready). + \value DataCarrierDetectSignal DCD (Data Carrier Detect). + \value DataSetReadySignal DSR (Data Set Ready). + \value RingIndicatorSignal RNG (Ring Indicator). + \value RequestToSendSignal RTS (Request To Send). + \value ClearToSendSignal CTS (Clear To Send). + \value SecondaryTransmittedDataSignal STD (Secondary Transmitted Data). + \value SecondaryReceivedDataSignal SRD (Secondary Received Data). + + \sa pinoutSignals(), QSerialPort::dataTerminalReady, + QSerialPort::requestToSend +*/ + +/*! + \enum QSerialPort::DataErrorPolicy + + This enum describes the policies for the received symbols + while parity errors were detected. + + \value SkipPolicy Skips the bad character. + \value PassZeroPolicy Replaces bad character to zero. + \value IgnorePolicy Ignores the error for a bad character. + \value StopReceivingPolicy Stops data reception on error. + \value UnknownPolicy Unknown policy. + + \sa QSerialPort::dataErrorPolicy +*/ + +/*! + \enum QSerialPort::SerialPortError + + This enum describes the errors that may be contained by the + QSerialPort::error property. + + \value NoError No error occurred. + \value DeviceNotFoundError An error occurred while attempting to + open an non-existing device. + \value PermissionError An error occurred while attempting to + open an already opened device by another process or a user not + having enough permission and credentials to open. + \value OpenError An error occurred while attempting to + open an already opened device in this object. + \value ParityError Parity error detected by the hardware while reading data. + \value FramingError Framing error detected by the hardware while reading data. + \value BreakConditionError Break condition detected by the hardware on + the input line. + \value WriteError An I/O error occurred while writing the data. + \value ReadError An I/O error occurred while reading the data. + \value ResourceError An I/O error occurred when a resource becomes unavailable, + e.g. when the device is unexpectedly removed from the system. + \value UnsupportedOperationError The requested device operation is + not supported or prohibited by the running operating system. + \value UnknownError An unidentified error occurred. + + \sa QSerialPort::error +*/ + + + +/*! + Constructs a new serial port object with the given \a parent. +*/ +QSerialPort::QSerialPort(QObject *parent) + : QIODevice(parent) + , d_ptr(new QSerialPortPrivate(this)) +{} + +/*! + Constructs a new serial port object with the given \a parent + to represent the serial port with the specified \a name. + + The name should have a specific format; see the setPort() method. +*/ +QSerialPort::QSerialPort(const QString &name, QObject *parent) + : QIODevice(parent) + , d_ptr(new QSerialPortPrivate(this)) +{ + setPortName(name); +} + +/*! + Constructs a new serial port object with the given \a parent + to represent the serial port with the specified helper class + \a serialPortInfo. +*/ +QSerialPort::QSerialPort(const QSerialPortInfo &serialPortInfo, QObject *parent) + : QIODevice(parent) + , d_ptr(new QSerialPortPrivate(this)) +{ + setPort(serialPortInfo); +} + +/*! + Closes the serial port, if necessary, and then destroys object. +*/ +QSerialPort::~QSerialPort() +{ + /**/ + close(); + delete d_ptr; +} + +/*! + Sets the \a name of the serial port. + + The name of the serial port can be passed on as either a short name or + the long system location if necessary. + + \sa portName(), QSerialPortInfo +*/ +void QSerialPort::setPortName(const QString &name) +{ + Q_D(QSerialPort); + d->systemLocation = QSerialPortPrivate::portNameToSystemLocation(name); +} + +/*! + Sets the port stored in the serial port info instance \a serialPortInfo. + + \sa portName(), QSerialPortInfo +*/ +void QSerialPort::setPort(const QSerialPortInfo &serialPortInfo) +{ + Q_D(QSerialPort); + d->systemLocation = QSerialPortPrivate::portNameToSystemLocation(serialPortInfo.systemLocation()); +} + +/*! + Returns the name set by setPort() or to the QSerialPort constructors. + This name is short, i.e. it extract and convert out from the internal + variable system location of the device. Conversion algorithm is + platform specific: + \table + \header + \li Platform + \li Brief Description + \row + \li Windows + \li Removes the prefix "\\\\.\\" from the system location + and returns the remainder of the string. + \row + \li Windows CE + \li Removes the postfix ":" from the system location + and returns the remainder of the string. + \row + \li Symbian + \li Returns the system location as it is, + as it is equivalent to the port name. + \row + \li GNU/Linux + \li Removes the prefix "/dev/" from the system location + and returns the remainder of the string. + \row + \li Mac OSX + \li Removes the prefix "/dev/cu." and "/dev/tty." from the + system location and returns the remainder of the string. + \row + \li Other *nix + \li The same as for GNU/Linux. + \endtable + + \sa setPort(), QSerialPortInfo::portName() +*/ +QString QSerialPort::portName() const +{ + Q_D(const QSerialPort); + return QSerialPortPrivate::portNameFromSystemLocation(d->systemLocation); +} + +/*! + \reimp + + Opens the serial port using OpenMode \a mode, and then returns true if + successful; otherwise returns false with and sets an error code which can be + obtained by calling the error() method. + + \warning The \a mode has to be QIODevice::ReadOnly, QIODevice::WriteOnly, + or QIODevice::ReadWrite. Other modes are unsupported. + + \sa QIODevice::OpenMode, setPort() +*/ +bool QSerialPort::open(OpenMode mode) +{ + Q_D(QSerialPort); + + if (isOpen()) { + setError(QSerialPort::OpenError); + return false; + } + + // Define while not supported modes. + static const OpenMode unsupportedModes = Append | Truncate | Text | Unbuffered; + if ((mode & unsupportedModes) || mode == NotOpen) { + setError(QSerialPort::UnsupportedOperationError); + return false; + } + + clearError(); + if (d->open(mode)) { + QIODevice::open(mode); + + d->dataTerminalReady = isDataTerminalReady(); + d->requestToSend = isRequestToSend(); + + return true; + } + return false; +} + +/*! + \reimp + + \sa QIODevice::close() +*/ +void QSerialPort::close() +{ + Q_D(QSerialPort); + if (!isOpen()) { + return; + } + + QIODevice::close(); + d->close(); +} + +/*! + \property QSerialPort::settingsRestoredOnClose + \brief the flag which allows to restore the previous settings while closing + the serial port. + + If this flag is true, the settings will be restored; otherwise not. + The default state of the QSerialPort class is configured to restore the + settings. +*/ +void QSerialPort::setSettingsRestoredOnClose(bool restore) +{ + Q_D(QSerialPort); + + if (d->settingsRestoredOnClose != restore) { + d->settingsRestoredOnClose = restore; + emit settingsRestoredOnCloseChanged(d->settingsRestoredOnClose); + } +} + +bool QSerialPort::settingsRestoredOnClose() const +{ + Q_D(const QSerialPort); + return d->settingsRestoredOnClose; +} + +/*! + \fn void QSerialPort::settingsRestoredOnCloseChanged(bool restore) + + This signal is emitted after the flag which allows to restore the + previous settings while closing the serial port has been changed. The new + flag which allows to restore the previous settings while closing the serial + port is passed as \a restore. + + \sa QSerialPort::settingsRestoredOnClose +*/ + +/*! + \property QSerialPort::baudRate + \brief the data baud rate for the desired direction + + If the setting is successful, returns true; otherwise returns false and sets + an error code which can be obtained by accessing the value of the + QSerialPort::error property. To set the baud rate, use the enumeration + QSerialPort::BaudRate or any positive qint32 value. + + \warning Only the AllDirections flag is support for setting this property on + Windows, Windows CE, and Symbian. + + \warning Returns equal baud rate in any direction on Windows, Windows CE, and + Symbian. +*/ +bool QSerialPort::setBaudRate(qint32 baudRate, Directions dir) +{ + Q_D(QSerialPort); + + if (d->setBaudRate(baudRate, dir)) { + if (dir & QSerialPort::Input) { + if (d->inputBaudRate != baudRate) + d->inputBaudRate = baudRate; + else + dir &= ~QSerialPort::Input; + } + + if (dir & QSerialPort::Output) { + if (d->outputBaudRate != baudRate) + d->outputBaudRate = baudRate; + else + dir &= ~QSerialPort::Output; + } + + if (dir) + emit baudRateChanged(baudRate, dir); + + return true; + } + + return false; +} + +qint32 QSerialPort::baudRate(Directions dir) const +{ + Q_D(const QSerialPort); + if (dir == QSerialPort::AllDirections) + return d->inputBaudRate == d->outputBaudRate ? + d->inputBaudRate : QSerialPort::UnknownBaud; + return dir & QSerialPort::Input ? d->inputBaudRate : d->outputBaudRate; +} + +/*! + \fn void QSerialPort::baudRateChanged(qint32 baudRate, Directions dir) + + This signal is emitted after the baud rate has been changed. The new baud + rate is passed as \a baudRate and directions as \a dir. + + \sa QSerialPort::baudRate +*/ + +/*! + \property QSerialPort::dataBits + \brief the data bits in a frame + + If the setting is successful, returns true; otherwise returns false and sets + an error code which can be obtained by accessing the value of the + QSerialPort::error property. +*/ +bool QSerialPort::setDataBits(DataBits dataBits) +{ + Q_D(QSerialPort); + + if (d->setDataBits(dataBits)) { + if (d->dataBits != dataBits) { + d->dataBits = dataBits; + emit dataBitsChanged(d->dataBits); + } + return true; + } + + return false; +} + +QSerialPort::DataBits QSerialPort::dataBits() const +{ + Q_D(const QSerialPort); + return d->dataBits; +} + +/*! + \fn void QSerialPort::dataBitsChanged(DataBits dataBits) + + This signal is emitted after the data bits in a frame has been changed. The + new data bits in a frame is passed as \a dataBits. + + \sa QSerialPort::dataBits +*/ + + +/*! + \property QSerialPort::parity + \brief the parity checking mode + + If the setting is successful, returns true; otherwise returns false and sets + an error code which can be obtained by accessing the value of the + QSerialPort::error property. +*/ +bool QSerialPort::setParity(Parity parity) +{ + Q_D(QSerialPort); + + if (d->setParity(parity)) { + if (d->parity != parity) { + d->parity = parity; + emit parityChanged(d->parity); + } + return true; + } + + return false; +} + +QSerialPort::Parity QSerialPort::parity() const +{ + Q_D(const QSerialPort); + return d->parity; +} + +/*! + \fn void QSerialPort::parityChanged(Parity parity) + + This signal is emitted after the parity checking mode has been changed. The + new parity checking mode is passed as \a parity. + + \sa QSerialPort::parity +*/ + +/*! + \property QSerialPort::stopBits + \brief the number of stop bits in a frame + + If the setting is successful, returns true; otherwise returns false and + sets an error code which can be obtained by accessing the value of the + QSerialPort::error property. +*/ +bool QSerialPort::setStopBits(StopBits stopBits) +{ + Q_D(QSerialPort); + + if (d->setStopBits(stopBits)) { + if (d->stopBits != stopBits) { + d->stopBits = stopBits; + emit stopBitsChanged(d->stopBits); + } + return true; + } + + return false; +} + +QSerialPort::StopBits QSerialPort::stopBits() const +{ + Q_D(const QSerialPort); + return d->stopBits; +} + +/*! + \fn void QSerialPort::stopBitsChanged(StopBits stopBits) + + This signal is emitted after the number of stop bits in a frame has been + changed. The new number of stop bits in a frame is passed as \a stopBits. + + \sa QSerialPort::stopBits +*/ + +/*! + \property QSerialPort::flowControl + \brief the desired flow control mode + + If the setting is successful, returns true; otherwise returns false and sets + an error code which can be obtained by accessing the value of the + QSerialPort::error property. +*/ +bool QSerialPort::setFlowControl(FlowControl flow) +{ + Q_D(QSerialPort); + + if (d->setFlowControl(flow)) { + if (d->flow != flow) { + d->flow = flow; + emit flowControlChanged(d->flow); + } + return true; + } + + return false; +} + +QSerialPort::FlowControl QSerialPort::flowControl() const +{ + Q_D(const QSerialPort); + return d->flow; +} + +/*! + \fn void QSerialPort::flowControlChanged(FlowControl flow) + + This signal is emitted after the flow control mode has been changed. The + new flow control mode is passed as \a flow. + + \sa QSerialPort::flowControl +*/ + +/*! + \property QSerialPort::dataTerminalReady + \brief the state (high or low) of the line signal DTR + + If the setting is successful, returns true; otherwise returns false. + If the flag is true then the DTR signal is set to high; otherwise low. + + \sa pinoutSignals() +*/ +bool QSerialPort::setDataTerminalReady(bool set) +{ + Q_D(QSerialPort); + + bool retval = d->setDataTerminalReady(set); + if (retval && (d->dataTerminalReady != set)) { + d->dataTerminalReady = set; + emit dataTerminalReadyChanged(set); + } + + return retval; +} + +bool QSerialPort::isDataTerminalReady() +{ + Q_D(const QSerialPort); + return d->pinoutSignals() & QSerialPort::DataTerminalReadySignal; +} + +/*! + \fn void QSerialPort::dataTerminalReadyChanged(bool set) + + This signal is emitted after the state (high or low) of the line signal DTR + has been changed. The new the state (high or low) of the line signal DTR is + passed as \a set. + + \sa QSerialPort::dataTerminalReady +*/ + +/*! + \property QSerialPort::requestToSend + \brief the state (high or low) of the line signal RTS + + If the setting is successful, returns true; otherwise returns false. + If the flag is true then the RTS signal is set to high; otherwise low. + + \sa pinoutSignals() +*/ +bool QSerialPort::setRequestToSend(bool set) +{ + Q_D(QSerialPort); + + bool retval = d->setRequestToSend(set); + if (retval && (d->requestToSend != set)) { + d->requestToSend = set; + emit requestToSendChanged(set); + } + + return retval; +} + +bool QSerialPort::isRequestToSend() +{ + Q_D(const QSerialPort); + return d->pinoutSignals() & QSerialPort::RequestToSendSignal; +} + +/*! + \fn void QSerialPort::requestToSendChanged(bool set) + + This signal is emitted after the state (high or low) of the line signal RTS + has been changed. The new the state (high or low) of the line signal RTS is + passed as \a set. + + \sa QSerialPort::requestToSend +*/ + +/*! + Returns the state of the line signals in a bitmap format. + + From this result, it is possible to allocate the state of the + desired signal by applying a mask "AND", where the mask is + the desired enumeration value from QSerialPort::PinoutSignals. + + Note that, this method performs a system call, thus ensuring that the line + signal states are returned properly. This is necessary when the underlying + operating systems cannot provide proper notifications about the changes. + + \sa isDataTerminalReady(), isRequestToSend, setDataTerminalReady(), + setRequestToSend() +*/ +QSerialPort::PinoutSignals QSerialPort::pinoutSignals() +{ + Q_D(const QSerialPort); + return d->pinoutSignals(); +} + +/*! + This function writes as much as possible from the internal write + buffer to the underlying serial port without blocking. If any data + was written, this function returns true; otherwise returns false. + + Call this function for sending the buffered data immediately to the serial + port. The number of bytes successfully written depends on the operating + system. In most cases, this function does not need to be called, because the + QSerialPort class will start sending data automatically once control is + returned to the event loop. In the absence of an event loop, call + waitForBytesWritten() instead. + + \sa write(), waitForBytesWritten() +*/ +bool QSerialPort::flush() +{ + Q_D(QSerialPort); + return d->flush(); +} + +/*! + Discards all characters from the output or input buffer, depending on + a given direction \a dir. Including clear an internal class buffers and + the UART (driver) buffers. Also terminate pending read or write operations. + If successful, returns true; otherwise returns false. +*/ +bool QSerialPort::clear(Directions dir) +{ + Q_D(QSerialPort); + if (dir & Input) + d->readBuffer.clear(); + if (dir & Output) + d->writeBuffer.clear(); + return d->clear(dir); +} + +/*! + \reimp + + Returns true if no more data is currently available for reading; otherwise + returns false. + + This function is most commonly used when reading data from the + serial port in a loop. For example: + + \code + // This slot is connected to QSerialPort::readyRead() + void QSerialPortClass::readyReadSlot() + { + while (!port.atEnd()) { + QByteArray data = port.read(100); + .... + } + } + \endcode + + \sa bytesAvailable(), readyRead() + */ +bool QSerialPort::atEnd() const +{ + Q_D(const QSerialPort); + return QIODevice::atEnd() && (!isOpen() || (d->bytesAvailable() == 0)); +} + +/*! + \property QSerialPort::dataErrorPolicy + \brief the error policy how the process receives the character in case of + parity error detection. + + If the setting is successful, returns true; otherwise returns false. The + default policy set is IgnorePolicy. +*/ +bool QSerialPort::setDataErrorPolicy(DataErrorPolicy policy) +{ + Q_D(QSerialPort); + + const bool ret = d->policy == policy || d->setDataErrorPolicy(policy); + if (ret && (d->policy != policy)) { + d->policy = policy; + emit dataErrorPolicyChanged(d->policy); + } + + return ret; +} + +QSerialPort::DataErrorPolicy QSerialPort::dataErrorPolicy() const +{ + Q_D(const QSerialPort); + return d->policy; +} + +/*! + \fn void QSerialPort::dataErrorPolicyChanged(DataErrorPolicy policy) + + This signal is emitted after the error policy how the process receives the + character in case of parity error detection has been changed. The new error + policy how the process receives the character in case of parity error + detection is passed as \a policy. + + \sa QSerialPort::dataErrorPolicy +*/ + +/*! + \property QSerialPort::error + \brief the error status of the serial port + + The I/O device status returns an error code. For example, if open() + returns false, or a read/write operation returns -1, this property can + be used to figure out the reason why the operation failed. + + The error code is set to the default QSerialPort::NoError after a call to + clearError() +*/ +QSerialPort::SerialPortError QSerialPort::error() const +{ + Q_D(const QSerialPort); + return d->error; +} + +void QSerialPort::clearError() +{ + setError(QSerialPort::NoError); +} + +/*! + \fn void QSerialPort::error(SerialPortError error) + + This signal is emitted after the error has been changed. The new erroris + passed as \a error. + + \sa QSerialPort::error +*/ + +/*! + Returns the size of the internal read buffer. This limits the + amount of data that the client can receive before calling the read() + or readAll() methods. + + A read buffer size of 0 (the default) means that the buffer has + no size limit, ensuring that no data is lost. + + \sa setReadBufferSize(), read() +*/ +qint64 QSerialPort::readBufferSize() const +{ + Q_D(const QSerialPort); + return d->readBufferMaxSize; +} + +/*! + Sets the size of QSerialPort's internal read buffer to be \a + size bytes. + + If the buffer size is limited to a certain size, QSerialPort + will not buffer more than this size of data. Exceptionally, a buffer + size of 0 means that the read buffer is unlimited and all + incoming data is buffered. This is the default. + + This option is useful if the data is only read at certain points + in time (for instance in a real-time streaming application) or if the serial + port should be protected against receiving too much data, which may + eventually causes that the application runs out of memory. + + \sa readBufferSize(), read() +*/ +void QSerialPort::setReadBufferSize(qint64 size) +{ + Q_D(QSerialPort); + + if (d->readBufferMaxSize == size) + return; + d->readBufferMaxSize = size; +} + +/*! + \reimp + + Always returns true. The serial port is a sequential device. +*/ +bool QSerialPort::isSequential() const +{ + return true; +} + +/*! + \reimp + + Returns the number of incoming bytes that are waiting to be read. + + \sa bytesToWrite(), read() +*/ +qint64 QSerialPort::bytesAvailable() const +{ + Q_D(const QSerialPort); + return d->bytesAvailable() + QIODevice::bytesAvailable(); +} + +/*! + \reimp + + Returns the number of bytes that are waiting to be written. The + bytes are written when control goes back to the event loop or + when flush() is called. + + \sa bytesAvailable(), flush() +*/ +qint64 QSerialPort::bytesToWrite() const +{ + Q_D(const QSerialPort); + return d->writeBuffer.size() + QIODevice::bytesToWrite(); +} + +/*! + \reimp + + Returns true if a line of data can be read from the serial port; + otherwise returns false. + + \sa readLine() +*/ +bool QSerialPort::canReadLine() const +{ + Q_D(const QSerialPort); + const bool hasLine = (d->bytesAvailable() > 0) && d->readBuffer.canReadLine(); + return hasLine || QIODevice::canReadLine(); +} + +/*! + \reimp + + This function blocks until new data is available for reading and the + \l{QIODevice::}{readyRead()} signal has been emitted. The function + will timeout after \a msecs milliseconds. + + The function returns true if the readyRead() signal is emitted and + there is new data available for reading; otherwise it returns false + (if an error occurred or the operation timed out). + + \sa waitForBytesWritten() +*/ +bool QSerialPort::waitForReadyRead(int msecs) +{ + Q_D(QSerialPort); + return d->waitForReadyRead(msecs); +} + +/*! + \reimp +*/ +bool QSerialPort::waitForBytesWritten(int msecs) +{ + Q_D(QSerialPort); + return d->waitForBytesWritten(msecs); +} + +/*! + Sends a continuous stream of zero bits during a specified period + of time \a duration in msec if the terminal is using asynchronous + serial data. If successful, returns true; otherwise returns false. + + If the duration is zero then zero bits are transmitted by at least + 0.25 seconds, but no more than 0.5 seconds. + + If the duration is non zero then zero bits are transmitted within a certain + period of time depending on the implementation. + + \sa setBreakEnabled() +*/ +bool QSerialPort::sendBreak(int duration) +{ + Q_D(QSerialPort); + return d->sendBreak(duration); +} + +/*! + Controls the signal break, depending on the flag \a set. + If successful, returns true; otherwise returns false. + + If \a set is true then enables the break transmission; otherwise disables. + + \sa sendBreak() +*/ +bool QSerialPort::setBreakEnabled(bool set) +{ + Q_D(QSerialPort); + return d->setBreakEnabled(set); +} + +/*! + \reimp +*/ +qint64 QSerialPort::readData(char *data, qint64 maxSize) +{ + Q_D(QSerialPort); + return d->readFromBuffer(data, maxSize); +} + +/*! + \reimp +*/ +qint64 QSerialPort::readLineData(char *data, qint64 maxSize) +{ + return QIODevice::readLineData(data, maxSize); +} + +/*! + \reimp +*/ +qint64 QSerialPort::writeData(const char *data, qint64 maxSize) +{ + Q_D(QSerialPort); + return d->writeToBuffer(data, maxSize); +} + +void QSerialPort::setError(QSerialPort::SerialPortError serialPortError, const QString &errorString) +{ + Q_D(QSerialPort); + + d->error = serialPortError; + + if (errorString.isNull()) + setErrorString(qt_error_string(-1)); + else + setErrorString(errorString); + + emit error(serialPortError); +} + +#include "moc_qserialport.cpp" + +QT_END_NAMESPACE diff --git a/libs/serialport/qserialport.h b/libs/serialport/qserialport.h new file mode 100644 index 000000000..8a2842fe0 --- /dev/null +++ b/libs/serialport/qserialport.h @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Copyright (C) 2013 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERIALPORT_H +#define QSERIALPORT_H + +#include + +#include +//#include +QT_BEGIN_NAMESPACE + +class QSerialPortInfo; +class QSerialPortPrivate; + +class Q_SERIALPORT_EXPORT QSerialPort : public QIODevice +{ + Q_OBJECT + + Q_PROPERTY(qint32 baudRate READ baudRate WRITE setBaudRate NOTIFY baudRateChanged) + Q_PROPERTY(DataBits dataBits READ dataBits WRITE setDataBits NOTIFY dataBitsChanged) + Q_PROPERTY(Parity parity READ parity WRITE setParity NOTIFY parityChanged) + Q_PROPERTY(StopBits stopBits READ stopBits WRITE setStopBits NOTIFY stopBitsChanged) + Q_PROPERTY(FlowControl flowControl READ flowControl WRITE setFlowControl NOTIFY flowControlChanged) + Q_PROPERTY(DataErrorPolicy dataErrorPolicy READ dataErrorPolicy WRITE setDataErrorPolicy NOTIFY dataErrorPolicyChanged) + Q_PROPERTY(bool dataTerminalReady READ isDataTerminalReady WRITE setDataTerminalReady NOTIFY dataTerminalReadyChanged) + Q_PROPERTY(bool requestToSend READ isRequestToSend WRITE setRequestToSend NOTIFY requestToSendChanged) + Q_PROPERTY(SerialPortError error READ error RESET clearError NOTIFY error) + Q_PROPERTY(bool settingsRestoredOnClose READ settingsRestoredOnClose WRITE setSettingsRestoredOnClose NOTIFY settingsRestoredOnCloseChanged) + + Q_ENUMS( Directions Rate DataBits Parity StopBits FlowControl PinoutSignals DataErrorPolicy SerialPortError ) + +public: + + enum Direction { + Input = 1, + Output = 2, + AllDirections = Input | Output + }; + Q_DECLARE_FLAGS(Directions, Direction) + + enum BaudRate { + Baud1200 = 1200, + Baud2400 = 2400, + Baud4800 = 4800, + Baud9600 = 9600, + Baud19200 = 19200, + Baud38400 = 38400, + Baud57600 = 57600, + Baud115200 = 115200, + UnknownBaud = -1 + }; + + enum DataBits { + Data5 = 5, + Data6 = 6, + Data7 = 7, + Data8 = 8, + UnknownDataBits = -1 + }; + + enum Parity { + NoParity = 0, + EvenParity = 2, + OddParity = 3, + SpaceParity = 4, + MarkParity = 5, + UnknownParity = -1 + }; + + enum StopBits { + OneStop = 1, + OneAndHalfStop = 3, + TwoStop = 2, + UnknownStopBits = -1 + }; + + enum FlowControl { + NoFlowControl, + HardwareControl, + SoftwareControl, + UnknownFlowControl = -1 + }; + + enum PinoutSignal { + NoSignal = 0x00, + TransmittedDataSignal = 0x01, + ReceivedDataSignal = 0x02, + DataTerminalReadySignal = 0x04, + DataCarrierDetectSignal = 0x08, + DataSetReadySignal = 0x10, + RingIndicatorSignal = 0x20, + RequestToSendSignal = 0x40, + ClearToSendSignal = 0x80, + SecondaryTransmittedDataSignal = 0x100, + SecondaryReceivedDataSignal = 0x200 + }; + Q_DECLARE_FLAGS(PinoutSignals, PinoutSignal) + + enum DataErrorPolicy { + SkipPolicy, + PassZeroPolicy, + IgnorePolicy, + StopReceivingPolicy, + UnknownPolicy = -1 + }; + + enum SerialPortError { + NoError, + DeviceNotFoundError, + PermissionError, + OpenError, + ParityError, + FramingError, + BreakConditionError, + WriteError, + ReadError, + ResourceError, + UnsupportedOperationError, + UnknownError + }; + + explicit QSerialPort(QObject *parent = 0); + explicit QSerialPort(const QString &name, QObject *parent = 0); + explicit QSerialPort(const QSerialPortInfo &info, QObject *parent = 0); + virtual ~QSerialPort(); + + void setPortName(const QString &name); + QString portName() const; + + void setPort(const QSerialPortInfo &info); + + bool open(OpenMode mode) Q_DECL_OVERRIDE; + void close() Q_DECL_OVERRIDE; + + void setSettingsRestoredOnClose(bool restore); + bool settingsRestoredOnClose() const; + + bool setBaudRate(qint32 baudRate, Directions dir = AllDirections); + qint32 baudRate(Directions dir = AllDirections) const; + + bool setDataBits(DataBits dataBits); + DataBits dataBits() const; + + bool setParity(Parity parity); + Parity parity() const; + + bool setStopBits(StopBits stopBits); + StopBits stopBits() const; + + bool setFlowControl(FlowControl flow); + FlowControl flowControl() const; + + bool setDataTerminalReady(bool set); + bool isDataTerminalReady(); + + bool setRequestToSend(bool set); + bool isRequestToSend(); + + PinoutSignals pinoutSignals(); + + bool flush(); + bool clear(Directions dir = AllDirections); + bool atEnd() const Q_DECL_OVERRIDE; + + bool setDataErrorPolicy(DataErrorPolicy policy = IgnorePolicy); + DataErrorPolicy dataErrorPolicy() const; + + SerialPortError error() const; + void clearError(); + + qint64 readBufferSize() const; + void setReadBufferSize(qint64 size); + + bool isSequential() const Q_DECL_OVERRIDE; + + qint64 bytesAvailable() const Q_DECL_OVERRIDE; + qint64 bytesToWrite() const Q_DECL_OVERRIDE; + bool canReadLine() const Q_DECL_OVERRIDE; + + bool waitForReadyRead(int msecs) Q_DECL_OVERRIDE; + bool waitForBytesWritten(int msecs) Q_DECL_OVERRIDE; + + bool sendBreak(int duration = 0); + bool setBreakEnabled(bool set = true); + +Q_SIGNALS: + void baudRateChanged(qint32 baudRate, QSerialPort::Directions dir); + void dataBitsChanged(QSerialPort::DataBits dataBits); + void parityChanged(QSerialPort::Parity parity); + void stopBitsChanged(QSerialPort::StopBits stopBits); + void flowControlChanged(QSerialPort::FlowControl flow); + void dataErrorPolicyChanged(QSerialPort::DataErrorPolicy policy); + void dataTerminalReadyChanged(bool set); + void requestToSendChanged(bool set); + void error(QSerialPort::SerialPortError serialPortError); + void settingsRestoredOnCloseChanged(bool restore); + +protected: + qint64 readData(char *data, qint64 maxSize) Q_DECL_OVERRIDE; + qint64 readLineData(char *data, qint64 maxSize) Q_DECL_OVERRIDE; + qint64 writeData(const char *data, qint64 maxSize) Q_DECL_OVERRIDE; + +private: + void setError(QSerialPort::SerialPortError error, const QString &errorString = QString()); + + QSerialPortPrivate * const d_ptr; + + Q_DECLARE_PRIVATE(QSerialPort) + Q_DISABLE_COPY(QSerialPort) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSerialPort::Directions) +Q_DECLARE_OPERATORS_FOR_FLAGS(QSerialPort::PinoutSignals) + +QT_END_NAMESPACE + +#endif // QSERIALPORT_H diff --git a/libs/serialport/qserialport_p.h b/libs/serialport/qserialport_p.h new file mode 100644 index 000000000..abd9cb633 --- /dev/null +++ b/libs/serialport/qserialport_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2011-2012 Denis Shienkov +** Copyright (C) 2011 Sergey Belyashov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERIALPORT_P_H +#define QSERIALPORT_P_H + +#include "qserialport.h" + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#else +#include "qt4support/qringbuffer_p.h" +#endif + +QT_BEGIN_NAMESPACE + +class QSerialPortPrivateData +{ + Q_DECLARE_PUBLIC(QSerialPort) +public: + enum IoConstants { + ReadChunkSize = 512, + WriteChunkSize = 512 + }; + + QSerialPortPrivateData(QSerialPort *q); + int timeoutValue(int msecs, int elapsed); + + qint64 readBufferMaxSize; + QRingBuffer readBuffer; + QRingBuffer writeBuffer; + QSerialPort::SerialPortError error; + QString systemLocation; + qint32 inputBaudRate; + qint32 outputBaudRate; + QSerialPort::DataBits dataBits; + QSerialPort::Parity parity; + QSerialPort::StopBits stopBits; + QSerialPort::FlowControl flow; + QSerialPort::DataErrorPolicy policy; + bool dataTerminalReady; + bool requestToSend; + bool settingsRestoredOnClose; + QSerialPort * const q_ptr; +}; + +QT_END_NAMESPACE + +#endif // QSERIALPORT_P_H diff --git a/libs/serialport/qserialport_symbian.cpp b/libs/serialport/qserialport_symbian.cpp new file mode 100644 index 000000000..2bba5a4d8 --- /dev/null +++ b/libs/serialport/qserialport_symbian.cpp @@ -0,0 +1,645 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialport_symbian_p.h" + +#include + +#include +//#include +#include + +QT_BEGIN_NAMESPACE + +// Physical device driver. +#ifdef __WINS__ +_LIT(KPddName, "ECDRV"); +#else // defined (__EPOC32__) +_LIT(KPddName, "EUART"); +#endif + +// Logical device driver. +_LIT(KLddName,"ECOMM"); + +// Modules names. +_LIT(KRS232ModuleName, "ECUART"); +_LIT(KBluetoothModuleName, "BTCOMM"); +_LIT(KInfraRedModuleName, "IRCOMM"); +_LIT(KACMModuleName, "ECACM"); + +// Return false on error load. +static bool loadDevices() +{ + TInt r = KErrNone; +#ifdef __WINS__ + RFs fileServer; + r = User::LeaveIfError(fileServer.Connect()); + if (r != KErrNone) + return false; + fileServer.Close (); +#endif + + r = User::LoadPhysicalDevice(KPddName); + if (r != KErrNone && r != KErrAlreadyExists) + return false; //User::Leave(r); + + r = User::LoadLogicalDevice(KLddName); + if (r != KErrNone && r != KErrAlreadyExists) + return false; //User::Leave(r); + +#ifndef __WINS__ + r = StartC32(); + if (r != KErrNone && r != KErrAlreadyExists) + return false; //User::Leave(r); +#endif + + return true; +} + +QSerialPortPrivate::QSerialPortPrivate(QSerialPort *q) + : QSerialPortPrivateData(q) + , errnum(KErrNone) +{ +} + +bool QSerialPortPrivate::open(QIODevice::OpenMode mode) +{ + // FIXME: Maybe need added check an ReadWrite open mode? + Q_UNUSED(mode) + + if (!loadDevices()) { + q_ptr->setError(QSerialPort::UnknownError); + return false; + } + + RCommServ server; + errnum = server.Connect(); + if (errnum != KErrNone) { + q_ptr->setError(decodeSystemError()); + return false; + } + + if (systemLocation.contains("BTCOMM")) + errnum = server.LoadCommModule(KBluetoothModuleName); + else if (systemLocation.contains("IRCOMM")) + errnum = server.LoadCommModule(KInfraRedModuleName); + else if (systemLocation.contains("ACM")) + errnum = server.LoadCommModule(KACMModuleName); + else + errnum = server.LoadCommModule(KRS232ModuleName); + + if (errnum != KErrNone) { + q_ptr->setError(decodeSystemError()); + return false; + } + + // In Symbian OS port opening only in R/W mode? + TPtrC portName(static_cast(systemLocation.utf16()), systemLocation.length()); + errnum = descriptor.Open(server, portName, ECommExclusive); + + if (errnum != KErrNone) { + q_ptr->setError(decodeSystemError()); + return false; + } + + // Save current port settings. + errnum = descriptor.Config(restoredSettings); + if (errnum != KErrNone) { + q_ptr->setError(decodeSystemError()); + return false; + } + + detectDefaultSettings(); + return true; +} + +void QSerialPortPrivate::close() +{ + if (settingsRestoredOnClose) + descriptor.SetConfig(restoredSettings); + descriptor.Close(); +} + +QSerialPort::PinoutSignals QSerialPortPrivate::pinoutSignals() const +{ + QSerialPort::PinoutSignals ret = QSerialPort::NoSignal; + + TUint signalMask = 0; + descriptor.Signals(signalMask); + + if (signalMask & KSignalCTS) + ret |= QSerialPort::ClearToSendSignal; + if (signalMask & KSignalDSR) + ret |= QSerialPort::DataSetReadySignal; + if (signalMask & KSignalDCD) + ret |= QSerialPort::DataCarrierDetectSignal; + if (signalMask & KSignalRNG) + ret |= QSerialPort::RingIndicatorSignal; + if (signalMask & KSignalRTS) + ret |= QSerialPort::RequestToSendSignal; + if (signalMask & KSignalDTR) + ret |= QSerialPort::DataTerminalReadySignal; + + //if (signalMask & KSignalBreak) + // ret |= + return ret; +} + +bool QSerialPortPrivate::setDataTerminalReady(bool set) +{ + TInt r; + if (set) + r = descriptor.SetSignalsToMark(KSignalDTR); + else + r = descriptor.SetSignalsToSpace(KSignalDTR); + + return r == KErrNone; +} + +bool QSerialPortPrivate::setRequestToSend(bool set) +{ + TInt r; + if (set) + r = descriptor.SetSignalsToMark(KSignalRTS); + else + r = descriptor.SetSignalsToSpace(KSignalRTS); + + return r == KErrNone; +} + +bool QSerialPortPrivate::flush() +{ + // TODO: Implement me + return false; +} + +bool QSerialPortPrivate::clear(QSerialPort::Directions dir) +{ + TUint flags = 0; + if (dir & QSerialPort::Input) + flags |= KCommResetRx; + if (dir & QSerialPort::Output) + flags |= KCommResetTx; + TInt r = descriptor.ResetBuffers(flags); + return r == KErrNone; +} + +bool QSerialPortPrivate::sendBreak(int duration) +{ + TRequestStatus status; + descriptor.Break(status, TTimeIntervalMicroSeconds32(duration * 1000)); + return false; +} + +bool QSerialPortPrivate::setBreakEnabled(bool set) +{ + // TODO: Implement me + return false; +} + +qint64 QSerialPortPrivate::systemInputQueueSize () const +{ + return descriptor.QueryReceiveBuffer(); +} + +qint64 QSerialPortPrivate::systemOutputQueueSize () const +{ + // TODO: Implement me + return 0; +} + +qint64 QSerialPortPrivate::bytesAvailable() const +{ + return readBuffer.size(); +} + +qint64 QSerialPortPrivate::readFromBuffer(char *data, qint64 maxSize) +{ + // TODO: Implement me + return -1; +} + +qint64 QSerialPortPrivate::writeToBuffer(const char *data, qint64 maxSize) +{ + // TODO: Implement me + return -1; +} + +bool QSerialPortPrivate::waitForReadyRead(int msec) +{ + // TODO: Implement me + return false; +} + +bool QSerialPortPrivate::waitForBytesWritten(int msec) +{ + // TODO: Implement me + return false; +} + +bool QSerialPortPrivate::setBaudRate(qint32 baudRate, QSerialPort::Directions dir) +{ + if (dir != QSerialPort::AllDirections) { + q_ptr->setError(QSerialPort::UnsupportedOperationError); + return false; + } + + baudRate = settingFromBaudRate(baudRate); + if (baudRate) + currentSettings().iRate = static_cast(baudRate); + else { + q_ptr->setError(QSerialPort::UnsupportedOperationError); + return false; + } + + return updateCommConfig(); +} + +bool QSerialPortPrivate::setDataBits(QSerialPort::DataBits dataBits) +{ + switch (dataBits) { + case QSerialPort::Data5: + currentSettings().iDataBits = EData5; + break; + case QSerialPort::Data6: + currentSettings().iDataBits = EData6; + break; + case QSerialPort::Data7: + currentSettings().iDataBits = EData7; + break; + case QSerialPort::Data8: + currentSettings().iDataBits = EData8; + break; + default: + currentSettings().iDataBits = EData8; + break; + } + + return updateCommConfig(); +} + +bool QSerialPortPrivate::setParity(QSerialPort::Parity parity) +{ + switch (parity) { + case QSerialPort::NoParity: + currentSettings().iParity = EParityNone; + break; + case QSerialPort::EvenParity: + currentSettings().iParity = EParityEven; + break; + case QSerialPort::OddParity: + currentSettings().iParity = EParityOdd; + break; + case QSerialPort::MarkParity: + currentSettings().iParity = EParityMark; + break; + case QSerialPort::SpaceParity: + currentSettings().iParity = EParitySpace; + break; + default: + currentSettings().iParity = EParityNone; + break; + } + + return updateCommConfig(); +} + +bool QSerialPortPrivate::setStopBits(QSerialPort::StopBits stopBits) +{ + switch (stopBits) { + case QSerialPort::OneStop: + currentSettings().iStopBits = EStop1; + break; + case QSerialPort::TwoStop: + currentSettings().iStopBits = EStop2; + break; + default: + currentSettings().iStopBits = EStop1; + break; + } + + return updateCommConfig(); +} + +bool QSerialPortPrivate::setFlowControl(QSerialPort::FlowControl flow) +{ + switch (flow) { + case QSerialPort::NoFlowControl: + currentSettings().iHandshake = KConfigFailDSR; + break; + case QSerialPort::HardwareControl: + currentSettings().iHandshake = KConfigObeyCTS | KConfigFreeRTS; + break; + case QSerialPort::SoftwareControl: + currentSettings().iHandshake = KConfigObeyXoff | KConfigSendXoff; + break; + default: + currentSettings().iHandshake = KConfigFailDSR; + break; + } + + return updateCommConfig(); +} + +bool QSerialPortPrivate::setDataErrorPolicy(QSerialPort::DataErrorPolicy policy) +{ + // TODO: Implement me + return false; +} + +bool QSerialPortPrivate::notifyRead() +{ + // TODO: Implement me + return false; +} + +bool QSerialPortPrivate::notifyWrite() +{ + // TODO: Implement me + return false; +} + +bool QSerialPortPrivate::updateCommConfig() +{ + if (descriptor.SetConfig(currentSettings) != KErrNone) { + q_ptr->setError(QSerialPort::UnsupportedOperationError); + return false; + } + return true; +} + +void QSerialPortPrivate::detectDefaultSettings() +{ + // Detect baud rate. + inputBaudRate = baudRateFromSetting(currentSettings().iRate); + outputBaudRate = inputBaudRate; + + // Detect databits. + switch (currentSettings().iDataBits) { + case EData5: + dataBits = QSerialPort::Data5; + break; + case EData6: + dataBits = QSerialPort::Data6; + break; + case EData7: + dataBits = QSerialPort::Data7; + break; + case EData8: + dataBits = QSerialPort::Data8; + break; + default: + dataBits = QSerialPort::UnknownDataBits; + break; + } + + // Detect parity. + switch (currentSettings().iParity) { + case EParityNone: + parity = QSerialPort::NoParity; + break; + case EParityEven: + parity = QSerialPort::EvenParity; + break; + case EParityOdd: + parity = QSerialPort::OddParity; + break; + case EParityMark: + parity = QSerialPort::MarkParity; + break; + case EParitySpace: + parity = QSerialPort::SpaceParity; + break; + default: + parity = QSerialPort::UnknownParity; + break; + } + + // Detect stopbits. + switch (currentSettings().iStopBits) { + case EStop1: + stopBits = QSerialPort::OneStop; + break; + case EStop2: + stopBits = QSerialPort::TwoStop; + break; + default: + stopBits = QSerialPort::UnknownStopBits; + break; + } + + // Detect flow control. + if ((currentSettings().iHandshake & (KConfigObeyXoff | KConfigSendXoff)) + == (KConfigObeyXoff | KConfigSendXoff)) + flow = QSerialPort::SoftwareControl; + else if ((currentSettings().iHandshake & (KConfigObeyCTS | KConfigFreeRTS)) + == (KConfigObeyCTS | KConfigFreeRTS)) + flow = QSerialPort::HardwareControl; + else if (currentSettings().iHandshake & KConfigFailDSR) + flow = QSerialPort::NoFlowControl; + else + flow = QSerialPort::UnknownFlowControl; +} + +QSerialPort::SerialPortError QSerialPortPrivate::decodeSystemError() const +{ + QSerialPort::SerialPortError error; + switch (errnum) { + case KErrPermissionDenied: + error = QSerialPort::DeviceNotFoundError; + break; + case KErrLocked: + error = QSerialPort::PermissionError; + break; + case KErrAccessDenied: + error = QSerialPort::PermissionError; + break; + default: + error = QSerialPort::UnknownError; + break; + } + return error; +} + +bool QSerialPortPrivate::waitForReadOrWrite(bool *selectForRead, bool *selectForWrite, + bool checkRead, bool checkWrite, + int msecs, bool *timedOut) +{ + + // FIXME: I'm not sure in implementation this method. + // Someone needs to check and correct. + + TRequestStatus timerStatus; + TRequestStatus readStatus; + TRequestStatus writeStatus; + + if (msecs > 0) { + if (!selectTimer.Handle()) { + if (selectTimer.CreateLocal() != KErrNone) + return false; + } + selectTimer.HighRes(timerStatus, msecs * 1000); + } + + if (checkRead) + descriptor.NotifyDataAvailable(readStatus); + + if (checkWrite) + descriptor.NotifyOutputEmpty(writeStatus); + + enum { STATUSES_COUNT = 3 }; + TRequestStatus *statuses[STATUSES_COUNT]; + TInt num = 0; + statuses[num++] = &timerStatus; + statuses[num++] = &readStatus; + statuses[num++] = &writeStatus; + + User::WaitForNRequest(statuses, num); + + bool result = false; + + // By timeout? + if (timerStatus != KRequestPending) { + Q_ASSERT(selectForRead); + *selectForRead = false; + Q_ASSERT(selectForWrite); + *selectForWrite = false; + } else { + selectTimer.Cancel(); + User::WaitForRequest(timerStatus); + + // By read? + if (readStatus != KRequestPending) { + Q_ASSERT(selectForRead); + *selectForRead = true; + } + + // By write? + if (writeStatus != KRequestPending) { + Q_ASSERT(selectForWrite); + *selectForWrite = true; + } + + if (checkRead) + descriptor.NotifyDataAvailableCancel(); + if (checkWrite) + descriptor.NotifyOutputEmptyCancel(); + + result = true; + } + return result; +} + +QString QSerialPortPrivate::portNameToSystemLocation(const QString &port) +{ + // Port name is equval to port systemLocation. + return port; +} + +QString QSerialPortPrivate::portNameFromSystemLocation(const QString &location) +{ + // Port name is equval to port systemLocation. + return location; +} + +typedef QMap BaudRateMap; + +// This table contains correspondences standard pairs values of +// baud rates that are defined in files +// - d32comm.h for Symbian^3 +// - d32public.h for Symbian SR1 + +static const BaudRateMap createStandardBaudRateMap() +{ + BaudRateMap baudRateMap; + + baudRateMap.insert(50, EBps50) + baudRateMap.insert(75, EBps75) + baudRateMap.insert(110, EBps110) + baudRateMap.insert(134, EBps134) + baudRateMap.insert(150, EBps150) + baudRateMap.insert(300, EBps300) + baudRateMap.insert(600, EBps600) + baudRateMap.insert(1200, EBps1200) + baudRateMap.insert(1800, EBps1800) + baudRateMap.insert(2000, EBps2000) + baudRateMap.insert(2400, EBps2400) + baudRateMap.insert(3600, EBps3600) + baudRateMap.insert(4800, EBps4800) + baudRateMap.insert(7200, EBps7200) + baudRateMap.insert(9600, EBps9600) + baudRateMap.insert(19200, EBps19200) + baudRateMap.insert(38400, EBps38400) + baudRateMap.insert(57600, EBps57600) + baudRateMap.insert(115200, EBps115200) + baudRateMap.insert(230400, EBps230400) + baudRateMap.insert(460800, EBps460800) + baudRateMap.insert(576000, EBps576000) + baudRateMap.insert(921600, EBps921600) + baudRateMap.insert(1152000, EBps1152000) + // << baudRateMap.insert(1843200, EBps1843200) only for Symbian SR1 + baudRateMap.insert(4000000, EBps4000000); + + return baudRateMap; +} + +static const BaudRateMap& standardBaudRateMap() +{ + static const BaudRateMap baudRateMap = createStandardBaudRateMap(); + return baudRateMap; +} + +qint32 QSerialPortPrivate::baudRateFromSetting(qint32 setting) +{ + return standardBaudRateMap().key(setting); +} + +qint32 QSerialPortPrivate::settingFromBaudRate(qint32 baudRate) +{ + return standardBaudRateMap().value(baudRate); +} + +QList QSerialPortPrivate::standardBaudRates() +{ + return standardBaudRateMap().keys(); +} + +QT_END_NAMESPACE diff --git a/libs/serialport/qserialport_symbian_p.h b/libs/serialport/qserialport_symbian_p.h new file mode 100644 index 000000000..7f9eaddd3 --- /dev/null +++ b/libs/serialport/qserialport_symbian_p.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERIALPORT_SYMBIAN_P_H +#define QSERIALPORT_SYMBIAN_P_H + +#include "qserialport_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QSerialPortPrivate : public QSerialPortPrivateData +{ +public: + QSerialPortPrivate(QSerialPort *q); + + bool open(QIODevice::OpenMode mode); + void close(); + + QSerialPort::PinoutSignals pinoutSignals() const; + + bool setDataTerminalReady(bool set); + bool setRequestToSend(bool set); + + bool flush(); + bool clear(QSerialPort::Directions dir); + + bool sendBreak(int duration); + bool setBreakEnabled(bool set); + + qint64 systemInputQueueSize () const; + qint64 systemOutputQueueSize () const; + + qint64 bytesAvailable() const; + + qint64 readFromBuffer(char *data, qint64 maxSize); + qint64 writeToBuffer(const char *data, qint64 maxSize); + + bool waitForReadyRead(int msec); + bool waitForBytesWritten(int msec); + + bool setBaudRate(qint32 baudRate, QSerialPort::Directions dir); + bool setDataBits(QSerialPort::DataBits dataBits); + bool setParity(QSerialPort::Parity parity); + bool setStopBits(QSerialPort::StopBits stopBits); + bool setFlowControl(QSerialPort::FlowControl flowControl); + bool setDataErrorPolicy(QSerialPort::DataErrorPolicy policy); + + bool notifyRead(); + bool notifyWrite(); + + static QString portNameToSystemLocation(const QString &port); + static QString portNameFromSystemLocation(const QString &location); + + static qint32 baudRateFromSetting(qint32 setting); + static qint32 settingFromBaudRate(qint32 baudRate); + + static QList standardBaudRates(); + + TCommConfig currentSettings; + TCommConfig restoredSettings; + RComm descriptor; + mutable RTimer selectTimer; + TInt errnum; + +private: + bool updateCommConfig(); + + void detectDefaultSettings(); + QSerialPort::SerialPortError decodeSystemError() const; + + bool waitForReadOrWrite(bool *selectForRead, bool *selectForWrite, + bool checkRead, bool checkWrite, + int msecs, bool *timedOut); +}; + +QT_END_NAMESPACE + +#endif // QSERIALPORT_SYMBIAN_P_H diff --git a/libs/serialport/qserialport_unix.cpp b/libs/serialport/qserialport_unix.cpp new file mode 100644 index 000000000..2f6c8ccba --- /dev/null +++ b/libs/serialport/qserialport_unix.cpp @@ -0,0 +1,1343 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Copyright (C) 2012 Laszlo Papp +** Copyright (C) 2012 Andre Hartmann +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialport_unix_p.h" +#include "qttylocker_unix_p.h" + +#include +#include +#include +#include +#include + +#ifdef Q_OS_MAC +#if defined (MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4) +#include +#endif +#endif + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class ReadNotifier : public QSocketNotifier +{ +public: + ReadNotifier(QSerialPortPrivate *d, QObject *parent) + : QSocketNotifier(d->descriptor, QSocketNotifier::Read, parent) + , dptr(d) + {} + +protected: + bool event(QEvent *e) Q_DECL_OVERRIDE { + bool ret = QSocketNotifier::event(e); + if (ret) + dptr->readNotification(); + return ret; + } + +private: + QSerialPortPrivate *dptr; +}; + +class WriteNotifier : public QSocketNotifier +{ +public: + WriteNotifier(QSerialPortPrivate *d, QObject *parent) + : QSocketNotifier(d->descriptor, QSocketNotifier::Write, parent) + , dptr(d) + {} + +protected: + bool event(QEvent *e) Q_DECL_OVERRIDE { + bool ret = QSocketNotifier::event(e); + if (ret) + dptr->writeNotification(QSerialPortPrivateData::WriteChunkSize); + return ret; + } + +private: + QSerialPortPrivate *dptr; +}; + +class ExceptionNotifier : public QSocketNotifier +{ +public: + ExceptionNotifier(QSerialPortPrivate *d, QObject *parent) + : QSocketNotifier(d->descriptor, QSocketNotifier::Exception, parent) + , dptr(d) + {} + +protected: + bool event(QEvent *e) Q_DECL_OVERRIDE { + bool ret = QSocketNotifier::event(e); + if (ret) + dptr->exceptionNotification(); + return ret; + } + +private: + QSerialPortPrivate *dptr; +}; + +QSerialPortPrivate::QSerialPortPrivate(QSerialPort *q) + : QSerialPortPrivateData(q) + , descriptor(-1) + , isCustomBaudRateSupported(false) + , readNotifier(0) + , writeNotifier(0) + , exceptionNotifier(0) + , readPortNotifierCalled(false) + , readPortNotifierState(false) + , readPortNotifierStateSet(false) + , emittedReadyRead(false) + , emittedBytesWritten(false) +{ +} + +bool QSerialPortPrivate::open(QIODevice::OpenMode mode) +{ + QByteArray portName = portNameFromSystemLocation(systemLocation).toLocal8Bit(); + const char *ptr = portName.constData(); + + bool byCurrPid = false; + if (QTtyLocker::isLocked(ptr, &byCurrPid)) { + q_ptr->setError(QSerialPort::PermissionError); + return false; + } + + int flags = O_NOCTTY | O_NONBLOCK; + + switch (mode & QIODevice::ReadWrite) { + case QIODevice::WriteOnly: + flags |= O_WRONLY; + break; + case QIODevice::ReadWrite: + flags |= O_RDWR; + break; + default: + flags |= O_RDONLY; + break; + } + + descriptor = ::open(systemLocation.toLocal8Bit().constData(), flags); + + if (descriptor == -1) { + q_ptr->setError(decodeSystemError()); + return false; + } + + ::fcntl(descriptor, F_SETFL, FNDELAY); + + QTtyLocker::lock(ptr); + if (!QTtyLocker::isLocked(ptr, &byCurrPid)) { + q_ptr->setError(QSerialPort::PermissionError); + return false; + } + +#ifdef TIOCEXCL + ::ioctl(descriptor, TIOCEXCL); +#endif + + if (::tcgetattr(descriptor, &restoredTermios) == -1) { + q_ptr->setError(decodeSystemError()); + return false; + } + + currentTermios = restoredTermios; + ::cfmakeraw(¤tTermios); + currentTermios.c_cflag |= CLOCAL; + currentTermios.c_cc[VTIME] = 0; + currentTermios.c_cc[VMIN] = 0; + + if (mode & QIODevice::ReadOnly) + currentTermios.c_cflag |= CREAD; + + if (!updateTermios()) + return false; + + setExceptionNotificationEnabled(true); + + if ((flags & O_WRONLY) == 0) + setReadNotificationEnabled(true); + + detectDefaultSettings(); + return true; +} + +void QSerialPortPrivate::close() +{ + if (settingsRestoredOnClose) { + ::tcsetattr(descriptor, TCSANOW, &restoredTermios); +#ifdef Q_OS_LINUX + if (isCustomBaudRateSupported) + ::ioctl(descriptor, TIOCSSERIAL, &restoredSerialInfo); +#endif + } + +#ifdef TIOCNXCL + ::ioctl(descriptor, TIOCNXCL); +#endif + + if (readNotifier) { + readNotifier->setEnabled(false); + readNotifier->deleteLater(); + readNotifier = 0; + } + + if (writeNotifier) { + writeNotifier->setEnabled(false); + writeNotifier->deleteLater(); + writeNotifier = 0; + } + + if (exceptionNotifier) { + exceptionNotifier->setEnabled(false); + exceptionNotifier->deleteLater(); + exceptionNotifier = 0; + } + + ::close(descriptor); + + QByteArray portName = portNameFromSystemLocation(systemLocation).toLocal8Bit(); + const char *ptr = portName.constData(); + + bool byCurrPid = false; + if (QTtyLocker::isLocked(ptr, &byCurrPid) && byCurrPid) + QTtyLocker::unlock(ptr); + + descriptor = -1; + isCustomBaudRateSupported = false; +} + +QSerialPort::PinoutSignals QSerialPortPrivate::pinoutSignals() const +{ + int arg = 0; + QSerialPort::PinoutSignals ret = QSerialPort::NoSignal; + + if (::ioctl(descriptor, TIOCMGET, &arg) == -1) { + q_ptr->setError(decodeSystemError()); + return ret; + } + +#ifdef TIOCM_LE + if (arg & TIOCM_LE) + ret |= QSerialPort::DataSetReadySignal; +#endif +#ifdef TIOCM_DTR + if (arg & TIOCM_DTR) + ret |= QSerialPort::DataTerminalReadySignal; +#endif +#ifdef TIOCM_RTS + if (arg & TIOCM_RTS) + ret |= QSerialPort::RequestToSendSignal; +#endif +#ifdef TIOCM_ST + if (arg & TIOCM_ST) + ret |= QSerialPort::SecondaryTransmittedDataSignal; +#endif +#ifdef TIOCM_SR + if (arg & TIOCM_SR) + ret |= QSerialPort::SecondaryReceivedDataSignal; +#endif +#ifdef TIOCM_CTS + if (arg & TIOCM_CTS) + ret |= QSerialPort::ClearToSendSignal; +#endif +#ifdef TIOCM_CAR + if (arg & TIOCM_CAR) + ret |= QSerialPort::DataCarrierDetectSignal; +#elif defined TIOCM_CD + if (arg & TIOCM_CD) + ret |= QSerialPort::DataCarrierDetectSignal; +#endif +#ifdef TIOCM_RNG + if (arg & TIOCM_RNG) + ret |= QSerialPort::RingIndicatorSignal; +#elif defined TIOCM_RI + if (arg & TIOCM_RI) + ret |= QSerialPort::RingIndicatorSignal; +#endif +#ifdef TIOCM_DSR + if (arg & TIOCM_DSR) + ret |= QSerialPort::DataSetReadySignal; +#endif + + return ret; +} + +bool QSerialPortPrivate::setDataTerminalReady(bool set) +{ + int status = TIOCM_DTR; + return ::ioctl(descriptor, set ? TIOCMBIS : TIOCMBIC, &status) != -1; +} + +bool QSerialPortPrivate::setRequestToSend(bool set) +{ + int status = TIOCM_RTS; + return ::ioctl(descriptor, set ? TIOCMBIS : TIOCMBIC, &status) != -1; +} + +bool QSerialPortPrivate::flush() +{ + return writeNotification() && (::tcdrain(descriptor) != -1); +} + +bool QSerialPortPrivate::clear(QSerialPort::Directions dir) +{ + return ::tcflush(descriptor, (dir == QSerialPort::AllDirections) + ? TCIOFLUSH : (dir & QSerialPort::Input) ? TCIFLUSH : TCOFLUSH) != -1; +} + +bool QSerialPortPrivate::sendBreak(int duration) +{ + return ::tcsendbreak(descriptor, duration) != -1; +} + +bool QSerialPortPrivate::setBreakEnabled(bool set) +{ + return ::ioctl(descriptor, set ? TIOCSBRK : TIOCCBRK) != -1; +} + +qint64 QSerialPortPrivate::systemInputQueueSize () const +{ + int nbytes = 0; +#ifdef TIOCINQ + if (::ioctl(descriptor, TIOCINQ, &nbytes) == -1) + return -1; +#endif + return nbytes; +} + +qint64 QSerialPortPrivate::systemOutputQueueSize () const +{ + int nbytes = 0; +#ifdef TIOCOUTQ + if (::ioctl(descriptor, TIOCOUTQ, &nbytes) == -1) + return -1; +#endif + return nbytes; +} + +qint64 QSerialPortPrivate::bytesAvailable() const +{ + return readBuffer.size(); +} + +qint64 QSerialPortPrivate::readFromBuffer(char *data, qint64 maxSize) +{ + if (readBuffer.isEmpty()) + return 0; + + if (maxSize == 1) { + *data = readBuffer.getChar(); + if (readBuffer.isEmpty()) + setReadNotificationEnabled(true); + return 1; + } + + const qint64 bytesToRead = qMin(qint64(readBuffer.size()), maxSize); + qint64 readSoFar = 0; + while (readSoFar < bytesToRead) { + const char *ptr = readBuffer.readPointer(); + const int bytesToReadFromThisBlock = qMin(int(bytesToRead - readSoFar), + readBuffer.nextDataBlockSize()); + ::memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock); + readSoFar += bytesToReadFromThisBlock; + readBuffer.free(bytesToReadFromThisBlock); + } + + if (!isReadNotificationEnabled()) + setReadNotificationEnabled(true); + + if (readSoFar > 0) { + if (readBuffer.isEmpty()) + setReadNotificationEnabled(true); + return readSoFar; + } + + return readSoFar; +} + +qint64 QSerialPortPrivate::writeToBuffer(const char *data, qint64 maxSize) +{ + char *ptr = writeBuffer.reserve(maxSize); + if (maxSize == 1) + *ptr = *data; + else + ::memcpy(ptr, data, maxSize); + + const qint64 written = maxSize; + + if (!writeBuffer.isEmpty() && !isWriteNotificationEnabled()) + setWriteNotificationEnabled(true); + + return written; +} + +bool QSerialPortPrivate::waitForReadyRead(int msecs) +{ + QElapsedTimer stopWatch; + + stopWatch.start(); + + do { + bool readyToRead = false; + bool readyToWrite = false; + bool timedOut = false; + if (!waitForReadOrWrite(&readyToRead, &readyToWrite, true, !writeBuffer.isEmpty(), + timeoutValue(msecs, stopWatch.elapsed()), &timedOut)) { + q_ptr->setError(decodeSystemError()); + return false; + } + + if (readyToRead) { + if (readNotification()) + return true; + } + + if (readyToWrite) + writeNotification(WriteChunkSize); + + } while (msecs == -1 || timeoutValue(msecs, stopWatch.elapsed()) > 0); + return false; +} + +bool QSerialPortPrivate::waitForBytesWritten(int msecs) +{ + if (writeBuffer.isEmpty()) + return false; + + QElapsedTimer stopWatch; + + stopWatch.start(); + + forever { + bool readyToRead = false; + bool readyToWrite = false; + bool timedOut = false; + if (!waitForReadOrWrite(&readyToRead, &readyToWrite, true, !writeBuffer.isEmpty(), + timeoutValue(msecs, stopWatch.elapsed()), &timedOut)) { + q_ptr->setError(decodeSystemError()); + return false; + } + + if (readyToRead && !readNotification()) + return false; + + if (readyToWrite) + return writeNotification(WriteChunkSize); + } + return false; +} + +bool QSerialPortPrivate::setBaudRate(qint32 baudRate, QSerialPort::Directions dir) +{ + bool ret = baudRate > 0; + + // prepare section + + if (ret) { + const qint32 unixBaudRate = QSerialPortPrivate::settingFromBaudRate(baudRate); + if (unixBaudRate > 0) { + // try prepate to set standard baud rate +#ifdef Q_OS_LINUX + // prepare to forcefully reset the custom mode + if (isCustomBaudRateSupported) { + //currentSerialInfo.flags |= ASYNC_SPD_MASK; + currentSerialInfo.flags &= ~(ASYNC_SPD_CUST /* | ASYNC_LOW_LATENCY*/); + currentSerialInfo.custom_divisor = 0; + } +#endif + // prepare to set standard baud rate + ret = !(((dir & QSerialPort::Input) && ::cfsetispeed(¤tTermios, unixBaudRate) < 0) + || ((dir & QSerialPort::Output) && ::cfsetospeed(¤tTermios, unixBaudRate) < 0)); + } else { + // try prepate to set custom baud rate +#ifdef Q_OS_LINUX + // prepare to forcefully set the custom mode + if (isCustomBaudRateSupported) { + currentSerialInfo.flags &= ~ASYNC_SPD_MASK; + currentSerialInfo.flags |= (ASYNC_SPD_CUST /* | ASYNC_LOW_LATENCY*/); + currentSerialInfo.custom_divisor = currentSerialInfo.baud_base / baudRate; + if (currentSerialInfo.custom_divisor == 0) + currentSerialInfo.custom_divisor = 1; + // for custom mode needed prepare to set B38400 baud rate + ret = (::cfsetspeed(¤tTermios, B38400) != -1); + } else { + ret = false; + } +#elif defined(Q_OS_MAC) + +# if defined (MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4) + // Starting with Tiger, the IOSSIOSPEED ioctl can be used to set arbitrary baud rates + // other than those specified by POSIX. The driver for the underlying serial hardware + // ultimately determines which baud rates can be used. This ioctl sets both the input + // and output speed. + ret = ::ioctl(descriptor, IOSSIOSPEED, &baudRate) != -1; +# else + // others MacOSX version, can't prepare to set custom baud rate + ret = false; +# endif + +#else + // others *nix OS, can't prepare to set custom baud rate + ret = false; +#endif + } + } + + // finally section + +#ifdef Q_OS_LINUX + if (ret && isCustomBaudRateSupported) // finally, set or reset the custom mode + ret = ::ioctl(descriptor, TIOCSSERIAL, ¤tSerialInfo) != -1; +#endif + + if (ret) // finally, set baud rate + ret = updateTermios(); + else + q_ptr->setError(decodeSystemError()); + return ret; +} + +bool QSerialPortPrivate::setDataBits(QSerialPort::DataBits dataBits) +{ + currentTermios.c_cflag &= ~CSIZE; + switch (dataBits) { + case QSerialPort::Data5: + currentTermios.c_cflag |= CS5; + break; + case QSerialPort::Data6: + currentTermios.c_cflag |= CS6; + break; + case QSerialPort::Data7: + currentTermios.c_cflag |= CS7; + break; + case QSerialPort::Data8: + currentTermios.c_cflag |= CS8; + break; + default: + currentTermios.c_cflag |= CS8; + break; + } + return updateTermios(); +} + +bool QSerialPortPrivate::setParity(QSerialPort::Parity parity) +{ + currentTermios.c_iflag &= ~(PARMRK | INPCK); + currentTermios.c_iflag |= IGNPAR; + + switch (parity) { + +#ifdef CMSPAR + // Here Installation parity only for GNU/Linux where the macro CMSPAR. + case QSerialPort::SpaceParity: + currentTermios.c_cflag &= ~PARODD; + currentTermios.c_cflag |= PARENB | CMSPAR; + break; + case QSerialPort::MarkParity: + currentTermios.c_cflag |= PARENB | CMSPAR | PARODD; + break; +#endif + case QSerialPort::NoParity: + currentTermios.c_cflag &= ~PARENB; + break; + case QSerialPort::EvenParity: + currentTermios.c_cflag &= ~PARODD; + currentTermios.c_cflag |= PARENB; + break; + case QSerialPort::OddParity: + currentTermios.c_cflag |= PARENB | PARODD; + break; + default: + currentTermios.c_cflag |= PARENB; + currentTermios.c_iflag |= PARMRK | INPCK; + currentTermios.c_iflag &= ~IGNPAR; + break; + } + + return updateTermios(); +} + +bool QSerialPortPrivate::setStopBits(QSerialPort::StopBits stopBits) +{ + switch (stopBits) { + case QSerialPort::OneStop: + currentTermios.c_cflag &= ~CSTOPB; + break; + case QSerialPort::TwoStop: + currentTermios.c_cflag |= CSTOPB; + break; + default: + currentTermios.c_cflag &= ~CSTOPB; + break; + } + return updateTermios(); +} + +bool QSerialPortPrivate::setFlowControl(QSerialPort::FlowControl flow) +{ + switch (flow) { + case QSerialPort::NoFlowControl: + currentTermios.c_cflag &= ~CRTSCTS; + currentTermios.c_iflag &= ~(IXON | IXOFF | IXANY); + break; + case QSerialPort::HardwareControl: + currentTermios.c_cflag |= CRTSCTS; + currentTermios.c_iflag &= ~(IXON | IXOFF | IXANY); + break; + case QSerialPort::SoftwareControl: + currentTermios.c_cflag &= ~CRTSCTS; + currentTermios.c_iflag |= IXON | IXOFF | IXANY; + break; + default: + currentTermios.c_cflag &= ~CRTSCTS; + currentTermios.c_iflag &= ~(IXON | IXOFF | IXANY); + break; + } + return updateTermios(); +} + +bool QSerialPortPrivate::setDataErrorPolicy(QSerialPort::DataErrorPolicy policy) +{ + tcflag_t parmrkMask = PARMRK; +#ifndef CMSPAR + // in space/mark parity emulation also used PARMRK flag + if (parity == QSerialPort::SpaceParity + || parity == QSerialPort::MarkParity) { + parmrkMask = 0; + } +#endif //CMSPAR + switch (policy) { + case QSerialPort::SkipPolicy: + currentTermios.c_iflag &= ~parmrkMask; + currentTermios.c_iflag |= IGNPAR | INPCK; + break; + case QSerialPort::PassZeroPolicy: + currentTermios.c_iflag &= ~(IGNPAR | parmrkMask); + currentTermios.c_iflag |= INPCK; + break; + case QSerialPort::IgnorePolicy: + currentTermios.c_iflag &= ~INPCK; + break; + case QSerialPort::StopReceivingPolicy: + currentTermios.c_iflag &= ~IGNPAR; + currentTermios.c_iflag |= parmrkMask | INPCK; + break; + default: + currentTermios.c_iflag &= ~INPCK; + break; + } + return updateTermios(); +} + +bool QSerialPortPrivate::readNotification() +{ + // Prevent recursive calls + if (readPortNotifierCalled) { + if (!readPortNotifierStateSet) { + readPortNotifierStateSet = true; + readPortNotifierState = isReadNotificationEnabled(); + setReadNotificationEnabled(false); + } + } + + readPortNotifierCalled = true; + + // Always buffered, read data from the port into the read buffer + qint64 newBytes = readBuffer.size(); + qint64 bytesToRead = policy == QSerialPort::IgnorePolicy ? ReadChunkSize : 1; + + if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) { + bytesToRead = readBufferMaxSize - readBuffer.size(); + if (bytesToRead == 0) { + // Buffer is full. User must read data from the buffer + // before we can read more from the port. + return false; + } + } + + char *ptr = readBuffer.reserve(bytesToRead); + const qint64 readBytes = readFromPort(ptr, bytesToRead); + + if (readBytes <= 0) { + readBuffer.chop(bytesToRead); + return false; + } + + readBuffer.chop(bytesToRead - qMax(readBytes, qint64(0))); + + newBytes = readBuffer.size() - newBytes; + + // If read buffer is full, disable the read port notifier. + if (readBufferMaxSize && readBuffer.size() == readBufferMaxSize) + setReadNotificationEnabled(false); + + // only emit readyRead() when not recursing, and only if there is data available + const bool hasData = newBytes > 0; + + if (!emittedReadyRead && hasData) { + emittedReadyRead = true; + emit q_ptr->readyRead(); + emittedReadyRead = false; + } + + if (!hasData) + setReadNotificationEnabled(true); + + // reset the read port notifier state if we reentered inside the + // readyRead() connected slot. + if (readPortNotifierStateSet + && readPortNotifierState != isReadNotificationEnabled()) { + setReadNotificationEnabled(readPortNotifierState); + readPortNotifierStateSet = false; + } + return true; +} + +bool QSerialPortPrivate::writeNotification(int maxSize) +{ + const int tmp = writeBuffer.size(); + + if (writeBuffer.isEmpty()) { + setWriteNotificationEnabled(false); + return false; + } + + int nextSize = qMin(writeBuffer.nextDataBlockSize(), maxSize); + + const char *ptr = writeBuffer.readPointer(); + + // Attempt to write it chunk. + qint64 written = writeToPort(ptr, nextSize); + if (written < 0) + return false; + + // Remove what we wrote so far. + writeBuffer.free(written); + if (written > 0) { + // Don't emit bytesWritten() recursively. + if (!emittedBytesWritten) { + emittedBytesWritten = true; + emit q_ptr->bytesWritten(written); + emittedBytesWritten = false; + } + } + + if (writeBuffer.isEmpty()) + setWriteNotificationEnabled(false); + + return (writeBuffer.size() < tmp); +} + +bool QSerialPortPrivate::exceptionNotification() +{ + QSerialPort::SerialPortError error = decodeSystemError(); + q_ptr->setError(error); + + return true; +} + +bool QSerialPortPrivate::updateTermios() +{ + if (::tcsetattr(descriptor, TCSANOW, ¤tTermios) == -1) { + q_ptr->setError(decodeSystemError()); + return false; + } + return true; +} + +void QSerialPortPrivate::detectDefaultSettings() +{ + // Detect baud rate. + const speed_t inputUnixBaudRate = ::cfgetispeed(¤tTermios); + const speed_t outputUnixBaudRate = ::cfgetospeed(¤tTermios); + bool isCustomBaudRateCurrentSet = false; + +#ifdef Q_OS_LINUX + // try detect the ability to support custom baud rate + isCustomBaudRateSupported = ::ioctl(descriptor, TIOCGSERIAL, ¤tSerialInfo) != -1 + && ::ioctl(descriptor, TIOCSSERIAL, ¤tSerialInfo) != -1; + + if (isCustomBaudRateSupported) { + restoredSerialInfo = currentSerialInfo; + + // assume that the baud rate is a custom + isCustomBaudRateCurrentSet = inputUnixBaudRate == B38400 && outputUnixBaudRate == B38400; + + if (isCustomBaudRateCurrentSet) { + if ((currentSerialInfo.flags & ASYNC_SPD_CUST) + && currentSerialInfo.custom_divisor > 0) { + + // yes, speed is really custom + inputBaudRate = currentSerialInfo.baud_base / currentSerialInfo.custom_divisor; + outputBaudRate = inputBaudRate; + } else { + // no, we were wrong and the speed is a standard 38400 baud + isCustomBaudRateCurrentSet = false; + } + } + } +#else + // other *nix +#endif + if (!isCustomBaudRateSupported || !isCustomBaudRateCurrentSet) { + inputBaudRate = QSerialPortPrivate::baudRateFromSetting(inputUnixBaudRate); + outputBaudRate = QSerialPortPrivate::baudRateFromSetting(outputUnixBaudRate); + } + + // Detect databits. + switch (currentTermios.c_cflag & CSIZE) { + case CS5: + dataBits = QSerialPort::Data5; + break; + case CS6: + dataBits = QSerialPort::Data6; + break; + case CS7: + dataBits = QSerialPort::Data7; + break; + case CS8: + dataBits = QSerialPort::Data8; + break; + default: + dataBits = QSerialPort::UnknownDataBits; + break; + } + + // Detect parity. +#ifdef CMSPAR + if (currentTermios.c_cflag & CMSPAR) { + parity = currentTermios.c_cflag & PARODD ? + QSerialPort::MarkParity : QSerialPort::SpaceParity; + } else { +#endif + if (currentTermios.c_cflag & PARENB) { + parity = currentTermios.c_cflag & PARODD ? + QSerialPort::OddParity : QSerialPort::EvenParity; + } else { + parity = QSerialPort::NoParity; + } +#ifdef CMSPAR + } +#endif + + // Detect stopbits. + stopBits = currentTermios.c_cflag & CSTOPB ? + QSerialPort::TwoStop : QSerialPort::OneStop; + + // Detect flow control. + if ((!(currentTermios.c_cflag & CRTSCTS)) && (!(currentTermios.c_iflag & (IXON | IXOFF | IXANY)))) + flow = QSerialPort::NoFlowControl; + else if ((!(currentTermios.c_cflag & CRTSCTS)) && (currentTermios.c_iflag & (IXON | IXOFF | IXANY))) + flow = QSerialPort::SoftwareControl; + else if ((currentTermios.c_cflag & CRTSCTS) && (!(currentTermios.c_iflag & (IXON | IXOFF | IXANY)))) + flow = QSerialPort::HardwareControl; + else + flow = QSerialPort::UnknownFlowControl; +} + +QSerialPort::SerialPortError QSerialPortPrivate::decodeSystemError() const +{ + QSerialPort::SerialPortError error; + switch (errno) { + case ENODEV: + error = QSerialPort::DeviceNotFoundError; + break; + case EACCES: + error = QSerialPort::PermissionError; + break; + case EBUSY: + error = QSerialPort::PermissionError; + break; + case EAGAIN: + error = QSerialPort::ResourceError; + break; + case EIO: + error = QSerialPort::ResourceError; + break; + case EBADF: + error = QSerialPort::ResourceError; + break; +#ifdef Q_OS_MAC + case ENXIO: + error = QSerialPort::ResourceError; + break; +#endif + default: + error = QSerialPort::UnknownError; + break; + } + return error; +} + +bool QSerialPortPrivate::isReadNotificationEnabled() const +{ + return readNotifier && readNotifier->isEnabled(); +} + +void QSerialPortPrivate::setReadNotificationEnabled(bool enable) +{ + if (readNotifier) { + readNotifier->setEnabled(enable); + } else if (enable) { + readNotifier = new ReadNotifier(this, q_ptr); + readNotifier->setEnabled(true); + } +} + +bool QSerialPortPrivate::isWriteNotificationEnabled() const +{ + return writeNotifier && writeNotifier->isEnabled(); +} + +void QSerialPortPrivate::setWriteNotificationEnabled(bool enable) +{ + if (writeNotifier) { + writeNotifier->setEnabled(enable); + } else if (enable) { + writeNotifier = new WriteNotifier(this, q_ptr); + writeNotifier->setEnabled(true); + } +} + +bool QSerialPortPrivate::isExceptionNotificationEnabled() const +{ + return exceptionNotifier && exceptionNotifier->isEnabled(); +} + +void QSerialPortPrivate::setExceptionNotificationEnabled(bool enable) +{ + if (exceptionNotifier) { + exceptionNotifier->setEnabled(enable); + } else if (enable) { + exceptionNotifier = new ExceptionNotifier(this, q_ptr); + exceptionNotifier->setEnabled(true); + } +} + +bool QSerialPortPrivate::waitForReadOrWrite(bool *selectForRead, bool *selectForWrite, + bool checkRead, bool checkWrite, + int msecs, bool *timedOut) +{ + Q_ASSERT(selectForRead); + Q_ASSERT(selectForWrite); + Q_ASSERT(timedOut); + + fd_set fdread; + FD_ZERO(&fdread); + if (checkRead) + FD_SET(descriptor, &fdread); + + fd_set fdwrite; + FD_ZERO(&fdwrite); + if (checkWrite) + FD_SET(descriptor, &fdwrite); + + struct timeval tv; + tv.tv_sec = msecs / 1000; + tv.tv_usec = (msecs % 1000) * 1000; + + int ret = ::select(descriptor + 1, &fdread, &fdwrite, 0, msecs < 0 ? 0 : &tv); + if (ret < 0) + return false; + if (ret == 0) { + *timedOut = true; + return false; + } + + *selectForRead = FD_ISSET(descriptor, &fdread); + *selectForWrite = FD_ISSET(descriptor, &fdwrite); + + return ret; +} + +qint64 QSerialPortPrivate::readFromPort(char *data, qint64 maxSize) +{ + qint64 bytesRead = 0; +#if defined (CMSPAR) + if (parity == QSerialPort::NoParity + || policy != QSerialPort::StopReceivingPolicy) { +#else + if (parity != QSerialPort::MarkParity + && parity != QSerialPort::SpaceParity) { +#endif + bytesRead = ::read(descriptor, data, maxSize); + } else {// Perform parity emulation. + bytesRead = readPerChar(data, maxSize); + } + + if (bytesRead <= 0) { + QSerialPort::SerialPortError error = decodeSystemError(); + if (error != QSerialPort::ResourceError) + error = QSerialPort::ReadError; + q_ptr->setError(error); + } + + return bytesRead; +} + +qint64 QSerialPortPrivate::writeToPort(const char *data, qint64 maxSize) +{ + qint64 bytesWritten = 0; +#if defined (CMSPAR) + bytesWritten = ::write(descriptor, data, maxSize); +#else + if (parity != QSerialPort::MarkParity + && parity != QSerialPort::SpaceParity) { + bytesWritten = ::write(descriptor, data, maxSize); + } else {// Perform parity emulation. + bytesWritten = writePerChar(data, maxSize); + } +#endif + + if (bytesWritten < 0) { + QSerialPort::SerialPortError error = decodeSystemError(); + if (error != QSerialPort::ResourceError) + error = QSerialPort::WriteError; + q_ptr->setError(error); + } + + return bytesWritten; +} + +static inline bool evenParity(quint8 c) +{ + c ^= c >> 4; //(c7 ^ c3)(c6 ^ c2)(c5 ^ c1)(c4 ^ c0) + c ^= c >> 2; //[(c7 ^ c3)(c5 ^ c1)][(c6 ^ c2)(c4 ^ c0)] + c ^= c >> 1; + return c & 1; //(c7 ^ c3)(c5 ^ c1)(c6 ^ c2)(c4 ^ c0) +} + +#ifndef CMSPAR + +qint64 QSerialPortPrivate::writePerChar(const char *data, qint64 maxSize) +{ + qint64 ret = 0; + quint8 const charMask = (0xFF >> (8 - dataBits)); + + while (ret < maxSize) { + + bool par = evenParity(*data & charMask); + // False if need EVEN, true if need ODD. + par ^= parity == QSerialPort::MarkParity; + if (par ^ (currentTermios.c_cflag & PARODD)) { // Need switch parity mode? + currentTermios.c_cflag ^= PARODD; + flush(); //force sending already buffered data, because updateTermios() cleares buffers + //todo: add receiving buffered data!!! + if (!updateTermios()) + break; + } + + int r = ::write(descriptor, data, 1); + if (r < 0) + return -1; + if (r > 0) { + data += r; + ret += r; + } + } + return ret; +} + +#endif //CMSPAR + +qint64 QSerialPortPrivate::readPerChar(char *data, qint64 maxSize) +{ + qint64 ret = 0; + quint8 const charMask = (0xFF >> (8 - dataBits)); + + // 0 - prefix not started, + // 1 - received 0xFF, + // 2 - received 0xFF and 0x00 + int prefix = 0; + while (ret < maxSize) { + + qint64 r = ::read(descriptor, data, 1); + if (r < 0) { + if (errno == EAGAIN) // It is ok for nonblocking mode. + break; + return -1; + } + if (r == 0) + break; + + bool par = true; + switch (prefix) { + case 2: // Previously received both 0377 and 0. + par = false; + prefix = 0; + break; + case 1: // Previously received 0377. + if (*data == '\0') { + ++prefix; + continue; + } + prefix = 0; + break; + default: + if (*data == '\377') { + prefix = 1; + continue; + } + break; + } + // Now: par contains parity ok or error, *data contains received character + par ^= evenParity(*data & charMask); //par contains parity bit value for EVEN mode + par ^= (currentTermios.c_cflag & PARODD); //par contains parity bit value for current mode + if (par ^ (parity == QSerialPort::SpaceParity)) { //if parity error + switch (policy) { + case QSerialPort::SkipPolicy: + continue; //ignore received character + case QSerialPort::StopReceivingPolicy: + if (parity != QSerialPort::NoParity) + q_ptr->setError(QSerialPort::ParityError); + else + q_ptr->setError(*data == '\0' ? + QSerialPort::BreakConditionError : QSerialPort::FramingError); + return ++ret; //abort receiving + break; + case QSerialPort::UnknownPolicy: + // Unknown error policy is used! Falling back to PassZeroPolicy + case QSerialPort::PassZeroPolicy: + *data = '\0'; //replace received character by zero + break; + case QSerialPort::IgnorePolicy: + break; //ignore error and pass received character + } + } + ++data; + ++ret; + } + return ret; +} + +#ifdef Q_OS_MAC +static const QLatin1String defaultFilePathPrefix("/dev/cu."); +static const QLatin1String unusedFilePathPrefix("/dev/tty."); +#else +static const QLatin1String defaultFilePathPrefix("/dev/"); +#endif + +QString QSerialPortPrivate::portNameToSystemLocation(const QString &port) +{ + QString ret = port; + +#ifdef Q_OS_MAC + ret.remove(unusedFilePathPrefix); +#endif + + if (!ret.contains(defaultFilePathPrefix)) + ret.prepend(defaultFilePathPrefix); + return ret; +} + +QString QSerialPortPrivate::portNameFromSystemLocation(const QString &location) +{ + QString ret = location; + +#ifdef Q_OS_MAC + ret.remove(unusedFilePathPrefix); +#endif + + ret.remove(defaultFilePathPrefix); + return ret; +} + +typedef QMap BaudRateMap; + +// The OS specific defines can be found in termios.h + +static const BaudRateMap createStandardBaudRateMap() +{ + BaudRateMap baudRateMap; + +#ifdef B50 + baudRateMap.insert(50, B50); +#endif + +#ifdef B75 + baudRateMap.insert(75, B75); +#endif + +#ifdef B110 + baudRateMap.insert(110, B110); +#endif + +#ifdef B134 + baudRateMap.insert(134, B134); +#endif + +#ifdef B150 + baudRateMap.insert(150, B150); +#endif + +#ifdef B200 + baudRateMap.insert(200, B200); +#endif + +#ifdef B300 + baudRateMap.insert(300, B300); +#endif + +#ifdef B600 + baudRateMap.insert(600, B600); +#endif + +#ifdef B1200 + baudRateMap.insert(1200, B1200); +#endif + +#ifdef B1800 + baudRateMap.insert(1800, B1800); +#endif + +#ifdef B2400 + baudRateMap.insert(2400, B2400); +#endif + +#ifdef B4800 + baudRateMap.insert(4800, B4800); +#endif + +#ifdef B9600 + baudRateMap.insert(9600, B9600); +#endif + +#ifdef B19200 + baudRateMap.insert(19200, B19200); +#endif + +#ifdef B38400 + baudRateMap.insert(38400, B38400); +#endif + +#ifdef B57600 + baudRateMap.insert(57600, B57600); +#endif + +#ifdef B115200 + baudRateMap.insert(115200, B115200); +#endif + +#ifdef B230400 + baudRateMap.insert(230400, B230400); +#endif + +#ifdef B460800 + baudRateMap.insert(460800, B460800); +#endif + +#ifdef B500000 + baudRateMap.insert(500000, B500000); +#endif + +#ifdef B576000 + baudRateMap.insert(576000, B576000); +#endif + +#ifdef B921600 + baudRateMap.insert(921600, B921600); +#endif + +#ifdef B1000000 + baudRateMap.insert(1000000, B1000000); +#endif + +#ifdef B1152000 + baudRateMap.insert(1152000, B1152000); +#endif + +#ifdef B1500000 + baudRateMap.insert(1500000, B1500000); +#endif + +#ifdef B2000000 + baudRateMap.insert(2000000, B2000000); +#endif + +#ifdef B2500000 + baudRateMap.insert(2500000, B2500000); +#endif + +#ifdef B3000000 + baudRateMap.insert(3000000, B3000000); +#endif + +#ifdef B3500000 + baudRateMap.insert(3500000, B3500000); +#endif + +#ifdef B4000000 + baudRateMap.insert(4000000, B4000000); +#endif + + return baudRateMap; +} + +static const BaudRateMap& standardBaudRateMap() +{ + static const BaudRateMap baudRateMap = createStandardBaudRateMap(); + return baudRateMap; +} + +qint32 QSerialPortPrivate::baudRateFromSetting(qint32 setting) +{ + return standardBaudRateMap().key(setting); +} + +qint32 QSerialPortPrivate::settingFromBaudRate(qint32 baudRate) +{ + return standardBaudRateMap().value(baudRate); +} + +QList QSerialPortPrivate::standardBaudRates() +{ + return standardBaudRateMap().keys(); +} + +QT_END_NAMESPACE diff --git a/libs/serialport/qserialport_unix_p.h b/libs/serialport/qserialport_unix_p.h new file mode 100644 index 000000000..ce70c243b --- /dev/null +++ b/libs/serialport/qserialport_unix_p.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERIALPORT_UNIX_P_H +#define QSERIALPORT_UNIX_P_H + +#include "qserialport_p.h" + +#include +#include +#ifdef Q_OS_LINUX +# include +#endif + +QT_BEGIN_NAMESPACE + +class QSocketNotifier; + +class QSerialPortPrivate : public QSerialPortPrivateData +{ +public: + QSerialPortPrivate(QSerialPort *q); + + bool open(QIODevice::OpenMode mode); + void close(); + + QSerialPort::PinoutSignals pinoutSignals() const; + + bool setDataTerminalReady(bool set); + bool setRequestToSend(bool set); + + bool flush(); + bool clear(QSerialPort::Directions dir); + + bool sendBreak(int duration); + bool setBreakEnabled(bool set); + + qint64 systemInputQueueSize () const; + qint64 systemOutputQueueSize () const; + + qint64 bytesAvailable() const; + + qint64 readFromBuffer(char *data, qint64 maxSize); + qint64 writeToBuffer(const char *data, qint64 maxSize); + + bool waitForReadyRead(int msecs); + bool waitForBytesWritten(int msecs); + + bool setBaudRate(qint32 baudRate, QSerialPort::Directions dir); + bool setDataBits(QSerialPort::DataBits dataBits); + bool setParity(QSerialPort::Parity parity); + bool setStopBits(QSerialPort::StopBits stopBits); + bool setFlowControl(QSerialPort::FlowControl flow); + bool setDataErrorPolicy(QSerialPort::DataErrorPolicy policy); + + bool readNotification(); + bool writeNotification(int maxSize = INT_MAX); + bool exceptionNotification(); + + static QString portNameToSystemLocation(const QString &port); + static QString portNameFromSystemLocation(const QString &location); + + static qint32 baudRateFromSetting(qint32 setting); + static qint32 settingFromBaudRate(qint32 baudRate); + + static QList standardBaudRates(); + + struct termios currentTermios; + struct termios restoredTermios; +#ifdef Q_OS_LINUX + struct serial_struct currentSerialInfo; + struct serial_struct restoredSerialInfo; +#endif + int descriptor; + bool isCustomBaudRateSupported; + + QSocketNotifier *readNotifier; + QSocketNotifier *writeNotifier; + QSocketNotifier *exceptionNotifier; + + bool readPortNotifierCalled; + bool readPortNotifierState; + bool readPortNotifierStateSet; + + bool emittedReadyRead; + bool emittedBytesWritten; + +private: + bool updateTermios(); + + void detectDefaultSettings(); + QSerialPort::SerialPortError decodeSystemError() const; + + bool isReadNotificationEnabled() const; + void setReadNotificationEnabled(bool enable); + bool isWriteNotificationEnabled() const; + void setWriteNotificationEnabled(bool enable); + bool isExceptionNotificationEnabled() const; + void setExceptionNotificationEnabled(bool enable); + + bool waitForReadOrWrite(bool *selectForRead, bool *selectForWrite, + bool checkRead, bool checkWrite, + int msecs, bool *timedOut); + + qint64 readFromPort(char *data, qint64 maxSize); + qint64 writeToPort(const char *data, qint64 maxSize); + +#ifndef CMSPAR + qint64 writePerChar(const char *data, qint64 maxSize); +#endif + qint64 readPerChar(char *data, qint64 maxSize); + +}; + +QT_END_NAMESPACE + +#endif // QSERIALPORT_UNIX_P_H diff --git a/libs/serialport/qserialport_win.cpp b/libs/serialport/qserialport_win.cpp new file mode 100644 index 000000000..e8d4d62ad --- /dev/null +++ b/libs/serialport/qserialport_win.cpp @@ -0,0 +1,1091 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Copyright (C) 2012 Laszlo Papp +** Copyright (C) 2012 Andre Hartmann +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialport_win_p.h" + +#ifndef Q_OS_WINCE +#include +#include +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#else +#include "qt4support/qwineventnotifier_p.h" +#endif + +#ifndef CTL_CODE +# define CTL_CODE(DeviceType, Function, Method, Access) ( \ + ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \ + ) +#endif + +#ifndef FILE_DEVICE_SERIAL_PORT +# define FILE_DEVICE_SERIAL_PORT 27 +#endif + +#ifndef METHOD_BUFFERED +# define METHOD_BUFFERED 0 +#endif + +#ifndef FILE_ANY_ACCESS +# define FILE_ANY_ACCESS 0x00000000 +#endif + +#ifndef IOCTL_SERIAL_GET_DTRRTS +# define IOCTL_SERIAL_GET_DTRRTS \ + CTL_CODE(FILE_DEVICE_SERIAL_PORT, 30, METHOD_BUFFERED, FILE_ANY_ACCESS) +#endif + +#ifndef SERIAL_DTR_STATE +# define SERIAL_DTR_STATE 0x00000001 +#endif + +#ifndef SERIAL_RTS_STATE +# define SERIAL_RTS_STATE 0x00000002 +#endif + +QT_BEGIN_NAMESPACE + +#ifndef Q_OS_WINCE + +class AbstractOverlappedEventNotifier : public QWinEventNotifier +{ +public: + enum Type { CommEvent, ReadCompletionEvent, WriteCompletionEvent }; + + AbstractOverlappedEventNotifier(QSerialPortPrivate *d, Type type, bool manual, QObject *parent) + : QWinEventNotifier(parent), dptr(d), t(type) { + ::memset(&o, 0, sizeof(o)); + o.hEvent = ::CreateEvent(NULL, manual, FALSE, NULL); + setHandle(o.hEvent); + dptr->notifiers[o.hEvent] = this; + } + + virtual bool processCompletionRoutine() = 0; + + virtual ~AbstractOverlappedEventNotifier() { + setEnabled(false); + ::CancelIo(o.hEvent); + ::CloseHandle(o.hEvent); + } + + Type type() const { return t; } + OVERLAPPED *overlappedPointer() { return &o; } + +protected: + bool event(QEvent *e) Q_DECL_OVERRIDE { + const bool ret = QWinEventNotifier::event(e); + processCompletionRoutine(); + return ret; + } + + QSerialPortPrivate *dptr; + Type t; + OVERLAPPED o; +}; + +class CommOverlappedEventNotifier : public AbstractOverlappedEventNotifier +{ +public: + CommOverlappedEventNotifier(QSerialPortPrivate *d, DWORD eventMask, QObject *parent) + : AbstractOverlappedEventNotifier(d, CommEvent, false, parent) + , originalEventMask(eventMask), triggeredEventMask(0) { + ::SetCommMask(dptr->descriptor, originalEventMask); + startWaitCommEvent(); + } + + void startWaitCommEvent() { ::WaitCommEvent(dptr->descriptor, &triggeredEventMask, &o); } + + bool processCompletionRoutine() Q_DECL_OVERRIDE { + DWORD numberOfBytesTransferred = 0; + ::GetOverlappedResult(dptr->descriptor, &o, &numberOfBytesTransferred, FALSE); + + bool error = false; + + // Check for unexpected event. This event triggered when pulled previously + // opened device from the system, when opened as for not to read and not to + // write options and so forth. + if (triggeredEventMask == 0) + error = true; + + // Workaround for standard CDC ACM serial ports, for which triggered an + // unexpected event EV_TXEMPTY at data transmission. + if ((originalEventMask & triggeredEventMask) == 0) { + if ((triggeredEventMask & EV_TXEMPTY) == 0) + error = true; + } + + // Start processing a caught error. + if (error || (EV_ERR & triggeredEventMask)) + dptr->processIoErrors(error); + + if (!error) + dptr->startAsyncRead(); + + return !error; + } + +private: + DWORD originalEventMask; + DWORD triggeredEventMask; +}; + +class ReadOverlappedCompletionNotifier : public AbstractOverlappedEventNotifier +{ +public: + ReadOverlappedCompletionNotifier(QSerialPortPrivate *d, QObject *parent) + : AbstractOverlappedEventNotifier(d, ReadCompletionEvent, false, parent) {} + + bool processCompletionRoutine() Q_DECL_OVERRIDE { + DWORD numberOfBytesTransferred = 0; + ::GetOverlappedResult(dptr->descriptor, &o, &numberOfBytesTransferred, FALSE); + bool ret = dptr->completeAsyncRead(numberOfBytesTransferred); + + // start async read for possible remainder into driver queue + if (ret && (numberOfBytesTransferred > 0) && (dptr->policy == QSerialPort::IgnorePolicy)) { + dptr->startAsyncRead(); + } else { // driver queue is emplty, so startup wait comm event + CommOverlappedEventNotifier *n = + reinterpret_cast(dptr->lookupCommEventNotifier()); + if (n) + n->startWaitCommEvent(); + } + + return ret; + } +}; + +class WriteOverlappedCompletionNotifier : public AbstractOverlappedEventNotifier +{ +public: + WriteOverlappedCompletionNotifier(QSerialPortPrivate *d, QObject *parent) + : AbstractOverlappedEventNotifier(d, WriteCompletionEvent, false, parent) {} + + bool processCompletionRoutine() Q_DECL_OVERRIDE { + setEnabled(false); + DWORD numberOfBytesTransferred = 0; + ::GetOverlappedResult(dptr->descriptor, &o, &numberOfBytesTransferred, FALSE); + return dptr->completeAsyncWrite(numberOfBytesTransferred); + } +}; + +QSerialPortPrivate::QSerialPortPrivate(QSerialPort *q) + : QSerialPortPrivateData(q) + , descriptor(INVALID_HANDLE_VALUE) + , parityErrorOccurred(false) + , actualReadBufferSize(0) + , actualWriteBufferSize(0) + , acyncWritePosition(0) + , readyReadEmitted(0) + , writeSequenceStarted(false) +{ +} + +bool QSerialPortPrivate::open(QIODevice::OpenMode mode) +{ + DWORD desiredAccess = 0; + DWORD originalEventMask = EV_ERR; + + if (mode & QIODevice::ReadOnly) { + desiredAccess |= GENERIC_READ; + originalEventMask |= EV_RXCHAR; + } + if (mode & QIODevice::WriteOnly) + desiredAccess |= GENERIC_WRITE; + + descriptor = ::CreateFile(reinterpret_cast(systemLocation.utf16()), + desiredAccess, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + + if (descriptor == INVALID_HANDLE_VALUE) { + q_ptr->setError(decodeSystemError()); + return false; + } + + if (!::GetCommState(descriptor, &restoredDcb)) { + q_ptr->setError(decodeSystemError()); + return false; + } + + currentDcb = restoredDcb; + currentDcb.fBinary = TRUE; + currentDcb.fInX = FALSE; + currentDcb.fOutX = FALSE; + currentDcb.fAbortOnError = FALSE; + currentDcb.fNull = FALSE; + currentDcb.fErrorChar = FALSE; + + if (!updateDcb()) + return false; + + if (!::GetCommTimeouts(descriptor, &restoredCommTimeouts)) { + q_ptr->setError(decodeSystemError()); + return false; + } + + ::memset(¤tCommTimeouts, 0, sizeof(currentCommTimeouts)); + currentCommTimeouts.ReadIntervalTimeout = MAXDWORD; + + if (!updateCommTimeouts()) + return false; + + if (originalEventMask & EV_RXCHAR) { + QWinEventNotifier *n = new ReadOverlappedCompletionNotifier(this, q_ptr); + n->setEnabled(true); + } + + QWinEventNotifier *n = new CommOverlappedEventNotifier(this, originalEventMask, q_ptr); + n->setEnabled(true); + + detectDefaultSettings(); + return true; +} + +void QSerialPortPrivate::close() +{ + ::CancelIo(descriptor); + + qDeleteAll(notifiers); + notifiers.clear(); + + readBuffer.clear(); + actualReadBufferSize = 0; + + writeSequenceStarted = false; + writeBuffer.clear(); + actualWriteBufferSize = 0; + acyncWritePosition = 0; + + readyReadEmitted = false; + parityErrorOccurred = false; + + if (settingsRestoredOnClose) { + ::SetCommState(descriptor, &restoredDcb); + ::SetCommTimeouts(descriptor, &restoredCommTimeouts); + } + + ::CloseHandle(descriptor); + descriptor = INVALID_HANDLE_VALUE; +} + +#endif // #ifndef Q_OS_WINCE + +QSerialPort::PinoutSignals QSerialPortPrivate::pinoutSignals() const +{ + DWORD modemStat = 0; + QSerialPort::PinoutSignals ret = QSerialPort::NoSignal; + + if (!::GetCommModemStatus(descriptor, &modemStat)) { + q_ptr->setError(decodeSystemError()); + return ret; + } + + if (modemStat & MS_CTS_ON) + ret |= QSerialPort::ClearToSendSignal; + if (modemStat & MS_DSR_ON) + ret |= QSerialPort::DataSetReadySignal; + if (modemStat & MS_RING_ON) + ret |= QSerialPort::RingIndicatorSignal; + if (modemStat & MS_RLSD_ON) + ret |= QSerialPort::DataCarrierDetectSignal; + + DWORD bytesReturned = 0; + if (::DeviceIoControl(descriptor, IOCTL_SERIAL_GET_DTRRTS, NULL, 0, + &modemStat, sizeof(modemStat), + &bytesReturned, NULL)) { + + if (modemStat & SERIAL_DTR_STATE) + ret |= QSerialPort::DataTerminalReadySignal; + if (modemStat & SERIAL_RTS_STATE) + ret |= QSerialPort::RequestToSendSignal; + } + + return ret; +} + +bool QSerialPortPrivate::setDataTerminalReady(bool set) +{ + return ::EscapeCommFunction(descriptor, set ? SETDTR : CLRDTR); +} + +bool QSerialPortPrivate::setRequestToSend(bool set) +{ + return ::EscapeCommFunction(descriptor, set ? SETRTS : CLRRTS); +} + +#ifndef Q_OS_WINCE + +bool QSerialPortPrivate::flush() +{ + return startAsyncWrite() && ::FlushFileBuffers(descriptor); +} + +bool QSerialPortPrivate::clear(QSerialPort::Directions dir) +{ + DWORD flags = 0; + if (dir & QSerialPort::Input) { + flags |= PURGE_RXABORT | PURGE_RXCLEAR; + actualReadBufferSize = 0; + } + if (dir & QSerialPort::Output) { + flags |= PURGE_TXABORT | PURGE_TXCLEAR; + actualWriteBufferSize = 0; + acyncWritePosition = 0; + writeSequenceStarted = false; + } + return ::PurgeComm(descriptor, flags); +} + +#endif + +bool QSerialPortPrivate::sendBreak(int duration) +{ + // FIXME: + if (setBreakEnabled(true)) { + ::Sleep(duration); + if (setBreakEnabled(false)) + return true; + } + return false; +} + +bool QSerialPortPrivate::setBreakEnabled(bool set) +{ + if (set) + return ::SetCommBreak(descriptor); + return ::ClearCommBreak(descriptor); +} + +qint64 QSerialPortPrivate::systemInputQueueSize () const +{ + COMSTAT cs; + ::memset(&cs, 0, sizeof(cs)); + if (!::ClearCommError(descriptor, NULL, &cs)) + return -1; + return cs.cbInQue; +} + +qint64 QSerialPortPrivate::systemOutputQueueSize () const +{ + COMSTAT cs; + ::memset(&cs, 0, sizeof(cs)); + if (!::ClearCommError(descriptor, NULL, &cs)) + return -1; + return cs.cbOutQue; +} + +#ifndef Q_OS_WINCE + +qint64 QSerialPortPrivate::bytesAvailable() const +{ + return actualReadBufferSize; +} + +qint64 QSerialPortPrivate::readFromBuffer(char *data, qint64 maxSize) +{ + if (actualReadBufferSize == 0) + return 0; + + qint64 readSoFar = -1; + if (maxSize == 1 && actualReadBufferSize > 0) { + *data = readBuffer.getChar(); + actualReadBufferSize--; + readSoFar = 1; + } else { + const qint64 bytesToRead = qMin(qint64(actualReadBufferSize), maxSize); + readSoFar = 0; + while (readSoFar < bytesToRead) { + const char *ptr = readBuffer.readPointer(); + const int bytesToReadFromThisBlock = qMin(bytesToRead - readSoFar, + qint64(readBuffer.nextDataBlockSize())); + ::memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock); + readSoFar += bytesToReadFromThisBlock; + readBuffer.free(bytesToReadFromThisBlock); + actualReadBufferSize -= bytesToReadFromThisBlock; + } + } + + return readSoFar; +} + +qint64 QSerialPortPrivate::writeToBuffer(const char *data, qint64 maxSize) +{ + char *ptr = writeBuffer.reserve(maxSize); + if (maxSize == 1) { + *ptr = *data; + actualWriteBufferSize++; + } else { + ::memcpy(ptr, data, maxSize); + actualWriteBufferSize += maxSize; + } + + if (!writeSequenceStarted) + startAsyncWrite(WriteChunkSize); + + return maxSize; +} + +bool QSerialPortPrivate::waitForReadyRead(int msecs) +{ + QElapsedTimer stopWatch; + stopWatch.start(); + + do { + bool timedOut = false; + AbstractOverlappedEventNotifier *n = 0; + + if (!waitAnyEvent(timeoutValue(msecs, stopWatch.elapsed()), &timedOut, &n) || !n) { + // This is occur timeout or another error + q_ptr->setError(decodeSystemError()); + return false; + } + + switch (n->type()) { + case AbstractOverlappedEventNotifier::CommEvent: + if (!n->processCompletionRoutine()) + return false; + break; + case AbstractOverlappedEventNotifier::ReadCompletionEvent: + return n->processCompletionRoutine(); + case AbstractOverlappedEventNotifier::WriteCompletionEvent: + n->processCompletionRoutine(); + break; + default: // newer called + return false; + } + } while (msecs == -1 || timeoutValue(msecs, stopWatch.elapsed()) > 0); + + return false; +} + +bool QSerialPortPrivate::waitForBytesWritten(int msecs) +{ + if (writeBuffer.isEmpty()) + return false; + + QElapsedTimer stopWatch; + stopWatch.start(); + + forever { + bool timedOut = false; + AbstractOverlappedEventNotifier *n = 0; + + if (!waitAnyEvent(timeoutValue(msecs, stopWatch.elapsed()), &timedOut, &n) || !n) { + q_ptr->setError(decodeSystemError()); + return false; + } + + switch (n->type()) { + case AbstractOverlappedEventNotifier::CommEvent: + // do nothing, jump to ReadCompletionEvent case + case AbstractOverlappedEventNotifier::ReadCompletionEvent: + n->processCompletionRoutine(); + break; + case AbstractOverlappedEventNotifier::WriteCompletionEvent: + return n->processCompletionRoutine(); + default: // newer called + return false; + } + } + + return false; +} + +#endif // #ifndef Q_OS_WINCE + +bool QSerialPortPrivate::setBaudRate(qint32 baudRate, QSerialPort::Directions dir) +{ + if (dir != QSerialPort::AllDirections) { + q_ptr->setError(QSerialPort::UnsupportedOperationError); + return false; + } + currentDcb.BaudRate = baudRate; + return updateDcb(); +} + +bool QSerialPortPrivate::setDataBits(QSerialPort::DataBits dataBits) +{ + currentDcb.ByteSize = dataBits; + return updateDcb(); +} + +bool QSerialPortPrivate::setParity(QSerialPort::Parity parity) +{ + currentDcb.fParity = TRUE; + switch (parity) { + case QSerialPort::NoParity: + currentDcb.Parity = NOPARITY; + currentDcb.fParity = FALSE; + break; + case QSerialPort::OddParity: + currentDcb.Parity = ODDPARITY; + break; + case QSerialPort::EvenParity: + currentDcb.Parity = EVENPARITY; + break; + case QSerialPort::MarkParity: + currentDcb.Parity = MARKPARITY; + break; + case QSerialPort::SpaceParity: + currentDcb.Parity = SPACEPARITY; + break; + default: + currentDcb.Parity = NOPARITY; + currentDcb.fParity = FALSE; + break; + } + return updateDcb(); +} + +bool QSerialPortPrivate::setStopBits(QSerialPort::StopBits stopBits) +{ + switch (stopBits) { + case QSerialPort::OneStop: + currentDcb.StopBits = ONESTOPBIT; + break; + case QSerialPort::OneAndHalfStop: + currentDcb.StopBits = ONE5STOPBITS; + break; + case QSerialPort::TwoStop: + currentDcb.StopBits = TWOSTOPBITS; + break; + default: + currentDcb.StopBits = ONESTOPBIT; + break; + } + return updateDcb(); +} + +bool QSerialPortPrivate::setFlowControl(QSerialPort::FlowControl flow) +{ + currentDcb.fInX = FALSE; + currentDcb.fOutX = FALSE; + currentDcb.fOutxCtsFlow = FALSE; + currentDcb.fRtsControl = RTS_CONTROL_DISABLE; + switch (flow) { + case QSerialPort::NoFlowControl: + break; + case QSerialPort::SoftwareControl: + currentDcb.fInX = TRUE; + currentDcb.fOutX = TRUE; + break; + case QSerialPort::HardwareControl: + currentDcb.fOutxCtsFlow = TRUE; + currentDcb.fRtsControl = RTS_CONTROL_HANDSHAKE; + break; + default: + break; + } + return updateDcb(); +} + +bool QSerialPortPrivate::setDataErrorPolicy(QSerialPort::DataErrorPolicy policy) +{ + policy = policy; + return true; +} + +#ifndef Q_OS_WINCE + +bool QSerialPortPrivate::startAsyncRead() +{ + DWORD bytesToRead = policy == QSerialPort::IgnorePolicy ? ReadChunkSize : 1; + + if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) { + bytesToRead = readBufferMaxSize - readBuffer.size(); + if (bytesToRead == 0) { + // Buffer is full. User must read data from the buffer + // before we can read more from the port. + return false; + } + } + + AbstractOverlappedEventNotifier *n = lookupReadCompletionNotifier(); + if (!n) { + q_ptr->setError(QSerialPort::ResourceError); + return false; + } + + char *ptr = readBuffer.reserve(bytesToRead); + + if (::ReadFile(descriptor, ptr, bytesToRead, NULL, n->overlappedPointer())) + return true; + + QSerialPort::SerialPortError error = decodeSystemError(); + if (error != QSerialPort::NoError) { + if (error != QSerialPort::ResourceError) + error = QSerialPort::ReadError; + q_ptr->setError(error); + + readBuffer.truncate(actualReadBufferSize); + return false; + } + + return true; +} + +bool QSerialPortPrivate::startAsyncWrite(int maxSize) +{ + qint64 nextSize = 0; + const char *ptr = writeBuffer.readPointerAtPosition(acyncWritePosition, nextSize); + + nextSize = qMin(nextSize, qint64(maxSize)); + acyncWritePosition += nextSize; + + // no more data to write + if (!ptr || nextSize == 0) + return true; + + writeSequenceStarted = true; + + AbstractOverlappedEventNotifier *n = lookupFreeWriteCompletionNotifier(); + if (!n) { + q_ptr->setError(QSerialPort::ResourceError); + return false; + } + + n->setEnabled(true); + + if (::WriteFile(descriptor, ptr, nextSize, NULL, n->overlappedPointer())) + return true; + + QSerialPort::SerialPortError error = decodeSystemError(); + if (error != QSerialPort::NoError) { + writeSequenceStarted = false; + + if (error != QSerialPort::ResourceError) + error = QSerialPort::WriteError; + + q_ptr->setError(error); + return false; + } + + return true; +} + +#endif // #ifndef Q_OS_WINCE + +bool QSerialPortPrivate::processIoErrors(bool error) +{ + if (error) { + q_ptr->setError(QSerialPort::ResourceError); + return true; + } + + DWORD errors = 0; + const bool ret = ::ClearCommError(descriptor, &errors, NULL); + if (ret && errors) { + if (errors & CE_FRAME) { + q_ptr->setError(QSerialPort::FramingError); + } else if (errors & CE_RXPARITY) { + q_ptr->setError(QSerialPort::ParityError); + parityErrorOccurred = true; + } else if (errors & CE_BREAK) { + q_ptr->setError(QSerialPort::BreakConditionError); + } else { + q_ptr->setError(QSerialPort::UnknownError); + } + } + return ret; +} + +#ifndef Q_OS_WINCE + +bool QSerialPortPrivate::completeAsyncRead(DWORD numberOfBytes) +{ + actualReadBufferSize += qint64(numberOfBytes); + readBuffer.truncate(actualReadBufferSize); + + if (numberOfBytes > 0) { + + // Process emulate policy. + if ((policy != QSerialPort::IgnorePolicy) && parityErrorOccurred) { + + parityErrorOccurred = false; + + // Ignore received character, remove it from buffer + if (policy == QSerialPort::SkipPolicy) { + readBuffer.getChar(); + // Force returning without emitting a readyRead() signal + return true; + } + + // Abort receiving + if (policy == QSerialPort::StopReceivingPolicy) { + readyReadEmitted = true; + emit q_ptr->readyRead(); + return true; + } + + // Replace received character by zero + if (policy == QSerialPort::PassZeroPolicy) { + readBuffer.getChar(); + readBuffer.putChar('\0'); + } + + } + + readyReadEmitted = true; + emit q_ptr->readyRead(); + } + return true; +} + +bool QSerialPortPrivate::completeAsyncWrite(DWORD numberOfBytes) +{ + writeBuffer.free(numberOfBytes); + actualWriteBufferSize -= qint64(numberOfBytes); + acyncWritePosition -= qint64(numberOfBytes); + + if (numberOfBytes > 0) + emit q_ptr->bytesWritten(numberOfBytes); + + if (writeBuffer.isEmpty()) + writeSequenceStarted = false; + else + startAsyncWrite(WriteChunkSize); + + return true; +} + +AbstractOverlappedEventNotifier *QSerialPortPrivate::lookupFreeWriteCompletionNotifier() +{ + // find first free not running write notifier + foreach (AbstractOverlappedEventNotifier *n, notifiers) { + if ((n->type() == AbstractOverlappedEventNotifier::WriteCompletionEvent) + && !n->isEnabled()) { + return n; + } + } + // if all write notifiers in use, then create new write notifier + return new WriteOverlappedCompletionNotifier(this, q_ptr); +} + +AbstractOverlappedEventNotifier *QSerialPortPrivate::lookupCommEventNotifier() +{ + foreach (AbstractOverlappedEventNotifier *n, notifiers) { + if (n->type() == AbstractOverlappedEventNotifier::CommEvent) + return n; + } + return 0; +} + +AbstractOverlappedEventNotifier *QSerialPortPrivate::lookupReadCompletionNotifier() +{ + foreach (AbstractOverlappedEventNotifier *n, notifiers) { + if (n->type() == AbstractOverlappedEventNotifier::ReadCompletionEvent) + return n; + } + return 0; +} + +bool QSerialPortPrivate::updateDcb() +{ + if (!::SetCommState(descriptor, ¤tDcb)) { + q_ptr->setError(decodeSystemError()); + return false; + } + return true; +} + +bool QSerialPortPrivate::updateCommTimeouts() +{ + if (!::SetCommTimeouts(descriptor, ¤tCommTimeouts)) { + q_ptr->setError(decodeSystemError()); + return false; + } + return true; +} + +#endif // #ifndef Q_OS_WINCE + +void QSerialPortPrivate::detectDefaultSettings() +{ + // Detect baud rate. + inputBaudRate = quint32(currentDcb.BaudRate); + outputBaudRate = inputBaudRate; + + // Detect databits. + switch (currentDcb.ByteSize) { + case 5: + dataBits = QSerialPort::Data5; + break; + case 6: + dataBits = QSerialPort::Data6; + break; + case 7: + dataBits = QSerialPort::Data7; + break; + case 8: + dataBits = QSerialPort::Data8; + break; + default: + dataBits = QSerialPort::UnknownDataBits; + break; + } + + // Detect parity. + if ((currentDcb.Parity == NOPARITY) && !currentDcb.fParity) + parity = QSerialPort::NoParity; + else if ((currentDcb.Parity == SPACEPARITY) && currentDcb.fParity) + parity = QSerialPort::SpaceParity; + else if ((currentDcb.Parity == MARKPARITY) && currentDcb.fParity) + parity = QSerialPort::MarkParity; + else if ((currentDcb.Parity == EVENPARITY) && currentDcb.fParity) + parity = QSerialPort::EvenParity; + else if ((currentDcb.Parity == ODDPARITY) && currentDcb.fParity) + parity = QSerialPort::OddParity; + else + parity = QSerialPort::UnknownParity; + + // Detect stopbits. + switch (currentDcb.StopBits) { + case ONESTOPBIT: + stopBits = QSerialPort::OneStop; + break; + case ONE5STOPBITS: + stopBits = QSerialPort::OneAndHalfStop; + break; + case TWOSTOPBITS: + stopBits = QSerialPort::TwoStop; + break; + default: + stopBits = QSerialPort::UnknownStopBits; + break; + } + + // Detect flow control. + if (!currentDcb.fOutxCtsFlow && (currentDcb.fRtsControl == RTS_CONTROL_DISABLE) + && !currentDcb.fInX && !currentDcb.fOutX) { + flow = QSerialPort::NoFlowControl; + } else if (!currentDcb.fOutxCtsFlow && (currentDcb.fRtsControl == RTS_CONTROL_DISABLE) + && currentDcb.fInX && currentDcb.fOutX) { + flow = QSerialPort::SoftwareControl; + } else if (currentDcb.fOutxCtsFlow && (currentDcb.fRtsControl == RTS_CONTROL_HANDSHAKE) + && !currentDcb.fInX && !currentDcb.fOutX) { + flow = QSerialPort::HardwareControl; + } else + flow = QSerialPort::UnknownFlowControl; +} + +QSerialPort::SerialPortError QSerialPortPrivate::decodeSystemError() const +{ + QSerialPort::SerialPortError error; + switch (::GetLastError()) { + case ERROR_IO_PENDING: + error = QSerialPort::NoError; + break; + case ERROR_MORE_DATA: + error = QSerialPort::NoError; + break; + case ERROR_FILE_NOT_FOUND: + error = QSerialPort::DeviceNotFoundError; + break; + case ERROR_ACCESS_DENIED: + error = QSerialPort::PermissionError; + break; + case ERROR_INVALID_HANDLE: + error = QSerialPort::ResourceError; + break; + case ERROR_INVALID_PARAMETER: + error = QSerialPort::UnsupportedOperationError; + break; + case ERROR_BAD_COMMAND: + error = QSerialPort::ResourceError; + break; + case ERROR_DEVICE_REMOVED: + error = QSerialPort::ResourceError; + break; + default: + error = QSerialPort::UnknownError; + break; + } + return error; +} + +#ifndef Q_OS_WINCE + +bool QSerialPortPrivate::waitAnyEvent(int msecs, bool *timedOut, + AbstractOverlappedEventNotifier **triggeredNotifier) +{ + Q_ASSERT(timedOut); + + QVector handles = notifiers.keys().toVector(); + DWORD waitResult = ::WaitForMultipleObjects(handles.count(), + handles.constData(), + FALSE, // wait any event + qMax(msecs, 0)); + if (waitResult == WAIT_TIMEOUT) { + *timedOut = true; + return false; + } + + if (int(waitResult) > (handles.count() - 1)) + return false; + + HANDLE h = handles.at(waitResult - WAIT_OBJECT_0); + *triggeredNotifier = notifiers.value(h); + return true; +} + +static const QLatin1String defaultPathPrefix("\\\\.\\"); + +QString QSerialPortPrivate::portNameToSystemLocation(const QString &port) +{ + QString ret = port; + if (!ret.contains(defaultPathPrefix)) + ret.prepend(defaultPathPrefix); + return ret; +} + +QString QSerialPortPrivate::portNameFromSystemLocation(const QString &location) +{ + QString ret = location; + if (ret.contains(defaultPathPrefix)) + ret.remove(defaultPathPrefix); + return ret; +} + +#endif // #ifndef Q_OS_WINCE + +// This table contains standard values of baud rates that +// are defined in MSDN and/or in Win SDK file winbase.h + +static const QList standardBaudRatePairList() +{ + + static const QList standardBaudRatesTable = QList() + + #ifdef CBR_110 + << CBR_110 + #endif + + #ifdef CBR_300 + << CBR_300 + #endif + + #ifdef CBR_600 + << CBR_600 + #endif + + #ifdef CBR_1200 + << CBR_1200 + #endif + + #ifdef CBR_2400 + << CBR_2400 + #endif + + #ifdef CBR_4800 + << CBR_4800 + #endif + + #ifdef CBR_9600 + << CBR_9600 + #endif + + #ifdef CBR_14400 + << CBR_14400 + #endif + + #ifdef CBR_19200 + << CBR_19200 + #endif + + #ifdef CBR_38400 + << CBR_38400 + #endif + + #ifdef CBR_56000 + << CBR_56000 + #endif + + #ifdef CBR_57600 + << CBR_57600 + #endif + + #ifdef CBR_115200 + << CBR_115200 + #endif + + #ifdef CBR_128000 + << CBR_128000 + #endif + + #ifdef CBR_256000 + << CBR_256000 + #endif + ; + + return standardBaudRatesTable; +}; + +qint32 QSerialPortPrivate::baudRateFromSetting(qint32 setting) +{ + const QList baudRatePairs = standardBaudRatePairList(); + const QList::const_iterator baudRatePairListConstIterator = qFind(baudRatePairs, setting); + + return (baudRatePairListConstIterator != baudRatePairs.constEnd()) ? *baudRatePairListConstIterator : 0; +} + +qint32 QSerialPortPrivate::settingFromBaudRate(qint32 baudRate) +{ + const QList baudRatePairList = standardBaudRatePairList(); + const QList::const_iterator baudRatePairListConstIterator = qFind(baudRatePairList, baudRate); + + return (baudRatePairListConstIterator != baudRatePairList.constEnd()) ? *baudRatePairListConstIterator : 0; +} + +QList QSerialPortPrivate::standardBaudRates() +{ + QList ret; + const QList baudRatePairs = standardBaudRatePairList(); + + foreach (qint32 baudRatePair, baudRatePairs) { + ret.append(baudRatePair); + } + + return ret; +} + +QT_END_NAMESPACE diff --git a/libs/serialport/qserialport_win_p.h b/libs/serialport/qserialport_win_p.h new file mode 100644 index 000000000..a59ea9a7b --- /dev/null +++ b/libs/serialport/qserialport_win_p.h @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERIALPORT_WIN_P_H +#define QSERIALPORT_WIN_P_H + +#include "qserialport_p.h" + +#include + +#ifndef Q_OS_WINCE +#include +QT_BEGIN_NAMESPACE +class QWinEventNotifier; +#else +#include +QT_BEGIN_NAMESPACE +class QThread; +#endif + +#ifndef Q_OS_WINCE +class AbstractOverlappedEventNotifier; +#endif + +class QSerialPortPrivate : public QSerialPortPrivateData +{ +public: + QSerialPortPrivate(QSerialPort *q); + + bool open(QIODevice::OpenMode mode); + void close(); + + QSerialPort::PinoutSignals pinoutSignals() const; + + bool setDataTerminalReady(bool set); + bool setRequestToSend(bool set); + + bool flush(); + bool clear(QSerialPort::Directions dir); + + bool sendBreak(int duration); + bool setBreakEnabled(bool set); + + qint64 systemInputQueueSize () const; + qint64 systemOutputQueueSize () const; + + qint64 bytesAvailable() const; + + qint64 readFromBuffer(char *data, qint64 maxSize); + qint64 writeToBuffer(const char *data, qint64 maxSize); + + bool waitForReadyRead(int msec); + bool waitForBytesWritten(int msec); + + bool setBaudRate(qint32 baudRate, QSerialPort::Directions dir); + bool setDataBits(QSerialPort::DataBits dataBits); + bool setParity(QSerialPort::Parity parity); + bool setStopBits(QSerialPort::StopBits stopBits); + bool setFlowControl(QSerialPort::FlowControl flowControl); + bool setDataErrorPolicy(QSerialPort::DataErrorPolicy policy); + + bool processIoErrors(bool error); +#ifndef Q_OS_WINCE + bool startAsyncRead(); + bool startAsyncWrite(int maxSize = INT_MAX); + bool completeAsyncRead(DWORD numberOfBytes); + bool completeAsyncWrite(DWORD numberOfBytes); + AbstractOverlappedEventNotifier *lookupFreeWriteCompletionNotifier(); + AbstractOverlappedEventNotifier *lookupCommEventNotifier(); + AbstractOverlappedEventNotifier *lookupReadCompletionNotifier(); +#else + bool notifyRead(); + bool notifyWrite(int maxSize = INT_MAX); +#endif + + static QString portNameToSystemLocation(const QString &port); + static QString portNameFromSystemLocation(const QString &location); + + static qint32 baudRateFromSetting(qint32 setting); + static qint32 settingFromBaudRate(qint32 baudRate); + + static QList standardBaudRates(); + + DCB currentDcb; + DCB restoredDcb; + COMMTIMEOUTS currentCommTimeouts; + COMMTIMEOUTS restoredCommTimeouts; + HANDLE descriptor; + bool parityErrorOccurred; + +#ifndef Q_OS_WINCE + QHash notifiers; + qint64 actualReadBufferSize; + qint64 actualWriteBufferSize; + qint64 acyncWritePosition; + bool readyReadEmitted; + bool writeSequenceStarted; +#else + QThread *eventNotifier; + QMutex settingsChangeMutex; +#endif + +private: + bool updateDcb(); + bool updateCommTimeouts(); + + void detectDefaultSettings(); + QSerialPort::SerialPortError decodeSystemError() const; + +#ifndef Q_OS_WINCE + bool waitAnyEvent(int msecs, bool *timedOut, + AbstractOverlappedEventNotifier **triggeredNotifier); +#else + bool waitForReadOrWrite(bool *selectForRead, bool *selectForWrite, + bool checkRead, bool checkWrite, + int msecs, bool *timedOut); +#endif + +}; + +QT_END_NAMESPACE + +#endif // QSERIALPORT_WIN_P_H diff --git a/libs/serialport/qserialport_wince.cpp b/libs/serialport/qserialport_wince.cpp new file mode 100644 index 000000000..78d2746b1 --- /dev/null +++ b/libs/serialport/qserialport_wince.cpp @@ -0,0 +1,509 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Copyright (C) 2012 Laszlo Papp +** Copyright (C) 2012 Andre Hartmann +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialport_win_p.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QSerialPortPrivate; + +class CommEventNotifier : public QThread +{ + Q_OBJECT +signals: + void eventMask(quint32 mask); + +public: + CommEventNotifier(DWORD mask, QSerialPortPrivate *d, QObject *parent) + : QThread(parent), dptr(d), running(true) { + connect(this, SIGNAL(eventMask(quint32)), this, SLOT(processNotification(quint32))); + ::SetCommMask(dptr->descriptor, mask); + } + + virtual ~CommEventNotifier() { + running = false; + ::SetCommMask(dptr->descriptor, 0); + wait(); + } + +protected: + void run() Q_DECL_OVERRIDE { + DWORD mask = 0; + while (running) { + if (::WaitCommEvent(dptr->descriptor, &mask, FALSE)) { + // Wait until complete the operation changes the port settings, + // see updateDcb(). + dptr->settingsChangeMutex.lock(); + dptr->settingsChangeMutex.unlock(); + emit eventMask(quint32(mask)); + } + } + } + +private slots: + void processNotification(quint32 eventMask) { + + bool error = false; + + // Check for unexpected event. This event triggered when pulled previously + // opened device from the system, when opened as for not to read and not to + // write options and so forth. + if ((eventMask == 0) + || ((eventMask & (EV_ERR | EV_RXCHAR | EV_TXEMPTY)) == 0)) { + error = true; + } + + if (error || (EV_ERR & eventMask)) + dptr->processIoErrors(error); + if (EV_RXCHAR & eventMask) + dptr->notifyRead(); + if (EV_TXEMPTY & eventMask) + dptr->notifyWrite(QSerialPortPrivateData::WriteChunkSize); + } + +private: + QSerialPortPrivate *dptr; + mutable bool running; +}; + +class WaitCommEventBreaker : public QThread +{ + Q_OBJECT +public: + WaitCommEventBreaker(HANDLE descriptor, int timeout, QObject *parent = 0) + : QThread(parent), descriptor(descriptor), timeout(timeout), worked(false) { + start(); + } + + virtual ~WaitCommEventBreaker() { + stop(); + wait(); + } + + void stop() { + exit(0); + } + + bool isWorked() const { + return worked; + } + +protected: + void run() { + QTimer timer; + QObject::connect(&timer, SIGNAL(timeout()), this, SLOT(processTimeout()), Qt::DirectConnection); + timer.start(timeout); + exec(); + worked = true; + } + +private slots: + void processTimeout() { + ::SetCommMask(descriptor, 0); + stop(); + } + +private: + HANDLE descriptor; + int timeout; + mutable bool worked; +}; + +QSerialPortPrivate::QSerialPortPrivate(QSerialPort *q) + : QSerialPortPrivateData(q) + , descriptor(INVALID_HANDLE_VALUE) + , parityErrorOccurred(false) + , eventNotifier(0) +{ +} + +bool QSerialPortPrivate::open(QIODevice::OpenMode mode) +{ + DWORD desiredAccess = 0; + DWORD eventMask = EV_ERR; + + if (mode & QIODevice::ReadOnly) { + desiredAccess |= GENERIC_READ; + eventMask |= EV_RXCHAR; + } + if (mode & QIODevice::WriteOnly) { + desiredAccess |= GENERIC_WRITE; + eventMask |= EV_TXEMPTY; + } + + descriptor = ::CreateFile(reinterpret_cast(systemLocation.utf16()), + desiredAccess, 0, NULL, OPEN_EXISTING, 0, NULL); + + if (descriptor == INVALID_HANDLE_VALUE) { + q_ptr->setError(decodeSystemError()); + return false; + } + + if (!::GetCommState(descriptor, &restoredDcb)) { + q_ptr->setError(decodeSystemError()); + return false; + } + + currentDcb = restoredDcb; + currentDcb.fBinary = true; + currentDcb.fInX = false; + currentDcb.fOutX = false; + currentDcb.fAbortOnError = false; + currentDcb.fNull = false; + currentDcb.fErrorChar = false; + + if (!updateDcb()) + return false; + + if (!::GetCommTimeouts(descriptor, &restoredCommTimeouts)) { + q_ptr->setError(decodeSystemError()); + return false; + } + + ::memset(¤tCommTimeouts, 0, sizeof(currentCommTimeouts)); + currentCommTimeouts.ReadIntervalTimeout = MAXDWORD; + + if (!updateCommTimeouts()) + return false; + + eventNotifier = new CommEventNotifier(eventMask, this, q_ptr); + eventNotifier->start(); + + detectDefaultSettings(); + return true; +} + +void QSerialPortPrivate::close() +{ + if (eventNotifier) { + eventNotifier->deleteLater(); + eventNotifier = 0; + } + + if (settingsRestoredOnClose) { + ::SetCommState(descriptor, &restoredDcb); + ::SetCommTimeouts(descriptor, &restoredCommTimeouts); + } + + ::CloseHandle(descriptor); + descriptor = INVALID_HANDLE_VALUE; +} + +bool QSerialPortPrivate::flush() +{ + return notifyWrite() && ::FlushFileBuffers(descriptor); +} + +bool QSerialPortPrivate::clear(QSerialPort::Directions dir) +{ + DWORD flags = 0; + if (dir & QSerialPort::Input) + flags |= PURGE_RXABORT | PURGE_RXCLEAR; + if (dir & QSerialPort::Output) + flags |= PURGE_TXABORT | PURGE_TXCLEAR; + return ::PurgeComm(descriptor, flags); +} + +qint64 QSerialPortPrivate::bytesAvailable() const +{ + return readBuffer.size(); +} + +qint64 QSerialPortPrivate::readFromBuffer(char *data, qint64 maxSize) +{ + if (readBuffer.isEmpty()) + return 0; + + if (maxSize == 1) { + *data = readBuffer.getChar(); + return 1; + } + + const qint64 bytesToRead = qMin(qint64(readBuffer.size()), maxSize); + qint64 readSoFar = 0; + while (readSoFar < bytesToRead) { + const char *ptr = readBuffer.readPointer(); + const int bytesToReadFromThisBlock = qMin(int(bytesToRead - readSoFar), + readBuffer.nextDataBlockSize()); + ::memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock); + readSoFar += bytesToReadFromThisBlock; + readBuffer.free(bytesToReadFromThisBlock); + } + + return readSoFar; +} + +qint64 QSerialPortPrivate::writeToBuffer(const char *data, qint64 maxSize) +{ + char *ptr = writeBuffer.reserve(maxSize); + if (maxSize == 1) + *ptr = *data; + else + ::memcpy(ptr, data, maxSize); + + // trigger write sequence + notifyWrite(QSerialPortPrivateData::WriteChunkSize); + + return maxSize; +} + +bool QSerialPortPrivate::waitForReadyRead(int msec) +{ + if (!readBuffer.isEmpty()) + return true; + + QElapsedTimer stopWatch; + + stopWatch.start(); + + forever { + bool readyToRead = false; + bool readyToWrite = false; + bool timedOut = false; + if (!waitForReadOrWrite(&readyToRead, &readyToWrite, + true, !writeBuffer.isEmpty(), + timeoutValue(msec, stopWatch.elapsed()), + &timedOut)) { + return false; + } + if (readyToRead) { + if (notifyRead()) + return true; + } + if (readyToWrite) + notifyWrite(WriteChunkSize); + } + return false; +} + +bool QSerialPortPrivate::waitForBytesWritten(int msec) +{ + if (writeBuffer.isEmpty()) + return false; + + QElapsedTimer stopWatch; + + stopWatch.start(); + + forever { + bool readyToRead = false; + bool readyToWrite = false; + bool timedOut = false; + if (!waitForReadOrWrite(&readyToRead, &readyToWrite, + true, !writeBuffer.isEmpty(), + timeoutValue(msec, stopWatch.elapsed()), + &timedOut)) { + return false; + } + if (readyToRead) { + if (!notifyRead()) + return false; + } + if (readyToWrite) { + if (notifyWrite(WriteChunkSize)) + return true; + } + } + return false; +} + +bool QSerialPortPrivate::notifyRead() +{ + DWORD bytesToRead = (policy == QSerialPort::IgnorePolicy) ? ReadChunkSize : 1; + + if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) { + bytesToRead = readBufferMaxSize - readBuffer.size(); + if (bytesToRead == 0) { + // Buffer is full. User must read data from the buffer + // before we can read more from the port. + return false; + } + } + + char *ptr = readBuffer.reserve(bytesToRead); + + DWORD readBytes = 0; + BOOL sucessResult = ::ReadFile(descriptor, ptr, bytesToRead, &readBytes, NULL); + + if (!sucessResult) { + readBuffer.truncate(bytesToRead); + q_ptr->setError(QSerialPort::ReadError); + return false; + } + + readBuffer.truncate(readBytes); + + // Process emulate policy. + if ((policy != QSerialPort::IgnorePolicy) && parityErrorOccurred) { + + parityErrorOccurred = false; + + switch (policy) { + case QSerialPort::SkipPolicy: + readBuffer.getChar(); + return true; + case QSerialPort::PassZeroPolicy: + readBuffer.getChar(); + readBuffer.putChar('\0'); + break; + case QSerialPort::StopReceivingPolicy: + // FIXME: Maybe need disable read notifier? + break; + default: + break; + } + } + + if (readBytes > 0) + emit q_ptr->readyRead(); + + return true; +} + +bool QSerialPortPrivate::notifyWrite(int maxSize) +{ + int nextSize = qMin(writeBuffer.nextDataBlockSize(), maxSize); + + const char *ptr = writeBuffer.readPointer(); + + DWORD bytesWritten = 0; + if (!::WriteFile(descriptor, ptr, nextSize, &bytesWritten, NULL)) { + q_ptr->setError(QSerialPort::WriteError); + return false; + } + + writeBuffer.free(bytesWritten); + + if (bytesWritten > 0) + emit q_ptr->bytesWritten(bytesWritten); + + return true; +} + +bool QSerialPortPrivate::waitForReadOrWrite(bool *selectForRead, bool *selectForWrite, + bool checkRead, bool checkWrite, + int msecs, bool *timedOut) +{ + DWORD eventMask = 0; + // FIXME: Here the situation is not properly handled with zero timeout: + // breaker can work out before you call a method WaitCommEvent() + // and so it will loop forever! + WaitCommEventBreaker breaker(descriptor, qMax(msecs, 0)); + ::WaitCommEvent(descriptor, &eventMask, NULL); + breaker.stop(); + + if (breaker.isWorked()) + *timedOut = true; + + if (!breaker.isWorked()) { + if (checkRead) { + Q_ASSERT(selectForRead); + *selectForRead = eventMask & EV_RXCHAR; + } + if (checkWrite) { + Q_ASSERT(selectForWrite); + *selectForWrite = eventMask & EV_TXEMPTY; + } + + return true; + } + + return false; +} + +bool QSerialPortPrivate::updateDcb() +{ + QMutexLocker locker(&settingsChangeMutex); + + DWORD eventMask = 0; + // Save the event mask + if (!::GetCommMask(descriptor, &eventMask)) + return false; + + // Break event notifier from WaitCommEvent + ::SetCommMask(descriptor, 0); + // Change parameters + bool ret = ::SetCommState(descriptor, ¤tDcb); + if (!ret) + q_ptr->setError(decodeSystemError()); + // Restore the event mask + ::SetCommMask(descriptor, eventMask); + + return ret; +} + +bool QSerialPortPrivate::updateCommTimeouts() +{ + if (!::SetCommTimeouts(descriptor, ¤tCommTimeouts)) { + q_ptr->setError(decodeSystemError()); + return false; + } + return true; +} + +static const QLatin1String defaultPathPostfix(":"); + +QString QSerialPortPrivate::portNameToSystemLocation(const QString &port) +{ + QString ret = port; + if (!ret.contains(defaultPathPostfix)) + ret.append(defaultPathPostfix); + return ret; +} + +QString QSerialPortPrivate::portNameFromSystemLocation(const QString &location) +{ + QString ret = location; + if (ret.contains(defaultPathPostfix)) + ret.remove(defaultPathPostfix); + return ret; +} + +#include "qserialport_wince.moc" + +QT_END_NAMESPACE diff --git a/libs/serialport/qserialportglobal.h b/libs/serialport/qserialportglobal.h new file mode 100644 index 000000000..03cd6a62b --- /dev/null +++ b/libs/serialport/qserialportglobal.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERIALPORTGLOBAL_H +#define QSERIALPORTGLOBAL_H + +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_STATIC +# if defined(QT_BUILD_SERIALPORT_LIB) +# define Q_SERIALPORT_EXPORT Q_DECL_EXPORT +# else +# define Q_SERIALPORT_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_SERIALPORT_EXPORT +#endif + +// The macro has been available only since Qt 5.0 +#ifndef Q_DECL_OVERRIDE +#define Q_DECL_OVERRIDE +#endif + +QT_END_NAMESPACE + +#endif // QSERIALPORTGLOBAL_H diff --git a/libs/serialport/qserialportinfo.cpp b/libs/serialport/qserialportinfo.cpp new file mode 100644 index 000000000..241d344ae --- /dev/null +++ b/libs/serialport/qserialportinfo.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Copyright (C) 2011-2012 Denis Shienkov +** Copyright (C) 2011 Sergey Belyashov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialportinfo.h" +#include "qserialportinfo_p.h" +#include "qserialport.h" + +QT_BEGIN_NAMESPACE + + +/*! + \class QSerialPortInfo + + \brief Provides information about existing serial ports. + + \ingroup serialport-main + \inmodule QtSerialPort + \since 5.1 + + Use the static functions to generate a list of QSerialPortInfo + objects. Each QSerialPortInfo object in the list represents a single + serial port and can be queried for the port name, system location, + description, and manufacturer. The QSerialPortInfo class can also be + used as an input parameter for the setPort() method of the QSerialPort + class. + + \sa QSerialPort +*/ + +/*! + Constructs an empty QSerialPortInfo object. + + \sa isNull() +*/ +QSerialPortInfo::QSerialPortInfo() + : d_ptr(new QSerialPortInfoPrivate) +{ +} + +/*! + Constructs a copy of \a other. +*/ +QSerialPortInfo::QSerialPortInfo(const QSerialPortInfo &other) + : d_ptr(other.d_ptr ? new QSerialPortInfoPrivate(*other.d_ptr) : 0) +{ +} + +/*! + Constructs a QSerialPortInfo object from serial \a port. +*/ +QSerialPortInfo::QSerialPortInfo(const QSerialPort &port) + : d_ptr(new QSerialPortInfoPrivate) +{ + foreach (const QSerialPortInfo &serialPortInfo, availablePorts()) { + if (port.portName() == serialPortInfo.portName()) { + *this = serialPortInfo; + break; + } + } +} + +/*! + Constructs a QSerialPortInfo object from serial port \a name. + + This constructor finds the relevant serial port among the available ones + according to the port name \a name, and constructs the serial port info + instance for that port. +*/ +QSerialPortInfo::QSerialPortInfo(const QString &name) + : d_ptr(new QSerialPortInfoPrivate) +{ + foreach (const QSerialPortInfo &serialPortInfo, availablePorts()) { + if (name == serialPortInfo.portName()) { + *this = serialPortInfo; + break; + } + } +} + +/*! + Destroys the QSerialPortInfo object. References to the values in the + object become invalid. +*/ +QSerialPortInfo::~QSerialPortInfo() +{ +} + +/*! \fn void QSerialPortInfo::swap(QSerialPortInfo &other) + + Swaps QSerialPortInfo \a other with this QSerialPortInfo. This operation is + very fast and never fails. +*/ +void QSerialPortInfo::swap(QSerialPortInfo &other) +{ + d_ptr.swap(other.d_ptr); +} + +/*! + Sets the QSerialPortInfo object to be equal to \a other. +*/ +QSerialPortInfo& QSerialPortInfo::operator=(const QSerialPortInfo &other) +{ + QSerialPortInfo(other).swap(*this); + return *this; +} + +/*! + Returns the name of the serial port. +*/ +QString QSerialPortInfo::portName() const +{ + Q_D(const QSerialPortInfo); + return !d ? QString() : d->portName; +} + +/*! + Returns the system location of the serial port. +*/ +QString QSerialPortInfo::systemLocation() const +{ + Q_D(const QSerialPortInfo); + return !d ? QString() : d->device; +} + +/*! + Returns the description string of the serial port, + if available; otherwise returns an empty string. +*/ +QString QSerialPortInfo::description() const +{ + Q_D(const QSerialPortInfo); + return !d ? QString() : d->description; +} + +/*! + Returns the manufacturer string of the serial port, + if available; otherwise returns an empty string. +*/ +QString QSerialPortInfo::manufacturer() const +{ + Q_D(const QSerialPortInfo); + return !d ? QString() : d->manufacturer; +} + +/*! + Returns the 16-bit vendor number for the serial port, if available; + otherwise returns zero. +*/ +quint16 QSerialPortInfo::vendorIdentifier() const +{ + Q_D(const QSerialPortInfo); + return !d ? 0 : d->vendorIdentifier; +} + +/*! + Returns the 16-bit product number for the serial port, if available; + otherwise returns zero. +*/ +quint16 QSerialPortInfo::productIdentifier() const +{ + Q_D(const QSerialPortInfo); + return !d ? 0 : d->productIdentifier; +} + +/*! + Returns true if there is a valid 16-bit vendor number present; otherwise + returns false. +*/ +bool QSerialPortInfo::hasVendorIdentifier() const +{ + Q_D(const QSerialPortInfo); + return !d ? false : d->hasVendorIdentifier; +} + +/*! + Returns true if there is a valid 16-bit product number present; otherwise + returns false. +*/ +bool QSerialPortInfo::hasProductIdentifier() const +{ + Q_D(const QSerialPortInfo); + return !d ? false : d->hasProductIdentifier; +} + +/*! + \fn bool QSerialPortInfo::isNull() const + + Returns whether this QSerialPortInfo object holds a + serial port definition. +*/ + +/*! + \fn bool QSerialPortInfo::isBusy() const + + Returns true if serial port is busy; + otherwise returns false. +*/ + +/*! + \fn bool QSerialPortInfo::isValid() const + + Returns true if serial port is present on system; + otherwise returns false. +*/ + +/*! + \fn QList QSerialPortInfo::standardBaudRates() + + Returns a list of available standard baud rates supported by + the current serial port. +*/ + +/*! + \fn QList QSerialPortInfo::availablePorts() + + Returns a list of available serial ports on the system. +*/ + +QT_END_NAMESPACE diff --git a/libs/serialport/qserialportinfo.h b/libs/serialport/qserialportinfo.h new file mode 100644 index 000000000..cf47939e6 --- /dev/null +++ b/libs/serialport/qserialportinfo.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERIALPORTINFO_H +#define QSERIALPORTINFO_H + +#include +#include + +#include +//#include + +QT_BEGIN_NAMESPACE + +class QSerialPort; +class QSerialPortInfoPrivate; +class QSerialPortInfoPrivateDeleter; + +class Q_SERIALPORT_EXPORT QSerialPortInfo +{ + Q_DECLARE_PRIVATE(QSerialPortInfo) +public: + QSerialPortInfo(); + explicit QSerialPortInfo(const QSerialPort &port); + explicit QSerialPortInfo(const QString &name); + QSerialPortInfo(const QSerialPortInfo &other); + ~QSerialPortInfo(); + + QSerialPortInfo& operator=(const QSerialPortInfo &other); + void swap(QSerialPortInfo &other); + + QString portName() const; + QString systemLocation() const; + QString description() const; + QString manufacturer() const; + + quint16 vendorIdentifier() const; + quint16 productIdentifier() const; + + bool hasVendorIdentifier() const; + bool hasProductIdentifier() const; + + bool isNull() const; + bool isBusy() const; + bool isValid() const; + + static QList standardBaudRates(); + static QList availablePorts(); + +private: + QScopedPointer d_ptr; +}; + +inline bool QSerialPortInfo::isNull() const +{ return !d_ptr; } + +QT_END_NAMESPACE + +#endif // QSERIALPORTINFO_H diff --git a/libs/serialport/qserialportinfo_mac.cpp b/libs/serialport/qserialportinfo_mac.cpp new file mode 100644 index 000000000..1a751dfca --- /dev/null +++ b/libs/serialport/qserialportinfo_mac.cpp @@ -0,0 +1,272 @@ +/**************************************************************************** +** +** Copyright (C) 2011-2012 Denis Shienkov +** Copyright (C) 2011 Sergey Belyashov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialportinfo.h" +#include "qserialportinfo_p.h" + +#include + +#include +#include + +#include +#include // for kIOPropertyProductNameKey +#include +#if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4) +# include +#endif +#include + +QT_BEGIN_NAMESPACE + +enum { MATCHING_PROPERTIES_COUNT = 6 }; + +QList QSerialPortInfo::availablePorts() +{ + QList serialPortInfoList; + + int matchingPropertiesCounter = 0; + + + ::CFMutableDictionaryRef matching = ::IOServiceMatching(kIOSerialBSDServiceValue); + if (!matching) + return serialPortInfoList; + + ::CFDictionaryAddValue(matching, + CFSTR(kIOSerialBSDTypeKey), + CFSTR(kIOSerialBSDAllTypes)); + + io_iterator_t iter = 0; + kern_return_t kr = ::IOServiceGetMatchingServices(kIOMasterPortDefault, + matching, + &iter); + + if (kr != kIOReturnSuccess) + return serialPortInfoList; + + io_registry_entry_t service; + + while ((service = ::IOIteratorNext(iter))) { + + ::CFTypeRef device = 0; + ::CFTypeRef portName = 0; + ::CFTypeRef description = 0; + ::CFTypeRef manufacturer = 0; + ::CFTypeRef vendorIdentifier = 0; + ::CFTypeRef productIdentifier = 0; + + io_registry_entry_t entry = service; + + // Find MacOSX-specific properties names. + do { + + if (!device) { + device = + ::IORegistryEntrySearchCFProperty(entry, + kIOServicePlane, + CFSTR(kIOCalloutDeviceKey), + kCFAllocatorDefault, + 0); + if (device) + ++matchingPropertiesCounter; + } + + if (!portName) { + portName = + ::IORegistryEntrySearchCFProperty(entry, + kIOServicePlane, + CFSTR(kIOTTYDeviceKey), + kCFAllocatorDefault, + 0); + if (portName) + ++matchingPropertiesCounter; + } + + if (!description) { + description = + ::IORegistryEntrySearchCFProperty(entry, + kIOServicePlane, + CFSTR(kIOPropertyProductNameKey), + kCFAllocatorDefault, + 0); + if (!description) + description = + ::IORegistryEntrySearchCFProperty(entry, + kIOServicePlane, + CFSTR(kUSBProductString), + kCFAllocatorDefault, + 0); + if (!description) + description = + ::IORegistryEntrySearchCFProperty(entry, + kIOServicePlane, + CFSTR("BTName"), + kCFAllocatorDefault, + 0); + + if (description) + ++matchingPropertiesCounter; + } + + if (!manufacturer) { + manufacturer = + ::IORegistryEntrySearchCFProperty(entry, + kIOServicePlane, + CFSTR(kUSBVendorString), + kCFAllocatorDefault, + 0); + if (manufacturer) + ++matchingPropertiesCounter; + + } + + if (!vendorIdentifier) { + vendorIdentifier = + ::IORegistryEntrySearchCFProperty(entry, + kIOServicePlane, + CFSTR(kUSBVendorID), + kCFAllocatorDefault, + 0); + if (vendorIdentifier) + ++matchingPropertiesCounter; + + } + + if (!productIdentifier) { + productIdentifier = + ::IORegistryEntrySearchCFProperty(entry, + kIOServicePlane, + CFSTR(kUSBProductID), + kCFAllocatorDefault, + 0); + if (productIdentifier) + ++matchingPropertiesCounter; + + } + + // If all matching properties is found, then force break loop. + if (matchingPropertiesCounter == MATCHING_PROPERTIES_COUNT) + break; + + kr = ::IORegistryEntryGetParentEntry(entry, kIOServicePlane, &entry); + + } while (kr == kIOReturnSuccess); + + (void) ::IOObjectRelease(entry); + + // Convert from MacOSX-specific properties to Qt4-specific. + if (matchingPropertiesCounter > 0) { + + QSerialPortInfo serialPortInfo; + QByteArray buffer(MAXPATHLEN, 0); + + if (device) { + if (::CFStringGetCString(CFStringRef(device), + buffer.data(), + buffer.size(), + kCFStringEncodingUTF8)) { + serialPortInfo.d_ptr->device = QString::fromUtf8(buffer); + } + ::CFRelease(device); + } + + if (portName) { + if (::CFStringGetCString(CFStringRef(portName), + buffer.data(), + buffer.size(), + kCFStringEncodingUTF8)) { + serialPortInfo.d_ptr->portName = QString::fromUtf8(buffer); + } + ::CFRelease(portName); + } + + if (description) { + if (::CFStringGetCString(CFStringRef(description), + buffer.data(), + buffer.size(), + kCFStringEncodingUTF8)) { + serialPortInfo.d_ptr->description = QString::fromUtf8(buffer); + } + ::CFRelease(description); + } + + if (manufacturer) { + if (::CFStringGetCString(CFStringRef(manufacturer), + buffer.data(), + buffer.size(), + kCFStringEncodingUTF8)) { + serialPortInfo.d_ptr->manufacturer = QString::fromUtf8(buffer); + } + ::CFRelease(manufacturer); + } + + quint16 value = 0; + + if (vendorIdentifier) { + serialPortInfo.d_ptr->hasVendorIdentifier = ::CFNumberGetValue(CFNumberRef(vendorIdentifier), kCFNumberIntType, &value); + if (serialPortInfo.d_ptr->hasVendorIdentifier) + serialPortInfo.d_ptr->vendorIdentifier = value; + + ::CFRelease(vendorIdentifier); + } + + if (productIdentifier) { + serialPortInfo.d_ptr->hasProductIdentifier = ::CFNumberGetValue(CFNumberRef(productIdentifier), kCFNumberIntType, &value); + if (serialPortInfo.d_ptr->hasProductIdentifier) + serialPortInfo.d_ptr->productIdentifier = value; + + ::CFRelease(productIdentifier); + } + + serialPortInfoList.append(serialPortInfo); + } + + (void) ::IOObjectRelease(service); + } + + (void) ::IOObjectRelease(iter); + + return serialPortInfoList; +} + +QT_END_NAMESPACE diff --git a/libs/serialport/qserialportinfo_p.h b/libs/serialport/qserialportinfo_p.h new file mode 100644 index 000000000..1f12e69ba --- /dev/null +++ b/libs/serialport/qserialportinfo_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2011-2012 Denis Shienkov +** Copyright (C) 2011 Sergey Belyashov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERIALPORTINFO_P_H +#define QSERIALPORTINFO_P_H + +#include + +QT_BEGIN_NAMESPACE + +class QSerialPortInfoPrivate +{ +public: + QSerialPortInfoPrivate() + : vendorIdentifier(0) + , productIdentifier(0) + , hasVendorIdentifier(false) + , hasProductIdentifier(false) + {} + + ~QSerialPortInfoPrivate() {} + + QString portName; + QString device; + QString description; + QString manufacturer; + + quint16 vendorIdentifier; + quint16 productIdentifier; + + bool hasVendorIdentifier; + bool hasProductIdentifier; +}; + +class QSerialPortInfoPrivateDeleter +{ +public: + static void cleanup(QSerialPortInfoPrivate *p) { + delete p; + } +}; + +QT_END_NAMESPACE + +#endif // QSERIALPORTINFO_P_H diff --git a/libs/serialport/qserialportinfo_symbian.cpp b/libs/serialport/qserialportinfo_symbian.cpp new file mode 100644 index 000000000..55120485e --- /dev/null +++ b/libs/serialport/qserialportinfo_symbian.cpp @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Copyright (C) 2011-2012 Denis Shienkov +** Copyright (C) 2011 Sergey Belyashov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialportinfo.h" +#include "qserialportinfo_p.h" +#include "qserialport_symbian_p.h" + +#include +//#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +// Physical device driver. +#ifdef __WINS__ +_LIT(KPddName, "ECDRV"); +#else // defined (__EPOC32__) +_LIT(KPddName, "EUART"); +#endif + +// Logical native device driver. +_LIT(KLddName,"ECOMM"); + +// Modules names. +_LIT(KRS232ModuleName , "ECUART"); +_LIT(KBluetoothModuleName , "BTCOMM"); +_LIT(KInfraRedModuleName , "IRCOMM"); +_LIT(KACMModuleName, "ECACM"); + +// Return false on error load. +static bool loadDevices() +{ + TInt r = KErrNone; +#ifdef __WINS__ + RFs fileServer; + r = User::LeaveIfError(fileServer.Connect()); + if (r != KErrNone) + return false; + fileServer.Close (); +#endif + + r = User::LoadPhysicalDevice(KPddName); + if (r != KErrNone && r != KErrAlreadyExists) + return false; //User::Leave(r); + + r = User::LoadLogicalDevice(KLddName); + if (r != KErrNone && r != KErrAlreadyExists) + return false; //User::Leave(r); + +#ifndef __WINS__ + r = StartC32(); + if (r != KErrNone && r != KErrAlreadyExists) + return false; //User::Leave(r); +#endif + + return true; +} + +QList QSerialPortInfo::availablePorts() +{ + QList serialPortInfoList; + + if (!loadDevices()) + return serialPortInfoList; + + RCommServ server; + TInt r = server.Connect(); + if (r != KErrNone) + return serialPortInfoList; //User::LeaveIfError(r); + + TSerialInfo nativeSerialInfo; // Native Symbian OS port info class. + QString s("%1::%2"); + + // FIXME: Get info about RS232 ports. + r = server.LoadCommModule(KRS232ModuleName); + //User::LeaveIfError(r); + if (r == KErrNone) { + r = server.GetPortInfo(KRS232ModuleName, nativeSerialInfo); + if (r == KErrNone) { + for (quint32 i = nativeSerialInfo.iLowUnit; i < nativeSerialInfo.iHighUnit + 1; ++i) { + + QSerialPortInfo serialPortInfo; + + serialPortInfo.d_ptr->device = s + .arg(QString::fromUtf16(nativeSerialInfo.iName.Ptr(), nativeSerialInfo.iName.Length())) + .arg(i); + serialPortInfo.d_ptr->portName = serialPortInfo.d_ptr->device; + serialPortInfo.d_ptr->description = + QString::fromUtf16(nativeSerialInfo.iDescription.Ptr(), nativeSerialInfo.iDescription.Length()); + serialPortInfo.d_ptr->manufacturer = QString(QObject::tr("Unknown.")); + serialPortInfoList.append(serialPortInfo); + } + } + } + + // FIXME: Get info about Bluetooth ports. + r = server.LoadCommModule(KBluetoothModuleName); + //User::LeaveIfError(r); + if (r == KErrNone) { + r = server.GetPortInfo(KBluetoothModuleName, nativeSerialInfo); + if (r == KErrNone) { + for (quint32 i = nativeSerialInfo.iLowUnit; i < nativeSerialInfo.iHighUnit + 1; ++i) { + + QSerialPortInfo serialPortInfo; + + serialPortInfo.d_ptr->device = s + .arg(QString::fromUtf16(nativeSerialInfo.iName.Ptr(), nativeSerialInfo.iName.Length())) + .arg(i); + serialPortInfo.d_ptr->portName = serialPortInfo.d_ptr->device; + serialPortInfo.d_ptr->description = + QString::fromUtf16(nativeSerialInfo.iDescription.Ptr(), nativeSerialInfo.iDescription.Length()); + serialPortInfo.d_ptr->manufacturer = QString(QObject::tr("Unknown.")); + serialPortInfoList.append(serialPortInfo); + } + } + } + + // FIXME: Get info about InfraRed ports. + r = server.LoadCommModule(KInfraRedModuleName); + //User::LeaveIfError(r); + if (r == KErrNone) { + r = server.GetPortInfo(KInfraRedModuleName, nativeSerialInfo); + if (r == KErrNone) { + for (quint32 i = nativeSerialInfo.iLowUnit; i < nativeSerialInfo.iHighUnit + 1; ++i) { + + QSerialPortInfo serialPortInfo; + + serialPortInfo.d_ptr->device = s + .arg(QString::fromUtf16(nativeSerialInfo.iName.Ptr(), nativeSerialInfo.iName.Length())) + .arg(i); + serialPortInfo.d_ptr->portName = serialPortInfo.d_ptr->device; + serialPortInfo.d_ptr->description = + QString::fromUtf16(nativeSerialInfo.iDescription.Ptr(), nativeSerialInfo.iDescription.Length()); + serialPortInfo.d_ptr->manufacturer = QString(QObject::tr("Unknown.")); + serialPortInfoList.append(serialPortInfo); + } + } + } + + // FIXME: Get info about ACM ports. + r = server.LoadCommModule(KACMModuleName); + //User::LeaveIfError(r); + if (r == KErrNone) { + r = server.GetPortInfo(KACMModuleName, nativeSerialInfo); + if (r == KErrNone) { + for (quint32 i = nativeSerialInfo.iLowUnit; i < nativeSerialInfo.iHighUnit + 1; ++i) { + + QSerialPortInfo serialPortInfo; + + serialPortInfo.d_ptr->device = s + .arg(QString::fromUtf16(nativeSerialInfo.iName.Ptr(), nativeSerialInfo.iName.Length())) + .arg(i); + serialPortInfo.d_ptr->portName = QSerialPortPrivate::portNameFromSystemLocation(serialPortInfo.d_ptr->device); + serialPortInfo.d_ptr->description = + QString::fromUtf16(nativeSerialInfo.iDescription.Ptr(), nativeSerialInfo.iDescription.Length()); + serialPortInfo.d_ptr->manufacturer = QString(QObject::tr("Unknown.")); + serialPortInfoList.append(serialPortInfo); + } + } + } + + return serialPortInfoList; +} + +QList QSerialPortInfo::standardBaudRates() +{ + return QSerialPortPrivate::standardBaudRates(); +} + +bool QSerialPortInfo::isBusy() const +{ + if (!loadDevices()) + return false; + + RCommServ server; + TInt r = server.Connect(); + if (r != KErrNone) + return false; + + RComm port; + TPtrC portName(static_cast(systemLocation().utf16()), systemLocation().length()); + r = port.Open(server, portName, ECommExclusive); + if (r == KErrNone) + port.Close(); + return r == KErrLocked; +} + +bool QSerialPortInfo::isValid() const +{ + if (!loadDevices()) + return false; + + RCommServ server; + TInt r = server.Connect(); + if (r != KErrNone) + return false; + + RComm port; + TPtrC portName(static_cast(systemLocation().utf16()), systemLocation().length()); + r = port.Open(server, portName, ECommExclusive); + if (r == KErrNone) + port.Close(); + return r == KErrNone || r == KErrLocked; +} + +QT_END_NAMESPACE diff --git a/libs/serialport/qserialportinfo_unix.cpp b/libs/serialport/qserialportinfo_unix.cpp new file mode 100644 index 000000000..1d56f4e22 --- /dev/null +++ b/libs/serialport/qserialportinfo_unix.cpp @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2011-2012 Denis Shienkov +** Copyright (C) 2011 Sergey Belyashov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialportinfo.h" +#include "qserialportinfo_p.h" +#include "qttylocker_unix_p.h" +#include "qserialport_unix_p.h" +#include + +#ifndef Q_OS_MAC + +#if defined (Q_OS_LINUX) && defined (HAVE_LIBUDEV) +extern "C" +{ +#include +} +#else +#include +#include +#endif + +#endif // Q_OS_MAC + +QT_BEGIN_NAMESPACE + +#ifndef Q_OS_MAC + +#if !(defined (Q_OS_LINUX) && defined (HAVE_LIBUDEV)) + +static inline const QStringList& filtersOfDevices() +{ + static const QStringList deviceFileNameFilterList = QStringList() + +# ifdef Q_OS_LINUX + << QLatin1String("ttyS*") // Standart UART 8250 and etc. + << QLatin1String("ttyUSB*") // Usb/serial converters PL2303 and etc. + << QLatin1String("ttyACM*") // CDC_ACM converters (i.e. Mobile Phones). + << QLatin1String("ttyGS*") // Gadget serial device (i.e. Mobile Phones with gadget serial driver). + << QLatin1String("ttyMI*") // MOXA pci/serial converters. + << QLatin1String("ttyAMA*") // AMBA serial device for embedded platform on ARM (i.e. Raspberry Pi). + << QLatin1String("rfcomm*") // Bluetooth serial device. + << QLatin1String("ircomm*"); // IrDA serial device. +# elif defined (Q_OS_FREEBSD) + << QLatin1String("cu*"); +# else + ; // Here for other *nix OS. +# endif + + return deviceFileNameFilterList; +} + +#endif + +QList QSerialPortInfo::availablePorts() +{ + QList serialPortInfoList; + +#if defined (Q_OS_LINUX) && defined (HAVE_LIBUDEV) + + // White list for devices without a parent + static const QString rfcommDeviceName(QLatin1String("rfcomm")); + + struct ::udev *udev = ::udev_new(); + if (udev) { + + struct ::udev_enumerate *enumerate = + ::udev_enumerate_new(udev); + + if (enumerate) { + + ::udev_enumerate_add_match_subsystem(enumerate, "tty"); + ::udev_enumerate_scan_devices(enumerate); + + struct ::udev_list_entry *devices = + ::udev_enumerate_get_list_entry(enumerate); + + struct ::udev_list_entry *dev_list_entry; + udev_list_entry_foreach(dev_list_entry, devices) { + + struct ::udev_device *dev = + ::udev_device_new_from_syspath(udev, + ::udev_list_entry_get_name(dev_list_entry)); + + if (dev) { + + QSerialPortInfo serialPortInfo; + + serialPortInfo.d_ptr->device = + QLatin1String(::udev_device_get_devnode(dev)); + serialPortInfo.d_ptr->portName = + QLatin1String(::udev_device_get_sysname(dev)); + + struct ::udev_device *parentdev = ::udev_device_get_parent(dev); + + bool canAppendToList = true; + + if (parentdev) { + + QLatin1String subsys(::udev_device_get_subsystem(parentdev)); + + if (subsys == QLatin1String("usb-serial") + || subsys == QLatin1String("usb")) { // USB bus type + // Append this devices and try get additional information about them. + serialPortInfo.d_ptr->description = QString( + QLatin1String(::udev_device_get_property_value(dev, + "ID_MODEL"))).replace('_', ' '); + serialPortInfo.d_ptr->manufacturer = QString( + QLatin1String(::udev_device_get_property_value(dev, + "ID_VENDOR"))).replace('_', ' '); + + serialPortInfo.d_ptr->vendorIdentifier = + QString::fromLatin1(::udev_device_get_property_value(dev, + "ID_VENDOR_ID")).toInt(&serialPortInfo.d_ptr->hasVendorIdentifier, 16); + + serialPortInfo.d_ptr->productIdentifier = + QString::fromLatin1(::udev_device_get_property_value(dev, + "ID_MODEL_ID")).toInt(&serialPortInfo.d_ptr->hasProductIdentifier, 16); + + } else if (subsys == QLatin1String("pnp")) { // PNP bus type + // Append this device. + // FIXME: How to get additional information about serial devices + // with this subsystem? + } else if (subsys == QLatin1String("platform")) { // Platform 'pseudo' bus for legacy device. + // Skip this devices because this type of subsystem does + // not include a real physical serial device. + canAppendToList = false; + } else { // Others types of subsystems. + // Append this devices because we believe that any other types of + // subsystems provide a real serial devices. For example, for devices + // such as ttyGSx, its driver provide an empty subsystem name, but it + // devices is a real physical serial devices. + // FIXME: How to get additional information about serial devices + // with this subsystems? + } + } else { // Devices without a parent + if (serialPortInfo.d_ptr->portName.startsWith(rfcommDeviceName)) { // Bluetooth device + bool ok; + // Check for an unsigned decimal integer at the end of the device name: "rfcomm0", "rfcomm15" + // devices with negative and invalid numbers in the name are rejected + int portNumber = serialPortInfo.d_ptr->portName.mid(rfcommDeviceName.length()).toInt(&ok); + + if (!ok || (portNumber < 0) || (portNumber > 255)) { + canAppendToList = false; + } + } else { + canAppendToList = false; + } + } + + if (canAppendToList) + serialPortInfoList.append(serialPortInfo); + + ::udev_device_unref(dev); + } + + } + + ::udev_enumerate_unref(enumerate); + } + + ::udev_unref(udev); + } + +#elif defined (Q_OS_FREEBSD) && defined (HAVE_LIBUSB) + // TODO: Implement me. +#else + + QDir devDir(QLatin1String("/dev")); + if (devDir.exists()) { + + devDir.setNameFilters(filtersOfDevices()); + devDir.setFilter(QDir::Files | QDir::System | QDir::NoSymLinks); + + QStringList foundDevices; // Found devices list. + + foreach (const QFileInfo &deviceFileInfo, devDir.entryInfoList()) { + QString deviceFilePath = deviceFileInfo.absoluteFilePath(); + if (!foundDevices.contains(deviceFilePath)) { + foundDevices.append(deviceFilePath); + + QSerialPortInfo serialPortInfo; + + serialPortInfo.d_ptr->device = deviceFilePath; + serialPortInfo.d_ptr->portName = QSerialPortPrivate::portNameFromSystemLocation(deviceFilePath); + + // Get description, manufacturer, vendor identifier, product + // identifier are not supported. + + serialPortInfoList.append(serialPortInfo); + + } + } + } + +#endif + + return serialPortInfoList; +} + +#endif // Q_OS_MAC + +// common part + +QList QSerialPortInfo::standardBaudRates() +{ + return QSerialPortPrivate::standardBaudRates(); +} + +bool QSerialPortInfo::isBusy() const +{ + bool currentPid = false; + return QTtyLocker::isLocked(portName().toLocal8Bit().constData(), ¤tPid); +} + +bool QSerialPortInfo::isValid() const +{ + QFile f(systemLocation()); + return f.exists(); +} + +QT_END_NAMESPACE diff --git a/libs/serialport/qserialportinfo_win.cpp b/libs/serialport/qserialportinfo_win.cpp new file mode 100644 index 000000000..7ca1f5ef4 --- /dev/null +++ b/libs/serialport/qserialportinfo_win.cpp @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2011-2012 Denis Shienkov +** Copyright (C) 2011 Sergey Belyashov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialportinfo.h" +#include "qserialportinfo_p.h" +#include "qserialport_win_p.h" + +#ifndef Q_OS_WINCE +#include +#include +#endif + +#include +#include + +QT_BEGIN_NAMESPACE + +#ifndef Q_OS_WINCE + +static const GUID guidsArray[] = +{ + // Windows Ports Class GUID + { 0x4D36E978, 0xE325, 0x11CE, { 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } }, + // Virtual Ports Class GUID (i.e. com0com and etc) + { 0xDF799E12, 0x3C56, 0x421B, { 0xB2, 0x98, 0xB6, 0xD3, 0x64, 0x2B, 0xC8, 0x78 } }, + // Windows Modems Class GUID + { 0x4D36E96D, 0xE325, 0x11CE, { 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } }, + // Eltima Virtual Serial Port Driver v4 GUID + { 0xCC0EF009, 0xB820, 0x42F4, { 0x95, 0xA9, 0x9B, 0xFA, 0x6A, 0x5A, 0xB7, 0xAB } }, + // Advanced Virtual COM Port GUID + { 0x9341CD95, 0x4371, 0x4A37, { 0xA5, 0xAF, 0xFD, 0xB0, 0xA9, 0xD1, 0x96, 0x31 } }, +}; + +static QVariant deviceRegistryProperty(HDEVINFO deviceInfoSet, + PSP_DEVINFO_DATA deviceInfoData, + DWORD property) +{ + DWORD dataType = 0; + DWORD dataSize = 0; + ::SetupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, + property, &dataType, NULL, 0, &dataSize); + QByteArray data(dataSize, 0); + if (!::SetupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, NULL, + reinterpret_cast(data.data()), + dataSize, NULL) + || !dataSize) { + return QVariant(); + } + + switch (dataType) { + + case REG_EXPAND_SZ: + case REG_SZ: { + return QVariant(QString::fromWCharArray(reinterpret_cast(data.constData()))); + } + + case REG_MULTI_SZ: { + QStringList list; + int i = 0; + forever { + QString s = QString::fromWCharArray(reinterpret_cast(data.constData()) + i); + i += s.length() + 1; + if (s.isEmpty()) + break; + list.append(s); + } + return QVariant(list); + } + + default: + break; + } + + return QVariant(); +} + +static QString devicePortName(HDEVINFO deviceInfoSet, PSP_DEVINFO_DATA deviceInfoData) +{ + static const wchar_t portKeyName[] = L"PortName"; + + const HKEY key = ::SetupDiOpenDevRegKey(deviceInfoSet, deviceInfoData, DICS_FLAG_GLOBAL, + 0, DIREG_DEV, KEY_READ); + if (key == INVALID_HANDLE_VALUE) + return QString(); + + DWORD dataSize; + if (::RegQueryValueEx(key, portKeyName, NULL, NULL, NULL, &dataSize) != ERROR_SUCCESS) { + ::RegCloseKey(key); + return QString(); + } + + QByteArray data(dataSize, 0); + + if (::RegQueryValueEx(key, portKeyName, NULL, NULL, + reinterpret_cast(data.data()), &dataSize) != ERROR_SUCCESS) { + ::RegCloseKey(key); + return QString(); + } + ::RegCloseKey(key); + return QString::fromWCharArray(((const wchar_t *)data.constData())); +} + +QList QSerialPortInfo::availablePorts() +{ + static const QString vendorIdentifierPrefix(QLatin1String("VID_")); + static const int vendorIdentifierSize = 4; + static const QString productIdentifierPrefix(QLatin1String("PID_")); + static const int productIdentifierSize = 4; + + QList serialPortInfoList; + static const int guidCount = sizeof(guidsArray)/sizeof(guidsArray[0]); + + for (int i = 0; i < guidCount; ++i) { + const HDEVINFO deviceInfoSet = ::SetupDiGetClassDevs(&guidsArray[i], NULL, 0, DIGCF_PRESENT); + if (deviceInfoSet == INVALID_HANDLE_VALUE) + return serialPortInfoList; + + SP_DEVINFO_DATA deviceInfoData; + ::memset(&deviceInfoData, 0, sizeof(deviceInfoData)); + deviceInfoData.cbSize = sizeof(deviceInfoData); + + DWORD index = 0; + while (::SetupDiEnumDeviceInfo(deviceInfoSet, index++, &deviceInfoData)) { + QSerialPortInfo serialPortInfo; + + QString s = devicePortName(deviceInfoSet, &deviceInfoData); + if (s.isEmpty() || s.contains(QLatin1String("LPT"))) + continue; + + serialPortInfo.d_ptr->portName = s; + serialPortInfo.d_ptr->device = QSerialPortPrivate::portNameToSystemLocation(s); + serialPortInfo.d_ptr->description = + deviceRegistryProperty(deviceInfoSet, &deviceInfoData, SPDRP_DEVICEDESC).toString(); + serialPortInfo.d_ptr->manufacturer = + deviceRegistryProperty(deviceInfoSet, &deviceInfoData, SPDRP_MFG).toString(); + + s = deviceRegistryProperty(deviceInfoSet, &deviceInfoData, SPDRP_HARDWAREID).toStringList().first().toUpper(); + + int index = s.indexOf(vendorIdentifierPrefix); + if (index != -1) + serialPortInfo.d_ptr->vendorIdentifier = s.mid(index + vendorIdentifierPrefix.size(), vendorIdentifierSize) + .toInt(&serialPortInfo.d_ptr->hasVendorIdentifier, 16); + + index = s.indexOf(productIdentifierPrefix); + if (index != -1) + serialPortInfo.d_ptr->productIdentifier = s.mid(index + productIdentifierPrefix.size(), productIdentifierSize) + .toInt(&serialPortInfo.d_ptr->hasProductIdentifier, 16); + + serialPortInfoList.append(serialPortInfo); + } + ::SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + return serialPortInfoList; +} + +#endif + +// common part + +QList QSerialPortInfo::standardBaudRates() +{ + return QSerialPortPrivate::standardBaudRates(); +} + +bool QSerialPortInfo::isBusy() const +{ + const HANDLE descriptor = ::CreateFile(reinterpret_cast(systemLocation().utf16()), + GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + + if (descriptor == INVALID_HANDLE_VALUE) { + if (::GetLastError() == ERROR_ACCESS_DENIED) + return true; + } else { + ::CloseHandle(descriptor); + } + return false; +} + +bool QSerialPortInfo::isValid() const +{ + const HANDLE descriptor = ::CreateFile(reinterpret_cast(systemLocation().utf16()), + GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + + if (descriptor == INVALID_HANDLE_VALUE) { + if (::GetLastError() != ERROR_ACCESS_DENIED) + return false; + } else { + ::CloseHandle(descriptor); + } + return true; +} + +QT_END_NAMESPACE diff --git a/libs/serialport/qserialportinfo_wince.cpp b/libs/serialport/qserialportinfo_wince.cpp new file mode 100644 index 000000000..279aceae4 --- /dev/null +++ b/libs/serialport/qserialportinfo_wince.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2011-2012 Denis Shienkov +** Copyright (C) 2011 Sergey Belyashov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialportinfo.h" +#include "qserialportinfo_p.h" +#include "qserialport_win_p.h" + +#include + +QT_BEGIN_NAMESPACE + +static QString findDescription(HKEY parentKeyHandle, const QString &subKey) +{ + const static QString valueName(QLatin1String("FriendlyName")); + + QString result; + HKEY hSubKey = 0; + LONG res = ::RegOpenKeyEx(parentKeyHandle, reinterpret_cast(subKey.utf16()), + 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hSubKey); + + if (res == ERROR_SUCCESS) { + + DWORD dataType = 0; + DWORD dataSize = 0; + res = ::RegQueryValueEx(hSubKey, reinterpret_cast(valueName.utf16()), + NULL, &dataType, NULL, &dataSize); + + if (res == ERROR_SUCCESS) { + QByteArray data(dataSize, 0); + res = ::RegQueryValueEx(hSubKey, reinterpret_cast(valueName.utf16()), + NULL, NULL, + reinterpret_cast(data.data()), + &dataSize); + + if (res == ERROR_SUCCESS) { + switch (dataType) { + case REG_EXPAND_SZ: + case REG_SZ: + if (dataSize) + result = QString::fromWCharArray(reinterpret_cast(data.constData())); + break; + default: + break; + } + } + } else { + DWORD index = 0; + dataSize = 255; // Max. key length (see MSDN). + QByteArray data(dataSize, 0); + while (::RegEnumKeyEx(hSubKey, index++, + reinterpret_cast(data.data()), &dataSize, + NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { + + result = findDescription(hSubKey, + QString::fromUtf16(reinterpret_cast(data.data()), dataSize)); + if (!result.isEmpty()) + break; + } + } + ::RegCloseKey(hSubKey); + } + return result; +} + +QList QSerialPortInfo::availablePorts() +{ + QList serialPortInfoList; + + DEVMGR_DEVICE_INFORMATION di; + di.dwSize = sizeof(di); + const HANDLE hSearch = ::FindFirstDevice(DeviceSearchByLegacyName, + L"COM*", + &di); + if (hSearch != INVALID_HANDLE_VALUE) { + do { + QSerialPortInfo serialPortInfo; + serialPortInfo.d_ptr->device = QString::fromWCharArray(di.szLegacyName); + serialPortInfo.d_ptr->portName = QSerialPortPrivate::portNameFromSystemLocation(serialPortInfo.d_ptr->device); + serialPortInfo.d_ptr->description = findDescription(HKEY_LOCAL_MACHINE, + QString::fromWCharArray(di.szDeviceKey)); + + // Get manufacturer, vendor identifier, product identifier are not + // possible. + + serialPortInfoList.append(serialPortInfo); + + } while (::FindNextDevice(hSearch, &di)); + ::FindClose(hSearch); + } + + return serialPortInfoList; +} + +QT_END_NAMESPACE diff --git a/libs/serialport/qt4support/install-helper.pri b/libs/serialport/qt4support/install-helper.pri new file mode 100644 index 000000000..52577095e --- /dev/null +++ b/libs/serialport/qt4support/install-helper.pri @@ -0,0 +1,43 @@ +QTSERIALPORT_PROJECT_INCLUDEDIR = $$QTSERIALPORT_BUILD_ROOT/include/QtSerialPort +QTSERIALPORT_PROJECT_INCLUDEDIR ~=s,/,$$QMAKE_DIR_SEP, + +system("$$QMAKE_MKDIR $$QTSERIALPORT_PROJECT_INCLUDEDIR") + +for(header_file, PUBLIC_HEADERS) { + header_file ~=s,/,$$QMAKE_DIR_SEP, + system("$$QMAKE_COPY \"$${header_file}\" \"$$QTSERIALPORT_PROJECT_INCLUDEDIR\"") +} + +# This is a quick workaround for generating forward header with Qt4. + +unix { + system("echo \'$${LITERAL_HASH}include \"qserialport.h\"\' > \"$$QTSERIALPORT_PROJECT_INCLUDEDIR/QSerialPort\"") + system("echo \'$${LITERAL_HASH}include \"qserialportinfo.h\"\' > \"$$QTSERIALPORT_PROJECT_INCLUDEDIR/QSerialPortInfo\"") +} win32 { + system("echo $${LITERAL_HASH}include \"qserialport.h\" > \"$$QTSERIALPORT_PROJECT_INCLUDEDIR/QSerialPort\"") + system("echo $${LITERAL_HASH}include \"qserialportinfo.h\" > \"$$QTSERIALPORT_PROJECT_INCLUDEDIR/QSerialPortInfo\"") +} + +PUBLIC_HEADERS += \ + $$PUBLIC_HEADERS \ + \"$$QTSERIALPORT_PROJECT_INCLUDEDIR/QSerialPort\" \ + \"$$QTSERIALPORT_PROJECT_INCLUDEDIR/QSerialPortInfo\" + +target_headers.files = $$PUBLIC_HEADERS +target_headers.path = $$[QT_INSTALL_PREFIX]/include/QtSerialPort +INSTALLS += target_headers + +mkspecs_features.files = $$QTSERIALPORT_PROJECT_ROOT/src/serialport/qt4support/serialport.prf +mkspecs_features.path = $$[QT_INSTALL_DATA]/mkspecs/features +INSTALLS += mkspecs_features + +win32 { + dlltarget.path = $$[QT_INSTALL_BINS] + INSTALLS += dlltarget +} + +target.path = $$[QT_INSTALL_LIBS] +INSTALLS += target + +INCLUDEPATH += $$QTSERIALPORT_BUILD_ROOT/include $$QTSERIALPORT_BUILD_ROOT/include/QtSerialPort +DEFINES += QT_BUILD_SERIALPORT_LIB diff --git a/libs/serialport/qt4support/qringbuffer_p.h b/libs/serialport/qt4support/qringbuffer_p.h new file mode 100644 index 000000000..ddd10e543 --- /dev/null +++ b/libs/serialport/qt4support/qringbuffer_p.h @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRINGBUFFER_P_H +#define QRINGBUFFER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QRingBuffer +{ +public: + explicit inline QRingBuffer(int growth = 4096) : basicBlockSize(growth) { + buffers << QByteArray(); + clear(); + } + + inline int nextDataBlockSize() const { + return (tailBuffer == 0 ? tail : buffers.first().size()) - head; + } + + inline const char *readPointer() const { + return buffers.isEmpty() ? 0 : (buffers.first().constData() + head); + } + + // access the bytes at a specified position + // the out-variable length will contain the amount of bytes readable + // from there, e.g. the amount still the same QByteArray + inline const char *readPointerAtPosition(qint64 pos, qint64 &length) const { + if (buffers.isEmpty()) { + length = 0; + return 0; + } + + if (pos >= bufferSize) { + length = 0; + return 0; + } + + // special case: it is in the first buffer + int nextDataBlockSizeValue = nextDataBlockSize(); + if (pos - head < nextDataBlockSizeValue) { + length = nextDataBlockSizeValue - pos; + return buffers.at(0).constData() + head + pos; + } + + // special case: we only had one buffer and tried to read over it + if (buffers.length() == 1) { + length = 0; + return 0; + } + + // skip the first + pos -= nextDataBlockSizeValue; + + // normal case: it is somewhere in the second to the-one-before-the-tailBuffer + for (int i = 1; i < tailBuffer; i++) { + if (pos >= buffers[i].size()) { + pos -= buffers[i].size(); + continue; + } + + length = buffers[i].length() - pos; + return buffers[i].constData() + pos; + } + + // it is in the tail buffer + length = tail - pos; + return buffers[tailBuffer].constData() + pos; + } + + inline void free(int bytes) { + bufferSize -= bytes; + if (bufferSize < 0) + bufferSize = 0; + + for (;;) { + int nextBlockSize = nextDataBlockSize(); + if (bytes < nextBlockSize) { + head += bytes; + if (head == tail && tailBuffer == 0) + head = tail = 0; + break; + } + + bytes -= nextBlockSize; + if (buffers.count() == 1) { + if (buffers.at(0).size() != basicBlockSize) + buffers[0].resize(basicBlockSize); + head = tail = 0; + tailBuffer = 0; + break; + } + + buffers.removeAt(0); + --tailBuffer; + head = 0; + } + + if (isEmpty()) + clear(); // try to minify/squeeze us + } + + inline char *reserve(int bytes) { + // if this is a fresh empty QRingBuffer + if (bufferSize == 0) { + buffers[0].resize(qMax(basicBlockSize, bytes)); + bufferSize += bytes; + tail = bytes; + return buffers[tailBuffer].data(); + } + + bufferSize += bytes; + + // if there is already enough space, simply return. + if (tail + bytes <= buffers.at(tailBuffer).size()) { + char *writePtr = buffers[tailBuffer].data() + tail; + tail += bytes; + return writePtr; + } + + // if our buffer isn't half full yet, simply resize it. + if (tail < buffers.at(tailBuffer).size() / 2) { + buffers[tailBuffer].resize(tail + bytes); + char *writePtr = buffers[tailBuffer].data() + tail; + tail += bytes; + return writePtr; + } + + // shrink this buffer to its current size + buffers[tailBuffer].resize(tail); + + // create a new QByteArray with the right size + buffers << QByteArray(); + ++tailBuffer; + buffers[tailBuffer].resize(qMax(basicBlockSize, bytes)); + tail = bytes; + return buffers[tailBuffer].data(); + } + + inline void truncate(int pos) { + if (pos < size()) + chop(size() - pos); + } + + inline void chop(int bytes) { + bufferSize -= bytes; + if (bufferSize < 0) + bufferSize = 0; + + for (;;) { + // special case: head and tail are in the same buffer + if (tailBuffer == 0) { + tail -= bytes; + if (tail <= head) + tail = head = 0; + return; + } + + if (bytes <= tail) { + tail -= bytes; + return; + } + + bytes -= tail; + buffers.removeAt(tailBuffer); + + --tailBuffer; + tail = buffers.at(tailBuffer).size(); + } + + if (isEmpty()) + clear(); // try to minify/squeeze us + } + + inline bool isEmpty() const { + return tailBuffer == 0 && tail == 0; + } + + inline int getChar() { + if (isEmpty()) + return -1; + char c = *readPointer(); + free(1); + return int(uchar(c)); + } + + inline void putChar(char c) { + char *ptr = reserve(1); + *ptr = c; + } + + inline void ungetChar(char c) { + --head; + if (head < 0) { + buffers.prepend(QByteArray()); + buffers[0].resize(basicBlockSize); + head = basicBlockSize - 1; + ++tailBuffer; + } + buffers[0][head] = c; + ++bufferSize; + } + + inline int size() const { + return bufferSize; + } + + inline void clear() { + buffers.erase(buffers.begin() + 1, buffers.end()); + buffers[0].resize(0); + buffers[0].squeeze(); + + head = tail = 0; + tailBuffer = 0; + bufferSize = 0; + } + + inline int indexOf(char c) const { + int index = 0; + for (int i = 0; i < buffers.size(); ++i) { + int start = 0; + int end = buffers.at(i).size(); + + if (i == 0) + start = head; + if (i == tailBuffer) + end = tail; + const char *ptr = buffers.at(i).data() + start; + for (int j = start; j < end; ++j) { + if (*ptr++ == c) + return index; + ++index; + } + } + return -1; + } + + inline int indexOf(char c, int maxLength) const { + int index = 0; + int remain = qMin(size(), maxLength); + for (int i = 0; remain && i < buffers.size(); ++i) { + int start = 0; + int end = buffers.at(i).size(); + + if (i == 0) + start = head; + if (i == tailBuffer) + end = tail; + if (remain < end - start) { + end = start + remain; + remain = 0; + } else { + remain -= end - start; + } + const char *ptr = buffers.at(i).data() + start; + for (int j = start; j < end; ++j) { + if (*ptr++ == c) + return index; + ++index; + } + } + return -1; + } + + inline int read(char *data, int maxLength) { + int bytesToRead = qMin(size(), maxLength); + int readSoFar = 0; + while (readSoFar < bytesToRead) { + const char *ptr = readPointer(); + int bytesToReadFromThisBlock = qMin(bytesToRead - readSoFar, nextDataBlockSize()); + if (data) + memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock); + readSoFar += bytesToReadFromThisBlock; + free(bytesToReadFromThisBlock); + } + return readSoFar; + } + + inline QByteArray read(int maxLength) { + QByteArray tmp; + tmp.resize(qMin(maxLength, size())); + read(tmp.data(), tmp.size()); + return tmp; + } + + inline QByteArray readAll() { + return read(size()); + } + + // read an unspecified amount (will read the first buffer) + inline QByteArray read() { + if (bufferSize == 0) + return QByteArray(); + + // multiple buffers, just take the first one + if (head == 0 && tailBuffer != 0) { + QByteArray qba = buffers.takeFirst(); + --tailBuffer; + bufferSize -= qba.length(); + return qba; + } + + // one buffer with good value for head. Just take it. + if (head == 0 && tailBuffer == 0) { + QByteArray qba = buffers.takeFirst(); + qba.resize(tail); + buffers << QByteArray(); + bufferSize = 0; + tail = 0; + return qba; + } + + // Bad case: We have to memcpy. + // We can avoid by initializing the QRingBuffer with basicBlockSize of 0 + // and only using this read() function. + QByteArray qba(readPointer(), nextDataBlockSize()); + buffers.removeFirst(); + head = 0; + if (tailBuffer == 0) { + buffers << QByteArray(); + tail = 0; + } else { + --tailBuffer; + } + bufferSize -= qba.length(); + return qba; + } + + // append a new buffer to the end + inline void append(const QByteArray &qba) { + buffers[tailBuffer].resize(tail); + buffers << qba; + ++tailBuffer; + tail = qba.length(); + bufferSize += qba.length(); + } + + inline QByteArray peek(int maxLength) const { + int bytesToRead = qMin(size(), maxLength); + if (maxLength <= 0) + return QByteArray(); + QByteArray ret; + ret.resize(bytesToRead); + int readSoFar = 0; + for (int i = 0; readSoFar < bytesToRead && i < buffers.size(); ++i) { + int start = 0; + int end = buffers.at(i).size(); + if (i == 0) + start = head; + if (i == tailBuffer) + end = tail; + const int len = qMin(ret.size()-readSoFar, end-start); + memcpy(ret.data()+readSoFar, buffers.at(i).constData()+start, len); + readSoFar += len; + } + Q_ASSERT(readSoFar == ret.size()); + return ret; + } + + inline int skip(int length) { + return read(0, length); + } + + inline int readLine(char *data, int maxLength) { + int index = indexOf('\n'); + if (index == -1) + return read(data, maxLength); + if (maxLength <= 0) + return -1; + + int readSoFar = 0; + while (readSoFar < index + 1 && readSoFar < maxLength - 1) { + int bytesToRead = qMin((index + 1) - readSoFar, nextDataBlockSize()); + bytesToRead = qMin(bytesToRead, (maxLength - 1) - readSoFar); + memcpy(data + readSoFar, readPointer(), bytesToRead); + readSoFar += bytesToRead; + free(bytesToRead); + } + + // Terminate it. + data[readSoFar] = '\0'; + return readSoFar; + } + + inline bool canReadLine() const { + return indexOf('\n') != -1; + } + +private: + QList buffers; + int head, tail; + int tailBuffer; // always buffers.size() - 1 + int basicBlockSize; + int bufferSize; +}; + +QT_END_NAMESPACE + +#endif // QRINGBUFFER_P_H diff --git a/libs/serialport/qt4support/qwineventnotifier_p.h b/libs/serialport/qt4support/qwineventnotifier_p.h new file mode 100644 index 000000000..bd1203e59 --- /dev/null +++ b/libs/serialport/qt4support/qwineventnotifier_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINEVENTNOTIFIER_P_H +#define QWINEVENTNOTIFIER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qobject.h" +#include "QtCore/qt_windows.h" + +QT_BEGIN_NAMESPACE + +class Q_CORE_EXPORT QWinEventNotifier : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QObject) + +public: + explicit QWinEventNotifier(QObject *parent = 0); + explicit QWinEventNotifier(HANDLE hEvent, QObject *parent = 0); + ~QWinEventNotifier(); + + void setHandle(HANDLE hEvent); + HANDLE handle() const; + + bool isEnabled() const; + +public Q_SLOTS: + void setEnabled(bool enable); + +Q_SIGNALS: + void activated(HANDLE hEvent); + +protected: + bool event(QEvent * e); + +private: + Q_DISABLE_COPY(QWinEventNotifier) + + HANDLE handleToEvent; + bool enabled; +}; + +QT_END_NAMESPACE + +#endif // QWINEVENTNOTIFIER_P_H diff --git a/libs/serialport/qt4support/serialport.prf b/libs/serialport/qt4support/serialport.prf new file mode 100644 index 000000000..9779b4c82 --- /dev/null +++ b/libs/serialport/qt4support/serialport.prf @@ -0,0 +1,27 @@ +qtAddLibrary(QtSerialPort) + +!isEmpty(QTSERIALPORT_BUILD_ROOT) { + INCLUDEPATH -= $$QMAKE_INCDIR_QT/QtSerialPort + QMAKE_INCDIR += $$QTSERIALPORT_BUILD_ROOT/include $$QTSERIALPORT_BUILD_ROOT/include/QtSerialPort + + QTSERIALPORT_BUILD_SUBDIR = src/serialport + debug_and_release_target { + CONFIG(debug, debug|release) { + QTSERIALPORT_BUILD_SUBDIR = $$QTSERIALPORT_BUILD_SUBDIR/debug + } else { + QTSERIALPORT_BUILD_SUBDIR = $$QTSERIALPORT_BUILD_SUBDIR/release + } + } + + QMAKE_LIBDIR += $$QTSERIALPORT_BUILD_ROOT/$$QTSERIALPORT_BUILD_SUBDIR +} + +mac { + LIBS -= -framework QtSerialPort$${QT_LIBINFIX} + + if(!debug_and_release|build_pass):CONFIG(debug, debug|release) { + LIBS += -lQtSerialPort$${QT_LIBINFIX}_debug + } else { + LIBS += -lQtSerialPort$${QT_LIBINFIX} + } +} diff --git a/libs/serialport/qttylocker_unix.cpp b/libs/serialport/qttylocker_unix.cpp new file mode 100644 index 000000000..8184bd9c0 --- /dev/null +++ b/libs/serialport/qttylocker_unix.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qttylocker_unix_p.h" + +#ifdef HAVE_BAUDBOY_H +# include +# include +#elif defined (HAVE_LOCKDEV_H) +# include +# include +#else +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined (HAVE_BAUDBOY_H) + +QT_BEGIN_NAMESPACE + +#if !(defined (HAVE_BAUDBOY_H) || defined (HAVE_LOCKDEV_H)) + +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +# define QStringLiteral QLatin1String +#endif + +static inline const QStringList& lockDirectoryList() +{ + static const QStringList lockDirectoryEntries = QStringList() + << QStringLiteral("/var/lock") + << QStringLiteral("/etc/locks") + << QStringLiteral("/var/spool/locks") + << QStringLiteral("/var/spool/uucp") + << QStringLiteral("/tmp"); + + return lockDirectoryEntries; +} + +// Returns the full path first found in the directory where you can create a lock file +// (ie a directory with access to the read/write). +// Verification of directories is of the order in accordance with the order +// of records in the variable lockDirList. +static +QString lookupFirstSharedLockDir() +{ + QStringList directoryList = lockDirectoryList(); + + foreach (const QString &lockDirectory, directoryList) { + if (::access(lockDirectory.toLocal8Bit().constData(), R_OK | W_OK) == 0) + return lockDirectory; + } + return QString(); +} + +// Returns the name of the lock file which is tied to the +// device name, eg "LCK..ttyS0", etc. +static +QString generateLockFileNameAsNamedForm(const char *portName) +{ + QString result(lookupFirstSharedLockDir()); + if (!result.isEmpty()) { + result.append(QLatin1String("/LCK..")); + result.append(QString::fromLatin1(portName).replace(QLatin1Char('/'), QLatin1Char('_'))); + } + return result; +} + +#endif //!(defined (HAVE_BAUDBOY_H) || defined (HAVE_LOCKDEV_H)) + +// Try lock serial device. However, other processes can not access it. +bool QTtyLocker::lock(const char *portName) +{ +#ifdef HAVE_BAUDBOY_H + if (::ttylock(portName) + ::ttywait(portName); + return ::ttylock(portName) != -1; +#elif defined (HAVE_LOCKDEV_H) + return ::dev_lock(portName) != -1; +#else + QFile f(generateLockFileNameAsNamedForm(portName)); + if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + QString content(QLatin1String(" %1 %2\x0A")); + content = content.arg(::getpid()).arg(::getuid()); + if (f.write(content.toLocal8Bit()) > 0) { + f.close(); + return true; + } + f.close(); + } + return false; +#endif +} + +// Try unlock serial device. However, other processes can access it. +bool QTtyLocker::unlock(const char *portName) +{ +#ifdef HAVE_BAUDBOY_H + return ::ttyunlock(portName != -1; +#elif defined (HAVE_LOCKDEV_H) + return ::dev_unlock(portName, ::getpid()) != -1; +#else + QFile f(generateLockFileNameAsNamedForm(portName)); + return f.remove(); +#endif +} + +// Verifies the device is locked or not. +// If returned currentPid = true - this means that the device is locked the current process. +bool QTtyLocker::isLocked(const char *portName, bool *currentPid) +{ + if (!currentPid) + return true; + + *currentPid = false; + +#ifdef HAVE_BAUDBOY_H + return ::ttylocked(portName) != -1; +#elif defined (HAVE_LOCKDEV_H) + return ::dev_testlock(portName) != -1; +#else + + QFile f(generateLockFileNameAsNamedForm(portName)); + if (!f.exists()) + return false; + if (!f.open(QIODevice::ReadOnly)) + return true; + + QString content(QLatin1String(f.readAll())); + f.close(); + + const pid_t pid = content.section(' ', 0, 0, QString::SectionSkipEmpty).toInt(); + + if (::kill(pid, 0) == -1) { + if (errno == ESRCH) // Process does not exists + return false; + } else { + if (::getpid() == pid) // Process exists and it is "their", i.e current + *currentPid = true; + } + + return true; + +#endif +} + +QT_END_NAMESPACE diff --git a/libs/serialport/qttylocker_unix_p.h b/libs/serialport/qttylocker_unix_p.h new file mode 100644 index 000000000..9dce5d05d --- /dev/null +++ b/libs/serialport/qttylocker_unix_p.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#ifndef TTYLOCKER_UNIX_P_H +#define TTYLOCKER_UNIX_P_H + +QT_BEGIN_NAMESPACE + +class QTtyLocker +{ +public: + static bool lock(const char *portName); + static bool unlock(const char *portName); + static bool isLocked(const char *portName, bool *currentPid); +}; + +QT_END_NAMESPACE + +#endif // TTYLOCKER_UNIX_P_H diff --git a/libs/serialport/serialport-lib.pri b/libs/serialport/serialport-lib.pri new file mode 100644 index 000000000..db8ff7460 --- /dev/null +++ b/libs/serialport/serialport-lib.pri @@ -0,0 +1,87 @@ +INCLUDEPATH += $$PWD + +unix { + CONFIG += link_pkgconfig + + packagesExist(libudev) { + DEFINES += HAVE_LIBUDEV + PKGCONFIG += libudev + } +} + +PUBLIC_HEADERS += \ + $$PWD/qserialportglobal.h \ + $$PWD/qserialport.h \ + $$PWD/qserialportinfo.h + +PRIVATE_HEADERS += \ + $$PWD/qserialport_p.h \ + $$PWD/qserialportinfo_p.h + +SOURCES += \ + $$PWD/qserialport.cpp \ + $$PWD/qserialportinfo.cpp + +win32 { + PRIVATE_HEADERS += \ + $$PWD/qserialport_win_p.h + + SOURCES += \ + $$PWD/qserialport_win.cpp \ + $$PWD/qserialportinfo_win.cpp + + !wince*: { + LIBS += -lsetupapi -ladvapi32 + } else { + SOURCES += \ + $$PWD/qserialport_wince.cpp \ + $$PWD/qserialportinfo_wince.cpp + } +} + +symbian { + MMP_RULES += EXPORTUNFROZEN + #MMP_RULES += DEBUGGABLE_UDEBONLY + TARGET.UID3 = 0xE7E62DFD + TARGET.CAPABILITY = + TARGET.EPOCALLOWDLLDATA = 1 + addFiles.sources = QtSerialPort.dll + addFiles.path = !:/sys/bin + DEPLOYMENT += addFiles + + # FIXME !!! + #INCLUDEPATH += c:/Nokia/devices/Nokia_Symbian3_SDK_v1.0/epoc32/include/platform + INCLUDEPATH += c:/QtSDK/Symbian/SDKs/Symbian3Qt473/epoc32/include/platform + + PRIVATE_HEADERS += \ + $$PWD/qserialport_symbian_p.h + + SOURCES += \ + $$PWD/qserialport_symbian.cpp \ + $$PWD/qserialportinfo_symbian.cpp + + LIBS += -leuser -lefsrv -lc32 +} + +unix:!symbian { + PRIVATE_HEADERS += \ + $$PWD/qttylocker_unix_p.h \ + $$PWD/qserialport_unix_p.h + + SOURCES += \ + $$PWD/qttylocker_unix.cpp \ + $$PWD/qserialport_unix.cpp \ + $$PWD/qserialportinfo_unix.cpp + + macx { + SOURCES += $$PWD/qserialportinfo_mac.cpp + + LIBS += -framework IOKit -framework CoreFoundation + } else { + linux*:contains( DEFINES, HAVE_LIBUDEV ) { + LIBS += -ludev + } + } +} + +HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS diff --git a/libs/serialport/serialport.pro b/libs/serialport/serialport.pro new file mode 100644 index 000000000..d329a0b78 --- /dev/null +++ b/libs/serialport/serialport.pro @@ -0,0 +1,17 @@ +QT = core + +QMAKE_DOCS = $$PWD/doc/qtserialport.qdocconf +include($$PWD/serialport-lib.pri) + +greaterThan(QT_MAJOR_VERSION, 4) { + load(qt_build_config) + QT += core-private + TARGET = QtSerialPort + load(qt_module) +} else { + TEMPLATE = lib + TARGET = $$qtLibraryTarget(QtSerialPort$$QT_LIBINFIX) + include($$PWD/qt4support/install-helper.pri) + CONFIG += module create_prl + mac:QMAKE_FRAMEWORK_BUNDLE_NAME = $$TARGET +} diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index bf6c51e0f..ca1222745 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -20,7 +20,8 @@ # Qt configuration CONFIG += qt \ - thread + thread \ + QT += network \ opengl \ svg \ @@ -93,7 +94,7 @@ DEPENDPATH += \ INCLUDEPATH += \ libs/utils \ libs \ - libs/opmapcontrol + libs/opmapcontrol \ # If the user config file exists, it will be included. # if the variable MAVLINK_CONF contains the name of an @@ -135,30 +136,22 @@ INCLUDEPATH += \ include(src/apps/mavlinkgen/mavlinkgen.pri) - - # Include QWT plotting library include(libs/qwt/qwt.pri) + DEPENDPATH += . \ - plugins \ - libs/thirdParty/qserialport/include \ - libs/thirdParty/qserialport/include/QtSerialPort \ - libs/thirdParty/qserialport \ - libs/qextserialport - -INCLUDEPATH += . \ - libs/thirdParty/qserialport/include \ - libs/thirdParty/qserialport/include/QtSerialPort \ - libs/thirdParty/qserialport/src \ - libs/qextserialport -# Include serial port library (QSerial) -include(qserialport.pri) - -# Serial port detection (ripped-off from qextserialport library) -macx|macx-g++|macx-g++42::SOURCES += libs/qextserialport/qextserialenumerator_osx.cpp -linux-g++::SOURCES += libs/qextserialport/qextserialenumerator_unix.cpp -linux-g++-64::SOURCES += libs/qextserialport/qextserialenumerator_unix.cpp -win32-msvc2008|win32-msvc2010|win32-msvc2012::SOURCES += libs/qextserialport/qextserialenumerator_win.cpp + plugins + +INCLUDEPATH += . + +# Include serial port library (QSerialPort) +include(libs/serialport/apmserial.pri) + +## Serial port detection (ripped-off from qextserialport library) +#macx|macx-g++|macx-g++42::SOURCES += libs/qextserialport/qextserialenumerator_osx.cpp +#linux-g++::SOURCES += libs/qextserialport/qextserialenumerator_unix.cpp +#linux-g++-64::SOURCES += libs/qextserialport/qextserialenumerator_unix.cpp +#win32-msvc2008|win32-msvc2010|win32-msvc2012::SOURCES += libs/qextserialport/qextserialenumerator_win.cpp # Input FORMS += src/ui/MainWindow.ui \ src/ui/CommSettings.ui \ @@ -345,7 +338,7 @@ HEADERS += src/MG.h \ src/ui/map/Waypoint2DIcon.h \ src/ui/map/QGCMapTool.h \ src/ui/map/QGCMapToolBar.h \ - libs/qextserialport/qextserialenumerator.h \ +# libs/qextserialport/qextserialenumerator.h \ src/QGCGeo.h \ src/ui/QGCToolBar.h \ src/ui/QGCStatusBar.h \ diff --git a/src/comm/SerialLink.cc b/src/comm/SerialLink.cc index 4865ecf6e..a0e52ae98 100644 --- a/src/comm/SerialLink.cc +++ b/src/comm/SerialLink.cc @@ -12,257 +12,62 @@ #include #include #include +#include +#include #include "SerialLink.h" #include "LinkManager.h" #include "QGC.h" #include -#include -#ifdef _WIN32 -#include "windows.h" -#endif -#ifdef _WIN32 -#include -#endif -#if defined (__APPLE__) && defined (__MACH__) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __MWERKS__ -#define __CF_USE_FRAMEWORK_INCLUDES__ -#endif - - -#include - -#include -#include -#if defined(MAC_OS_X_VERSION_10_3) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3) -#include -#endif -#include - -// Apple internal modems default to local echo being on. If your modem has local echo disabled, -// undefine the following macro. -#define LOCAL_ECHO - -#define kATCommandString "AT\r" - -#ifdef LOCAL_ECHO -#define kOKResponseString "AT\r\r\nOK\r\n" -#else -#define kOKResponseString "\r\nOK\r\n" -#endif -#endif - - -// Some helper functions for serial port enumeration -#if defined (__APPLE__) && defined (__MACH__) - -enum { - kNumRetries = 3 -}; - -// Function prototypes -static kern_return_t FindModems(io_iterator_t *matchingServices); -static kern_return_t GetModemPath(io_iterator_t serialPortIterator, char *bsdPath, CFIndex maxPathSize); - -// Returns an iterator across all known modems. Caller is responsible for -// releasing the iterator when iteration is complete. -static kern_return_t FindModems(io_iterator_t *matchingServices) -{ - kern_return_t kernResult; - CFMutableDictionaryRef classesToMatch; - - /*! @function IOServiceMatching - @abstract Create a matching dictionary that specifies an IOService class match. - @discussion A very common matching criteria for IOService is based on its class. IOServiceMatching will create a matching dictionary that specifies any IOService of a class, or its subclasses. The class is specified by C-string name. - @param name The class name, as a const C-string. Class matching is successful on IOService's of this class or any subclass. - @result The matching dictionary created, is returned on success, or zero on failure. The dictionary is commonly passed to IOServiceGetMatchingServices or IOServiceAddNotification which will consume a reference, otherwise it should be released with CFRelease by the caller. */ - - // Serial devices are instances of class IOSerialBSDClient - classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); - if (classesToMatch == NULL) { - printf("IOServiceMatching returned a NULL dictionary.\n"); - } else { - /*! - @function CFDictionarySetValue - Sets the value of the key in the dictionary. - @param theDict The dictionary to which the value is to be set. If this - parameter is not a valid mutable CFDictionary, the behavior is - undefined. If the dictionary is a fixed-capacity dictionary and - it is full before this operation, and the key does not exist in - the dictionary, the behavior is undefined. - @param key The key of the value to set into the dictionary. If a key - which matches this key is already present in the dictionary, only - the value is changed ("add if absent, replace if present"). If - no key matches the given key, the key-value pair is added to the - dictionary. If added, the key is retained by the dictionary, - using the retain callback provided - when the dictionary was created. If the key is not of the sort - expected by the key retain callback, the behavior is undefined. - @param value The value to add to or replace into the dictionary. The value - is retained by the dictionary using the retain callback provided - when the dictionary was created, and the previous value if any is - released. If the value is not of the sort expected by the - retain or release callbacks, the behavior is undefined. - */ - CFDictionarySetValue(classesToMatch, - CFSTR(kIOSerialBSDTypeKey), - CFSTR(kIOSerialBSDModemType)); - - // Each serial device object has a property with key - // kIOSerialBSDTypeKey and a value that is one of kIOSerialBSDAllTypes, - // kIOSerialBSDModemType, or kIOSerialBSDRS232Type. You can experiment with the - // matching by changing the last parameter in the above call to CFDictionarySetValue. - - // As shipped, this sample is only interested in modems, - // so add this property to the CFDictionary we're matching on. - // This will find devices that advertise themselves as modems, - // such as built-in and USB modems. However, this match won't find serial modems. - } - - /*! @function IOServiceGetMatchingServices - @abstract Look up registered IOService objects that match a matching dictionary. - @discussion This is the preferred method of finding IOService objects currently registered by IOKit. IOServiceAddNotification can also supply this information and install a notification of new IOServices. The matching information used in the matching dictionary may vary depending on the class of service being looked up. - @param masterPort The master port obtained from IOMasterPort(). - @param matching A CF dictionary containing matching information, of which one reference is consumed by this function. IOKitLib can contruct matching dictionaries for common criteria with helper functions such as IOServiceMatching, IOOpenFirmwarePathMatching. - @param existing An iterator handle is returned on success, and should be released by the caller when the iteration is finished. - @result A kern_return_t error code. */ - - kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, matchingServices); - if (KERN_SUCCESS != kernResult) { - printf("IOServiceGetMatchingServices returned %d\n", kernResult); - goto exit; - } - -exit: - return kernResult; -} - -/** Given an iterator across a set of modems, return the BSD path to the first one. - * If no modems are found the path name is set to an empty string. - */ -static kern_return_t GetModemPath(io_iterator_t serialPortIterator, char *bsdPath, CFIndex maxPathSize) -{ - io_object_t modemService; - kern_return_t kernResult = KERN_FAILURE; - Boolean modemFound = false; - - // Initialize the returned path - *bsdPath = '\0'; - - // Iterate across all modems found. In this example, we bail after finding the first modem. - - while ((modemService = IOIteratorNext(serialPortIterator)) && !modemFound) { - CFTypeRef bsdPathAsCFString; - - // Get the callout device's path (/dev/cu.xxxxx). The callout device should almost always be - // used: the dialin device (/dev/tty.xxxxx) would be used when monitoring a serial port for - // incoming calls, e.g. a fax listener. - - bsdPathAsCFString = IORegistryEntryCreateCFProperty(modemService, - CFSTR(kIOCalloutDeviceKey), - kCFAllocatorDefault, - 0); - if (bsdPathAsCFString) { - Boolean result; - - // Convert the path from a CFString to a C (NUL-terminated) string for use - // with the POSIX open() call. - - result = CFStringGetCString((CFStringRef)bsdPathAsCFString, - bsdPath, - maxPathSize, - kCFStringEncodingUTF8); - CFRelease(bsdPathAsCFString); - - if (result) { - //printf("Modem found with BSD path: %s", bsdPath); - modemFound = true; - kernResult = KERN_SUCCESS; - } - } - - printf("\n"); - - // Release the io_service_t now that we are done with it. - - (void) IOObjectRelease(modemService); - } - - return kernResult; -} -#endif - -using namespace TNX; SerialLink::SerialLink(QString portname, int baudRate, bool hardwareFlowControl, bool parity, int dataBits, int stopBits) : - port(NULL), - ports(new QVector()), + m_bytesRead(0), + m_port(NULL), + m_ports(new QVector()), m_stopp(false), - bytesRead(0), m_reqReset(false) { // Setup settings - this->porthandle = portname.trimmed(); + m_portName = portname.trimmed(); - if (this->porthandle == "" && getCurrentPorts()->size() > 0) + if (m_portName == "" && getCurrentPorts()->size() > 0) { - this->porthandle = getCurrentPorts()->first().trimmed(); + m_portName = m_ports->first().trimmed(); } -#ifdef _WIN32 - // Port names above 20 need the network path format - if the port name is not already in this format - // catch this special case - if (this->porthandle.size() > 0 && !this->porthandle.startsWith("\\")) { - // Append \\.\ before the port handle. Additional backslashes are used for escaping. - this->porthandle = "\\\\.\\" + this->porthandle; - } -#endif // Set unique ID and add link to the list of links - this->id = getNextLinkId(); + m_id = getNextLinkId(); + + m_baud = baudRate; - setBaudRate(baudRate); if (hardwareFlowControl) { - portSettings.setFlowControl(QPortSettings::FLOW_HARDWARE); + m_flowControl = QSerialPort::HardwareControl; } else { - portSettings.setFlowControl(QPortSettings::FLOW_OFF); + m_flowControl = QSerialPort::NoFlowControl; } if (parity) { - portSettings.setParity(QPortSettings::PAR_EVEN); + m_parity = QSerialPort::EvenParity; } else { - portSettings.setParity(QPortSettings::PAR_NONE); + m_parity = QSerialPort::NoParity; } - setDataBits(dataBits); - setStopBits(stopBits); + + m_dataBits = dataBits; + m_stopBits = stopBits; // Set the port name - if (porthandle == "") + if (m_portName == "") { - name = tr("Serial Link ") + QString::number(getId()); + m_name = tr("Serial Link ") + QString::number(getId()); } else { - name = portname.trimmed(); + m_name = portname.trimmed(); } loadSettings(); } @@ -275,91 +80,26 @@ void SerialLink::requestReset() SerialLink::~SerialLink() { disconnect(); - if(port) delete port; - port = NULL; - if (ports) delete ports; - ports = NULL; + if(m_port) delete m_port; + m_port = NULL; + if (m_ports) delete m_ports; + m_ports = NULL; } QVector* SerialLink::getCurrentPorts() { - ports->clear(); -#ifdef __linux - - // TODO Linux has no standard way of enumerating serial ports - // However the device files are only present when the physical - // device is connected, therefore listing the files should be - // sufficient. - - QString devdir = "/dev"; - QDir dir(devdir); - dir.setFilter(QDir::System); - - QFileInfoList list = dir.entryInfoList(); - for (int i = 0; i < list.size(); ++i) { - QFileInfo fileInfo = list.at(i); - if (fileInfo.fileName().contains(QString("ttyUSB")) || fileInfo.fileName().contains(QString("ttyS")) || fileInfo.fileName().contains(QString("ttyACM"))) - { - ports->append(fileInfo.canonicalFilePath()); - } - } -#endif - -#if defined (__APPLE__) && defined (__MACH__) - - // Enumerate serial ports - kern_return_t kernResult; // on PowerPC this is an int (4 bytes) - io_iterator_t serialPortIterator; - char bsdPath[MAXPATHLEN]; - kernResult = FindModems(&serialPortIterator); - kernResult = GetModemPath(serialPortIterator, bsdPath, sizeof(bsdPath)); - IOObjectRelease(serialPortIterator); // Release the iterator. - - // Add found modems - if (bsdPath[0]) + Q_ASSERT_X(m_ports != NULL, "getCurrentPorts", "m_ports is NULL"); + m_ports->clear(); + // Example use QSerialPortInfo + foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) { - ports->append(QString(bsdPath)); - } +// qDebug() << "PortName : " << info.portName() +// << "Description : " << info.description(); +// qDebug() << "Manufacturer: " << info.manufacturer(); - // Add USB serial port adapters - // TODO Strangely usb serial port adapters are not enumerated, even when connected - QString devdir = "/dev"; - QDir dir(devdir); - dir.setFilter(QDir::System); - - QFileInfoList list = dir.entryInfoList(); - for (int i = list.size() - 1; i >= 0; i--) { - QFileInfo fileInfo = list.at(i); - if (fileInfo.fileName().contains(QString("ttyUSB")) || - fileInfo.fileName().contains(QString("tty.")) || - fileInfo.fileName().contains(QString("ttyS")) || - fileInfo.fileName().contains(QString("ttyACM"))) - { - ports->append(fileInfo.canonicalFilePath()); - } + m_ports->append(info.portName()); } -#endif - -#ifdef _WIN32 - // Get the ports available on this system - QList ports = QextSerialEnumerator::getPorts(); - - // Add the ports in reverse order, because we prepend them to the list - for (int i = ports.size() - 1; i >= 0; i--) - { - QextPortInfo portInfo = ports.at(i); - QString portString = QString(portInfo.portName.toLocal8Bit().constData());// + " - " + QString(ports.at(i).friendName.toLocal8Bit().constData()).split("(").first(); - // Prepend newly found port to the list - this->ports->append(portString); - } - - //printf("port name: %s\n", ports.at(i).portName.toLocal8Bit().constData()); - //printf("friendly name: %s\n", ports.at(i).friendName.toLocal8Bit().constData()); - //printf("physical name: %s\n", ports.at(i).physName.toLocal8Bit().constData()); - //printf("enumerator name: %s\n", ports.at(i).enumName.toLocal8Bit().constData()); - //printf("===================================\n\n"); -#endif - return this->ports; + return m_ports; } void SerialLink::loadSettings() @@ -369,12 +109,12 @@ void SerialLink::loadSettings() settings.sync(); if (settings.contains("SERIALLINK_COMM_PORT")) { - setPortName(settings.value("SERIALLINK_COMM_PORT").toString()); - setBaudRateType(settings.value("SERIALLINK_COMM_BAUD").toInt()); - setParityType(settings.value("SERIALLINK_COMM_PARITY").toInt()); - setStopBits(settings.value("SERIALLINK_COMM_STOPBITS").toInt()); - setDataBits(settings.value("SERIALLINK_COMM_DATABITS").toInt()); - setFlowType(settings.value("SERIALLINK_COMM_FLOW_CONTROL").toInt()); + m_portName = settings.value("SERIALLINK_COMM_PORT").toString(); + m_baud = settings.value("SERIALLINK_COMM_BAUD").toInt(); + m_parity = settings.value("SERIALLINK_COMM_PARITY").toInt(); + m_stopBits = settings.value("SERIALLINK_COMM_STOPBITS").toInt(); + m_dataBits = settings.value("SERIALLINK_COMM_DATABITS").toInt(); + m_flowControl = settings.value("SERIALLINK_COMM_FLOW_CONTROL").toInt(); } } @@ -402,18 +142,19 @@ void SerialLink::run() if (!hardwareConnect()) { //Need to error out here. - emit communicationError(getName(),"Error connecting: " + port->errorString()); + emit communicationError(getName(),"Error connecting: " + m_port->errorString()); return; } // Qt way to make clear what a while(1) loop does - quint64 msecs = QDateTime::currentMSecsSinceEpoch(); - quint64 initialmsecs = QDateTime::currentMSecsSinceEpoch(); + qint64 msecs = QDateTime::currentMSecsSinceEpoch(); + qint64 initialmsecs = QDateTime::currentMSecsSinceEpoch(); quint64 bytes = 0; bool triedreset = false; bool triedDTR = false; - int timeout = 5000; + qint64 timeout = 5000; + forever { { @@ -427,16 +168,16 @@ void SerialLink::run() { this->m_reqReset = false; communicationUpdate(getName(),"Reset requested via DTR signal"); - port->setDtr(true); + m_port->setDataTerminalReady(true); this->msleep(250); - port->setDtr(false); + m_port->setDataTerminalReady(false); } } // Check if new bytes have arrived, if yes, emit the notification signal checkForBytes(); - if (bytes != bytesRead) + if (bytes != m_bytesRead) { - bytes = bytesRead; + bytes = m_bytesRead; msecs = QDateTime::currentMSecsSinceEpoch(); } else @@ -459,15 +200,15 @@ void SerialLink::run() triedDTR = true; communicationUpdate(getName(),"No data to receive on COM port. Attempting to reset via DTR signal"); qDebug() << "No data!!! Attempting reset via DTR."; - port->setDtr(true); + m_port->setDataTerminalReady(true); this->msleep(250); - port->setDtr(false); + m_port->setDataTerminalReady(false); } else if (!triedreset) { qDebug() << "No data!!! Attempting reset via reboot command."; communicationUpdate(getName(),"No data to receive on COM port. Assuming possible terminal mode, attempting to reset via \"reboot\" command"); - port->write("reboot\r\n",8); + m_port->write("reboot\r\n",8); triedreset = true; } else @@ -481,13 +222,12 @@ void SerialLink::run() * from consuming too much processing time */ MG::SLEEP::msleep(SerialLink::poll_interval); - } - if (port) { - port->flushInBuffer(); - port->flushOutBuffer(); - port->close(); - delete port; - port = NULL; + } // end forever loop + + if (m_port) { + m_port->close(); + delete m_port; + m_port = NULL; } } @@ -495,23 +235,23 @@ void SerialLink::run() void SerialLink::checkForBytes() { /* Check if bytes are available */ - if(port && port->isOpen() && port->isWritable()) + if(m_port && m_port->isOpen() && m_port->isWritable()) { - dataMutex.lock(); - qint64 available = port->bytesAvailable(); - dataMutex.unlock(); + m_dataMutex.lock(); + qint64 available = m_port->bytesAvailable(); + m_dataMutex.unlock(); if(available > 0) { readBytes(); - bytesRead += available; + m_bytesRead += available; } else if (available < 0) { /* Error, close port */ - port->close(); + m_port->close(); emit disconnected(); emit connected(false); - emit communicationError(this->getName(), tr("Could not send data - link %1 is disconnected!").arg(this->getName())); + emit communicationError(this->getName(), tr("Could not read data - link %1 is disconnected!").arg(this->getName())); } } // else @@ -524,27 +264,26 @@ void SerialLink::checkForBytes() void SerialLink::writeBytes(const char* data, qint64 size) { - if(port && port->isOpen()) { - int b = port->write(data, size); + if(m_port && m_port->isOpen()) { + qDebug() << "writeBytes" << m_portName << "attempting to tx " << size << "bytes."; + int b = m_port->write(data, size); if (b > 0) { + Q_ASSERT_X(b = size, "writeBytes", "failed to write all bytes"); - // qDebug() << "Serial link " << this->getName() << "transmitted" << b << "bytes:"; + qDebug() << "writeBytes " << m_portName << "tx'd" << b << "bytes:"; // Increase write counter - bitsSentTotal += size * 8; + m_bitsSentTotal += size * 8; + QByteArray* byteArray = new QByteArray(data,size); + + qDebug() << byteArray->toHex(); + delete byteArray; - // int i; - // for (i=0; igetName(), tr("Could not send data - link %1 is disconnected!").arg(this->getName())); + emit communicationError(getName(), tr("Could not send data - link %1 is disconnected!").arg(getName())); } } } @@ -557,18 +296,18 @@ void SerialLink::writeBytes(const char* data, qint64 size) **/ void SerialLink::readBytes() { - dataMutex.lock(); - if(port && port->isOpen()) { + m_dataMutex.lock(); + if(m_port && m_port->isOpen()) { const qint64 maxLength = 2048; char data[maxLength]; - qint64 numBytes = port->bytesAvailable(); + qint64 numBytes = m_port->bytesAvailable(); //qDebug() << "numBytes: " << numBytes; if(numBytes > 0) { /* Read as much data in buffer as possible without overflow */ if(maxLength < numBytes) numBytes = maxLength; - port->read(data, numBytes); + m_port->read(data, numBytes); QByteArray b(data, numBytes); emit bytesReceived(this, b); @@ -580,10 +319,10 @@ void SerialLink::readBytes() // fprintf(stderr,"%02x ", v); // } // fprintf(stderr,"\n"); - bitsReceivedTotal += numBytes * 8; + m_bitsReceivedTotal += numBytes * 8; } } - dataMutex.unlock(); + m_dataMutex.unlock(); } @@ -594,8 +333,8 @@ void SerialLink::readBytes() **/ qint64 SerialLink::bytesAvailable() { - if (port) { - return port->bytesAvailable(); + if (m_port) { + return m_port->bytesAvailable(); } else { return 0; } @@ -608,44 +347,24 @@ qint64 SerialLink::bytesAvailable() **/ bool SerialLink::disconnect() { - if(this->isRunning()) + qDebug() << "disconnect" << m_port->portName(); + Q_ASSERT_X(m_port != NULL, "disconnect", "m_port is NULL"); + + if (isRunning()) { { - QMutexLocker locker(&this->m_stoppMutex); - this->m_stopp = true; + QMutexLocker locker(&m_stoppMutex); + m_stopp = true; } - this->wait(); - - // if (port) { - //#if !defined _WIN32 || !defined _WIN64 - /* Block the thread until it returns from run() */ - //#endif - // port->flushInBuffer(); - // port->flushOutBuffer(); - // port->close(); - // delete port; - // port = NULL; - - // if(this->isRunning()) this->terminate(); //stop running the thread, restart it upon connect - - bool closed = true; - //port->isOpen(); + wait(); // This will terminate the thread and close the serial port - emit disconnected(); + emit disconnected(); // [TODO] There are signals from QSerialPort we should use emit connected(false); - if (port) { - port->close(); - } - return closed; - } - else { - // not running - if (port) { - port->close(); - } + return true; + } else { + m_port->close(); return true; } - } /** @@ -655,12 +374,14 @@ bool SerialLink::disconnect() **/ bool SerialLink::connect() { - if (this->isRunning()) this->disconnect(); + if (isRunning()) + disconnect(); { QMutexLocker locker(&this->m_stoppMutex); - this->m_stopp = false; + m_stopp = false; } - this->start(LowPriority); + + start(LowPriority); return true; } @@ -674,36 +395,52 @@ bool SerialLink::connect() **/ bool SerialLink::hardwareConnect() { - if(port) { - port->close(); - delete port; + if(m_port) + { + qDebug() << "SerialLink:" << QString::number((long)this, 16) << "closing port"; + m_port->close(); + delete m_port; + m_port = NULL; } - port = new QSerialPort(porthandle, portSettings); - QObject::connect(port,SIGNAL(aboutToClose()),this,SIGNAL(disconnected())); - port->setCommTimeouts(QSerialPort::CtScheme_NonBlockingRead); - connectionStartTime = MG::TIME::getGroundTimeNow(); + qDebug() << "SerialLink: hardwareConnect to " << m_portName; + m_port = new QSerialPort(m_portName); - if (!port->open()) + if (m_port == NULL) { - emit communicationUpdate(getName(),"Error opening port: " + port->errorString()); + emit communicationUpdate(getName(),"Error opening port: " + m_port->errorString()); + return false; // couldn't create serial port. } - else + + QObject::connect(m_port,SIGNAL(aboutToClose()),this,SIGNAL(disconnected())); + +// port->setCommTimeouts(QSerialPort::CtScheme_NonBlockingRead); + m_connectionStartTime = MG::TIME::getGroundTimeNow(); + + if (!m_port->open(QIODevice::ReadWrite)) { - emit communicationUpdate(getName(),"Opened port!"); + emit communicationUpdate(getName(),"Error opening port: " + m_port->errorString()); + m_port->close(); + return false; // couldn't open serial port } - bool connectionUp = isConnected(); - if(connectionUp) { - emit connected(); - emit connected(true); - } + emit communicationUpdate(getName(),"Opened port!"); - //qDebug() << "CONNECTING LINK: " << __FILE__ << __LINE__ << "with settings" << port->portName() << getBaudRate() << getDataBits() << getParityType() << getStopBits(); + // Need to configure the port + m_port->setBaudRate(m_baud); + m_port->setDataBits(static_cast(m_dataBits)); + m_port->setFlowControl(static_cast(m_flowControl)); + m_port->setStopBits(static_cast(m_stopBits)); + m_port->setParity(static_cast(m_parity)); + emit connected(); + emit connected(true); + + qDebug() << "CONNECTING LINK: " << __FILE__ << __LINE__ << "with settings" << m_port->portName() + << getBaudRate() << getDataBits() << getParityType() << getStopBits(); writeSettings(); - return connectionUp; + return true; // successful connection } @@ -714,138 +451,82 @@ bool SerialLink::hardwareConnect() **/ bool SerialLink::isConnected() { - if (port) { - return port->isOpen(); + + if (m_port) { + bool isConnected = m_port->isOpen(); + qDebug() << "SerialLink #" << __LINE__ << ":"<< m_port->portName() + << " isConnected =" << QString::number(isConnected); + return isConnected; } else { + qDebug() << "SerialLink #" << __LINE__ << ":" << m_portName + << " isConnected = NULL"; return false; } } int SerialLink::getId() { - return id; + return m_id; } QString SerialLink::getName() { - return name; -} - -void SerialLink::setName(QString name) -{ - this->name = name; - emit nameChanged(this->name); + return m_name; } - /** * This function maps baud rate constants to numerical equivalents. * It relies on the mapping given in qportsettings.h from the QSerialPort library. */ qint64 SerialLink::getNominalDataRate() { - qint64 dataRate = 0; - switch (portSettings.baudRate()) { - - // Baud rates supported only by POSIX systems -#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) - case QPortSettings::BAUDR_50: - dataRate = 50; - break; - case QPortSettings::BAUDR_75: - dataRate = 75; - break; - case QPortSettings::BAUDR_134: - dataRate = 134; - break; - case QPortSettings::BAUDR_150: - dataRate = 150; - break; - case QPortSettings::BAUDR_200: - dataRate = 200; - break; - case QPortSettings::BAUDR_1800: - dataRate = 1800; - break; -#endif - - // Baud rates supported only by Windows -#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) - case QPortSettings::BAUDR_14400: - dataRate = 14400; - break; - case QPortSettings::BAUDR_56000: - dataRate = 56000; - break; - case QPortSettings::BAUDR_128000: - dataRate = 128000; - break; - case QPortSettings::BAUDR_256000: - dataRate = 256000; -#endif - - case QPortSettings::BAUDR_110: - dataRate = 110; - break; - case QPortSettings::BAUDR_300: - dataRate = 300; - break; - case QPortSettings::BAUDR_600: - dataRate = 600; - break; - case QPortSettings::BAUDR_1200: - dataRate = 1200; - break; - case QPortSettings::BAUDR_2400: - dataRate = 2400; - break; - case QPortSettings::BAUDR_4800: - dataRate = 4800; - break; - case QPortSettings::BAUDR_9600: - dataRate = 9600; - break; - case QPortSettings::BAUDR_19200: - dataRate = 19200; - break; - case QPortSettings::BAUDR_38400: - dataRate = 38400; - break; - case QPortSettings::BAUDR_57600: - dataRate = 57600; - break; - case QPortSettings::BAUDR_115200: - dataRate = 115200; - break; - case QPortSettings::BAUDR_230400: - dataRate = 230400; - break; - case QPortSettings::BAUDR_460800: - dataRate = 460800; - break; - case QPortSettings::BAUDR_500000: - dataRate = 500000; - break; - case QPortSettings::BAUDR_576000: - dataRate = 576000; - break; - case QPortSettings::BAUDR_921600: - dataRate = 921600; - break; - - // Otherwise do nothing. - case QPortSettings::BAUDR_UNKNOWN: - default: - break; + int baudRate; + if (m_port) { + baudRate = m_port->baudRate(); + } else { + baudRate = m_baud; + } + qint64 dataRate; + switch (baudRate) + { + case QSerialPort::Baud1200: + dataRate = 1200; + break; + case QSerialPort::Baud2400: + dataRate = 2400; + break; + case QSerialPort::Baud4800: + dataRate = 4800; + break; + case QSerialPort::Baud9600: + dataRate = 9600; + break; + case QSerialPort::Baud19200: + dataRate = 19200; + break; + case QSerialPort::Baud38400: + dataRate = 38400; + break; + case QSerialPort::Baud57600: + dataRate = 57600; + break; + case QSerialPort::Baud115200: + dataRate = 115200; + break; + // Otherwise do nothing. + case QSerialPort::UnknownBaud: + default: + dataRate = -1; + break; } return dataRate; } qint64 SerialLink::getTotalUpstream() { - statisticsMutex.lock(); - return bitsSentTotal / ((MG::TIME::getGroundTimeNow() - connectionStartTime) / 1000); - statisticsMutex.unlock(); + m_statisticsMutex.lock(); + return m_bitsSentTotal / ((MG::TIME::getGroundTimeNow() - m_connectionStartTime) / 1000); + m_statisticsMutex.unlock(); } qint64 SerialLink::getCurrentUpstream() @@ -860,19 +541,19 @@ qint64 SerialLink::getMaxUpstream() qint64 SerialLink::getBitsSent() { - return bitsSentTotal; + return m_bitsSentTotal; } qint64 SerialLink::getBitsReceived() { - return bitsReceivedTotal; + return m_bitsReceivedTotal; } qint64 SerialLink::getTotalDownstream() { - statisticsMutex.lock(); - return bitsReceivedTotal / ((MG::TIME::getGroundTimeNow() - connectionStartTime) / 1000); - statisticsMutex.unlock(); + m_statisticsMutex.lock(); + return m_bitsReceivedTotal / ((MG::TIME::getGroundTimeNow() - m_connectionStartTime) / 1000); + m_statisticsMutex.unlock(); } qint64 SerialLink::getCurrentDownstream() @@ -899,9 +580,11 @@ int SerialLink::getLinkQuality() QString SerialLink::getPortName() { - return porthandle; + return m_portName; } +// We should replace the accessors below with one to get the QSerialPort + int SerialLink::getBaudRate() { return getNominalDataRate(); @@ -909,43 +592,80 @@ int SerialLink::getBaudRate() int SerialLink::getBaudRateType() { - return portSettings.baudRate(); + int baudRate; + if (m_port) { + baudRate = m_port->baudRate(); + } else { + baudRate = m_baud; + } + return baudRate; } int SerialLink::getFlowType() { - return portSettings.flowControl(); + int flowControl; + if (m_port) { + flowControl = m_port->flowControl(); + } else { + flowControl = m_flowControl; + } + return flowControl; } int SerialLink::getParityType() { - return portSettings.parity(); + int parity; + if (m_port) { + parity = m_port->parity(); + } else { + parity = m_parity; + } + return parity; } int SerialLink::getDataBitsType() { - return portSettings.dataBits(); + int dataBits; + if (m_port) { + dataBits = m_port->dataBits(); + } else { + dataBits = m_dataBits; + } + return dataBits; } int SerialLink::getStopBitsType() { - return portSettings.stopBits(); + int stopBits; + if (m_port) { + stopBits = m_port->stopBits(); + } else { + stopBits = m_stopBits; + } + return stopBits; } int SerialLink::getDataBits() { - int ret = -1; - switch (portSettings.dataBits()) { - case QPortSettings::DB_5: + int ret; + int dataBits; + if (m_port) { + dataBits = m_port->dataBits(); + } else { + dataBits = m_dataBits; + } + + switch (dataBits) { + case QSerialPort::Data5: ret = 5; break; - case QPortSettings::DB_6: + case QSerialPort::Data6: ret = 6; break; - case QPortSettings::DB_7: + case QSerialPort::Data7: ret = 7; break; - case QPortSettings::DB_8: + case QSerialPort::Data8: ret = 8; break; default: @@ -957,12 +677,18 @@ int SerialLink::getDataBits() int SerialLink::getStopBits() { + int stopBits; + if (m_port) { + stopBits = m_port->stopBits(); + } else { + stopBits = m_stopBits; + } int ret = -1; - switch (portSettings.stopBits()) { - case QPortSettings::STOP_1: + switch (stopBits) { + case QSerialPort::OneStop: ret = 1; break; - case QPortSettings::STOP_2: + case QSerialPort::TwoStop: ret = 2; break; default: @@ -974,56 +700,36 @@ int SerialLink::getStopBits() bool SerialLink::setPortName(QString portName) { - if(portName.trimmed().length() > 0) { - bool reconnect = false; - if (isConnected()) reconnect = true; - disconnect(); - - this->porthandle = portName.trimmed(); - setName(tr("serial port ") + portName.trimmed()); -#ifdef _WIN32 - // Port names above 20 need the network path format - if the port name is not already in this format - // catch this special case - if (!this->porthandle.startsWith("\\")) { - // Append \\.\ before the port handle. Additional backslashes are used for escaping. - this->porthandle = "\\\\.\\" + this->porthandle; - } -#endif + qDebug() << "current portName " << m_portName; + qDebug() << "setPortName to " << portName; + bool accepted = false; + if ((portName != m_portName) + && (portName.trimmed().length() > 0)) { + m_portName = portName.trimmed(); + m_name = tr("serial port ") + portName.trimmed(); // [TODO] Do we need this? + if(m_port) + m_port->setPortName(portName); - if(reconnect) connect(); - return true; - } else { - return false; + emit nameChanged(m_name); // [TODO] maybe we can eliminate this + return accepted; } + return false; } bool SerialLink::setBaudRateType(int rateIndex) { - bool reconnect = false; - bool accepted = true; // This is changed if none of the data rates matches - if(isConnected()) reconnect = true; - disconnect(); + Q_ASSERT_X(m_port != NULL, "setBaudRateType", "m_port is NULL"); + // These minimum and maximum baud rates were based on those enumerated in qserialport.h + const int minBaud = (int)QSerialPort::Baud1200; + const int maxBaud = (int)QSerialPort::Baud115200; - // These minimum and maximum baud rates were based on those enumerated in qportsettings.h. -#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) - const int minBaud = (int)QPortSettings::BAUDR_110; - const int maxBaud = (int)QPortSettings::BAUDR_921600; -#elif defined(Q_OS_LINUX) - const int minBaud = (int)QPortSettings::BAUDR_50; - const int maxBaud = (int)QPortSettings::BAUDR_921600; -#elif defined(Q_OS_UNIX) || defined(Q_OS_DARWIN) - const int minBaud = (int)QPortSettings::BAUDR_50; - const int maxBaud = (int)QPortSettings::BAUDR_921600; -#endif - - if (rateIndex >= minBaud && rateIndex <= maxBaud) + if (m_port && (rateIndex >= minBaud && rateIndex <= maxBaud)) { - portSettings.setBaudRate((QPortSettings::BaudRate)rateIndex); + return m_port->setBaudRate(static_cast(rateIndex)); } - if(reconnect) connect(); - return accepted; + return false; } bool SerialLink::setBaudRateString(const QString& rate) @@ -1036,260 +742,104 @@ bool SerialLink::setBaudRateString(const QString& rate) bool SerialLink::setBaudRate(int rate) { - bool reconnect = false; - bool accepted = true; // This is changed if none of the data rates matches - if(isConnected()) { - reconnect = true; - } - disconnect(); - - // This switch-statment relies on the mapping given in qportsettings.h from the QSerialPort library. - switch (rate) { - - // Baud rates supported only by non-Windows systems -#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) - case 50: - portSettings.setBaudRate(QPortSettings::BAUDR_50); - break; - case 75: - portSettings.setBaudRate(QPortSettings::BAUDR_75); - break; - case 134: - portSettings.setBaudRate(QPortSettings::BAUDR_134); - break; - case 150: - portSettings.setBaudRate(QPortSettings::BAUDR_150); - break; - case 200: - portSettings.setBaudRate(QPortSettings::BAUDR_200); - break; - case 1800: - portSettings.setBaudRate(QPortSettings::BAUDR_1800); - break; -#endif - - // Baud rates supported only by windows -#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) - case 14400: - portSettings.setBaudRate(QPortSettings::BAUDR_14400); - break; - case 56000: - portSettings.setBaudRate(QPortSettings::BAUDR_56000); - break; - case 128000: - portSettings.setBaudRate(QPortSettings::BAUDR_128000); - break; - case 256000: - portSettings.setBaudRate(QPortSettings::BAUDR_256000); - break; -#endif - - // Supported by all OSes: - case 110: - portSettings.setBaudRate(QPortSettings::BAUDR_110); - break; - case 300: - portSettings.setBaudRate(QPortSettings::BAUDR_300); - break; - case 600: - portSettings.setBaudRate(QPortSettings::BAUDR_600); - break; - case 1200: - portSettings.setBaudRate(QPortSettings::BAUDR_1200); - break; - case 2400: - portSettings.setBaudRate(QPortSettings::BAUDR_2400); - break; - case 4800: - portSettings.setBaudRate(QPortSettings::BAUDR_4800); - break; - case 9600: - portSettings.setBaudRate(QPortSettings::BAUDR_9600); - break; - case 19200: - portSettings.setBaudRate(QPortSettings::BAUDR_19200); - break; - case 38400: - portSettings.setBaudRate(QPortSettings::BAUDR_38400); - break; - case 57600: - portSettings.setBaudRate(QPortSettings::BAUDR_57600); - break; - case 115200: - portSettings.setBaudRate(QPortSettings::BAUDR_115200); - break; - case 230400: - portSettings.setBaudRate(QPortSettings::BAUDR_230400); - break; - case 460800: - portSettings.setBaudRate(QPortSettings::BAUDR_460800); - break; - case 500000: - portSettings.setBaudRate(QPortSettings::BAUDR_500000); - break; - case 576000: - portSettings.setBaudRate(QPortSettings::BAUDR_576000); - break; - case 921600: - portSettings.setBaudRate(QPortSettings::BAUDR_921600); - break; - default: - // If none of the above cases matches, there must be an error - accepted = false; - break; + bool accepted = false; + if (rate != m_baud) { + m_baud = rate; + accepted = true; + if (m_port) + accepted = m_port->setBaudRate(rate); } - - if(reconnect) connect(); return accepted; - } bool SerialLink::setFlowType(int flow) { - bool reconnect = false; - bool accepted = true; - if(isConnected()) reconnect = true; - disconnect(); - - switch (flow) { - case (int)QPortSettings::FLOW_OFF: - portSettings.setFlowControl(QPortSettings::FLOW_OFF); - break; - case (int)QPortSettings::FLOW_HARDWARE: - portSettings.setFlowControl(QPortSettings::FLOW_HARDWARE); - break; - case (int)QPortSettings::FLOW_XONXOFF: - portSettings.setFlowControl(QPortSettings::FLOW_XONXOFF); - break; - default: - // If none of the above cases matches, there must be an error - accepted = false; - break; + bool accepted = false; + if (flow != m_flowControl) { + m_flowControl = static_cast(flow); + accepted = true; + if (m_port) + accepted = m_port->setFlowControl(static_cast(flow)); } - - if(reconnect) connect(); return accepted; } bool SerialLink::setParityType(int parity) { - bool reconnect = false; - bool accepted = true; - if (isConnected()) reconnect = true; - disconnect(); - - switch (parity) { - case (int)QPortSettings::PAR_NONE: - portSettings.setParity(QPortSettings::PAR_NONE); - break; - case (int)QPortSettings::PAR_ODD: - portSettings.setParity(QPortSettings::PAR_ODD); - break; - case (int)QPortSettings::PAR_EVEN: - portSettings.setParity(QPortSettings::PAR_EVEN); - break; - case (int)QPortSettings::PAR_SPACE: - portSettings.setParity(QPortSettings::PAR_SPACE); - break; - default: - // If none of the above cases matches, there must be an error - accepted = false; - break; + bool accepted = false; + if (parity != m_parity) { + m_parity = static_cast(parity); + accepted = true; + if (m_port) { + switch (parity) { + case QSerialPort::NoParity: + accepted = m_port->setParity(QSerialPort::NoParity); + break; + case 1: // Odd Parity setting for backwards compatibilty + accepted = m_port->setParity(QSerialPort::OddParity); + break; + case QSerialPort::EvenParity: + accepted = m_port->setParity(QSerialPort::EvenParity); + break; + case QSerialPort::OddParity: + accepted = m_port->setParity(QSerialPort::OddParity); + break; + default: + // If none of the above cases matches, there must be an error + accepted = false; + break; + } + } } - - if (reconnect) connect(); return accepted; } bool SerialLink::setDataBits(int dataBits) { - //qDebug() << "Setting" << dataBits << "data bits"; - bool reconnect = false; - if (isConnected()) reconnect = true; - bool accepted = true; - disconnect(); - - switch (dataBits) { - case 5: - portSettings.setDataBits(QPortSettings::DB_5); - break; - case 6: - portSettings.setDataBits(QPortSettings::DB_6); - break; - case 7: - portSettings.setDataBits(QPortSettings::DB_7); - break; - case 8: - portSettings.setDataBits(QPortSettings::DB_8); - break; - default: - // If none of the above cases matches, there must be an error - accepted = false; - break; + bool accepted = false; + if (dataBits != m_dataBits) { + m_dataBits = static_cast(dataBits); + accepted = true; + if (m_port) + accepted = m_port->setDataBits(static_cast(dataBits)); } - - if(reconnect) connect(); - return accepted; } bool SerialLink::setStopBits(int stopBits) { - bool reconnect = false; - bool accepted = true; - if(isConnected()) reconnect = true; - disconnect(); - - switch (stopBits) { - case 1: - portSettings.setStopBits(QPortSettings::STOP_1); - break; - case 2: - portSettings.setStopBits(QPortSettings::STOP_2); - break; - default: - // If none of the above cases matches, there must be an error - accepted = false; - break; + // Note 3 is OneAndAHalf stopbits. + bool accepted = false; + if (stopBits != m_stopBits) { + m_stopBits = static_cast(stopBits); + accepted = true; + if (m_port) + accepted = m_port->setStopBits(static_cast(stopBits)); } - - if(reconnect) connect(); return accepted; } bool SerialLink::setDataBitsType(int dataBits) { - bool reconnect = false; bool accepted = false; - - if (isConnected()) reconnect = true; - disconnect(); - - if (dataBits >= (int)QPortSettings::DB_5 && dataBits <= (int)QPortSettings::DB_8) { - portSettings.setDataBits((QPortSettings::DataBits) dataBits); - - if(reconnect) connect(); + if (dataBits != m_dataBits) { + m_dataBits = static_cast(dataBits); accepted = true; + if (m_port) + accepted = m_port->setDataBits(static_cast(dataBits)); } - return accepted; } bool SerialLink::setStopBitsType(int stopBits) { - bool reconnect = false; bool accepted = false; - if(isConnected()) reconnect = true; - disconnect(); - - if (stopBits >= (int)QPortSettings::STOP_1 && stopBits <= (int)QPortSettings::STOP_2) { - portSettings.setStopBits((QPortSettings::StopBits) stopBits); - - if(reconnect) connect(); + if (stopBits != m_stopBits) { + m_stopBits = static_cast(stopBits); accepted = true; + if (m_port) + accepted = m_port->setStopBits(static_cast(stopBits)); } - - if(reconnect) connect(); return accepted; } diff --git a/src/comm/SerialLink.h b/src/comm/SerialLink.h index d1f0aa239..883ffad9f 100644 --- a/src/comm/SerialLink.h +++ b/src/comm/SerialLink.h @@ -36,13 +36,9 @@ This file is part of the QGROUNDCONTROL project #include #include #include -#include "qserialport.h" +#include #include #include "SerialLinkInterface.h" -#ifdef _WIN32 -#include "windows.h" -#endif - /** * @brief The SerialLink class provides cross-platform access to serial links. @@ -146,37 +142,36 @@ protected slots: void checkForBytes(); protected: - quint64 bytesRead; - TNX::QSerialPort * port; - TNX::QPortSettings portSettings; -#ifdef _WIN32 - HANDLE winPort; - DCB winPortSettings; -#endif - QString porthandle; - QString name; - int timeout; - int id; - - quint64 bitsSentTotal; - quint64 bitsSentShortTerm; - quint64 bitsSentCurrent; - quint64 bitsSentMax; - quint64 bitsReceivedTotal; - quint64 bitsReceivedShortTerm; - quint64 bitsReceivedCurrent; - quint64 bitsReceivedMax; - quint64 connectionStartTime; - QMutex statisticsMutex; - QMutex dataMutex; - QVector* ports; + quint64 m_bytesRead; + QSerialPort* m_port; + int m_baud; + int m_dataBits; + int m_flowControl; + int m_stopBits; + int m_parity; + QString m_portName; + QString m_name; + int m_timeout; + int m_id; + + quint64 m_bitsSentTotal; + quint64 m_bitsSentShortTerm; + quint64 m_bitsSentCurrent; + quint64 m_bitsSentMax; + quint64 m_bitsReceivedTotal; + quint64 m_bitsReceivedShortTerm; + quint64 m_bitsReceivedCurrent; + quint64 m_bitsReceivedMax; + quint64 m_connectionStartTime; + QMutex m_statisticsMutex; + QMutex m_dataMutex; + QVector* m_ports; private: volatile bool m_stopp; volatile bool m_reqReset; QMutex m_stoppMutex; - void setName(QString name); bool hardwareConnect(); signals: diff --git a/src/ui/SerialConfigurationWindow.cc b/src/ui/SerialConfigurationWindow.cc index 499188ea2..3354deb6c 100644 --- a/src/ui/SerialConfigurationWindow.cc +++ b/src/ui/SerialConfigurationWindow.cc @@ -259,7 +259,7 @@ void SerialConfigurationWindow::setParityNone(bool accept) void SerialConfigurationWindow::setParityOdd(bool accept) { - if (accept) link->setParityType(1); + if (accept) link->setParityType(1); // [TODO] This needs to be Fixed [BB] } void SerialConfigurationWindow::setParityEven(bool accept) -- 2.22.0