98e8c2aed34880863d0db14a2566d9f6dc4a22c0
[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       Current_Cycle : Bit;
68    begin
69       TRBs.Get_Cycle (Data  => Ring (Next),
70                       Cycle => Current_Cycle);
71       return Current_Cycle = CCS;
72    end Ready;
73
74    procedure Update_Dequeue_Ptr
75    is
76       use type Word64;
77       Phys : constant Word64 := Last_Physical;
78    begin
79       Regs.Write (ER_Dequeue_Ptr_Lo, Phys and 16#ffff_ffff#);
80       Regs.Write (ER_Dequeue_Ptr_Hi, Shift_Right (Phys, 32));
81    end Update_Dequeue_Ptr;
82
83    --  advances to the next event
84    --  event ring has no link TRBs, it simply rolls over
85    procedure Advance
86    is
87       use type TRBs.Ring_Range;
88    begin
89       Next := Next + 1;
90       if Next = 0 then
91          CCS := 1 - CCS;
92
93          --  Update event dequeue pointer regularly.
94          Update_Dequeue_Ptr;
95       end if;
96    end Advance;
97
98    ----------------------------------------------------------------------------
99
100    procedure Handle_Transfer_Event
101    is
102       use type Word8;
103       CC         : TRBs.Completion_Code;
104       Length, EP : Natural;
105       Pointer    : Word64;
106       ID         : Word8;
107    begin
108       TRBs.Get_Slot_ID (Data    => Ring (Next),
109                         Slot_ID => ID);
110       TRBs.Get_Endpoint_ID (Data        => Ring (Next),
111                             Endpoint_ID => EP);
112       TRBs.Get_Parameter (Data      => Ring (Next),
113                           Parameter => Pointer);
114       TRBs.Get_Event_Length (Data   => Ring (Next),
115                              Length => Length);
116       TRBs.Get_Completion_Code (Data => Ring (Next),
117                                 Code => CC);
118
119       if ID = 1 and EP in Endpoint_Range then
120          pragma Debug (Debug_TD_Events, Debug.Put ("TD finished; Length: "));
121          pragma Debug (Debug_TD_Events, Debug.Put_Int32 (Int32 (Length)));
122          pragma Debug (Debug_TD_Events, Debug.Put ("; Status: "));
123          pragma Debug (Debug_TD_Events, Debug.Put_Int32 (Int32 (CC)));
124          pragma Debug (Debug_TD_Events, Debug.Put_Reg64 ("; Pointer", Pointer));
125          Transfer_Rings.Dequeue
126            (EP                => EP,
127             Pointer           => Pointer,
128             Status            => TRBs.CC_To_Usb_Error (CC),
129             Remaining_Length  => Length);
130       else
131          pragma Debug (Debug.Put
132            ("WARNING: Spurious transfer event for ID "));
133          pragma Debug (Debug.Put_Int32 (Int32 (ID)));
134          pragma Debug (Debug.Put (", EP "));
135          pragma Debug (Debug.Put_Int32 (Int32 (EP)));
136          pragma Debug (Debug.Put_Line (":"));
137          pragma Debug (Debug.Put_Reg64 ("  Pointer", Pointer));
138          pragma Debug (Debug.Put_Reg32 ("       TL", Word32 (Length)));
139          pragma Debug (Debug.Put_Reg8 ("       CC", Word8 (CC)));
140          null;
141       end if;
142       Advance;
143    end Handle_Transfer_Event;
144
145    procedure Handle_Host_Controller_Event
146    is
147       CC : TRBs.Completion_Code;
148    begin
149       TRBs.Get_Completion_Code (Data => Ring (Next),
150                                 Code => CC);
151       case CC is
152       when TRBs.Event_Ring_Full_Error =>
153          pragma Debug (Debug.Put_Line ("Event ring full!"));
154          --  If we get here, we have processed the whole queue:
155          --  xHC pushes this event, when it sees the ring full,
156          --  full of other events.
157          --  IMO it's save and necessary to update the dequeue
158          --  pointer here.
159          Advance;
160          Update_Dequeue_Ptr;
161       when others =>
162          pragma Debug (Debug.Put_Reg8
163            ("WARNING: Spurious host controller event", Word8 (CC)));
164          Advance;
165       end case;
166    end Handle_Host_Controller_Event;
167
168    procedure Handle_Event
169    is
170       TRB_Type : TRBs.TRB_Types;
171    begin
172       TRBs.Get_Type (Data     => Ring (Next),
173                      TRB_Type => TRB_Type);
174       case TRB_Type is
175          when TRBs.Transfer_Event =>
176             Handle_Transfer_Event;
177          when TRBs.Port_Status_Change_Event =>
178             pragma Debug (Debug.Put_Line ("Port status change event."));
179             Advance;
180          when TRBs.Host_Controller_Event =>
181             Handle_Host_Controller_Event;
182          when others =>
183             pragma Debug (Debug.Put_Reg8
184               ("WARNING: Spurious event", Word8 (TRB_Type)));
185             Advance;
186       end case;
187    end Handle_Event;
188
189    procedure Handle_Events
190    is
191       Update_Ptr : Boolean;
192    begin
193       Update_Ptr := Ready;
194       while Ready loop
195          Handle_Event;
196       end loop;
197       if Update_Ptr then
198          Update_Dequeue_Ptr;
199       end if;
200    end Handle_Events;
201
202 end HW.DbC.Events;
203
204 --  vim: set ts=8 sts=3 sw=3 et: