View Javadoc
1   package net.sf.atmodem4j.spsw;
2   
3   /*
4    * #%L
5    * SPSW Java
6    * %%
7    * Copyright (C) 2009 - 2014 atmodem4j
8    * %%
9    * atmodem4j - A serial port socket wrapper- http://atmodem4j.sourceforge.net/
10   * Copyright (C) 2009-2014, atmodem4j.sf.net, and individual contributors as indicated
11   * by the @authors tag. See the copyright.txt in the distribution for a
12   * full listing of individual contributors.
13   * 
14   * This is free software; you can redistribute it and/or modify it
15   * under the terms of the GNU General Public License as
16   * published by the Free Software Foundation; either version 3 of
17   * the License, or (at your option) any later version.
18   * 
19   * This software is distributed in the hope that it will be useful,
20   * but WITHOUT ANY WARRANTY; without even the implied warranty of
21   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22   * Lesser General Public License for more details.
23   * 
24   * You should have received a copy of the GNU Lesser General Public
25   * License along with this software; if not, write to the Free
26   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
27   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
28   * #L%
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   * @author scream3r
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                             //Do nothing
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      * Someone else has loaded the correct native lib somplace else, Or know
184      * what she is doingt...
185      *
186      * @param libName the name of the lib
187      * @return false if the lib was loaded before, false otherwise.
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     //TODO usable LOG INFOS ...
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                 //ERROR
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         //Make streams closed, so that they can not be reopend.
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      * Open port
443      *
444      * @param portName name of port for opening
445      *
446      */
447     protected abstract void open(String portName, int type) throws IOException;
448 
449     /**
450      * Close port
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      * Read data from port
565      *
566      * @param b the data to be written
567      * @param off the start offset in the data
568      * @param len the number of bytes that are written
569      * @exception IOException If an I/O error has occurred.
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      * Write data to port
577      *
578      * @param b
579      * @param off the start offset in the data.
580      * @param len the number of bytes to write.
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 }