C USB协议实现的程序设计

C USB协议实现的程序设计,c,usb,libusb,C,Usb,Libusb,我有一个USB协议,我想实现,但我有点迷失在最好的方式做它 USB协议涉及来回交换数据和确认数据包,如下所示: Device: data Host: ACK Host: reply Device: ACK 但有时,数据包可能以异步方式传入,如下所示: Device: data #1 Device: data #2 Host: ACK #1 ... [ Main thread ][ Processing thread ] | Read from pipe ||

我有一个USB协议,我想实现,但我有点迷失在最好的方式做它

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

Device: data
Host: ACK
Host: reply
Device: ACK
但有时,数据包可能以异步方式传入,如下所示:

Device: data #1
Device: data #2
Host: ACK #1
...
[ Main thread    ][ Processing thread   ]
| Read from pipe ||                     |
|                || USB packet comes in |
|                || Send ACK packet     |
|                || Extract data        |
|                || Write data to pipe  |
| Read succeeds  ||                     |
| Return data    ||                     |
我想有一个API,将抽象出USB的所有细节,让程序只与实际数据工作,而不必担心数据包头或确认数据包或类似的事情。理想情况下,将有一个
write\u to\u device
功能,该功能会一直阻止直到设备确认数据包,一个
read\u from\u device
功能会一直阻止直到接收到数据包,一个
is\u data\u available
功能会立即返回队列上是否有数据

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

当数据包进入时,处理线程将发送一个ACK数据包,然后提取原始数据并将其写入管道。
read\u from\u device
函数(从主线程调用)将简单地从此管道读取数据,并自然阻塞,直到有数据为止。但是如果我使用这个方案,我就没有一个干净的方法来实现
is\u data\u available
函数-如果不读取数据,就无法检查管道中是否有数据

大概是这样的:

Device: data #1
Device: data #2
Host: ACK #1
...
[ Main thread    ][ Processing thread   ]
| Read from pipe ||                     |
|                || USB packet comes in |
|                || Send ACK packet     |
|                || Extract data        |
|                || Write data to pipe  |
| Read succeeds  ||                     |
| Return data    ||                     |
真正的问题是实现一个
向设备写入
功能

[ 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\u wait
(来自信号量函数系列,
sem\u*
)是您想要查看的内容

然后,write函数将数据写入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);
}
#包括
#包括
#包括
类型定义结构{
int将_写入_管道,从_管道读取_;
sem_t*write_sem;
}烟斗;
MyPipe*pipe_new(){
int-fds[2];
if(管道(fds)){
//在这里处理错误
返回NULL;
}
sem_t*sem=NULL;
if(sem_init(sem,0,0)){
//在这里处理错误
关闭(fds[0]);
关闭(fds[1]);
返回NULL;
}
MyPipe*result=malloc(sizeof(MyPipe));
结果->将_写入_管道=fds[1];
结果->从管道读取管道=fds[0];
结果->写入\u sem=sem;
返回结果;
}
void pipe_write(MyPipe*pipe,常量unsigned char*buf,常量int size){
写入(管道->写入管道和大小,大小(int));
写入(管道->写入管道,buf,尺寸);
sem\u wait(管道->写入sem);
}
处理线程将知道
MyPipe
实例,并在需要时从
read\u from\u pipe
读取。它首先读取主线程写入管道的字节数,然后读取任意块中的所有字节。所有数据发送到设备并由其确认后,它可以
sem\u post
信号量,以便
pipe\u write
返回

或者,可以添加另一个信号量,该信号量
pipe\u write
posts使处理线程仅在实际有数据可用时读取数据


免责声明:未测试代码,仅检查代码是否编译。需要使用
-pthread
构建,以使
sem.*
可用。

libusb
或多或少已经完成了您描述的所有操作。每个USB端点都可以看作是一个数据报套接字,您可以使用
libusb\u中断\u传输
(或
libusb\u控制\u传输
)对其进行写入。在这些函数中,传递一个作为输入或输出的数组。不需要发送确认等。输入或输出的方向取决于端点的配置


还有一种方法,您可以启动传输并将一些文件描述符添加到主
select
poll
循环中,最终在I/O完成时得到回调。

这没有意义,USB已经实现了这一点。请注意本说明中的ACK握手数据包:ACK在应用程序协议级别是必需的。我正在使用的USB设备对所有内容都使用批量传输。ACK用于位于USB协议之上的应用程序协议。我已经在处理线程中使用了异步API。啊,我没有想到使用信号量作为一种信令方法。谢谢