Commit a1e453e9 authored by Dean Camera's avatar Dean Camera
Browse files

Make Host mode Class drivers only set the class driver instance's state values...

Make Host mode Class drivers only set the class driver instance's state values once a compatible interface has been found within the device.
parent 86367574
......@@ -43,24 +43,17 @@ uint8_t CDC_Host_ConfigurePipes(USB_ClassInfo_CDC_Host_t* const CDCInterfaceInfo
USB_Descriptor_Endpoint_t* DataINEndpoint = NULL;
USB_Descriptor_Endpoint_t* DataOUTEndpoint = NULL;
USB_Descriptor_Endpoint_t* NotificationEndpoint = NULL;
USB_Descriptor_Interface_t* CDCControlInterface = NULL;
memset(&CDCInterfaceInfo->State, 0x00, sizeof(CDCInterfaceInfo->State));
if (DESCRIPTOR_TYPE(ConfigDescriptorData) != DTYPE_Configuration)
return CDC_ENUMERROR_InvalidConfigDescriptor;
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_CDC_Host_NextCDCControlInterface) != DESCRIPTOR_SEARCH_COMP_Found)
{
return CDC_ENUMERROR_NoCompatibleInterfaceFound;
}
CDCInterfaceInfo->State.ControlInterfaceNumber = DESCRIPTOR_PCAST(ConfigDescriptorData,
USB_Descriptor_Interface_t)->InterfaceNumber;
while (!(DataINEndpoint) || !(DataOUTEndpoint) || !(NotificationEndpoint))
{
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
if (!(CDCControlInterface) ||
USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_CDC_Host_NextCDCInterfaceEndpoint) != DESCRIPTOR_SEARCH_COMP_Found)
{
if (NotificationEndpoint)
......@@ -82,8 +75,7 @@ uint8_t CDC_Host_ConfigurePipes(USB_ClassInfo_CDC_Host_t* const CDCInterfaceInfo
return CDC_ENUMERROR_NoCompatibleInterfaceFound;
}
CDCInterfaceInfo->State.ControlInterfaceNumber = DESCRIPTOR_PCAST(ConfigDescriptorData,
USB_Descriptor_Interface_t)->InterfaceNumber;
CDCControlInterface = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_Descriptor_Interface_t);
NotificationEndpoint = NULL;
}
......@@ -135,6 +127,7 @@ uint8_t CDC_Host_ConfigurePipes(USB_ClassInfo_CDC_Host_t* const CDCInterfaceInfo
}
}
CDCInterfaceInfo->State.ControlInterfaceNumber = CDCControlInterface->InterfaceNumber;
CDCInterfaceInfo->State.ControlLineStates.HostToDevice = (CDC_CONTROL_LINE_OUT_RTS | CDC_CONTROL_LINE_OUT_DTR);
CDCInterfaceInfo->State.ControlLineStates.DeviceToHost = (CDC_CONTROL_LINE_IN_DCD | CDC_CONTROL_LINE_IN_DSR);
CDCInterfaceInfo->State.IsActive = true;
......
......@@ -40,43 +40,20 @@ uint8_t HID_Host_ConfigurePipes(USB_ClassInfo_HID_Host_t* const HIDInterfaceInfo
uint16_t ConfigDescriptorSize,
void* ConfigDescriptorData)
{
USB_Descriptor_Interface_t* CurrentHIDInterface;
USB_Descriptor_Endpoint_t* DataINEndpoint = NULL;
USB_Descriptor_Endpoint_t* DataOUTEndpoint = NULL;
USB_Descriptor_Interface_t* HIDInterface = NULL;
USB_HID_Descriptor_HID_t* HIDDescriptor = NULL;
memset(&HIDInterfaceInfo->State, 0x00, sizeof(HIDInterfaceInfo->State));
if (DESCRIPTOR_TYPE(ConfigDescriptorData) != DTYPE_Configuration)
return HID_ENUMERROR_InvalidConfigDescriptor;
do
{
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_HID_Host_NextHIDInterface) != DESCRIPTOR_SEARCH_COMP_Found)
{
return HID_ENUMERROR_NoCompatibleInterfaceFound;
}
CurrentHIDInterface = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_Descriptor_Interface_t);
} while (HIDInterfaceInfo->Config.HIDInterfaceProtocol &&
(CurrentHIDInterface->Protocol != HIDInterfaceInfo->Config.HIDInterfaceProtocol));
HIDInterfaceInfo->State.InterfaceNumber = CurrentHIDInterface->InterfaceNumber;
HIDInterfaceInfo->State.SupportsBootProtocol = (CurrentHIDInterface->SubClass != HID_BOOTP_NonBootProtocol);
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_HID_Host_NextHID) != DESCRIPTOR_SEARCH_COMP_Found)
{
return HID_ENUMERROR_NoCompatibleInterfaceFound;
}
HIDInterfaceInfo->State.HIDReportSize = DESCRIPTOR_PCAST(ConfigDescriptorData,
USB_HID_Descriptor_HID_t)->HIDReportLength;
while (!(DataINEndpoint) || !(DataOUTEndpoint))
{
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
if (!(HIDInterface) ||
USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_HID_Host_NextHIDInterfaceEndpoint) != DESCRIPTOR_SEARCH_COMP_Found)
{
if (DataINEndpoint || DataOUTEndpoint)
......@@ -90,12 +67,9 @@ uint8_t HID_Host_ConfigurePipes(USB_ClassInfo_HID_Host_t* const HIDInterfaceInfo
return HID_ENUMERROR_NoCompatibleInterfaceFound;
}
CurrentHIDInterface = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_Descriptor_Interface_t);
HIDInterface = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_Descriptor_Interface_t);
} while (HIDInterfaceInfo->Config.HIDInterfaceProtocol &&
(CurrentHIDInterface->Protocol != HIDInterfaceInfo->Config.HIDInterfaceProtocol));
HIDInterfaceInfo->State.InterfaceNumber = CurrentHIDInterface->InterfaceNumber;
HIDInterfaceInfo->State.SupportsBootProtocol = (CurrentHIDInterface->SubClass != HID_BOOTP_NonBootProtocol);
(HIDInterface->Protocol != HIDInterfaceInfo->Config.HIDInterfaceProtocol));
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_HID_Host_NextHID) != DESCRIPTOR_SEARCH_COMP_Found)
......@@ -103,8 +77,7 @@ uint8_t HID_Host_ConfigurePipes(USB_ClassInfo_HID_Host_t* const HIDInterfaceInfo
return HID_ENUMERROR_NoCompatibleInterfaceFound;
}
HIDInterfaceInfo->State.HIDReportSize = DESCRIPTOR_PCAST(ConfigDescriptorData,
USB_HID_Descriptor_HID_t)->HIDReportLength;
HIDDescriptor = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_HID_Descriptor_HID_t);
DataINEndpoint = NULL;
DataOUTEndpoint = NULL;
......@@ -143,6 +116,9 @@ uint8_t HID_Host_ConfigurePipes(USB_ClassInfo_HID_Host_t* const HIDInterfaceInfo
}
}
HIDInterfaceInfo->State.InterfaceNumber = HIDInterface->InterfaceNumber;
HIDInterfaceInfo->State.HIDReportSize = HIDDescriptor->HIDReportLength;
HIDInterfaceInfo->State.SupportsBootProtocol = (HIDInterface->SubClass != HID_BOOTP_NonBootProtocol);
HIDInterfaceInfo->State.LargestReportSize = 8;
HIDInterfaceInfo->State.IsActive = true;
......
......@@ -42,36 +42,29 @@ uint8_t MIDI_Host_ConfigurePipes(USB_ClassInfo_MIDI_Host_t* const MIDIInterfaceI
{
USB_Descriptor_Endpoint_t* DataINEndpoint = NULL;
USB_Descriptor_Endpoint_t* DataOUTEndpoint = NULL;
USB_Descriptor_Interface_t* MIDIInterface = NULL;
memset(&MIDIInterfaceInfo->State, 0x00, sizeof(MIDIInterfaceInfo->State));
if (DESCRIPTOR_TYPE(ConfigDescriptorData) != DTYPE_Configuration)
return MIDI_ENUMERROR_InvalidConfigDescriptor;
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_MIDI_Host_NextMIDIStreamingInterface) != DESCRIPTOR_SEARCH_COMP_Found)
{
return MIDI_ENUMERROR_NoCompatibleInterfaceFound;
}
MIDIInterfaceInfo->State.InterfaceNumber = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_Descriptor_Interface_t)->InterfaceNumber;
while (!(DataINEndpoint) || !(DataOUTEndpoint))
{
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
if (!(MIDIInterface) ||
USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_MIDI_Host_NextMIDIStreamingDataEndpoint) != DESCRIPTOR_SEARCH_COMP_Found)
{
DataINEndpoint = NULL;
DataOUTEndpoint = NULL;
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_MIDI_Host_NextMIDIStreamingInterface) != DESCRIPTOR_SEARCH_COMP_Found)
{
return MIDI_ENUMERROR_NoCompatibleInterfaceFound;
}
MIDIInterfaceInfo->State.InterfaceNumber = DESCRIPTOR_PCAST(ConfigDescriptorData,
USB_Descriptor_Interface_t)->InterfaceNumber;
MIDIInterface = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_Descriptor_Interface_t);
DataINEndpoint = NULL;
DataOUTEndpoint = NULL;
continue;
}
......@@ -104,6 +97,7 @@ uint8_t MIDI_Host_ConfigurePipes(USB_ClassInfo_MIDI_Host_t* const MIDIInterfaceI
}
}
MIDIInterfaceInfo->State.InterfaceNumber = MIDIInterface->InterfaceNumber;
MIDIInterfaceInfo->State.IsActive = true;
return MIDI_ENUMERROR_NoError;
......
......@@ -42,36 +42,29 @@ uint8_t MS_Host_ConfigurePipes(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
{
USB_Descriptor_Endpoint_t* DataINEndpoint = NULL;
USB_Descriptor_Endpoint_t* DataOUTEndpoint = NULL;
USB_Descriptor_Interface_t* MassStorageInterface = NULL;
memset(&MSInterfaceInfo->State, 0x00, sizeof(MSInterfaceInfo->State));
if (DESCRIPTOR_TYPE(ConfigDescriptorData) != DTYPE_Configuration)
return MS_ENUMERROR_InvalidConfigDescriptor;
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_MS_Host_NextMSInterface) != DESCRIPTOR_SEARCH_COMP_Found)
{
return MS_ENUMERROR_NoCompatibleInterfaceFound;
}
MSInterfaceInfo->State.InterfaceNumber = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_Descriptor_Interface_t)->InterfaceNumber;
while (!(DataINEndpoint) || !(DataOUTEndpoint))
{
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
if (!(MassStorageInterface) ||
USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_MS_Host_NextMSInterfaceEndpoint) != DESCRIPTOR_SEARCH_COMP_Found)
{
DataINEndpoint = NULL;
DataOUTEndpoint = NULL;
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_MS_Host_NextMSInterface) != DESCRIPTOR_SEARCH_COMP_Found)
{
return MS_ENUMERROR_NoCompatibleInterfaceFound;
}
MSInterfaceInfo->State.InterfaceNumber = DESCRIPTOR_PCAST(ConfigDescriptorData,
USB_Descriptor_Interface_t)->InterfaceNumber;
MassStorageInterface = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_Descriptor_Interface_t);
DataINEndpoint = NULL;
DataOUTEndpoint = NULL;
continue;
}
......@@ -104,6 +97,7 @@ uint8_t MS_Host_ConfigurePipes(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
}
}
MSInterfaceInfo->State.InterfaceNumber = MassStorageInterface->InterfaceNumber;
MSInterfaceInfo->State.IsActive = true;
return MS_ENUMERROR_NoError;
......
......@@ -42,41 +42,29 @@ uint8_t PRNT_Host_ConfigurePipes(USB_ClassInfo_PRNT_Host_t* const PRNTInterfaceI
{
USB_Descriptor_Endpoint_t* DataINEndpoint = NULL;
USB_Descriptor_Endpoint_t* DataOUTEndpoint = NULL;
USB_Descriptor_Interface_t* PrinterInterface = NULL;
memset(&PRNTInterfaceInfo->State, 0x00, sizeof(PRNTInterfaceInfo->State));
if (DESCRIPTOR_TYPE(DeviceConfigDescriptor) != DTYPE_Configuration)
return PRNT_ENUMERROR_InvalidConfigDescriptor;
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &DeviceConfigDescriptor,
DCOMP_PRNT_Host_NextPRNTInterface) != DESCRIPTOR_SEARCH_COMP_Found)
{
return PRNT_ENUMERROR_NoCompatibleInterfaceFound;
}
PRNTInterfaceInfo->State.InterfaceNumber = DESCRIPTOR_PCAST(DeviceConfigDescriptor,
USB_Descriptor_Interface_t)->InterfaceNumber;
PRNTInterfaceInfo->State.AlternateSetting = DESCRIPTOR_PCAST(DeviceConfigDescriptor,
USB_Descriptor_Interface_t)->AlternateSetting;
while (!(DataINEndpoint) || !(DataOUTEndpoint))
{
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &DeviceConfigDescriptor,
if (!(PrinterInterface) ||
USB_GetNextDescriptorComp(&ConfigDescriptorSize, &DeviceConfigDescriptor,
DCOMP_PRNT_Host_NextPRNTInterfaceEndpoint) != DESCRIPTOR_SEARCH_COMP_Found)
{
DataINEndpoint = NULL;
DataOUTEndpoint = NULL;
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &DeviceConfigDescriptor,
DCOMP_PRNT_Host_NextPRNTInterface) != DESCRIPTOR_SEARCH_COMP_Found)
{
return PRNT_ENUMERROR_NoCompatibleInterfaceFound;
}
PRNTInterfaceInfo->State.InterfaceNumber = DESCRIPTOR_PCAST(DeviceConfigDescriptor,
USB_Descriptor_Interface_t)->InterfaceNumber;
PRNTInterfaceInfo->State.AlternateSetting = DESCRIPTOR_PCAST(DeviceConfigDescriptor,
USB_Descriptor_Interface_t)->AlternateSetting;
PrinterInterface = DESCRIPTOR_PCAST(DeviceConfigDescriptor, USB_Descriptor_Interface_t);
DataINEndpoint = NULL;
DataOUTEndpoint = NULL;
continue;
}
......@@ -109,6 +97,8 @@ uint8_t PRNT_Host_ConfigurePipes(USB_ClassInfo_PRNT_Host_t* const PRNTInterfaceI
}
}
PRNTInterfaceInfo->State.InterfaceNumber = PrinterInterface->InterfaceNumber;
PRNTInterfaceInfo->State.AlternateSetting = PrinterInterface->AlternateSetting;
PRNTInterfaceInfo->State.IsActive = true;
return PRNT_ENUMERROR_NoError;
......
......@@ -43,24 +43,19 @@ uint8_t RNDIS_Host_ConfigurePipes(USB_ClassInfo_RNDIS_Host_t* const RNDISInterfa
USB_Descriptor_Endpoint_t* DataINEndpoint = NULL;
USB_Descriptor_Endpoint_t* DataOUTEndpoint = NULL;
USB_Descriptor_Endpoint_t* NotificationEndpoint = NULL;
USB_Descriptor_Interface_t* RNDISControlInterface = NULL;
memset(&RNDISInterfaceInfo->State, 0x00, sizeof(RNDISInterfaceInfo->State));
if (DESCRIPTOR_TYPE(ConfigDescriptorData) != DTYPE_Configuration)
return RNDIS_ENUMERROR_InvalidConfigDescriptor;
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_RNDIS_Host_NextRNDISControlInterface) != DESCRIPTOR_SEARCH_COMP_Found)
{
return RNDIS_ENUMERROR_NoCompatibleInterfaceFound;
}
RNDISInterfaceInfo->State.ControlInterfaceNumber = DESCRIPTOR_PCAST(ConfigDescriptorData,
USB_Descriptor_Interface_t)->InterfaceNumber;
RNDISControlInterface = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_Descriptor_Interface_t);
while (!(DataINEndpoint) || !(DataOUTEndpoint) || !(NotificationEndpoint))
{
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
if (!(RNDISControlInterface) ||
USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_RNDIS_Host_NextRNDISInterfaceEndpoint) != DESCRIPTOR_SEARCH_COMP_Found)
{
if (NotificationEndpoint)
......@@ -82,8 +77,7 @@ uint8_t RNDIS_Host_ConfigurePipes(USB_ClassInfo_RNDIS_Host_t* const RNDISInterfa
return RNDIS_ENUMERROR_NoCompatibleInterfaceFound;
}
RNDISInterfaceInfo->State.ControlInterfaceNumber = DESCRIPTOR_PCAST(ConfigDescriptorData,
USB_Descriptor_Interface_t)->InterfaceNumber;
RNDISControlInterface = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_Descriptor_Interface_t);
NotificationEndpoint = NULL;
}
......@@ -135,6 +129,7 @@ uint8_t RNDIS_Host_ConfigurePipes(USB_ClassInfo_RNDIS_Host_t* const RNDISInterfa
}
}
RNDISInterfaceInfo->State.ControlInterfaceNumber = RNDISControlInterface->InterfaceNumber;
RNDISInterfaceInfo->State.IsActive = true;
return RNDIS_ENUMERROR_NoError;
......
......@@ -43,38 +43,30 @@ uint8_t SI_Host_ConfigurePipes(USB_ClassInfo_SI_Host_t* const SIInterfaceInfo,
USB_Descriptor_Endpoint_t* DataINEndpoint = NULL;
USB_Descriptor_Endpoint_t* DataOUTEndpoint = NULL;
USB_Descriptor_Endpoint_t* EventsEndpoint = NULL;
USB_Descriptor_Interface_t* StillImageInterface = NULL;
memset(&SIInterfaceInfo->State, 0x00, sizeof(SIInterfaceInfo->State));
if (DESCRIPTOR_TYPE(ConfigDescriptorData) != DTYPE_Configuration)
return SI_ENUMERROR_InvalidConfigDescriptor;
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_SI_Host_NextSIInterface) != DESCRIPTOR_SEARCH_COMP_Found)
{
return SI_ENUMERROR_NoCompatibleInterfaceFound;
}
SIInterfaceInfo->State.InterfaceNumber = DESCRIPTOR_PCAST(ConfigDescriptorData,
USB_Descriptor_Interface_t)->InterfaceNumber;
while (!(DataINEndpoint) || !(DataOUTEndpoint))
{
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
if (!(StillImageInterface) ||
USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_SI_Host_NextSIInterfaceEndpoint) != DESCRIPTOR_SEARCH_COMP_Found)
{
DataINEndpoint = NULL;
DataOUTEndpoint = NULL;
EventsEndpoint = NULL;
if (USB_GetNextDescriptorComp(&ConfigDescriptorSize, &ConfigDescriptorData,
DCOMP_SI_Host_NextSIInterface) != DESCRIPTOR_SEARCH_COMP_Found)
{
return SI_ENUMERROR_NoCompatibleInterfaceFound;
}
SIInterfaceInfo->State.InterfaceNumber = DESCRIPTOR_PCAST(ConfigDescriptorData,
USB_Descriptor_Interface_t)->InterfaceNumber;
StillImageInterface = DESCRIPTOR_PCAST(ConfigDescriptorData, USB_Descriptor_Interface_t);
DataINEndpoint = NULL;
DataOUTEndpoint = NULL;
EventsEndpoint = NULL;
continue;
}
......@@ -123,7 +115,9 @@ uint8_t SI_Host_ConfigurePipes(USB_ClassInfo_SI_Host_t* const SIInterfaceInfo,
}
}
SIInterfaceInfo->State.InterfaceNumber = StillImageInterface->InterfaceNumber;
SIInterfaceInfo->State.IsActive = true;
return SI_ENUMERROR_NoError;
}
......
......@@ -19,6 +19,7 @@
* -# Change makefiles to allow for absolute LUFA location to be used
* -# Re-add interrupt Pipe/Endpoint support
* -# Fix intermittent device mode enumeration errors
* -# Add HID report macros to make HID report editing easier
* - Documentation/Support
* -# Add detailed overviews of how each demo works
* -# Add board overviews
......
......@@ -23,7 +23,7 @@
* <b>Device Mode</b>
* - Endpoints MUST be allocated in ascending order to ensure that bank corruption does not occur. Ensure that your user application
* allocated endpoints in ascending order - or if your application uses the USB device mode class drivers, ensure that each instance's
* endpoint indexes are non-overlapped with other interface's endpoints.
* endpoint indexes are not overlapped with other interface's endpoints.
* - The signature for the CALLBACK_USB_GetDescriptor() callback has changed, the "void** const DescriptorAddress" parameter is
* now "const void** const DescriptorAddress". Existing applications should update their callback signatures to match this, and
* eliminate any casting of descriptor pointers to a non-const pointer.
......@@ -33,7 +33,7 @@
* <b>Host Mode</b>
* - Pipes MUST be allocated in ascending order to ensure that bank corruption does not occur. Ensure that your user application
* allocated pipes in ascending order - or if your application uses the USB host mode class drivers, ensure that each instance's
* pipe indexes are non-overlapped with other interface's pipes.
* pipe indexes are not overlapped with other interface's pipes.
* - The PRNT_Host_SendData() function has been renamed to \ref PRNT_Host_SendString(). Existing applications should simply
* replace all references to the obsolete function name with the new function name.
* - The names of the class specific descriptor type defines in the USB Class drivers have changed - refer to the driver documentation
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment