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;
31 Refined_State => (State => (Reset_Intermission_End, Connected, Running,
32 DbC_Run_Deadline, DbC_Poll_Deadline,
33 DbC_Stat_Deadline, Events.State,
34 Transfer_Info.State, Transfer_Rings.State),
35 DMA => (ERST, DbC_Context, Desc_Strings, Events.DMA,
39 Perform_Hardware_Reset : constant Boolean := True;
40 Apply_Intel_Quirk : constant Boolean := True;
41 Debug_xCap : constant Boolean := False;
43 Reset_Intermission_MS : constant := 736; -- seems reliable above 722ms
44 Reset_Intermission_End : Time.T;
48 DbC_Run_Deadline : Time.T;
49 DbC_Poll_Deadline : Time.T;
50 DbC_Stat_Deadline : Time.T;
52 ----------------------------------------------------------------------------
54 ERST : Events.ERST_Entry
56 Address => System'To_Address (DMA_Buffers.Event_Ring_Segment_Table_Base);
58 DbC_Context : Contexts.DbC_Context
60 Address => System'To_Address (DMA_Buffers.DbC_Context_Base);
62 ----------------------------------------------------------------------------
64 subtype Desc_Str_Range is Natural range 0 .. 14;
65 type Desc_Str is array (Desc_Str_Range) of Word16 with Pack;
66 type String_Descriptor is record
68 bDescriptor_Type : Byte;
72 type Desc_Strings_Type is (String0, Manufacturer, Product, Serial_Number);
73 type Desc_Strings_Array is
74 array (Desc_Strings_Type) of String_Descriptor with Pack;
75 Desc_Strings : Desc_Strings_Array
77 Address => System'To_Address (DMA_Buffers.Descriptor_Strings_Base);
79 procedure String_To_Desc (Dst : out String_Descriptor; Src : in String)
83 Dst.bLength := 2 + 2 * Byte'Min (Dst.wData'Length, Src'Length);
84 Dst.bDescriptor_Type := 16#03#;
85 for I in Desc_Str_Range loop
87 Dst.wData (I) := Character'Pos (Src (I + 1));
89 Dst.wData (I) := 16#0000#;
94 ----------------------------------------------------------------------------
96 procedure Find_Next_xCap (Cap_Id : in Word8; Success : out Boolean)
101 Temp_Offset : Word32;
104 if xCap_Regs.Byte_Offset = 0 then
105 Cap_Regs.Read (Temp_Offset, XHCI_Extended_Caps);
107 xCap_Regs.Read (Temp_Offset, Next_xCap);
110 Temp_Offset := Shift_Left (Temp_Offset, 2);
111 pragma Debug (Debug_xCap, Debug.Put_Reg32
112 ("Find_Next_xCap Offset", Temp_Offset));
114 Temp_Offset = 0 or else
115 xCap_Regs.Byte_Offset > MMIO_Size - Natural (Temp_Offset) - 2;
117 xCap_Regs.Byte_Offset := xCap_Regs.Byte_Offset + Natural (Temp_Offset);
119 xCap_Regs.Read (Current_Id, Capability_ID);
120 Success := Current_Id = Cap_Id;
121 pragma Debug (Debug_xCap, Debug.Put_Reg8
122 ("Find_Next_xCap Cap_Id", Current_Id));
125 xCap_Regs.Read (Temp_Offset, Next_xCap);
129 ----------------------------------------------------------------------------
131 procedure BIOS_Handover (Success : out Boolean)
137 xCap_Regs.Byte_Offset := 0;
138 Find_Next_xCap (1, Success);
140 Legacy_Support_Regs.Byte_Offset := xCap_Regs.Byte_Offset;
141 -- See if the BIOS claims ownership
142 Legacy_Support_Regs.Read (BIOS_Owned, HC_BIOS_Owned_Semaphore);
143 if BIOS_Owned = 1 then
144 pragma Debug (Debug.Put_Line ("DbC: BIOS claims ownership."));
146 Legacy_Support_Regs.Write (HC_OS_Owned_Semaphore, Word8'(1));
148 Deadline := Time.MS_From_Now (5_000);
150 Legacy_Support_Regs.Read (BIOS_Owned, HC_BIOS_Owned_Semaphore);
151 exit when BIOS_Owned = 0;
153 Timeout : constant Boolean := Time.Timed_Out (Deadline);
155 Success := not Timeout;
157 exit when not Success;
158 for I in 0 .. 1234 loop
159 null; -- Busy loop to reduce pressure on HC BIOS Owned
160 -- Semaphore. It shouldn't generate an SMI but
161 -- might congest the xHC?
165 pragma Debug (not Success, Debug.Put_Line
166 ("ERROR: BIOS didn't hand over xHC within 5s."));
167 pragma Debug (Success, Debug.Put_Line
168 ("DbC: BIOS hand-over succeeded."));
173 procedure Reset_xHC (Success : out Boolean)
180 Op_Regs.Write (Run_Stop, Word8'(0));
181 Deadline := Time.MS_From_Now (1_000);
184 Op_Regs.Read (HCH, HC_Halted);
186 Success := not Time.Timed_Out (Deadline);
187 exit when not Success;
189 pragma Debug (not Success, Debug.Put_Line
190 ("ERROR: xHC didn't halt within 1s."));
193 Op_Regs.Write (Host_Controller_Reset, Word8'(1));
194 Deadline := Time.MS_From_Now (1_000);
196 -- Some Intel xHCI implementations are known to freak out rarely
197 -- (anything can happen up to global reset assertion) if the
198 -- Host Controller Reset bit is polled before the controller is
200 Time.M_Delay (1); -- Delay here or hell freezes over
203 Op_Regs.Read (HCR, Host_Controller_Reset);
205 Success := not Time.Timed_Out (Deadline);
206 exit when not Success;
208 pragma Debug (not Success, Debug.Put_Line
209 ("ERROR: xHC didn't finish reset within 1s."));
213 procedure Reset (Initial_Reset : Boolean := False);
223 Cap_Regs.Read (Cap_Length, Capability_Registers_Length);
224 Op_Regs.Byte_Offset := Natural (Cap_Length);
226 Op_Regs.Read (CNR, Controller_Not_Ready);
230 pragma Debug (Debug.Put_Line ("WARNING: xHCI not ready!"));
231 Deadline := Time.MS_From_Now (1_000);
234 Op_Regs.Read (CNR, Controller_Not_Ready);
236 Success := not Time.Timed_Out (Deadline);
237 exit when not Success;
239 pragma Debug (not Success, Debug.Put_Line
240 ("ERROR: xHC not ready after 1s."));
244 BIOS_Handover (Success);
247 if Perform_Hardware_Reset and then Success then
249 Reset_Intermission_End := Time.MS_From_Now (Reset_Intermission_MS);
251 Reset_Intermission_End := Time.Now;
255 xCap_Regs.Byte_Offset := 0;
256 Find_Next_xCap (10, Success);
260 Regs.Byte_Offset := xCap_Regs.Byte_Offset;
262 ERST := Events.ERST_Entry'
263 (Segment_Base => DMA_Buffers.Event_Ring_Base,
264 Segment_Size => TRBs.TRBs_Per_Ring,
267 Desc_Strings (String0).bLength := 16#04#;
268 Desc_Strings (String0).bDescriptor_Type := 16#03#;
269 Desc_Strings (String0).wData := (0 => 16#0409#, others => 16#0000#);
270 String_To_Desc (Desc_Strings (Manufacturer), "secunet");
271 String_To_Desc (Desc_Strings (Product), "HW.DbC");
272 String_To_Desc (Desc_Strings (Serial_Number), "1");
274 Reset (Initial_Reset => True);
277 pragma Debug (Debug.Put_Line
278 ("ERROR: Couldn't find xHCI debug capability."));
282 ----------------------------------------------------------------------------
284 procedure Reset (Initial_Reset : Boolean := False)
292 if Regs.Byte_Offset /= 0 then
293 Regs.Write (DbC_Enable, Word8'(0));
295 Regs.Read (DCE, DbC_Enable);
299 Transfers.Reset (Initial_Reset);
301 Regs.Write (ERST_Size, Word16'(1));
302 Regs.Write (ERST_Base_Lo, Word32
303 (DMA_Buffers.Event_Ring_Segment_Table_Base mod 16#1_0000_0000#));
304 Regs.Write (ERST_Base_Hi, Word32
305 (DMA_Buffers.Event_Ring_Segment_Table_Base / 16#1_0000_0000#));
308 Regs.Write (ER_Dequeue_Ptr_Lo, Word32
309 (DMA_Buffers.Event_Ring_Base mod 16#1_0000_0000#));
310 Regs.Write (ER_Dequeue_Ptr_Hi, Word32
311 (DMA_Buffers.Event_Ring_Base / 16#1_0000_0000#));
313 Regs.Write (Context_Pointer_Lo, Word32
314 (DMA_Buffers.DbC_Context_Base mod 16#1_0000_0000#));
315 Regs.Write (Context_Pointer_Hi, Word32
316 (DMA_Buffers.DbC_Context_Base / 16#1_0000_0000#));
318 Contexts.Clear_DbC_Context (DbC_Context);
319 DbC_Context.DbC_Info :=
320 (String_0_Address => DMA_Buffers.Descriptor_Strings_Base,
321 Manufacturer_String_Address => DMA_Buffers.Descriptor_Strings_Base
322 + 1 * String_Descriptor'Size / 8,
323 Product_String_Address => DMA_Buffers.Descriptor_Strings_Base
324 + 2 * String_Descriptor'Size / 8,
325 Serial_Number_String_Address => DMA_Buffers.Descriptor_Strings_Base
326 + 3 * String_Descriptor'Size / 8,
327 String_0_Length => Desc_Strings (String0).bLength,
328 Manufacturer_String_Length => Desc_Strings (Manufacturer).bLength,
329 Product_String_Length => Desc_Strings (Product).bLength,
330 Serial_Number_String_Length => Desc_Strings (Serial_Number).bLength,
334 Regs.Read (MBS, Debug_Max_Burst_Size);
335 DbC_Context.OUT_EP.EP_Type := Contexts.Bulk_O;
336 DbC_Context.OUT_EP.Max_Burst_Size := MBS;
337 DbC_Context.OUT_EP.Max_Packet_Size := 1024;
338 DbC_Context.OUT_EP.TR_Dequeue_Pointer_Lo := Word28
339 (Shift_Right (Transfer_Rings.Physical (2), 4) and 16#0fff_ffff#);
340 DbC_Context.OUT_EP.TR_Dequeue_Pointer_Hi := Word32
341 (Shift_Right (Transfer_Rings.Physical (2), 32) and 16#ffff_ffff#);
342 DbC_Context.OUT_EP.Dequeue_Cycle_State := 1;
343 DbC_Context.OUT_EP.Average_TRB_Length := Max_Bulk_Size / 2;
344 DbC_Context.IN_EP.EP_Type := Contexts.Bulk_I;
345 DbC_Context.IN_EP.Max_Burst_Size := MBS;
346 DbC_Context.IN_EP.Max_Packet_Size := 1024;
347 DbC_Context.IN_EP.TR_Dequeue_Pointer_Lo := Word28
348 (Shift_Right (Transfer_Rings.Physical (3), 4) and 16#0fff_ffff#);
349 DbC_Context.IN_EP.TR_Dequeue_Pointer_Hi := Word32
350 (Shift_Right (Transfer_Rings.Physical (3), 32) and 16#ffff_ffff#);
351 DbC_Context.IN_EP.Dequeue_Cycle_State := 1;
352 DbC_Context.IN_EP.Average_TRB_Length := Max_Bulk_Size / 2;
354 Regs.Write (DbC_Protocol, Word16'(0)); -- Debug Target vendor defined.
355 Regs.Write (Vendor_ID, Word16 (16#ffff#));
356 Regs.Write (Product_ID, Word16 (16#dbc1#));
357 Regs.Write (Device_Revision, Word16 (16#0001#));
359 Time.Delay_Until (Reset_Intermission_End);
360 Regs.Write (DbC_Enable, Word8'(1));
362 Regs.Read (DCE, DbC_Enable);
366 if Apply_Intel_Quirk then
367 Intel_Quirk.Reset_Port;
372 DbC_Poll_Deadline := Time.Now;
373 DbC_Stat_Deadline := Time.MS_From_Now (12_345);
377 procedure Poll (Now : Boolean := False)
384 if Regs.Byte_Offset /= 0 then
385 Timed_Out := Now or else Time.Timed_Out (DbC_Poll_Deadline);
387 Regs.Read (Temp8, DbC_Enable);
389 Regs.Read (Temp8, Current_Connect_Status);
391 -- Something is connected...
392 DbC_Poll_Deadline := Time.MS_From_Now (10);
393 if not Connected then
394 pragma Debug (Debug.Put_Line ("DbC connected."));
395 DbC_Run_Deadline := Time.MS_From_Now (333);
398 Regs.Read (Temp8, DbC_Run);
402 pragma Debug (Debug.Put_Line ("DbC configured."));
407 pragma Debug (Debug.Put_Line
408 ("DbC still connected but deconfigured."));
409 DbC_Run_Deadline := Time.MS_From_Now (333);
412 Timed_Out := Time.Timed_Out (DbC_Run_Deadline);
414 pragma Debug (Debug.Put_Line
415 ("DbC connection timed out."));
421 DbC_Poll_Deadline := Time.MS_From_Now (333);
423 pragma Debug (Debug.Put_Line ("DbC disconnected."));
429 Reset_Intermission_End :=
430 Time.MS_From_Now (Reset_Intermission_MS);
431 pragma Debug (Debug.Put_Line ("DbC got disabled, huh?"));
434 Events.Handle_Events;
435 Timed_Out := Time.Timed_Out (DbC_Stat_Deadline);
437 pragma Debug (Transfer_Info.Dump_Stats);
438 DbC_Stat_Deadline := Time.MS_From_Now (12_345);
444 procedure Receive (Buf : in out Buffer; Len : in out Natural)
449 Transfers.Receive (Buf, Len);
452 procedure Send (Buf : Buffer; Len : in out Natural; Success : out Boolean)
460 Start_Now => Running,
464 procedure Ring_Doorbell (EP : Endpoint_Range)
468 Regs.Write (Doorbell_Target, Word8 (EP) - 2);
473 -- vim: set ts=8 sts=3 sw=3 et: