Skip to content

Commit b31121c

Browse files
committed
Windows reference code: associated interfaces
1 parent c0bbe3b commit b31121c

File tree

4 files changed

+82
-34
lines changed

4 files changed

+82
-34
lines changed

reference/windows/USB/tests.cpp

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,22 @@ void tests::run() {
4343
void tests::test_current_device() {
4444
try {
4545
std::cout << "Found test device" << std::endl;
46+
47+
is_composite = test_device->product_id() == 0xcea0;
48+
loopback_intf = is_composite ? 3 : 0;
49+
loopback_ep_out = is_composite ? 1 : 1;
50+
loopback_ep_in = is_composite ? 2 : 2;
51+
4652
test_device->open();
47-
test_device->claim_interface(0);
53+
test_device->claim_interface(loopback_intf);
4854

4955
test_control_transfers();
5056
test_bulk_transfers();
5157
test_speed();
5258

53-
test_device->release_interface(0);
59+
test_device->release_interface(loopback_intf);
5460
test_device->close();
61+
5562
std::cout << "Test completed" << std::endl;
5663
}
5764
catch (const std::exception& e) {
@@ -65,7 +72,7 @@ void tests::test_control_transfers() {
6572
usb_request_type::type_vendor, usb_request_type::recipient_interface);
6673
request_set_value_no_data.bRequest = 0x01;
6774
request_set_value_no_data.wValue = 0x9a41;
68-
request_set_value_no_data.wIndex = 0; // interface number
75+
request_set_value_no_data.wIndex = loopback_intf;
6976
request_set_value_no_data.wLength = 0;
7077
test_device->control_transfer(request_set_value_no_data);
7178

@@ -74,7 +81,7 @@ void tests::test_control_transfers() {
7481
usb_request_type::type_vendor, usb_request_type::recipient_interface);
7582
request_get_data.bRequest = 0x03;
7683
request_get_data.wValue = 0;
77-
request_get_data.wIndex = 0; // interface number
84+
request_get_data.wIndex = loopback_intf;
7885
request_get_data.wLength = 4;
7986
auto data = test_device->control_transfer_in(request_get_data);
8087
std::vector<uint8_t> expected_data{ 0x41, 0x9a, 0x00, 0x00 };
@@ -86,14 +93,37 @@ void tests::test_control_transfers() {
8693
usb_request_type::type_vendor, usb_request_type::recipient_interface);
8794
request_set_value_data.bRequest = 0x02;
8895
request_set_value_data.wValue = 0;
89-
request_set_value_data.wIndex = 0; // interface number
96+
request_set_value_data.wIndex = loopback_intf;
9097
request_set_value_data.wLength = static_cast<uint16_t>(sent_value.size());
9198
test_device->control_transfer_out(request_set_value_data, sent_value);
9299

93100
data = test_device->control_transfer_in(request_get_data);
94101
assert_equals(sent_value, data);
102+
103+
test_control_transfer_intf(loopback_intf);
104+
105+
if (is_composite) {
106+
test_device->claim_interface(2);
107+
test_control_transfer_intf(2);
108+
test_device->release_interface(2);
109+
}
95110
}
96111

112+
void tests::test_control_transfer_intf(int intf_num) {
113+
usb_control_request request_get_intf_num = { 0 };
114+
request_get_intf_num.bmRequestType = usb_control_request::request_type(usb_request_type::direction_in,
115+
usb_request_type::type_vendor, usb_request_type::recipient_interface);
116+
request_get_intf_num.bRequest = 0x05;
117+
request_get_intf_num.wValue = 0;
118+
request_get_intf_num.wIndex = intf_num;
119+
request_get_intf_num.wLength = 1;
120+
auto data = test_device->control_transfer_in(request_get_intf_num);
121+
122+
std::vector<uint8_t> expected_data{ (uint8_t)intf_num };
123+
assert_equals(expected_data, data);
124+
}
125+
126+
97127
void tests::test_bulk_transfers() {
98128
test_loopback(12);
99129
test_loopback(130);
@@ -113,7 +143,7 @@ void tests::test_loopback(int num_bytes) {
113143
std::thread reader([this, &rx_data, num_bytes]() {
114144
size_t bytes_read = 0;
115145
while (bytes_read < num_bytes) {
116-
auto data = test_device->transfer_in(2);
146+
auto data = test_device->transfer_in(loopback_ep_in);
117147
rx_data.insert(rx_data.end(), data.begin(), data.end());
118148
bytes_read += data.size();
119149
}
@@ -125,7 +155,7 @@ void tests::test_loopback(int num_bytes) {
125155
while (bytes_written < num_bytes) {
126156
int size = std::min(chunk_size, num_bytes - bytes_written);
127157
std::vector<uint8_t> chunk = {random_data.begin() + bytes_written, random_data.begin() + bytes_written + size};
128-
test_device->transfer_out(1, chunk);
158+
test_device->transfer_out(loopback_ep_out, chunk);
129159
bytes_written += size;
130160
}
131161

@@ -169,5 +199,6 @@ std::vector<uint8_t> tests::random_bytes(int num) {
169199
}
170200

171201
bool tests::is_test_device(usb_device_ptr device) {
172-
return device->vendor_id() == 0xcafe && device->product_id() == 0xceaf;
202+
return device->vendor_id() == 0xcafe
203+
&& (device->product_id() == 0xceaf || device->product_id() == 0xcea0);
173204
}

reference/windows/USB/tests.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class tests {
2121
void test_control_transfers();
2222
void test_bulk_transfers();
2323
void test_speed();
24+
void test_control_transfer_intf(int intf_num);
2425

2526
void test_loopback(int num_bytes);
2627

@@ -33,5 +34,9 @@ class tests {
3334

3435
usb_device_ptr test_device;
3536
usb_registry registry;
37+
bool is_composite;
38+
int loopback_intf;
39+
int loopback_ep_out;
40+
int loopback_ep_in;
3641
};
3742

reference/windows/USB/usb_device.cpp

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,15 @@ void usb_device::claim_interface(int interface_number) {
131131
interface_handle* intf_handle = get_interface_handle(interface_number);
132132
interface_handle* first_intf_handle = get_interface_handle(intf_handle->first_interface_num);
133133

134-
// open device if needed
134+
// both the device and the first interface must be opened for any interface belonging to the same function
135135
if (first_intf_handle->device_handle == nullptr) {
136136
auto device_path = get_interface_device_path(first_intf_handle->interface_num);
137137
if (device_path.empty())
138138
throw usb_error("failed to claim interface (function has no device path, might be missing WinUSB driver)");
139139

140140
std::wcerr << "opening device " << device_path << std::endl;
141141

142+
// open device
142143
first_intf_handle->device_handle = CreateFileW(device_path.c_str(),
143144
GENERIC_WRITE | GENERIC_READ,
144145
FILE_SHARE_WRITE | FILE_SHARE_READ,
@@ -147,21 +148,26 @@ void usb_device::claim_interface(int interface_number) {
147148
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
148149
nullptr);
149150
if (first_intf_handle->device_handle == INVALID_HANDLE_VALUE)
150-
usb_error::throw_error("cannot open USB device");
151+
usb_error::throw_error("failed to claim interface (cannot open USB device)");
151152

152-
// open interface
153-
if (!WinUsb_Initialize(first_intf_handle->device_handle, &first_intf_handle->intf_handle)) {
153+
// open first interface
154+
if (!WinUsb_Initialize(first_intf_handle->device_handle, &first_intf_handle->winusb_handle)) {
154155
auto err = GetLastError();
155156
CloseHandle(first_intf_handle->device_handle);
156157
first_intf_handle->device_handle = nullptr;
157-
throw usb_error("cannot open USB device (2)", err);
158+
throw usb_error("failed to claim interface (cannot open associated interface)", err);
158159
}
159160

160161
registry_->add_to_completion_port(first_intf_handle->device_handle);
161162
}
162163

164+
// open associated interface
165+
if (intf_handle != first_intf_handle) {
166+
if (!WinUsb_GetAssociatedInterface(first_intf_handle->winusb_handle, intf_handle->interface_num - first_intf_handle->interface_num - 1, &intf_handle->winusb_handle))
167+
throw usb_error("cannot open associated interface", GetLastError());
168+
}
169+
163170
first_intf_handle->device_open_count += 1;
164-
intf_handle->intf_handle = first_intf_handle->intf_handle;
165171
intf->set_claimed(true);
166172
}
167173

@@ -179,32 +185,38 @@ void usb_device::release_interface(int interface_number) {
179185
interface_handle* intf_handle = get_interface_handle(interface_number);
180186
interface_handle* first_intf_handle = get_interface_handle(intf_handle->first_interface_num);
181187

182-
intf_handle->intf_handle = nullptr;
183188
intf->set_claimed(false);
184189

190+
if (intf_handle != first_intf_handle) {
191+
// close assicated interface
192+
if (!WinUsb_Free(intf_handle->winusb_handle))
193+
throw usb_error("failed to release associated interface", GetLastError());
194+
intf_handle->winusb_handle = nullptr;
195+
}
196+
185197
// close device if needed
186198
first_intf_handle->device_open_count -= 1;
187199
if (first_intf_handle->device_open_count == 0) {
188-
WinUsb_Free(first_intf_handle->intf_handle);
200+
WinUsb_Free(first_intf_handle->winusb_handle);
189201
CloseHandle(first_intf_handle->device_handle);
190202
first_intf_handle->device_handle = nullptr;
191203
}
192204
}
193205

194206
std::vector<uint8_t> usb_device::transfer_in(int endpoint_number, int timeout) {
195207

196-
auto intf_handle = check_valid_endpoint(usb_direction::in, endpoint_number)->intf_handle;
208+
auto winusb_handle = check_valid_endpoint(usb_direction::in, endpoint_number)->winusb_handle;
197209
UCHAR endpoint_address = ep_address(usb_direction::in, endpoint_number);
198210
auto endpoint = get_endpoint_ptr(usb_direction::in, endpoint_number);
199211

200212
ULONG value = timeout;
201-
if (!WinUsb_SetPipePolicy(intf_handle, endpoint_address, PIPE_TRANSFER_TIMEOUT, sizeof(value), &value))
213+
if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, PIPE_TRANSFER_TIMEOUT, sizeof(value), &value))
202214
usb_error::throw_error("Failed to set endpoint timeout");
203215

204216
std::vector<uint8_t> data(endpoint->packet_size());
205217

206218
DWORD len = 0;
207-
if (!WinUsb_ReadPipe(intf_handle, endpoint_address, static_cast<PUCHAR>(data.data()), endpoint->packet_size(), &len, nullptr))
219+
if (!WinUsb_ReadPipe(winusb_handle, endpoint_address, static_cast<PUCHAR>(data.data()), endpoint->packet_size(), &len, nullptr))
208220
usb_error::throw_error("Cannot receive from USB endpoint");
209221

210222
data.resize(len);
@@ -215,15 +227,15 @@ void usb_device::transfer_out(int endpoint_number, const std::vector<uint8_t>& d
215227
if (len < 0 || len > data.size())
216228
len = static_cast<int>(data.size());
217229

218-
auto intf_handle = check_valid_endpoint(usb_direction::out, endpoint_number)->intf_handle;
230+
auto winusb_handle = check_valid_endpoint(usb_direction::out, endpoint_number)->winusb_handle;
219231
UCHAR endpoint_address = ep_address(usb_direction::out, endpoint_number);
220232

221233
ULONG value = timeout;
222-
if (!WinUsb_SetPipePolicy(intf_handle, endpoint_address, PIPE_TRANSFER_TIMEOUT, sizeof(value), &value))
234+
if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, PIPE_TRANSFER_TIMEOUT, sizeof(value), &value))
223235
usb_error::throw_error("Failed to set endpoint timeout");
224236

225237
DWORD tlen = 0;
226-
if (!WinUsb_WritePipe(intf_handle, endpoint_address, const_cast<PUCHAR>(data.data()), len, &tlen, nullptr))
238+
if (!WinUsb_WritePipe(winusb_handle, endpoint_address, const_cast<PUCHAR>(data.data()), len, &tlen, nullptr))
227239
usb_error::throw_error("Failed to transmit to USB endpoint");
228240
}
229241

@@ -232,10 +244,10 @@ int usb_device::control_transfer_core(const usb_control_request &request, uint8_
232244
if (!is_open())
233245
throw usb_error("USB device is not open");
234246

235-
auto handle = get_control_transfer_interface_handle(request)->intf_handle;
247+
auto winusb_handle = get_control_transfer_interface_handle(request)->winusb_handle;
236248

237249
ULONG value = timeout;
238-
if (!WinUsb_SetPipePolicy(handle, 0, PIPE_TRANSFER_TIMEOUT, sizeof(value), &value))
250+
if (!WinUsb_SetPipePolicy(winusb_handle, 0, PIPE_TRANSFER_TIMEOUT, sizeof(value), &value))
239251
usb_error::throw_error("Failed to set endpoint timeout");
240252

241253
WINUSB_SETUP_PACKET setup_packet = { 0 };
@@ -246,7 +258,7 @@ int usb_device::control_transfer_core(const usb_control_request &request, uint8_
246258
setup_packet.Length = request.wLength;
247259

248260
DWORD len = 0;
249-
if (!WinUsb_ControlTransfer(handle, setup_packet, data, request.wLength, &len, nullptr))
261+
if (!WinUsb_ControlTransfer(winusb_handle, setup_packet, data, request.wLength, &len, nullptr))
250262
usb_error::throw_error("Control transfer failed");
251263

252264
return len;
@@ -400,24 +412,24 @@ void usb_device::remove_completion_handler(OVERLAPPED* overlapped) {
400412
}
401413

402414
void usb_device::configure_for_async_io(usb_direction direction, int endpoint_number) {
403-
auto intf_handle = check_valid_endpoint(direction, endpoint_number)->intf_handle;
415+
auto winusb_handle = check_valid_endpoint(direction, endpoint_number)->winusb_handle;
404416
UCHAR endpoint_address = ep_address(direction, endpoint_number);
405417

406418
ULONG timeout = 0;
407-
if (!WinUsb_SetPipePolicy(intf_handle, endpoint_address, PIPE_TRANSFER_TIMEOUT, sizeof(timeout), &timeout))
419+
if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, PIPE_TRANSFER_TIMEOUT, sizeof(timeout), &timeout))
408420
usb_error::throw_error("Failed to set endpoint timeout");
409421

410422
UCHAR raw_io = 1;
411-
if (!WinUsb_SetPipePolicy(intf_handle, endpoint_address, RAW_IO, sizeof(raw_io), &raw_io))
423+
if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, RAW_IO, sizeof(raw_io), &raw_io))
412424
usb_error::throw_error("Failed to set endpoint for raw IO");
413425
}
414426

415427
void usb_device::submit_transfer_in(int endpoint_number, uint8_t* buffer, int buffer_len, OVERLAPPED* overlapped) {
416428

417-
auto intf_handle = check_valid_endpoint(usb_direction::in, endpoint_number)->intf_handle;
429+
auto winusb_handle = check_valid_endpoint(usb_direction::in, endpoint_number)->winusb_handle;
418430
UCHAR endpoint_address = ep_address(usb_direction::in, endpoint_number);
419431

420-
if (!WinUsb_ReadPipe(intf_handle, endpoint_address, buffer, buffer_len, nullptr, overlapped)) {
432+
if (!WinUsb_ReadPipe(winusb_handle, endpoint_address, buffer, buffer_len, nullptr, overlapped)) {
421433
DWORD err = GetLastError();
422434
if (err == ERROR_IO_PENDING)
423435
return;
@@ -427,10 +439,10 @@ void usb_device::submit_transfer_in(int endpoint_number, uint8_t* buffer, int bu
427439

428440
void usb_device::submit_transfer_out(int endpoint_number, uint8_t* data, int data_len, OVERLAPPED* overlapped) {
429441

430-
auto intf_handle = check_valid_endpoint(usb_direction::out, endpoint_number)->intf_handle;
442+
auto winusb_handle = check_valid_endpoint(usb_direction::out, endpoint_number)->winusb_handle;
431443
UCHAR endpoint_address = ep_address(usb_direction::out, endpoint_number);
432444

433-
if (!WinUsb_WritePipe(intf_handle, endpoint_address, data, data_len, nullptr, overlapped)) {
445+
if (!WinUsb_WritePipe(winusb_handle, endpoint_address, data, data_len, nullptr, overlapped)) {
434446
DWORD err = GetLastError();
435447
if (err == ERROR_IO_PENDING)
436448
return;
@@ -543,4 +555,4 @@ int usb_device::extract_interface_number(const std::vector<std::wstring>& hardwa
543555

544556
usb_device::interface_handle::interface_handle(int intf_num, int first_num, std::wstring&& path)
545557
: interface_num(intf_num), first_interface_num(first_num),
546-
device_handle(nullptr), intf_handle(nullptr), device_open_count(0) { }
558+
device_handle(nullptr), winusb_handle(nullptr), device_open_count(0) { }

reference/windows/USB/usb_device.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ class usb_device {
242242
int interface_num;
243243
int first_interface_num;
244244
HANDLE device_handle;
245-
WINUSB_INTERFACE_HANDLE intf_handle;
245+
WINUSB_INTERFACE_HANDLE winusb_handle;
246246
int device_open_count;
247247

248248
interface_handle(int intf_num, int first_num, std::wstring&& path);

0 commit comments

Comments
 (0)