Linux USB driver: Interrupt URBs - linux-kernel

I suppose I actually have two separate questions, but I think that they are related enough to include them both. The context is a Linux USB device driver (not userspace).
After transmitting a request URB, how do I receive the response once my complete callback is called?
How can I use interrupt URBs for single request/response pairs, and not as actual continuous interrupt polling (as they are intended)?
So for some background, I'm working on a driver for the Microchip MCP2210 a USB-to-SPI Protocol Converter with GPIO (USB 2.0, datasheet here). This device advertises as generic HID and exposes two interrupt endpoints (an in and an out) as well as it's control endpoint.
I am starting from a working, (but alpha-quality) demo driver written by somebody else and kindly shared with the community. However, this is a HID driver and the mechanism it uses to communicate with the device is very expensive! (sending a 64 byte message requires allocating a 6k HID report struct, and allocation is sometimes performed in the context of an interrupt, requiring GFP_ATOMIC!). We'll be accessing this from an embedded low-memory device.
I'm new to USB drivers and still pretty green with Linux device drivers in general. However, I'm trying to convert this to a plain-jane USB driver (not HID) so I can use the less expensive interrupt URBs for my communications. Here is my code for transmitting my request. For the sake of (attempted) brevity, I'm not including the definition of my structs, etc, but please let me know if you need more of my code. dev->cur_cmd is where I'm keeping the current command I'm processing.
/* use a local for brevity */
cmd = dev->cur_cmd;
if (cmd->state == MCP2210_CMD_STATE_NEW) {
usb_fill_int_urb(dev->int_out_urb,
dev->udev,
usb_sndintpipe(dev->udev, dev->int_out_ep->desc.bEndpointAddress),
&dev->out_buffer,
sizeof(dev->out_buffer), /* always 64 bytes */
cmd->type->complete,
cmd,
dev->int_out_ep->desc.bInterval);
ret = usb_submit_urb(dev->int_out_urb, GFP_KERNEL);
if (ret) {
/* snipped: handle error */
}
cmd->state = MCP2210_CMD_STATE_XMITED;
}
And here is my complete fn:
/* note that by "ctrl" I mean a control command, not the control endpoint */
static void ctrl_complete(struct urb *)
{
struct mcp2210_device *dev = urb->context;
struct mcp2210_command *cmd = dev->cur_cmd;
int ret;
if (unlikely(!cmd || !cmd->dev)) {
printk(KERN_ERR "mcp2210: ctrl_complete called w/o valid cmd "
"or dev\n");
return;
}
switch (cmd->state) {
/* Time to rx the response */
case MCP2210_CMD_STATE_XMITED:
/* FIXME: I think that I need to check the response URB's
* status to find out if it was even transmitted or not */
usb_fill_int_urb(dev->int_in_urb,
dev->udev,
usb_sndintpipe(dev->udev, dev->int_in_ep->desc
.bEndpointAddress),
&dev->in_buffer,
sizeof(dev->in_buffer),
cmd->type->complete,
dev,
dev->int_in_ep->desc.bInterval);
ret = usb_submit_urb(dev->int_in_urb, GFP_KERNEL);
if (ret) {
dev_err(&dev->udev->dev,
"while attempting to rx response, "
"usb_submit_urb returned %d\n", ret);
free_cur_cmd(dev);
return;
}
cmd->state = MCP2210_CMD_STATE_RXED;
return;
/* got response, now process it */
case MCP2210_CMD_STATE_RXED:
process_response(cmd);
default:
dev_err(&dev->udev->dev, "ctrl_complete called with unexpected state: %d", cmd->state);
free_cur_cmd(dev);
};
}
So am I at least close here? Secondly, both dev->int_out_ep->desc.bInterval and dev->int_in_ep->desc.bInterval are equal to 1, will this keep sending my request every 125 microseconds? And if so, how do I say "ok, ty, now stop this interrupt". The MCP2210 offers only one configuration, one interface and that has just the two interrupt endpoints. (I know everything has the control interface, not sure where that fits into the picture though.)
Rather than spam this question with the lsusb -v, I'm going to pastebin it.

Typically, request/response communication works as follows:
Submit the response URB;
submit the request URB;
in the request completion handler, if the request was not actually sent, cancel the response URB and abort;
in the response completion handler, handle the response data.
All that asynchronous completion handler stuff is a big hassle if you have a single URB that is completed almost immediately; therefore, there is the helper function usb_interrupt_msg() which works synchronously.
URBs to be used for polling must be resubmitted (typically from the completion handler).
If you do not resubmit the URB, no polling happens.

Related

AVFormatContext: interrupt callback proper usage?

AVFormatContext's interrupt_callback field is a
Custom interrupt callbacks for the I/O layer.
It's type is AVIOInterruptCB, and it explains in comment section:
Callback for checking whether to abort blocking functions.
AVERROR_EXIT is returned in this case by the interrupted function. During blocking operations, callback is called with opaque as parameter. If the callback returns 1, the blocking operation will be aborted.
No members can be added to this struct without a major bump, if new elements have been added after this struct in AVFormatContext or AVIOContext.
I have 2 questions:
what does the last section means? Especially "without a major bump"?
If I use this along with an RTSP source, when I close the input by avformat_close_input, the "TEARDOWN" message is being sent out, however it won't reach the RTSP server.
For 2: here is a quick pseudo-code for demo:
int pkts = 0;
bool early_exit = false;
int InterruptCallback(void* ctx) {
return early_exit ? 1 : 0;
}
void main() {
ctx = avformat_alloc_context
ctx->interrupt_callback.callback = InterruptCallback;
avformat_open_input
avformat_find_stream_info
pkts=0;
while(!early_exit) {
av_read_frame
if (pkts++ > 100) early_exit=true;
}
avformat_close_input
}
In case I don't use the interrupt callback at all, TEARDOWN is being sent out, and it also reaches the RTSP server so it can actually tear down the connection. Otherwise, it won't tear down it, and I have to wait until TCP socket times out.
What is the proper way of using this interrupt callback?
It means that they are not going to change anything for this structure (AVIOInterruptCB). However, if thats the case it would be in a major bump (major change from 4.4 eg to 5.0)
You need to pass a meaningful parameter to void* ctx. Anything that you like so you can check it within the static function. For example a bool that you will set as cancel so you will interrupt the av_read_frame (which will return an AVERROR_EXIT). Usually you pass a class of your decoder context or something similar which also holds all the info that you required to check whether to return 1 to interrupt or 0 to continue the requests properly. A real example would be that you open a wrong rtsp and then you want to open another one (the right one) so you need to cancel your previous requests.

Using IOCTL_VIDEO_QUERY_AVAIL_MODES to get a list of modes supported by a video adapter

I'm trying to query a list of supported modes from a video adapter driver:
// IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES - Retrieve the count of modes on the display adapter
// Input-Buffer: none
// Output-Buffer: VIDEO_NUM_MODES
VIDEO_NUM_MODES videoNumModes{};
// Send the IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES control code directly to the device driver
ULONG bytesReturned{};
if (::DeviceIoControl(
hDevice, // Handle to the display adapter device
IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES, // IOCTL code
nullptr, 0, // No input param struct
&videoNumModes, sizeof videoNumModes, // Address/size of output param struct
&bytesReturned, // Bytes returned in the output param struct
nullptr)) // Optional OVERLAPPED structure
{
// Allocate a buffer to receive the array of supported modes
const auto bufferSizeInBytes = videoNumModes.NumModes * videoNumModes.ModeInformationLength;
pVideoModeInfo = new VIDEO_MODE_INFORMATION[videoNumModes.NumModes];
// IOCTL_VIDEO_QUERY_AVAIL_MODES - Retrieve the array of supported modes
// Input-Buffer: none
// Output-Buffer: <allocated buffer>
// Send the IOCTL_VIDEO_QUERY_AVAIL_MODES control code directly to the device driver
if (::DeviceIoControl(
hDevice,
IOCTL_VIDEO_QUERY_AVAIL_MODES,
nullptr, 0,
pVideoModeInfo, bufferSizeInBytes,
&bytesReturned,
nullptr))
I get FALSE back on the first DeviceIoControl call with LastError set to ERROR_INVALID_FUNCTION (0x1).
I use this same code successfully to call custom IOCTL stuff in my drivers, so I'm confident that the implementation itself is sound. However, when I open a handle to the device, I'm supposed to use a string containing information about both the device and the interface I'm going to use. I defined the GUID for my custom IOCTL interface, and I use something like the following to send custom IOCTL commands:
hDevice = ::CreateFileW(L"\\\\?\\ROOT#DISPLAY#0000#{5f2f2b485bbd-5201-f1f9-4520-30f4bf353599}", ...);
But the documentation for IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES and IOCTL_VIDEO_QUERY_AVAIL_MODES doesn't mention which interface (GUID) they're a part of.
I assumed that I had to open the adapter device with the GUID_DEVINTERFACE_DISPLAY_ADAPTER interface, but I'm getting Incorrect Function on the first DeviceIoControl call. Same result if I open the adapter or one of its monitors with GUID_DEVINTERFACE_MONITOR.
I've searched online for any code examples, but all I find are from the driver side, responding to the query.
The display adapter driver that I'm issuing this against is an IddCx driver, if that helps. Any clues?

Heltec ESP32 LoRa Receive Interrupt

I’m trying to make a LoRa sender/receiver board.
Therefore the plan is to send some messages whenever I want, but if there is an incoming packet, I want to interrupt the sending-process (for beginning without any sending retries) and receive the incoming packet.
I tried the mix of the Heltec examples “OLED_LoRa_Sender” and “LoRaReceiverInterrupt”, and it works fine until I’m sending and receiving a message at the same time. Then the receive interrupt does not interrupt.
How can I solve that?
Thanks a lot in advance
if (sendMsg && !receivingMsg) { // sendMsg drives true, if PRG button is pressed receivingMsg drives try in "void onReceive(int packetSize)"
/*
Heltec.display->clear();
Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT);
Heltec.display->setFont(ArialMT_Plain_10);
//Serial.println(WiFi.macAddress());
Heltec.display->drawString(0, 0, "Sending packet: ");
Heltec.display->drawString(0, 15, WiFi.macAddress());
Heltec.display->drawString(100, 15, String(counter));
Heltec.display->display();
*/
LoRa.beginPacket();
LoRa.setTxPower(14,RF_PACONFIG_PASELECT_PABOOST);
LoRa.print("hello ");
LoRa.print(counter);
LoRa.endPacket();
delay(10); // give me time to bring up serial monitor
counter++;
// put the radio into receive mode
LoRa.receive();
sendMsg = false;
}
}

triggering user space with kernel

I need to send a string from kernel to a user space function without asking for it in particular from the user space, sort of triggering a function or application in the user space via some event in kernel.
So far I have tried an Ioctl that starts on an init in user space and then sleeps and kept reading about netlink but couldn't find a good working example for it.
Any suggestions or examples will be much obliged.
Here's how my process works, I would be interested in any suggestions for improvements as well:
Start the kernel module
Start user space application, which sends a custom command to the kernel module to register the user space PID for kernel module signals. In my case this was via a write to /dev/mymodule. The kernel module registers the PID:
...
printk("registering a new process id to receive signals: %d\n", current->pid);
signal_pid = current->pid;
...
The user space application also registers a handler with the kernel for certain types of signals.
void local_sig_handler(int signum) {
printf("received a signal from my module\n");
fflush(stdout); }
...
signal(SIGIO, local_sig_handler);
Kernel module generates a signal
...
struct siginfo info;
struct task_struct *t;
info.si_signo=SIGIO;
info.si_int=1;
info.si_code = SI_QUEUE;
printk("<1>IRQ received: %d\n", irq);
printk("<1>searching for task id: %d\n", signal_pid);
t= pid_task(find_vpid(signal_pid),PIDTYPE_PID);//user_pid has been fetched successfully
if(t == NULL){
printk("<1>no such pid, cannot send signal\n");
} else {
printk("<1>found the task, sending signal\n");
send_sig_info(SIGIO, &info, t);
}
Kernel relays the signal to the application's handler
You've got a few options:
Signals. User process defines a signal handler, and kernel signals the user process upon receipt of an event. This works well, but requires that the handling code run in an async signal handler (which makes it trickier to write correct code). The downside is that the amount of data you can transmit using a signal handler is somewhat limited. Make sure to use a signal that can be queued (e.g. a realtime signal) so you don't lose messages when the process is in the middle of handling a signal.
Blocking system call or file access. User process executes a system call (or reads/writes a file) which puts it to sleep. The kernel driver for the call maintains a queue of events, and dequeues events when they arrive and when a blocked waiter exists (this avoids losing events when the user process is unblocked).
Create a system call which configures a sigevent. On the kernel side, create a sigqueue to fire the relevant events.
An example I used in the past was to send signal to user space from hardware interrupt in kernel space.
KERNEL SPACE
You have to prepare siginfo and task_struct before sending a signal:
struct siginfo info;
struct task_struct *t;
info.si_signo = SIG_TEST;
info.si_code = SI_QUEUE;
info.si_int = 1234; // Any value you want to send
rcu_read_lock();
And also find the task with user space application PID. You have to send it from user-space to kernel-space through write or ioctl operations.
t = pid_task(find_pid_ns(pid, &init_pid_ns), PIDTYPE_PID);
Then you can send the signal.
rcu_read_unlock();
send_sig_info(SIG_TEST, &info, t);
I omitted here, but you must check the result of every operation.
The previous code prepare the signal structure and send it. Bear in mind that you need the application's PID. In my case the application from user space send its PID through ioctl driver procedure.
USER SPACE
You have to define and implement the callback function:
void signalFunction(int n, siginfo_t *info, void *unused) {
.....
.....
}
In main procedure:
struct sigaction sig;
sig.sa_sigaction = signalFunction; // Callback function
sig.sa_flags = SA_SIGINFO;
sigaction(SIG_TEST, &sig, NULL);
I hope it helps.

Duplex named pipe hangs on a certain write

I have a C++ pipe server app and a C# pipe client app communicating via Windows named pipe (duplex, message mode, wait/blocking in separate read thread).
It all works fine (both sending and receiving data via the pipe) until I try and write to the pipe from the client in response to a forms 'textchanged' event. When I do this, the client hangs on the pipe write call (or flush call if autoflush is off). Breaking into the server app reveals it's also waiting on the pipe ReadFile call and not returning.
I tried running the client write on another thread -- same result.
Suspect some sort of deadlock or race condition but can't see where... don't think I'm writing to the pipe simultaneously.
Update1: tried pipes in byte mode instead of message mode - same lockup.
Update2: Strangely, if (and only if) I pump lots of data from the server to the client, it cures the lockup!?
Server code:
DWORD ReadMsg(char* aBuff, int aBuffLen, int& aBytesRead)
{
DWORD byteCount;
if (ReadFile(mPipe, aBuff, aBuffLen, &byteCount, NULL))
{
aBytesRead = (int)byteCount;
aBuff[byteCount] = 0;
return ERROR_SUCCESS;
}
return GetLastError();
}
DWORD SendMsg(const char* aBuff, unsigned int aBuffLen)
{
DWORD byteCount;
if (WriteFile(mPipe, aBuff, aBuffLen, &byteCount, NULL))
{
return ERROR_SUCCESS;
}
mClientConnected = false;
return GetLastError();
}
DWORD CommsThread()
{
while (1)
{
std::string fullPipeName = std::string("\\\\.\\pipe\\") + mPipeName;
mPipe = CreateNamedPipeA(fullPipeName.c_str(),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
KTxBuffSize, // output buffer size
KRxBuffSize, // input buffer size
5000, // client time-out ms
NULL); // no security attribute
if (mPipe == INVALID_HANDLE_VALUE)
return 1;
mClientConnected = ConnectNamedPipe(mPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (!mClientConnected)
return 1;
char rxBuff[KRxBuffSize+1];
DWORD error=0;
while (mClientConnected)
{
Sleep(1);
int bytesRead = 0;
error = ReadMsg(rxBuff, KRxBuffSize, bytesRead);
if (error == ERROR_SUCCESS)
{
rxBuff[bytesRead] = 0; // terminate string.
if (mMsgCallback && bytesRead>0)
mMsgCallback(rxBuff, bytesRead, mCallbackContext);
}
else
{
mClientConnected = false;
}
}
Close();
Sleep(1000);
}
return 0;
}
client code:
public void Start(string aPipeName)
{
mPipeName = aPipeName;
mPipeStream = new NamedPipeClientStream(".", mPipeName, PipeDirection.InOut, PipeOptions.None);
Console.Write("Attempting to connect to pipe...");
mPipeStream.Connect();
Console.WriteLine("Connected to pipe '{0}' ({1} server instances open)", mPipeName, mPipeStream.NumberOfServerInstances);
mPipeStream.ReadMode = PipeTransmissionMode.Message;
mPipeWriter = new StreamWriter(mPipeStream);
mPipeWriter.AutoFlush = true;
mReadThread = new Thread(new ThreadStart(ReadThread));
mReadThread.IsBackground = true;
mReadThread.Start();
if (mConnectionEventCallback != null)
{
mConnectionEventCallback(true);
}
}
private void ReadThread()
{
byte[] buffer = new byte[1024 * 400];
while (true)
{
int len = 0;
do
{
len += mPipeStream.Read(buffer, len, buffer.Length);
} while (len>0 && !mPipeStream.IsMessageComplete);
if (len==0)
{
OnPipeBroken();
return;
}
if (mMessageCallback != null)
{
mMessageCallback(buffer, len);
}
Thread.Sleep(1);
}
}
public void Write(string aMsg)
{
try
{
mPipeWriter.Write(aMsg);
mPipeWriter.Flush();
}
catch (Exception)
{
OnPipeBroken();
}
}
If you are using separate threads you will be unable to read from the pipe at the same time you write to it. For example, if you are doing a blocking read from the pipe then a subsequent blocking write (from a different thread) then the write call will wait/block until the read call has completed and in many cases if this is unexpected behavior your program will become deadlocked.
I have not tested overlapped I/O, but it MAY be able to resolve this issue. However, if you are determined to use synchronous calls then the following models below may help you to solve the problem.
Master/Slave
You could implement a master/slave model in which the client or the server is the master and the other end only responds which is generally what you will find the MSDN examples to be.
In some cases you may find this problematic in the event the slave periodically needs to send data to the master. You must either use an external signaling mechanism (outside of the pipe) or have the master periodically query/poll the slave or you can swap the roles where the client is the master and the server is the slave.
Writer/Reader
You could use a writer/reader model where you use two different pipes. However, you must associate those two pipes somehow if you have multiple clients since each pipe will have a different handle. You could do this by having the client send a unique identifier value on connection to each pipe which would then let the server associate the two pipes. This number could be the current system time or even a unique identifier that is global or local.
Threads
If you are determined to use the synchronous API you can use threads with the master/slave model if you do not want to be blocked while waiting for a message on the slave side. You will however want to lock the reader after it reads a message (or encounters the end of a series of message) then write the response (as the slave should) and finally unlock the reader. You can lock and unlock the reader using locking mechanisms that put the thread to sleep as these would be most efficient.
Security Problem With TCP
The loss going with TCP instead of named pipes is also the biggest possible problem. A TCP stream does not contain any security natively. So if security is a concern you will have to implement that and you have the possibility of creating a security hole since you would have to handle authentication yourself. The named pipe can provide security if you properly set the parameters. Also, to note again more clearly: security is no simple matter and generally you will want to use existing facilities that have been designed to provide it.
I think you may be running into problems with named pipes message mode. In this mode, each write to the kernel pipe handle constitutes a message. This doesn't necessarily correspond with what your application regards a Message to be, and a message may be bigger than your read buffer.
This means that your pipe reading code needs two loops, the inner reading until the current [named pipe] message has been completely received, and the outer looping until your [application level] message has been received.
Your C# client code does have a correct inner loop, reading again if IsMessageComplete is false:
do
{
len += mPipeStream.Read(buffer, len, buffer.Length);
} while (len>0 && !mPipeStream.IsMessageComplete);
Your C++ server code doesn't have such a loop - the equivalent at the Win32 API level is testing for the return code ERROR_MORE_DATA.
My guess is that somehow this is leading to the client waiting for the server to read on one pipe instance, whilst the server is waiting for the client to write on another pipe instance.
It seems to me that what you are trying to do will rather not work as expected.
Some time ago I was trying to do something that looked like your code and got similar results, the pipe just hanged
and it was difficult to establish what had gone wrong.
I would rather suggest to use client in very simple way:
CreateFile
Write request
Read answer
Close pipe.
If you want to have two way communication with clients which are also able to receive unrequested data from server you should
rather implement two servers. This was the workaround I used: here you can find sources.

Resources