2 -- Copyright (C) 2016-2017 secunet Security Networks AG
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.
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.
20 with HW.DbC.Intel_Quirk;
21 with HW.DbC.DMA_Buffers;
22 with HW.DbC.Transfer_Info;
23 with HW.DbC.Transfer_Rings;
24 with HW.DbC.Transfers;
32 Refined_State => (State => (Connected, Running,
33 DbC_Run_Deadline, DbC_Poll_Deadline,
34 DbC_Stat_Deadline, Events.State,
35 Transfer_Info.State, Transfer_Rings.State),
36 DMA => (ERST, DbC_Context, Desc_Strings, Events.DMA,
40 Apply_Intel_Quirk : constant Boolean := True;
41 Debug_xCap : constant Boolean := False;
43 Connected : Boolean := False;
44 Running : Boolean := False;
45 DbC_Run_Deadline : Time.T := Time.T_First;
46 DbC_Poll_Deadline : Time.T := Time.T_First;
47 DbC_Stat_Deadline : Time.T := Time.T_First;
49 ----------------------------------------------------------------------------
51 ERST : Events.ERST_Entry
53 Address => System'To_Address (DMA_Buffers.Event_Ring_Segment_Table_Base);
55 DbC_Context : Contexts.DbC_Context
57 Address => System'To_Address (DMA_Buffers.DbC_Context_Base);
59 ----------------------------------------------------------------------------
61 subtype Desc_Str_Range is Natural range 0 .. 14;
62 type Desc_Str is array (Desc_Str_Range) of Word16 with Pack;
63 type String_Descriptor is record
65 bDescriptor_Type : Byte;
69 Null_String_Desc : constant String_Descriptor
71 bDescriptor_Type => 0,
72 wData => (others => 0));
74 type Desc_Strings_Type is (String0, Manufacturer, Product, Serial_Number);
75 type Desc_Strings_Array is
76 array (Desc_Strings_Type) of String_Descriptor with Pack;
77 Desc_Strings : Desc_Strings_Array
79 Address => System'To_Address (DMA_Buffers.Descriptor_Strings_Base);
81 procedure String_To_Desc (Dst : out String_Descriptor; Src : in String)
85 Dst.bLength := 2 + 2 * Byte'Min (Dst.wData'Length, Src'Length);
86 Dst.bDescriptor_Type := 16#03#;
87 for I in Desc_Str_Range loop
89 Dst.wData (I) := Character'Pos (Src (I + 1));
91 Dst.wData (I) := 16#0000#;
96 ----------------------------------------------------------------------------
98 procedure Find_Next_xCap (Cap_Id : in Word8; Success : out Boolean)
103 Temp_Offset : Word32;
106 if xCap_Regs.Byte_Offset = 0 then
107 Cap_Regs.Read (Temp_Offset, XHCI_Extended_Caps);
109 xCap_Regs.Read (Temp_Offset, Next_xCap);
112 Temp_Offset := Shift_Left (Temp_Offset, 2);
113 pragma Debug (Debug_xCap, Debug.Put_Reg32
114 ("Find_Next_xCap Offset", Temp_Offset));
116 Temp_Offset = 0 or else
117 xCap_Regs.Byte_Offset > MMIO_Size - Natural (Temp_Offset) - 2;
119 xCap_Regs.Byte_Offset := xCap_Regs.Byte_Offset + Natural (Temp_Offset);
121 xCap_Regs.Read (Current_Id, Capability_ID);
122 Success := Current_Id = Cap_Id;
123 pragma Debug (Debug_xCap, Debug.Put_Reg8
124 ("Find_Next_xCap Cap_Id", Current_Id));
127 xCap_Regs.Read (Temp_Offset, Next_xCap);
131 ----------------------------------------------------------------------------
133 procedure BIOS_Handover (Success : out Boolean)
139 xCap_Regs.Byte_Offset := 0;
140 Find_Next_xCap (1, Success);
142 Legacy_Support_Regs.Byte_Offset := xCap_Regs.Byte_Offset;
143 -- See if the BIOS claims ownership
144 Legacy_Support_Regs.Read (BIOS_Owned, HC_BIOS_Owned_Semaphore);
145 if BIOS_Owned = 1 then
146 pragma Debug (Debug.Put_Line ("DbC: BIOS claims ownership."));
148 Legacy_Support_Regs.Write (HC_OS_Owned_Semaphore, Word8'(1));
150 Deadline := Time.MS_From_Now (5_000);
152 Legacy_Support_Regs.Read (BIOS_Owned, HC_BIOS_Owned_Semaphore);
153 exit when BIOS_Owned = 0;
155 Timeout : constant Boolean := Time.Timed_Out (Deadline);
157 Success := not Timeout;
159 exit when not Success;
160 pragma Warnings (GNATprove, Off, "statement has no effect");
161 for I in 0 .. 1234 loop
162 null; -- Busy loop to reduce pressure on HC BIOS Owned
163 -- Semaphore. It shouldn't generate an SMI but
164 -- might congest the xHC?
166 pragma Warnings (GNATprove, On, "statement has no effect");
169 pragma Debug (not Success, Debug.Put_Line
170 ("ERROR: BIOS didn't hand over xHC within 5s."));
171 pragma Debug (Success, Debug.Put_Line
172 ("DbC: BIOS hand-over succeeded."));
177 procedure Reset (Initial_Reset : Boolean := False);
187 Cap_Regs.Read (Cap_Length, Capability_Registers_Length);
188 Op_Regs.Byte_Offset := Natural (Cap_Length);
190 Op_Regs.Read (CNR, Controller_Not_Ready);
194 pragma Debug (Debug.Put_Line ("WARNING: xHCI not ready!"));
195 Deadline := Time.MS_From_Now (1_000);
198 Op_Regs.Read (CNR, Controller_Not_Ready);
201 Timed_Out : constant Boolean := Time.Timed_Out (Deadline);
203 Success := not Timed_Out;
205 exit when not Success;
207 pragma Debug (not Success, Debug.Put_Line
208 ("ERROR: xHC not ready after 1s."));
212 BIOS_Handover (Success);
216 xCap_Regs.Byte_Offset := 0;
217 Find_Next_xCap (10, Success);
220 pragma Debug (not Success, Debug.Put_Line
221 ("ERROR: Couldn't find xHCI debug capability."));
224 Regs.Byte_Offset := xCap_Regs.Byte_Offset;
226 ERST := Events.ERST_Entry'
227 (Segment_Base => DMA_Buffers.Event_Ring_Base,
228 Segment_Size => TRBs.TRBs_Per_Ring,
231 Desc_Strings (String0).bLength := 16#04#;
232 Desc_Strings (String0).bDescriptor_Type := 16#03#;
233 Desc_Strings (String0).wData := (0 => 16#0409#, others => 16#0000#);
234 String_To_Desc (Desc_Strings (Manufacturer), "secunet");
235 String_To_Desc (Desc_Strings (Product), "HW.DbC");
236 String_To_Desc (Desc_Strings (Serial_Number), "1");
238 Reset (Initial_Reset => True);
242 ----------------------------------------------------------------------------
244 procedure Reset (Initial_Reset : Boolean := False)
252 if Regs.Byte_Offset /= 0 then
253 Regs.Write (DbC_Enable, Word8'(0));
255 Regs.Read (DCE, DbC_Enable);
259 Transfers.Reset (Initial_Reset);
261 Regs.Write (ERST_Size, Word16'(1));
262 Regs.Write (ERST_Base_Lo, Word32
263 (DMA_Buffers.Event_Ring_Segment_Table_Base mod 16#1_0000_0000#));
264 Regs.Write (ERST_Base_Hi, Word32
265 (DMA_Buffers.Event_Ring_Segment_Table_Base / 16#1_0000_0000#));
268 Regs.Write (ER_Dequeue_Ptr_Lo, Word32
269 (DMA_Buffers.Event_Ring_Base mod 16#1_0000_0000#));
270 Regs.Write (ER_Dequeue_Ptr_Hi, Word32
271 (DMA_Buffers.Event_Ring_Base / 16#1_0000_0000#));
273 Regs.Write (Context_Pointer_Lo, Word32
274 (DMA_Buffers.DbC_Context_Base mod 16#1_0000_0000#));
275 Regs.Write (Context_Pointer_Hi, Word32
276 (DMA_Buffers.DbC_Context_Base / 16#1_0000_0000#));
278 Contexts.Clear_DbC_Context (DbC_Context);
279 DbC_Context.DbC_Info :=
280 (String_0_Address => DMA_Buffers.Descriptor_Strings_Base,
281 Manufacturer_String_Address => DMA_Buffers.Descriptor_Strings_Base
282 + 1 * String_Descriptor'Size / 8,
283 Product_String_Address => DMA_Buffers.Descriptor_Strings_Base
284 + 2 * String_Descriptor'Size / 8,
285 Serial_Number_String_Address => DMA_Buffers.Descriptor_Strings_Base
286 + 3 * String_Descriptor'Size / 8,
287 String_0_Length => Desc_Strings (String0).bLength,
288 Manufacturer_String_Length => Desc_Strings (Manufacturer).bLength,
289 Product_String_Length => Desc_Strings (Product).bLength,
290 Serial_Number_String_Length => Desc_Strings (Serial_Number).bLength,
294 Regs.Read (MBS, Debug_Max_Burst_Size);
295 DbC_Context.OUT_EP.EP_Type := Contexts.Bulk_O;
296 DbC_Context.OUT_EP.Max_Burst_Size := MBS;
297 DbC_Context.OUT_EP.Max_Packet_Size := 1024;
298 DbC_Context.OUT_EP.TR_Dequeue_Pointer_Lo := Word28
299 (Shift_Right (Transfer_Rings.Physical (2), 4) and 16#0fff_ffff#);
300 DbC_Context.OUT_EP.TR_Dequeue_Pointer_Hi := Word32
301 (Shift_Right (Transfer_Rings.Physical (2), 32) and 16#ffff_ffff#);
302 DbC_Context.OUT_EP.Dequeue_Cycle_State := 1;
303 DbC_Context.OUT_EP.Average_TRB_Length := Max_Bulk_Size / 2;
304 DbC_Context.IN_EP.EP_Type := Contexts.Bulk_I;
305 DbC_Context.IN_EP.Max_Burst_Size := MBS;
306 DbC_Context.IN_EP.Max_Packet_Size := 1024;
307 DbC_Context.IN_EP.TR_Dequeue_Pointer_Lo := Word28
308 (Shift_Right (Transfer_Rings.Physical (3), 4) and 16#0fff_ffff#);
309 DbC_Context.IN_EP.TR_Dequeue_Pointer_Hi := Word32
310 (Shift_Right (Transfer_Rings.Physical (3), 32) and 16#ffff_ffff#);
311 DbC_Context.IN_EP.Dequeue_Cycle_State := 1;
312 DbC_Context.IN_EP.Average_TRB_Length := Max_Bulk_Size / 2;
314 Regs.Write (DbC_Protocol, Word16'(0)); -- Debug Target vendor defined.
315 Regs.Write (Vendor_ID, Word16 (16#ffff#));
316 Regs.Write (Product_ID, Word16 (16#dbc1#));
317 Regs.Write (Device_Revision, Word16 (16#0001#));
319 Regs.Write (DbC_Enable, Word8'(1));
321 Regs.Read (DCE, DbC_Enable);
325 if Apply_Intel_Quirk then
326 Intel_Quirk.Reset_Port;
331 DbC_Poll_Deadline := Time.Now;
332 DbC_Stat_Deadline := Time.MS_From_Now (12_345);
336 procedure Poll (Now : Boolean := False)
343 if Regs.Byte_Offset /= 0 then
344 Timed_Out := Time.Timed_Out (DbC_Poll_Deadline);
345 if Now or else Timed_Out then
346 Regs.Read (Temp8, DbC_Enable);
348 Regs.Read (Temp8, Current_Connect_Status);
350 -- Something is connected...
351 DbC_Poll_Deadline := Time.MS_From_Now (10);
352 if not Connected then
353 pragma Debug (Debug.Put_Line ("DbC connected."));
354 DbC_Run_Deadline := Time.MS_From_Now (333);
357 Regs.Read (Temp8, DbC_Run);
361 pragma Debug (Debug.Put_Line ("DbC configured."));
366 pragma Debug (Debug.Put_Line
367 ("DbC still connected but deconfigured."));
368 DbC_Run_Deadline := Time.MS_From_Now (333);
371 Timed_Out := Time.Timed_Out (DbC_Run_Deadline);
373 pragma Debug (Debug.Put_Line
374 ("DbC connection timed out."));
380 DbC_Poll_Deadline := Time.MS_From_Now (333);
382 pragma Debug (Debug.Put_Line ("DbC disconnected."));
388 pragma Debug (Debug.Put_Line ("DbC got disabled, huh?"));
391 Events.Handle_Events;
392 Timed_Out := Time.Timed_Out (DbC_Stat_Deadline);
394 pragma Debug (Transfer_Info.Dump_Stats);
395 DbC_Stat_Deadline := Time.MS_From_Now (12_345);
401 procedure Receive (Buf : in out Buffer; Len : in out Natural)
406 Transfers.Receive (Buf, Len);
409 procedure Send (Buf : Buffer; Len : in out Natural; Success : out Boolean)
417 Start_Now => Running,
421 procedure Ring_Doorbell (EP : Endpoint_Range)
425 Regs.Write (Doorbell_Target, Word8 (EP) - 2);
428 -------------------------------------------------------------------------
431 for Str of Desc_Strings loop
432 Str := Null_String_Desc;
436 -- vim: set ts=8 sts=3 sw=3 et: