stm32-usbd
stm32-usbd copied to clipboard
a single hid class didn't work
hid.rs:
use core::cmp::min;
use usb_device::Result;
use usb_device::bus::{InterfaceNumber, StringIndex, UsbBus, UsbBusAllocator};
use usb_device::class::{ControlIn, ControlOut, UsbClass};
use usb_device::control;
use usb_device::control::{Recipient, RequestType};
use usb_device::descriptor::DescriptorWriter;
use usb_device::endpoint::{EndpointAddress, EndpointIn, EndpointOut};
use usb_device::UsbError;
//use cortex_m_semihosting::hprintln;
pub const USB_CLASS_HID: u8 = 0x03;
const REPORT_DESCRIPTOR: &[u8] = &[
0x06, 0xD0, 0xF1, // Usage Page (Reserved 0xF1D0)
0x09, 0x01, // Usage (0x01)
0xA1, 0x01, // Collection (Application)
0x09, 0x20, // Usage (0x20)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x40, // Report Count (64)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x21, // Usage (0x21)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x40, // Report Count (64)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
];
pub struct HidClass<'a, B: UsbBus> {
intf: InterfaceNumber,
read_ep: EndpointOut<'a, B>,
write_ep: EndpointIn<'a, B>,
buf: [u8; 65],
len: usize,
}
impl<B: UsbBus> HidClass<'_, B> {
pub fn new(alloc: &UsbBusAllocator<B>) -> HidClass<'_, B> {
HidClass {
intf: alloc.interface(),
read_ep: alloc.interrupt(8, 10),
write_ep: alloc.interrupt(8, 10),
buf: [0; 65],
len: 0,
}
}
pub fn write(&mut self, data: &[u8]) -> Result<usize> {
match self.write_ep.write(data) {
Ok(count) => Ok(count),
Err(UsbError::WouldBlock) => Ok(0),
e => e,
}
}
pub fn read(&mut self, data: &mut [u8]) -> Result<usize> {
// Terrible buffering implementation for brevity's sake
if self.len == 0 {
self.len = match self.read_ep.read(&mut self.buf) {
Ok(0) | Err(UsbError::WouldBlock) => return Ok(0),
Ok(count) => count,
e => return e,
};
}
let count = min(data.len(), self.len);
&data[..count].copy_from_slice(&self.buf[0..count]);
self.buf.rotate_left(count);
self.len -= count;
Ok(count)
}
}
impl<B: UsbBus> UsbClass<B> for HidClass<'_, B> {
fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
writer.interface(
self.intf,
3, //USB_CLASS_HID,
0,
0)?;
let descriptor_len = REPORT_DESCRIPTOR.len();
//hprintln!("report len: {}", descriptor_len).unwrap();
let descriptor_len = (descriptor_len as u16).to_le_bytes();
writer.write(
0x21,
&[0x10, 0x01, 0x21, 0x01, 0x22, descriptor_len[0], descriptor_len[1]]
)?;
writer.endpoint(&self.write_ep)?;
writer.endpoint(&self.read_ep)?;
//hprintln!("get_configuration_descriptors!").unwrap();
Ok(())
}
fn endpoint_in_complete(&mut self, _addr: EndpointAddress) {
//hprintln!("endpoint_in_complete!").unwrap();
}
fn control_in(&mut self, xfer: ControlIn<B>) {
let req = xfer.request();
match (req.request_type, req.recipient) {
(RequestType::Standard, Recipient::Interface) => {
//hprintln!("control_in!").unwrap();
if req.request == control::Request::GET_DESCRIPTOR {
let (dtype, index) = req.descriptor_type_index();
if dtype == 0x22 && index == 0 {
let descriptor = REPORT_DESCRIPTOR;
xfer.accept_with(descriptor).ok();
}
}
}
_ => {}
}
}
}
main.rs:
#![no_std]
#![no_main]
extern crate panic_semihosting; // logs messages to the host stderr; requires a debugger
use cortex_m::asm::delay;
use cortex_m_rt::entry;
use stm32f1xx_hal::{prelude::*, stm32};
use usb_device::prelude::*;
use stm32_usbd::UsbBus;
//use cortex_m_semihosting::hprintln;
mod hid;
mod vendor;
const VID: u16 = 0x1122;
const PID: u16 = 0x3344;
#[entry]
fn main() -> ! {
let dp = stm32::Peripherals::take().unwrap();
let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();
let clocks = rcc
.cfgr
.use_hse(8.mhz())
.sysclk(48.mhz())
.pclk1(24.mhz())
.freeze(&mut flash.acr);
assert!(clocks.usbclk_valid());
let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
// BluePill board has a pull-up resistor on the D+ line.
// Pull the D+ pin down to send a RESET condition to the USB bus.
let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh);
usb_dp.set_low();
delay(clocks.sysclk().0 / 100);
let usb_dm = gpioa.pa11;
let usb_dp = usb_dp.into_floating_input(&mut gpioa.crh);
let usb_bus = UsbBus::new(dp.USB, (usb_dm, usb_dp));
let mut hid = hid::HidClass::new(&usb_bus);
//let mut vendor = vendor::HidClass::new(&usb_bus);
// vid/pid: http://pid.codes/1209/CC1D/
let mut usb_dev = UsbDeviceBuilder::new(
&usb_bus,
UsbVidPid(VID, PID),
)
.manufacturer("bitbegin")
.product("hid")
.serial_number("12345678")
//.device_class(3)
.build();
//hprintln!("reset!").unwrap();
//usb_dev.force_reset().expect("reset failed");
let mut buf = [0u8; 65];
loop {
if !usb_dev.poll(&mut [&mut hid, &mut vendor]) {
continue;
}
match hid.read(&mut buf) {
Ok(count) if count > 0 => {
// Echo back in upper case
hid.write(&buf[0..count]).ok();
},
_ => { },
}
}
}
this will result in c0000011
- "xact error".
if add device_class(0xff)
to UsbDeviceBuilder
, it will be detected by Windows. but i want to use hidapi, so how to configure hid-class?
I'm not sure I can help you with this. Try reproducing real device's descriptors and endpoint structure in your device.
@bitbegin Did you manage to solve the problem?