USB协议实现的程序设计

我有一个USB协议,我想实现,但我有点失去了最好的方法.

USB协议涉及来回交换数据和确认包,如下所示:

Device: data
Host: ACK
Host: reply
Device: ACK

但有时候,数据包可能会像这样异步进入:

Device: data #1
Device: data #2
Host: ACK #1
...

我希望有一个API可以抽象出USB的所有细节,并让程序只使用实际数据,而不必担心数据包标头或确认数据包或类似的东西.理想情况下,将有一个write_to_device函数阻塞,直到设备确认该数据包,read_from_device将阻塞直到收到数据包,is_data_available函数立即返回队列中是否有任何数据.

我正在考虑运行一个处理USB事件的独立线程.该线程将处理所有数据封装和确认.

当数据包进入时,处理线程将发送ACK数据包,然后提取原始数据并将其写入管道. read_from_device函数(从主线程调用)将简单地从该管道读取并自然地阻塞直到有数据.但是,如果我使用这个方案,我将无法实现is_data_available函数的干净方法 – 没有办法检查管道中是否有数据而没有读取它.

像这样的东西:

[ Main thread    ][ Processing thread   ]
| Read from pipe ||                     |
|                || USB packet comes in |
|                || Send ACK packet     |
|                || Extract data        |
|                || Write data to pipe  |
| Read succeeds  ||                     |
| Return data    ||                     |

真正的问题是实现write_to_device函数.

[ Main thread                ][ Processing thread      ]
| Somehow signal write       ||                        |
| Wait for write to complete ||                        |
|                            || Send the data          |
|                            || Wait for ACK packet    |
|                            || Somehow signal that write completed
| Return                     ||                        |

如何干净地实现发送数据包的方式,等待确认数据包然后返回?

最佳答案 我建议你创建一个自定义管道类或结构或东西.为此,您可以定义一个write方法,该方法还可以等待信号量触发.如果您使用的是linux,那么sem_wait(来自信号量函数系列,sem_ *)就是您想要查看的内容.

然后写函数将数据写入FIFO并等待标记信号量.但是,写入线程如何知道所有数据何时通过您想要发送的管道到达?如果线程必须阻塞读取,则此处可能会出现问题.

因此,我建议您在主线程到处理线程的管道内使用微格式,发送一个整数大小,用于定义要写入的字节数.然后,处理线程将读取该字节数,将其转发给设备,并在标记所有数据后立即标记信号量. write函数将等待信号量,因此非忙碌阻塞直到处理线程结束.

这是自定义管道结构和概述的写入函数的起草方式:

#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>

typedef struct {
    int write_to_pipe, read_from_pipe;
    sem_t *write_sem;
} MyPipe;

MyPipe *pipe_new() {
    int fds[2];
    if (pipe(fds)) {
        // handle error here
        return NULL;
    }

    sem_t *sem = NULL;
    if (sem_init(sem, 0, 0)) {
        // handle error here
        close(fds[0]);
        close(fds[1]);
        return NULL;
    }

    MyPipe *result = malloc(sizeof(MyPipe));
    result->write_to_pipe = fds[1];
    result->read_from_pipe = fds[0];
    result->write_sem = sem;
    return result;
}

void pipe_write(MyPipe *pipe, const unsigned char *buf, const int size) {
    write(pipe->write_to_pipe, &size, sizeof(int));
    write(pipe->write_to_pipe, buf, size);
    sem_wait(pipe->write_sem);
}

处理线程将知道MyPipe实例并随时从read_from_pipe读取.它首先读取主线程写入管道的字节数,然后读取所有字节的任意块.将所有数据发送到设备并由它确认后,它可以sem_post信号量,以便pipe_write返回.

可选地,可以添加另一个信号量,其中pipe_write发布以使处理线程仅在实际存在可用数据时读取数据.

免责声明:尚未测试代码,只检查它编译.需要使用-pthread构建,以使sem_ *可用.

点赞