1 package net.sf.atmodem4j.spsw;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 import java.io.BufferedReader;
32 import java.io.File;
33 import java.io.FileOutputStream;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.io.InputStreamReader;
37 import java.io.OutputStream;
38 import java.security.AccessController;
39 import java.security.PrivilegedExceptionAction;
40 import java.util.EnumSet;
41 import java.util.Properties;
42 import java.util.Set;
43 import java.util.logging.Level;
44 import java.util.logging.Logger;
45
46
47
48
49
50 public abstract class AbstractSerialPortSocket implements SerialPortSocket {
51
52 protected final static Logger LOG = Logger.getLogger("net.sf.atmodem4j.spsw");
53
54 static final int PORT_MODE_UNCHANGED = 0;
55 static final int PORT_MODE_RAW = 0x01;
56
57 static final int FLOW_CONTROL_NONE = 0;
58 static final int FLOW_CONTROL_RTS_CTS_IN = 0x01;
59 static final int FLOW_CONTROL_RTS_CTS_OUT = 0x02;
60 static final int FLOW_CONTROL_XON_XOFF_IN = 0x04;
61 static final int FLOW_CONTROL_XON_XOFF_OUT = 0x08;
62
63 static final int STOP_BITS_1 = 0;
64 static final int STOP_BITS_1_5 = 0x01;
65 static final int STOP_BITS_2 = 0x02;
66
67 static final int PARITY_NONE = 0;
68 static final int PARITY_ODD = 0x01;
69 static final int PARITY_EVEN = 0x02;
70 static final int PARITY_MARK = 0x03;
71 static final int PARITY_SPACE = 0x04;
72
73 private static boolean libLoaded;
74 private static String libName;
75 public final static String SPSW_PROPERTIES = "net/sf/atmodem4j/spsw/spsw.properties";
76
77 public static boolean isLibLoaded() {
78 return libLoaded;
79 }
80
81 public static String getLibName() {
82 return libName;
83 }
84
85 public static String getArch() {
86 String osArch = System.getProperty("os.arch");
87 switch (getOsName()) {
88 case "linux":
89 if ("arm".equals(osArch)) {
90 String floatStr = "sf";
91 if (System.getProperty("java.library.path").contains("gnueabihf") || System.getProperty("java.library.path").contains("armhf")) {
92 floatStr = "hf";
93 } else {
94 LOG.log(Level.WARNING, "Can't find hardware|software floating point in libpath try readelf");
95 try {
96 Process readelfProcess = Runtime.getRuntime().exec("readelf -A /proc/self/exe");
97 try (BufferedReader reader = new BufferedReader(new InputStreamReader(readelfProcess.getInputStream()))) {
98 String buffer;
99 while ((buffer = reader.readLine()) != null && !buffer.isEmpty()) {
100 if (buffer.toLowerCase().contains("Tag_ABI_VFP_args".toLowerCase())) {
101 floatStr = "hf";
102 break;
103 }
104 }
105 }
106 } catch (Exception ex) {
107
108 }
109 }
110 return osArch + floatStr;
111 } else {
112 return osArch;
113 }
114 default:
115 return osArch;
116 }
117
118 }
119
120 public static String getOsName() {
121 switch (System.getProperty("os.name")) {
122 case "Linux":
123 return "linux";
124 case "Mac OS":
125 throw new UnsupportedOperationException("Mac OS is currently not supported yet");
126 case "Mac OS X":
127 throw new UnsupportedOperationException("Mac OS X is currently not supported yet");
128 case "Windows 95":
129 return "windows";
130 case "Windows 98":
131 return "windows";
132 case "Windows Me":
133 return "windows";
134 case "Windows NT":
135 return "windows";
136 case "Windows 2000":
137 return "windows";
138 case "Windows XP":
139 return "windows";
140 case "Windows 2003":
141 return "windows";
142 case "Windows Vista":
143 return "windows";
144 case "Windows 2008":
145 return "windows";
146 case "Windows 7":
147 return "windows";
148 case "Windows 8":
149 return "windows";
150 case "Windows 2012":
151 return "windows";
152 case "Windows CE":
153 throw new UnsupportedOperationException("Windows CE is currently not supported yet");
154 case "OS/2":
155 throw new UnsupportedOperationException("OS/2 is currently not supported yet");
156 case "MPE/iX":
157 throw new UnsupportedOperationException("MPE/iX is currently not supported yet");
158 case "HP-UX":
159 throw new UnsupportedOperationException("HP-UX is currently not supported yet");
160 case "AIX":
161 throw new UnsupportedOperationException("AIX is currently not supported yet");
162 case "OS/390":
163 throw new UnsupportedOperationException("OS/390 is currently not supported yet");
164 case "FreeBSD":
165 throw new UnsupportedOperationException("FreeBSD is currently not supported yet");
166 case "Irix":
167 throw new UnsupportedOperationException("Irix is currently not supported yet");
168 case "Digital Unix":
169 throw new UnsupportedOperationException("Digital Unix is currently not supported yet");
170 case "NetWare 4.11":
171 throw new UnsupportedOperationException("NetWare 4.11 is currently not supported yet");
172 case "OSF1":
173 throw new UnsupportedOperationException("OSF1 is currently not supported yet");
174 case "OpenVMS":
175 throw new UnsupportedOperationException("OpenVMS is currently not supported yet");
176 default:
177 throw new RuntimeException("Can't figure out OS: " + System.getProperty("os.name"));
178 }
179
180 }
181
182
183
184
185
186
187
188
189 public static synchronized boolean yesIhaveLoadedTheNativeLibMyself(String libName) {
190
191 if (libLoaded) {
192 return false;
193 }
194 libLoaded = true;
195 AbstractSerialPortSocket.libName = libName;
196 return true;
197 }
198
199
200 public static synchronized boolean loadNativeLib() {
201 if (libLoaded) {
202 LOG.log(Level.INFO, "Lib was Loaded");
203 return false;
204 }
205 Properties p = new Properties();
206 try {
207 p.load(AbstractSerialPortSocket.class.getClassLoader().getResourceAsStream(SPSW_PROPERTIES));
208 } catch (IOException ex) {
209 LOG.log(Level.SEVERE, "Can't find spsw.properties");
210 throw new RuntimeException("Can't load version information", ex);
211 }
212
213 final String rawLibName = String.format("spsw-%s-%s-%s", getOsName(), getArch(), p.getProperty("version"));
214 LOG.log(Level.INFO, "Raw Libname: {0}", rawLibName);
215 libName = System.mapLibraryName(rawLibName);
216 LOG.log(Level.INFO, "Libname: {0}", libName);
217
218 try {
219 System.loadLibrary(libName);
220 LOG.log(Level.INFO, "Lib Loaded via System.loadLibrary(\"{0}\")", libName);
221 libLoaded = true;
222 return true;
223 } catch (Throwable t) {
224
225 }
226 try {
227 String file = AbstractSerialPortSocket.class.getClassLoader().getResource(libName).getFile();
228 System.load(file);
229 libLoaded = true;
230 libName = file;
231 LOG.log(Level.INFO, "Lib Loaded via System.load(\"{0}\")", file);
232 return true;
233 } catch (Throwable t) {
234
235 }
236
237 File tmpLib = null;
238 try (InputStream is = AbstractSerialPortSocket.class.getClassLoader().getResourceAsStream(libName)) {
239
240 int splitPos = libName.indexOf(rawLibName);
241 if (splitPos <= 0) {
242
243 }
244 splitPos += rawLibName.length();
245
246 tmpLib = File.createTempFile(libName.substring(0, splitPos), libName.substring(splitPos));
247 tmpLib.deleteOnExit();
248 try (FileOutputStream fos = new FileOutputStream(tmpLib)) {
249 byte[] buff = new byte[1024];
250 int i;
251 while ((i = is.read(buff)) > 0) {
252 fos.write(buff, 0, i);
253 }
254 fos.flush();
255 }
256
257 System.load(tmpLib.getAbsolutePath());
258 libLoaded = true;
259 libName = tmpLib.getAbsolutePath();
260 LOG.log(Level.INFO, "Lib Loaded via System.load(\"{0}\")", tmpLib.getAbsolutePath());
261 return true;
262 } catch (Throwable t) {
263 LOG.log(Level.SEVERE, "Giving up cant load the lib \"{0}\" List System Properties", tmpLib.getAbsolutePath());
264 for (String name : System.getProperties().stringPropertyNames()) {
265 LOG.log(Level.SEVERE, "System.property {0} = {1} ", new Object[] {name, System.getProperty(name)});
266 }
267 LOG.log(Level.SEVERE, "Giving up cant load the lib \"" +tmpLib.getAbsolutePath() + "\" ", t);
268 throw new RuntimeException("Can't load spsw native lib, giving up!", t);
269 }
270 }
271
272 protected SerialInputStream is;
273 protected SerialOutputStream os;
274
275 private String portName;
276 private boolean open = false;
277
278 public AbstractSerialPortSocket(String portName) {
279 if (portName == null) {
280 throw new IllegalArgumentException("portname must not null!");
281 }
282 if (!libLoaded) {
283 loadNativeLib();
284 }
285 this.portName = portName;
286 }
287
288 @Override
289 public boolean isClosed() {
290 return !open;
291 }
292
293 @Override
294 public synchronized void close() throws IOException {
295 open = false;
296
297 if (is != null) {
298 is.open = false;
299 is = null;
300 }
301 if (os != null) {
302 os.open = false;
303 os = null;
304 }
305 close0();
306 }
307
308 @Override
309 public synchronized InputStream getInputStream() throws IOException {
310 if (!isOpen()) {
311 throw new SerialPortException("Port is not opend");
312 }
313 InputStream result;
314 try {
315 result = AccessController.doPrivileged(
316 new PrivilegedExceptionAction<InputStream>() {
317 @Override
318 public InputStream run() throws IOException {
319 if (is == null) {
320 is = new SerialInputStream();
321 is.open = isOpen();
322 }
323 return is;
324 }
325 });
326 } catch (java.security.PrivilegedActionException e) {
327 throw (IOException) e.getException();
328 }
329 return result;
330 }
331
332 @Override
333 public OutputStream getOutputStream() throws IOException {
334 if (!isOpen()) {
335 throw new SerialPortException("Port is not opend");
336 }
337 OutputStream result;
338 try {
339 result = AccessController.doPrivileged(
340 new PrivilegedExceptionAction<OutputStream>() {
341 @Override
342 public OutputStream run() throws IOException {
343 if (os == null) {
344 os = new SerialOutputStream();
345 os.open = isOpen();
346 }
347 return os;
348 }
349 });
350 } catch (java.security.PrivilegedActionException e) {
351 throw (IOException) e.getException();
352 }
353 return result;
354 }
355
356 @Override
357 public String getPortName() {
358 return portName;
359 }
360
361 @Override
362 public boolean isOpen() {
363 return open;
364 }
365
366 @Override
367 public synchronized void openAsIs() throws IOException {
368 open(portName, PORT_MODE_UNCHANGED);
369 open = true;
370 }
371
372 @Override
373 public synchronized void openRaw() throws IOException {
374 open(portName, PORT_MODE_RAW);
375 open = true;
376 }
377
378 @Override
379 public synchronized void openTerminal() throws IOException {
380 throw new UnsupportedOperationException("Terminal mode not yet supported");
381 }
382
383 @Override
384 public synchronized void openModem() throws IOException {
385 throw new UnsupportedOperationException("Modem mode not yet supported");
386 }
387
388 @Override
389 public void openRaw(Baudrate baudRate, DataBits dataBits, StopBits stopBits, Parity parity, Set<FlowControl> flowControls) throws IOException {
390 openRaw();
391 setBaudrate(baudRate);
392 setDataBits(dataBits);
393 setStopBits(stopBits);
394 setParity(parity);
395 setFlowControl(flowControls);
396 }
397
398 @Override
399 public void setFlowControl(Set<FlowControl> flowControls) throws IOException {
400 int nativeValue = 0;
401 for (FlowControl fc : flowControls) {
402 switch (fc) {
403 case RTS_CTS_IN:
404 nativeValue |= FLOW_CONTROL_RTS_CTS_IN;
405 break;
406 case RTS_CTS_OUT:
407 nativeValue |= FLOW_CONTROL_RTS_CTS_OUT;
408 break;
409 case XON_XOFF_IN:
410 nativeValue |= FLOW_CONTROL_XON_XOFF_IN;
411 break;
412 case XON_XOFF_OUT:
413 nativeValue |= FLOW_CONTROL_XON_XOFF_OUT;
414 break;
415 default:
416 throw new RuntimeException("Cant handle Flowcontrol");
417 }
418 }
419 setFlowControl(nativeValue);
420 }
421
422 @Override
423 public Set<FlowControl> getFlowControl() throws IOException {
424 final int flowControl = getFlowControl0();
425 Set<FlowControl> s = EnumSet.noneOf(FlowControl.class);
426 if ((flowControl & FLOW_CONTROL_RTS_CTS_IN) == FLOW_CONTROL_RTS_CTS_IN) {
427 s.add(FlowControl.RTS_CTS_IN);
428 }
429 if ((flowControl & FLOW_CONTROL_RTS_CTS_OUT) == FLOW_CONTROL_RTS_CTS_OUT) {
430 s.add(FlowControl.RTS_CTS_OUT);
431 }
432 if ((flowControl & FLOW_CONTROL_XON_XOFF_IN) == FLOW_CONTROL_XON_XOFF_IN) {
433 s.add(FlowControl.XON_XOFF_IN);
434 }
435 if ((flowControl & FLOW_CONTROL_XON_XOFF_OUT) == FLOW_CONTROL_XON_XOFF_OUT) {
436 s.add(FlowControl.XON_XOFF_OUT);
437 }
438 return s;
439 }
440
441
442
443
444
445
446
447 protected abstract void open(String portName, int type) throws IOException;
448
449
450
451
452 protected abstract void close0() throws IOException;
453
454 @Override
455 @SuppressWarnings("FinalizeDeclaration")
456 protected void finalize() throws Throwable {
457 try {
458 if (isOpen()) {
459 close();
460 }
461 } catch (IOException e) {
462
463 } finally {
464 super.finalize();
465 }
466 }
467
468 @Override
469 public void setBaudrate(Baudrate baudrate) throws IOException {
470 setBaudrate(baudrate.value);
471 }
472
473 @Override
474 public void setDataBits(DataBits dataBits) throws IOException {
475 setDataBits(dataBits.value);
476 }
477
478 @Override
479 public void setStopBits(StopBits stopBits) throws IOException {
480 switch (stopBits) {
481 case SB_1:
482 setStopBits(STOP_BITS_1);
483 break;
484 case SB_1_5:
485 setStopBits(STOP_BITS_1_5);
486 break;
487 case SB_2:
488 setStopBits(STOP_BITS_2);
489 break;
490 default:
491 throw new IllegalArgumentException("Cant handle Stopbits");
492
493 }
494 }
495
496 @Override
497 public void setParity(Parity parity) throws IOException {
498 switch (parity) {
499 case EVEN:
500 setParity(PARITY_EVEN);
501 break;
502 case MARK:
503 setParity(PARITY_MARK);
504 break;
505 case NONE:
506 setParity(PARITY_NONE);
507 break;
508 case ODD:
509 setParity(PARITY_ODD);
510 break;
511 case SPACE:
512 setParity(PARITY_SPACE);
513 break;
514 default:
515 throw new RuntimeException("cant convert parity");
516 }
517 }
518
519 @Override
520 public Baudrate getBaudrate() throws IOException {
521 return Baudrate.fromNative(getBaudrate0());
522 }
523
524 @Override
525 public DataBits getDatatBits() throws IOException {
526 return DataBits.fromNative(getDataBits0());
527 }
528
529 @Override
530 public StopBits getStopBits() throws IOException {
531 switch (getStopBits0()) {
532 case STOP_BITS_1:
533 return StopBits.SB_1;
534 case STOP_BITS_1_5:
535 return StopBits.SB_1_5;
536 case STOP_BITS_2:
537 return StopBits.SB_2;
538 default:
539 throw new RuntimeException("Cant handle native value: " + getStopBits0());
540 }
541 }
542
543 @Override
544 public Parity getParity() throws IOException {
545 switch (getParity0()) {
546 case PARITY_NONE:
547 return Parity.NONE;
548 case PARITY_ODD:
549 return Parity.ODD;
550 case PARITY_EVEN:
551 return Parity.EVEN;
552 case PARITY_MARK:
553 return Parity.MARK;
554 case PARITY_SPACE:
555 return Parity.SPACE;
556 default:
557 throw new RuntimeException("Can't convert native value to parity: " + getParity0());
558 }
559 }
560
561 protected abstract int readSingle() throws IOException;
562
563
564
565
566
567
568
569
570
571 protected abstract int readBytes(byte[] b, int off, int len) throws IOException;
572
573 protected abstract void writeSingle(int b) throws IOException;
574
575
576
577
578
579
580
581
582
583 protected abstract void writeBytes(byte[] b, int off, int len) throws IOException;
584
585 protected abstract void setFlowControl(int mask) throws IOException;
586
587 protected abstract int getFlowControl0() throws IOException;
588
589 protected abstract void setBaudrate(int baudRate) throws IOException;
590
591 protected abstract void setDataBits(int value) throws IOException;
592
593 protected abstract void setStopBits(int value) throws IOException;
594
595 protected abstract void setParity(int parity) throws IOException;
596
597 protected abstract int getBaudrate0() throws IOException;
598
599 protected abstract int getDataBits0() throws IOException;
600
601 protected abstract int getStopBits0() throws IOException;
602
603 protected abstract int getParity0() throws IOException;
604
605 private class SerialInputStream extends InputStream {
606
607 private boolean open = false;
608
609 @Override
610 public void close() throws IOException {
611 AbstractSerialPortSocket.this.close();
612 }
613
614 @Override
615 public int read() throws IOException {
616 if (!open) {
617 return -1;
618 } else {
619 return AbstractSerialPortSocket.this.readSingle();
620 }
621 }
622
623 @Override
624 public int read(byte b[], int off, int len) throws IOException {
625 if (b == null) {
626 throw new NullPointerException();
627 } else if (off < 0 || len < 0 || len > b.length - off) {
628 throw new IndexOutOfBoundsException();
629 } else if (len == 0) {
630 return 0;
631 }
632 if (!open) {
633 return -1;
634 } else {
635 return AbstractSerialPortSocket.this.readBytes(b, off, len);
636 }
637 }
638
639 @Override
640 public int available() throws IOException {
641 if (!open) {
642 throw new SerialPortException("Port is closed");
643 } else {
644 return AbstractSerialPortSocket.this.getInBufferBytesCount();
645 }
646 }
647
648 }
649
650 private class SerialOutputStream extends OutputStream {
651
652 private boolean open;
653
654 @Override
655 public void close() throws IOException {
656 AbstractSerialPortSocket.this.close();
657 }
658
659 @Override
660 public void write(int b) throws IOException {
661 if (!open) {
662 throw new SerialPortException("port is closed");
663 } else {
664 AbstractSerialPortSocket.this.writeSingle(b);
665 }
666 }
667
668 @Override
669 public void write(byte b[], int off, int len) throws IOException {
670 if (b == null) {
671 throw new NullPointerException();
672 } else if ((off < 0) || (off > b.length) || (len < 0)
673 || ((off + len) > b.length) || ((off + len) < 0)) {
674 throw new IndexOutOfBoundsException();
675 } else if (len == 0) {
676 return;
677 }
678
679 if (!open) {
680 throw new SerialPortException("port is closed");
681 } else {
682 AbstractSerialPortSocket.this.writeBytes(b, 0, b.length);
683 }
684 }
685 }
686
687 @Override
688 public String toString() {
689 try {
690 return String.format("[portname=%s, baudrate= %s, dataBits= %s, stopBits= %s, parity= %s, flowControl= %s]", getPortName(), getBaudrate(), getDatatBits(), getStopBits(), getParity(), getFlowControl());
691 } catch (IOException e) {
692 return "Internal Error " + e;
693 }
694 }
695 }