dashera_0.14.0_ee112420/src/serial.adb

  1
  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
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
--  Copyright ©2022 Steve Merrony

--  Permission is hereby granted, free of charge, to any person obtaining a copy
--  of this software and associated documentation files (the "Software"), to deal
--  in the Software without restriction, including without limitation the rights
--  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
--  copies of the Software, and to permit persons to whom the Software is
--  furnished to do so, subject to the following conditions:
--  The above copyright notice and this permission notice shall be included in
--  all copies or substantial portions of the Software.
--
--  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
--  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
--  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
--  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
--  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
--  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
--  THE SOFTWARE.

--  Note the serial break does not seem to be provided by GNAT.Serial_Communications
--  It is claimed that...
--
--  Sending break can be achieved by:
--
--  * lowering the bit-rate
--  * sending 0x00 which will seem as break.
--  * change bit-rate back.
--
--  During the break, it won't be possible to receive data since the bit-rate is not correct.

with Ada.Exceptions; use Ada.Exceptions;
with Ada.IO_Exceptions;
with Ada.Streams;    use Ada.Streams;

with Logging;        use Logging;
with Redirector;

package body Serial is

   procedure Open (Port_Str  : String;
                   Rate      : Data_Rate;
                   Bits      : Data_Bits;
                   Parity    : Parity_Check;
                   Stop_Bits : Stop_Bits_Number) is
   begin
      GNAT.Serial_Communications.Open (Port, Port_Name (Port_Str));
      GNAT.Serial_Communications.Set (Port, Rate, Bits, Stop_Bits, Parity);
      --  Save the user-specified settings so we can reset after sending a Break
      User_Rate      := Rate;
      User_Bits      := Bits;
      User_Parity    := Parity;
      User_Stop_Bits := Stop_Bits;
      Log (DEBUG, "Serial port opened and set-up");
      Port_US := To_Unbounded_String (Port_Str);
      Receiver_Task := new Receiver;
      Receiver_Task.Start;
      Redirector.Router.Set_Destination (Redirector.Async);
      Keyboard_Sender_Task := new Keyboard_Sender;
      Keyboard_Sender_Task.Start;
      Log (DEBUG, "Serial port open complete");
   end Open;

   procedure Close is
   begin
      Close (Port);
      Keyboard_Sender_Task.Stop;
      Redirector.Router.Set_Destination (Redirector.Local);
   end Close;

   task body Receiver is
      B : Character;
   begin
      accept Start do
         Log (DEBUG, "Serial Receiver Started");
      end Start;
      loop
         begin
            Character'Read (Port'Access, B);
            Redirector.Router.Handle_Data (B);
         exception
            when Ada.IO_Exceptions.End_Error =>
               null;
         end;
      end loop;
   exception
      when Error : others =>
      Log (WARNING, Exception_Information (Error));
      Log (INFO,  "Serial Receiver loop exited");
      Close;
   end Receiver;

   task body Keyboard_Sender is
   begin
      accept Start do
         Log (DEBUG, "Serial Keyboard_Sender Started");
      end Start;
      loop
         select
            accept Accept_Data (Data : String) do
               declare
                  SEA : Stream_Element_Array (1 .. Data'Length);
               begin
                  for I in 1 .. Data'Length loop
                     SEA (Stream_Element_Offset (I)) := Stream_Element (Character'Pos (Data (I)));
                  end loop;
                  Write (Port, SEA);
               end;
            end Accept_Data;
         or
            accept Send_Break do
               declare
                  SEA : Stream_Element_Array (1 .. 1);
               begin
                  --  Set a very slow data rate
                  GNAT.Serial_Communications.Set (Port, B110, CS8, Two, None);
                  SEA (1) := 0; --  all zeroes
                  Write (Port, SEA);
                  --  Reset port to user settings
                  GNAT.Serial_Communications.Set (Port, User_Rate, User_Bits, User_Stop_Bits, User_Parity);
               end;
            end Send_Break;
         or
            accept Stop;
               Log (DEBUG, "Serial Keyboard_Sender Stopped");
               exit;
         or
            terminate;
         end select;
      end loop;
   end Keyboard_Sender;

end Serial;