stm32-usbd icon indicating copy to clipboard operation
stm32-usbd copied to clipboard

a single hid class didn't work

Open bitbegin opened this issue 5 years ago • 2 comments

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?

bitbegin avatar Jun 10 '19 15:06 bitbegin

I'm not sure I can help you with this. Try reproducing real device's descriptors and endpoint structure in your device.

Disasm avatar Jun 10 '19 19:06 Disasm

@bitbegin Did you manage to solve the problem?

Disasm avatar Oct 01 '20 22:10 Disasm