fab3f31a671bba3bfd986baa715b0101f4e026f0
[libxhcidbg.git] / src / hw-dbc-events.adb
1 --
2 -- Copyright (C) 2016-2017 secunet Security Networks AG
3 --
4 -- This program is free software; you can redistribute it and/or modify
5 -- it under the terms of the GNU General Public License as published by
6 -- the Free Software Foundation; either version 2 of the License, or
7 -- (at your option) any later version.
8 --
9 -- This program is distributed in the hope that it will be useful,
10 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
11 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 -- GNU General Public License for more details.
13 --
14
15 with System;
16
17 with HW.Debug;
18 with HW.DbC.DMA_Buffers;
19 with HW.DbC.Transfer_Rings;
20 with HW.DbC.TRBs;
21
22 package body HW.DbC.Events
23 with
24    Refined_State =>
25      (State => (Next, CCS),
26       DMA   => (Ring))
27 is
28
29    Debug_TD_Events : constant Boolean := False;
30
31    ----------------------------------------------------------------------------
32
33    Ring : TRBs.Transfer_Ring
34    with
35       Async_Readers,
36       Async_Writers,
37       Volatile,
38       Address => System'To_Address (DMA_Buffers.Event_Ring_Base);
39
40    Next : TRBs.Ring_Range;
41    CCS  : Bit;
42
43    ----------------------------------------------------------------------------
44
45    function Last_Physical return Word64
46    is
47       use type Word64;
48       use type TRBs.Ring_Range;
49    begin
50       return DMA_Buffers.Event_Ring_Base + Word64 (Next - 1) * TRBs.TRB_Size;
51    end Last_Physical;
52
53    ----------------------------------------------------------------------------
54
55    procedure Reset_Ring
56    is
57    begin
58       TRBs.Clear_Ring (Ring, 1);
59       Next := 0;
60       CCS := 1;
61    end Reset_Ring;
62
63    ----------------------------------------------------------------------------
64
65    function Ready return Boolean
66    is
67    begin
68       return TRBs.Get_Cycle (Ring (Next)) = CCS;
69    end Ready;
70
71    procedure Update_Dequeue_Ptr
72    is
73       use type Word64;
74       Phys : constant Word64 := Last_Physical;
75    begin
76       Regs.Write (ER_Dequeue_Ptr_Lo, Phys and 16#ffff_ffff#);
77       Regs.Write (ER_Dequeue_Ptr_Hi, Shift_Right (Phys, 32));
78    end Update_Dequeue_Ptr;
79
80    --  advances to the next event
81    --  event ring has no link TRBs, it simply rolls over
82    procedure Advance
83    is
84       use type TRBs.Ring_Range;
85    begin
86       Next := Next + 1;
87       if Next = 0 then
88          CCS := 1 - CCS;
89
90          --  Update event dequeue pointer regularly.
91          Update_Dequeue_Ptr;
92       end if;
93    end Advance;
94
95    ----------------------------------------------------------------------------
96
97    procedure Handle_Transfer_Event
98    is
99       use type Word8;
100       ID : constant Word8 := TRBs.Get_Slot_ID (Ring (Next));
101       EP : constant Natural := TRBs.Get_Endpoint_ID (Ring (Next));
102       Pointer : constant Word64 := TRBs.Get_Parameter (Ring (Next));
103       Length : constant Natural := TRBs.Get_Event_Length (Ring (Next));
104       CC : constant TRBs.Completion_Code :=
105          TRBs.Get_Completion_Code (Ring (Next));
106    begin
107       if ID = 1 and EP in Endpoint_Range then
108          pragma Debug (Debug_TD_Events, Debug.Put ("TD finished; Length: "));
109          pragma Debug (Debug_TD_Events, Debug.Put_Int32 (Int32 (Length)));
110          pragma Debug (Debug_TD_Events, Debug.Put ("; Status: "));
111          pragma Debug (Debug_TD_Events, Debug.Put_Int32 (Int32 (CC)));
112          pragma Debug (Debug_TD_Events, Debug.Put_Reg64 ("; Pointer", Pointer));
113          Transfer_Rings.Dequeue
114            (EP                => EP,
115             Pointer           => Pointer,
116             Status            => TRBs.CC_To_Usb_Error (CC),
117             Remaining_Length  => Length);
118       else
119          pragma Debug (Debug.Put
120            ("WARNING: Spurious transfer event for ID "));
121          pragma Debug (Debug.Put_Int32 (Int32 (ID)));
122          pragma Debug (Debug.Put (", EP "));
123          pragma Debug (Debug.Put_Int32 (Int32 (EP)));
124          pragma Debug (Debug.Put_Line (":"));
125          pragma Debug (Debug.Put_Reg64 ("  Pointer", Pointer));
126          pragma Debug (Debug.Put_Reg32 ("       TL", Word32 (Length)));
127          pragma Debug (Debug.Put_Reg8 ("       CC", Word8 (CC)));
128          null;
129       end if;
130       Advance;
131    end Handle_Transfer_Event;
132
133    procedure Handle_Host_Controller_Event
134    is
135       CC : TRBs.Completion_Code;
136    begin
137       CC := TRBs.Get_Completion_Code (Ring (Next));
138       case CC is
139       when TRBs.Event_Ring_Full_Error =>
140          pragma Debug (Debug.Put_Line ("Event ring full!"));
141          --  If we get here, we have processed the whole queue:
142          --  xHC pushes this event, when it sees the ring full,
143          --  full of other events.
144          --  IMO it's save and necessary to update the dequeue
145          --  pointer here.
146          Advance;
147          Update_Dequeue_Ptr;
148       when others =>
149          pragma Debug (Debug.Put_Reg8
150            ("WARNING: Spurious host controller event", Word8 (CC)));
151          Advance;
152       end case;
153    end Handle_Host_Controller_Event;
154
155    procedure Handle_Event
156    is
157       TRB_Type : TRBs.TRB_Types;
158    begin
159       TRB_Type := TRBs.Get_Type (Ring (Next));
160       case TRB_Type is
161          when TRBs.Transfer_Event =>
162             Handle_Transfer_Event;
163          when TRBs.Port_Status_Change_Event =>
164             pragma Debug (Debug.Put_Line ("Port status change event."));
165             Advance;
166          when TRBs.Host_Controller_Event =>
167             Handle_Host_Controller_Event;
168          when others =>
169             pragma Debug (Debug.Put_Reg8
170               ("WARNING: Spurious event", Word8 (TRB_Type)));
171             Advance;
172       end case;
173    end Handle_Event;
174
175    procedure Handle_Events
176    is
177       Update_Ptr : Boolean;
178    begin
179       Update_Ptr := Ready;
180       while Ready loop
181          Handle_Event;
182       end loop;
183       if Update_Ptr then
184          Update_Dequeue_Ptr;
185       end if;
186    end Handle_Events;
187
188 end HW.DbC.Events;
189
190 --  vim: set ts=8 sts=3 sw=3 et: