embassy_usb/class/
web_usb.rs1use core::mem::MaybeUninit;
6
7use crate::control::{InResponse, Recipient, Request, RequestType};
8use crate::descriptor::capability_type;
9use crate::driver::Driver;
10use crate::{Builder, Handler};
11
12const USB_CLASS_VENDOR: u8 = 0xff;
13const USB_SUBCLASS_NONE: u8 = 0x00;
14const USB_PROTOCOL_NONE: u8 = 0x00;
15
16const WEB_USB_REQUEST_GET_URL: u16 = 0x02;
17const WEB_USB_DESCRIPTOR_TYPE_URL: u8 = 0x03;
18
19pub struct Url<'d>(&'d str, u8);
23
24impl<'d> Url<'d> {
25 pub fn new(url: &'d str) -> Self {
27 let (prefix, stripped_url) = if let Some(stripped) = url.strip_prefix("https://") {
28 (1, stripped)
29 } else if let Some(stripped) = url.strip_prefix("http://") {
30 (0, stripped)
31 } else {
32 (255, url)
33 };
34 assert!(
35 stripped_url.len() <= 252,
36 "URL too long. ({} bytes). Maximum length is 252 bytes.",
37 stripped_url.len()
38 );
39 Self(stripped_url, prefix)
40 }
41
42 fn as_bytes(&self) -> &[u8] {
43 self.0.as_bytes()
44 }
45
46 fn scheme(&self) -> u8 {
47 self.1
48 }
49}
50
51pub struct Config<'d> {
53 pub max_packet_size: u16,
60 pub landing_url: Option<Url<'d>>,
64 pub vendor_code: u8,
68}
69
70struct Control<'d> {
71 ep_buf: [u8; 128],
72 vendor_code: u8,
73 landing_url: Option<&'d Url<'d>>,
74}
75
76impl<'d> Control<'d> {
77 fn new(config: &'d Config<'d>) -> Self {
78 Control {
79 ep_buf: [0u8; 128],
80 vendor_code: config.vendor_code,
81 landing_url: config.landing_url.as_ref(),
82 }
83 }
84}
85
86impl<'d> Handler for Control<'d> {
87 fn control_in(&mut self, req: Request, _data: &mut [u8]) -> Option<InResponse<'_>> {
88 let landing_value = if self.landing_url.is_some() { 1 } else { 0 };
89 if req.request_type == RequestType::Vendor
90 && req.recipient == Recipient::Device
91 && req.request == self.vendor_code
92 && req.value == landing_value
93 && req.index == WEB_USB_REQUEST_GET_URL
94 {
95 if let Some(url) = self.landing_url {
96 let url_bytes = url.as_bytes();
97 let len = url_bytes.len();
98
99 self.ep_buf[0] = len as u8 + 3;
100 self.ep_buf[1] = WEB_USB_DESCRIPTOR_TYPE_URL;
101 self.ep_buf[2] = url.scheme();
102 self.ep_buf[3..3 + len].copy_from_slice(url_bytes);
103
104 return Some(InResponse::Accepted(&self.ep_buf[..3 + len]));
105 }
106 }
107 None
108 }
109}
110
111pub struct State<'d> {
113 control: MaybeUninit<Control<'d>>,
114}
115
116impl<'d> Default for State<'d> {
117 fn default() -> Self {
118 Self::new()
119 }
120}
121
122impl<'d> State<'d> {
123 pub const fn new() -> Self {
125 State {
126 control: MaybeUninit::uninit(),
127 }
128 }
129}
130
131pub struct WebUsb<'d, D: Driver<'d>> {
137 _driver: core::marker::PhantomData<&'d D>,
138}
139
140impl<'d, D: Driver<'d>> WebUsb<'d, D> {
141 pub fn configure(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: &'d Config<'d>) {
145 let mut func = builder.function(USB_CLASS_VENDOR, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE);
146 let mut iface = func.interface();
147 let mut alt = iface.alt_setting(USB_CLASS_VENDOR, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None);
148
149 alt.bos_capability(
150 capability_type::PLATFORM,
151 &[
152 0x0,
154 0x38,
155 0xb6,
156 0x08,
157 0x34,
158 0xa9,
159 0x09,
160 0xa0,
161 0x47,
162 0x8b,
163 0xfd,
164 0xa0,
165 0x76,
166 0x88,
167 0x15,
168 0xb6,
169 0x65,
170 0x00,
172 0x01,
173 config.vendor_code,
175 if config.landing_url.is_some() { 1 } else { 0 },
177 ],
178 );
179
180 let control = state.control.write(Control::new(config));
181
182 drop(func);
183
184 builder.handler(control);
185 }
186}