DriverKit USB driver (dext) process doesn't terminate after device is unplugged - c++

I'm working on a USB driver using DriverKit for macOS (later iPadOS as well). The driver matches, loads, and is able to communicate with my device. However, I've noticed that when I unplug the device, the driver process is still running (as seen in Activity Monitor). If I plug it back in again, a second process starts up (and a third, and fourth, and so on). I've implemented the Stop() method like so:
kern_return_t
IMPL(MyDriver, Stop)
{
kern_return_t ret = kIOReturnSuccess;
os_log(OS_LOG_DEFAULT, "Stopping");
ret = ivars->device->Close(this, 0);
if (ret != kIOReturnSuccess) {
os_log(OS_LOG_DEFAULT, "Error closing device: 0x%08x", ret);
}
ret = Stop(provider, SUPERDISPATCH);
if (ret != kIOReturnSuccess) {
os_log(OS_LOG_DEFAULT, "Error closing device: 0x%08x", ret);
}
os_log(OS_LOG_DEFAULT, "Stop finished.");
return ret;
}
I've confirmed that the Stop method is indeed being called, and the return value is kIOReturnSuccess. My driver class's free() method is also being called. Might also be worth noting that no client app is communicating with the driver (haven't gotten that far yet).
Is there something else I need to do to tell the system it's OK to stop running the driver process? Is it normal for it to not terminate?

Turns out the problem was that I had forgotten to call super::free() in my driver object's free method. After fixing that the driver process terminates when the device is unplugged.

Related

How to test USB HID handle as invalid due to device being replugged

In Windows 7 I have a handle to a custom USB HID device. I'm trying to determine if the handle is no longer able to access the device because the device has been unplugged and re-plugged. When this happens I/O attempts using WriteFile fail. However I am looking for a way to test the handle before doing any I/O to determine if the HID device handle is still valid. And when I say valid I mean handle is connected to device such that I will be able to do I/O to the device. I understand that technically the handle itself may be valid even when it has lost connection to the physical device. The current method in my code is to test by using an attempt to open a new handle to the device and if this succeeds then handle is valid and if it fails handle is not valid. This detects disconnect as expected if the device is unplugged at the time of the test. The problem with this is that if the device has been plugged back in then the test open succeeds but the actual handle used to do I/O (opened before the disconnected/reconnect) is no longer connected to the device. I'm looking for a benign call I can do with the actual I/O handle that will tell me if the device is connected but without doing any actual I/O.
I've tried a few calls so far but none return an indication of the device connection as I require.
DWORD handleInformation;
LARGE_INTEGER size = { 0, 0 };
BOOL isConnected;
isConnected = GetFileSizeEx(m_HidWriteHandle, &size);
isConnected = GetHandleInformation(m_HidWriteHandle, &handleInformation);
GetFileSizeEx always fails and GetLastError() reports (1) Invalid Function
GetHandleInformation always succeeds even when device has been disconnected/reconnected and handle is not able to access device.
The call to use is HidD_GetAttributes(handle, &attributes)
// Try to get the HID attributes. This will fail if device is
// unplugged or has been unplugged since CreateFile() call that
// created m_HidWritehandle
HIDD_ATTRIBUTES Attributes;
Attributes.Size = sizeof(Attributes);
m_HidPresent = HidD_GetAttributes(m_HidWriteHandle, &Attributes) != 0;
This will return false even if device has been plugged back in and thus can be used as an indicator of need to reconnect to HID device.

DIrectshow function is blocked at system bootup

I have an Directshow based mediaplayer application . It works very well without any issues during normal playabck . But occasionally i am facing one issue when the Mediaplayer started just after system boot .
HRESULT CSDirectShow::RenderOutputPins (IBaseFilter* pFilter)
{
const char* funcName = "CSDirectShow::RenderOutputPins()";
HRESULT hr = S_OK;
// Enumerate all pins on the source filter,
// looking for the output pins so that I can call Render() on them
//
CComPtr< IEnumPins > pEnumPin;
if (!FAILED (pFilter->EnumPins (&pEnumPin)))
{
while (true)
{
// get the next pin
//
CComPtr< IPin > pPin;
if (pEnumPin->Next (1L, &pPin, NULL) != S_OK) break;
// I'm not interested in connected pins
// if this pin is an unconnected output pin, then render it.
//
CComPtr< IPin > pConnectedPin;
if (pPin->ConnectedTo (&pConnectedPin) == VFW_E_NOT_CONNECTED)
{
PIN_DIRECTION pinDirection;
PIN_INFO pinInfo;
//Get the information of the pin
if (pPin->QueryDirection (&pinDirection) == S_OK
&& pinDirection == PINDIR_OUTPUT
&& pPin->QueryPinInfo(&pinInfo) == S_OK
&& strstr((char*)pinInfo.achName,"~")==NULL)
{
if (FAILED (hr = m_pGB->Render (pPin)))
{
SafeRelease(&pinInfo.pFilter);
return hr;
}
}
SafeRelease(&pinInfo.pFilter);
}
}
}
TraceMsg ("%s: exit",funcName);
return S_OK;
}
When m_pGB->Render (pPin) is called ,This function never returns and it is blocked inside .I confirmed using logs .This issue happens only when i start my application immediately after bootup . When issues occures if I close and restart my application it works like a charm .Since application is designed to start automatically after system bootup this behaviour has become a bigger concern .Kindly help
IGraphBuilder.Render call does a lot internally, and specifically it goes over enumeration of potentially suitable filter, which in turn attempts to load additional DLLs registered with DirectShow environment. Such file could have missing dependencies, or dependencies on remote or temporarily inaccessible drivers (just one example).
If you experience a deadlock, you can troubleshoot it further (debug it) and get details on locked state, and on activity during Render call.
If the problem is caused by third party filters (esp. codec packs registering a collection of filters at once without thinking too much on compatibility) registered with the system in a not so good way, perhaps you could identify them and uninstall.
If you want to improve the player on your side, you should avoid Render call, and build your filter graph with smaller increments - adding specific filter and connecting pins, without leaving big tasks at mercy of Intelligent Connect, which works well in general but is sensitive to compatibility problems as mentioned above.

libudev strange behavior v1.7.2 onwards

I am facing a certain issue with libudev. I have written a listener thread that constantly keeps listening for devices connected over usb. I have used the libudev API udev_monitor_receive_device at the start of a continuous while loop as it is a blocking call. The source works fine with libudev v1.6.3, but when upgraded to v1.7.2, the call to udev_monitor_receive_device is not blocking anymore and the while loop keeps running continuously and the api keeps returning NULL. Below is a portion of the code that will help you understand the libudev usage in my code..
struct udev *udevObject ;
struct udev_device *mDev;
struct udev_enumerate *enumerate;
struct udev_monitor *mUdevMonitorObject;
udevObject = udev_new();
if(NULL == udevObject){
LOGERR((TEXT("Listener thread :: Error initialising Udev Library\r\n")));
return false;
}
mUdevMonitorObject = udev_monitor_new_from_netlink(udevObject, "udev");
udev_monitor_enable_receiving(mUdevMonitorObject);
// enumerate = udev_enumerate_new(udevObject);
// udev_enumerate_scan_devices(enumerate);
while(1)
{
// This loop keeps running continuously on libudev v1.7.3, but the call blocks for v1.6.3
mDev = udev_monitor_receive_device(mUdevMonitorObject);
LOGINFO((TEXT("Listener thread:: Processing UDEV trigger\r\n")));
}
This problem has been bugging me for a long time. Any help would be appreciated.
Yeah I see the same thing. Seems the only way to interact with udev_monitor_receive_device these days is with select/poll - I have a similar loop to you, and adding these lines before udev_monitor_recieve_device makes everything act sensible:
int fd = udev_monitor_get_fd(mUdevMonitorObject);
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
if(select(fd+1, &fdset, NULL, NULL, NULL) < 0) {
/* error in select */
continue;
}
It would be nice if receive_device still blocked until there was data ready instead of making you do this dance, but there you go.
See the API reference:
The monitor socket is by default set to NONBLOCK. A variant of poll() on the file descriptor returned by udev_monitor_get_fd() should to be used to wake up when new devices arrive, or alternatively the file descriptor switched into blocking mode.

How to control the connect timeout with the Winsock API?

I'm writing a program using the Winsock API because a friend wanted a simple program to check and see if a Minecraft server was running or not. It works fine if it is running, however if it is not running, the program freezes until, I'm assuming, the connection times out. Another issue is, if I have something like this (pseudo-code):
void connectButtonClicked()
{
setLabel1Text("Connecting");
attemptConnection();
setLabel1Text("Done Connecting!");
}
it seems to skip right to attemptConnection(), completely ignoring whats above it. I notice this because the program will freeze, but it wont change the label to "Connecting".
Here is my actual connection code:
bool CConnectionManager::ConnectToIp(String^ ipaddr)
{
if(!m_bValid)
return false;
const char* ip = StringToPConstChar(ipaddr);
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(isalpha(ip[0]))
{
ip = getIPFromAddress(ipaddr);
}
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr(ip);
service.sin_port = htons(MINECRAFT_PORT);
if(m_socket == NULL)
{
return false;
}
if (connect(m_socket, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR)
{
closesocket(m_socket);
return false;
}
else
{
closesocket(m_socket);
return true;
}
return true;
}
There is also code in the CConnectionManager's contructor to start up Winsock API and such.
So, how do I avoid this freeze, and allow me to update something like a progress bar during connection? Do I have to make the connection in a separate thread? I have only worked with threads in Java, so I have no idea how to do that :/
Also: I am using a CLR Windows Form Application
I am using Microsoft Visual C++ 2008 Express Edition
Your code does not skip the label update. The update simply involves issuing window messages that have not been processed yet, that is why you do not see the new text appear before connecting the socket. You will have to pump the message queue for new messages before connecting the socket.
As for the socket itself, there is no connect timeout in the WinSock API, unfortunately. You have two choices to implement a manual timeout:
1) Assuming you are using a blocking socket (sockets are blocking by default), perform the connect in a separate worker thread.
2) If you don't want to use a thread then switch the socket to non-blocking mode. Connecting the socket will always exit immediately, so your main code will not be blocked, then you will receive a notification later on if the connection was successful or not. There are several ways to detect that, depending on which API you use - WSAAsyncSelect(), WSAAsyncEvent(), or select().
Either way, while the connect is in progress, run a timer in your main thread. If the connect succeeds, stop the timer. If the timer elapses, disconnect the socket, which will cause the connect to abort with an error.
Maybe you want to read here:
To assure that all data is sent and received on a connected socket before it is closed, an application should use shutdown to close connection before calling closesocket. http://msdn.microsoft.com/en-us/library/ms740481%28v=VS.85%29.aspx
Since you are in the blocking mode there still might be some data...

Tracking down the source of handle leaks in WinSock MFC application

We are developing an application in which we are using a WinSock-based sime socket approach to communicate with an outside module. Our requirement is to make sure that the connection will always be on, so for that reason, we continuously retry to connect every 1 minute whenever we get disconnected.
Our problem starts here. We have observered that on every retry of socket reconnect, it is leaking exactly two Windows handles. We have tried so many options, but none of them are working. Which handles could be leaking, and how could we go about identifying the culprit?
Following is the code that we are using right now:
bool CSocketClass::ConnectToServer(int nLineNo)
{
string strIPAddress;
int nPortNo;
SOCKET* l_ClientSocket;
int ConnectionResult;
//----------------------
// Create a SOCKET for connecting to server
if (nLineNo == 1)
{
m_objLine1.m_ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
strIPAddress = m_objLine1.m_strIPAddress;
nPortNo = m_objLine1.m_nPortNo;
l_ClientSocket = &(m_objLine1.m_ClientSocket);
}
else
{
m_objLine2.m_ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
strIPAddress = m_objLine2.m_strIPAddress;
nPortNo = m_objLine2.m_nPortNo;
l_ClientSocket = &(m_objLine2.m_ClientSocket);
}
if(INVALID_SOCKET == *l_ClientSocket)
{
return false;
}
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
sockaddr_in clientService;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr( strIPAddress.c_str() );
clientService.sin_port = htons( nPortNo );
//----------------------
// Connect to server.
ConnectionResult = connect( *l_ClientSocket, (SOCKADDR*) &clientService, sizeof(clientService) ) ; if (ConnectionResult == SOCKET_ERROR)
{
if (nLineNo == 1)
{
//ERROR in line1
}
else
{
//ERROR in line2
}
return false;
}
else
//In case of successful connection
{
//Other actions
}
return true;
}
Try the free Process Explorer from Microsoft. It will display all the open handles for a process along with information such as name (for file, mutex, event, etc. handles). It will also highlight newly created and closed handles, so if you step through a loop of your code and refresh the display, you can see the exact handles that were leaked.
Let's say you acquired socket correctly:
m_objLine1.m_ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
m_objLine1.m_ClientSocket != INVALID_SOCKET // true
but then, you can't connect, so
ConnectionResult = connect( *l_ClientSocket, (SOCKADDR*) &clientService,
sizeof(clientService) )
ConnectionResult == SOCKET_ERROR // true
in that case, you should close that acquired socket handle:
closesocket(m_objLine1.m_ClientSocket);
You have two lines, so I guess that you call this function twice, once for each line, so
that's why two leaked handles.
I would suggest that you try Intel Parallel Inspector in order to identify the memory leaks and where they are occurring.
There is a trial download if you wish to try it.
A simple way to find handle leaks is to log everything.
Every time you obtain a handle, log that you obtained it, as well as any other details about the circumstances. Every time you release a handle, log that you released it. Include both times the actual handle (just some hex).
Then you get a log that looks like this (just for example):
Obtained handle 0xf000 (nLineNo = 5)
Obtained handle 0xb000 (nLineNo = 6)
Obtained handle 0xd0d0 (nLineNo = 7)
Released handle 0xf000
Released handle 0xb000
Picking through this by hand, you can see that you obtained handle 0xd0d0 when nLineNo was 7, and it never got released. It's not much but it does help, and if the going gets tough, you can even try logging stack traces at each obtain/release. Also, if the log is always reliably produced like that, you can start putting in breakpoints based on the actual values (e.g. break at a point in the program when the handle is 0xd0d0, so you can see what's happening to it).
If it's more practical, you can start wrapping your handles inside the program itself, e.g. a std::set of all obtained handles, along with any details about when they were obtained, and you can effectively start hacking your program to keep track of what it's doing (then undo all your changes once you fixed it).
Hope that helps - it's part of the reason I tend to at least keep a std::set of everything I obtain, so if worst comes to worst you can iterate over them on shutdown and release them all (and log a big "FIX THIS!" message!)
Try adding a shutdown(SD_BOTH) on the socket handles after the closesocket(); Also, try adding a Sleep for about 100ms (only for a test) and see how it goes.