Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example SystemControlReport code #51

Open
m5p3nc3r opened this issue Jan 4, 2023 · 11 comments
Open

Example SystemControlReport code #51

m5p3nc3r opened this issue Jan 4, 2023 · 11 comments

Comments

@m5p3nc3r
Copy link

m5p3nc3r commented Jan 4, 2023

Hi Guys

I would like to write a small application using SystemControlReport's to put a PC to sleep or wake it up. Is there any example code that shows how this could be done?

My initial attempts to use SystemControlReport's does not seem to do anything (tested on Mac, Windows 10 and Linux!)

The base code has been modified from the twitching usb mouse example (which is working fine).

pseudo code is:

let usb_hid = HIDClass::new(bus_ref, SystemControlReport::desc(), 60);

let usb_dev = UsbDeviceBuilder::new(bus_ref, UsbVidPid(0x16c0, 0x27da))
    .manufacturer("My_Comany")
    .product("My_Product")
    .serial_number("0000")
    .device_class(0)
    .build();

let report = SystemControlReport {
    usage_id: SystemControlKey::Sleep.into()
}

critical_section::with(|_| unsafe {
    USB_HID.as_mut().map(|hid| hid.push_input(&report))
}).unwrap()

The sending of the sleep is triggered by a GPIO pin, and I can see that the report is being sent, with the return value being 1, which I believe means that one byte was successfully sent (which is the size of the SystemControlKey event.

Any ideas? Or any pointers to functional example code?

@m5p3nc3r
Copy link
Author

m5p3nc3r commented Jan 4, 2023

Hi Guys

Thought I would let you know that I found the solution to my problems. Down to user error as these things mostly are.

I changed the way I construct the usage_id by doing something like this:

[gen_hid_descriptor(
    (collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = SYSTEM_CONTROL) = {
        (usage_min = 0x81, usage_max = 0x8f, logical_min = 1) = {
            #[item_settings data,array,absolute,not_null] usage_id=input;
        };
    }
)]
#[derive(Default)]
pub struct SystemControlReport {
    pub usage_id: u16,
}

impl SystemControlReport {
    pub fn new() -> Self {
        Self {
            ..Default::default()
        }
    }

    pub fn set(&mut self, control: SystemControlKey) {
        // Convert the control key into a u8 value
        let val: u8 = control.into();
        let min: u8 = SystemControlKey::PowerDown.into();
        self.usage_id |= 1 << (val - min);
    }
}

This sets the correct bit in the usage_id through the set function, the only difference from the upstream code is that I am using a u16 for usage_id and you have a u8. I believe there are 15 control codes (0x81 to 0x8f), so you might want to consider changing upstream.

Final example pseudo code would be

// Generate an empty report
let mut report = SystemControlReport::new();
// Set the controls needed
report.set(SystemControlKey::Sleep);
// Send the report
critical_section::with(|_| unsafe {
    USB_HID.as_mut().map(|hid| hid.push_input(&report))
}).unwrap()

@m5p3nc3r
Copy link
Author

m5p3nc3r commented Jan 6, 2023

Hi Community

I have had limited success with this so far. I am reliably able to set the host into sleep mode, but I am not able to wake any up from sleep. Looking further, all devices return false when querying usb_dev.remote_wakeup_enabled().

A question to you - is this something that needs to be configured host side (such as here or here, or do I need to configure something additional device side?

I ask because when on Linux (Fedora 37), I cannot see a wakeup node under /sys/bus/usb/devices/<my-device>/power/wakeup

Any help would be greatly appreciated.

@haata
Copy link
Collaborator

haata commented Jan 6, 2023

Remote wakeup requires support of the mcu you are working with to function correctly.

Which MCU are you using? I've implemented this in the past for both mk20dx256 (in C) and in rust for atsam4-hal.
https://github.com/atsam-rs/atsam4-hal/blob/master/src/udp/bus.rs#L180

@m5p3nc3r
Copy link
Author

m5p3nc3r commented Jan 6, 2023

Thanks for the response.

I am using the Seeed studio xiao rp2040, with the rp_pico as the HAL.

Are you aware of support for the rp2040?

@m5p3nc3r
Copy link
Author

m5p3nc3r commented Jan 6, 2023

Ok, so I updated the rp-pico hal to the latest version, which pulls in the latest rp2040-hal - and on first attempt it bought my dev machine out of sleep (Linux/Fedora 37). Will try now on the other systems.

Thanks for your help @haata

@m5p3nc3r
Copy link
Author

m5p3nc3r commented Jan 6, 2023

Think I spoke too soon... Needs more investigation.

Is there a proper time to call remote_wakeup() - is there an example solution available online?

@haata
Copy link
Collaborator

haata commented Jan 7, 2023

I did some quick poking around and I think this is what you want https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/usb.rs#L474

In the rp2040 datasheet (https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf page 401), you can find USB: SIE_CTRL Register.

That's the special register to initiate the remote wakeup. You only want to call this if the device is in UsbDeviceState::Suspend (similar to what I'm doing here https://github.com/kiibohd/kiibohd-firmware/blob/main/common/atsam4s/src/lib.rs#L407).

@m5p3nc3r
Copy link
Author

m5p3nc3r commented Jan 7, 2023

Thanks for the pointers again @haata.

Any ideas why usb_dev.remote_wakeup_enabled(); is always returning false - no matter what system I plug my device into?

@m5p3nc3r
Copy link
Author

m5p3nc3r commented Jan 9, 2023

That was the missing piece of the puzzle. I is working now thanks. Your help with this has been amazing.

One interesting observation though, with respect to remote_wakeup_enabled,

OS remote_wakeup_enabled returns remote wakeup works?
Linux Always false Yes
MacOS Always true Yes
Windows 10 True when in sleep mode Yes

Is this to be expected?

@haata
Copy link
Collaborator

haata commented Jan 9, 2023

that function just returns

    /// Gets whether host remote wakeup has been enabled by the host.
    pub fn remote_wakeup_enabled(&self) -> bool {
        self.remote_wakeup_enabled
    }

Which is only set

It looks like the USB stack for Linux isn't setting the feature for some reason (probably a bug in Linux that should get fixed) and Windows is probably doing what it's supposed to be doing. But this may require some studying of the USB spec to find the correct answer (or it may actually not be specified...in which case both MacOS and Windows 10 might be correct).

I took a brief look at http://janaxelson.com/usbc.htm and couldn't find what the behaviour is supposed to be (great book).

You're probably good, but I would recommend running this tool on Windows 10 https://www.usb.org/compliancetools#anchor_usb3cv (yes you want to run xhci as you likely have a usb 3.x chipset). This tool will also catch bad hid descriptors among other things (you don't need a 100% pass, but it's a good way to figure out where you stand). Sadly this tool only works from Windows machines (VMs don't work well) so I keep an old laptop around just for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants