diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-02-07 22:49:14 +0100 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-02-08 00:17:08 +0100 |
| commit | 3af991ab63d14cfad6f50d28bfb944d1895d1c70 (patch) | |
| tree | 575fecf6f47fbfd7116070aff2ffd5f4e84c7cf1 /embassy-usb/src/class | |
| parent | 1d841cc8ac74feacc4d231958ce2c46419ae3bda (diff) | |
usb: unify ControlHandler+DeviceStateHandler, route all control requests to all handlers.
- Allows classes to handle vendor requests.
- Allows classes to use a single handler for multiple interfaces.
- Allows classes to access the other events (previously only `reset` was available).
Diffstat (limited to 'embassy-usb/src/class')
| -rw-r--r-- | embassy-usb/src/class/cdc_acm.rs | 50 | ||||
| -rw-r--r-- | embassy-usb/src/class/cdc_ncm/mod.rs | 83 | ||||
| -rw-r--r-- | embassy-usb/src/class/hid.rs | 170 |
3 files changed, 179 insertions, 124 deletions
diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index fb9eaeca7..ff82ad40d 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs | |||
| @@ -6,10 +6,10 @@ use core::sync::atomic::{AtomicBool, Ordering}; | |||
| 6 | 6 | ||
| 7 | use embassy_sync::blocking_mutex::CriticalSectionMutex; | 7 | use embassy_sync::blocking_mutex::CriticalSectionMutex; |
| 8 | 8 | ||
| 9 | use crate::control::{self, ControlHandler, InResponse, OutResponse, Request}; | 9 | use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; |
| 10 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; | 10 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; |
| 11 | use crate::types::*; | 11 | use crate::types::*; |
| 12 | use crate::Builder; | 12 | use crate::{Builder, Handler}; |
| 13 | 13 | ||
| 14 | /// This should be used as `device_class` when building the `UsbDevice`. | 14 | /// This should be used as `device_class` when building the `UsbDevice`. |
| 15 | pub const USB_CLASS_CDC: u8 = 0x02; | 15 | pub const USB_CLASS_CDC: u8 = 0x02; |
| @@ -67,6 +67,7 @@ pub struct CdcAcmClass<'d, D: Driver<'d>> { | |||
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | struct Control<'a> { | 69 | struct Control<'a> { |
| 70 | comm_if: InterfaceNumber, | ||
| 70 | shared: &'a ControlShared, | 71 | shared: &'a ControlShared, |
| 71 | } | 72 | } |
| 72 | 73 | ||
| @@ -98,7 +99,7 @@ impl<'a> Control<'a> { | |||
| 98 | } | 99 | } |
| 99 | } | 100 | } |
| 100 | 101 | ||
| 101 | impl<'d> ControlHandler for Control<'d> { | 102 | impl<'d> Handler for Control<'d> { |
| 102 | fn reset(&mut self) { | 103 | fn reset(&mut self) { |
| 103 | let shared = self.shared(); | 104 | let shared = self.shared(); |
| 104 | shared.line_coding.lock(|x| x.set(LineCoding::default())); | 105 | shared.line_coding.lock(|x| x.set(LineCoding::default())); |
| @@ -106,12 +107,18 @@ impl<'d> ControlHandler for Control<'d> { | |||
| 106 | shared.rts.store(false, Ordering::Relaxed); | 107 | shared.rts.store(false, Ordering::Relaxed); |
| 107 | } | 108 | } |
| 108 | 109 | ||
| 109 | fn control_out(&mut self, req: control::Request, data: &[u8]) -> OutResponse { | 110 | fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> { |
| 111 | if (req.request_type, req.recipient, req.index) | ||
| 112 | != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) | ||
| 113 | { | ||
| 114 | return None; | ||
| 115 | } | ||
| 116 | |||
| 110 | match req.request { | 117 | match req.request { |
| 111 | REQ_SEND_ENCAPSULATED_COMMAND => { | 118 | REQ_SEND_ENCAPSULATED_COMMAND => { |
| 112 | // We don't actually support encapsulated commands but pretend we do for standards | 119 | // We don't actually support encapsulated commands but pretend we do for standards |
| 113 | // compatibility. | 120 | // compatibility. |
| 114 | OutResponse::Accepted | 121 | Some(OutResponse::Accepted) |
| 115 | } | 122 | } |
| 116 | REQ_SET_LINE_CODING if data.len() >= 7 => { | 123 | REQ_SET_LINE_CODING if data.len() >= 7 => { |
| 117 | let coding = LineCoding { | 124 | let coding = LineCoding { |
| @@ -123,7 +130,7 @@ impl<'d> ControlHandler for Control<'d> { | |||
| 123 | self.shared().line_coding.lock(|x| x.set(coding)); | 130 | self.shared().line_coding.lock(|x| x.set(coding)); |
| 124 | debug!("Set line coding to: {:?}", coding); | 131 | debug!("Set line coding to: {:?}", coding); |
| 125 | 132 | ||
| 126 | OutResponse::Accepted | 133 | Some(OutResponse::Accepted) |
| 127 | } | 134 | } |
| 128 | REQ_SET_CONTROL_LINE_STATE => { | 135 | REQ_SET_CONTROL_LINE_STATE => { |
| 129 | let dtr = (req.value & 0x0001) != 0; | 136 | let dtr = (req.value & 0x0001) != 0; |
| @@ -134,13 +141,19 @@ impl<'d> ControlHandler for Control<'d> { | |||
| 134 | shared.rts.store(rts, Ordering::Relaxed); | 141 | shared.rts.store(rts, Ordering::Relaxed); |
| 135 | debug!("Set dtr {}, rts {}", dtr, rts); | 142 | debug!("Set dtr {}, rts {}", dtr, rts); |
| 136 | 143 | ||
| 137 | OutResponse::Accepted | 144 | Some(OutResponse::Accepted) |
| 138 | } | 145 | } |
| 139 | _ => OutResponse::Rejected, | 146 | _ => Some(OutResponse::Rejected), |
| 140 | } | 147 | } |
| 141 | } | 148 | } |
| 142 | 149 | ||
| 143 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { | 150 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> { |
| 151 | if (req.request_type, req.recipient, req.index) | ||
| 152 | != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) | ||
| 153 | { | ||
| 154 | return None; | ||
| 155 | } | ||
| 156 | |||
| 144 | match req.request { | 157 | match req.request { |
| 145 | // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. | 158 | // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. |
| 146 | REQ_GET_LINE_CODING if req.length == 7 => { | 159 | REQ_GET_LINE_CODING if req.length == 7 => { |
| @@ -151,9 +164,9 @@ impl<'d> ControlHandler for Control<'d> { | |||
| 151 | buf[4] = coding.stop_bits as u8; | 164 | buf[4] = coding.stop_bits as u8; |
| 152 | buf[5] = coding.parity_type as u8; | 165 | buf[5] = coding.parity_type as u8; |
| 153 | buf[6] = coding.data_bits; | 166 | buf[6] = coding.data_bits; |
| 154 | InResponse::Accepted(&buf[0..7]) | 167 | Some(InResponse::Accepted(&buf[0..7])) |
| 155 | } | 168 | } |
| 156 | _ => InResponse::Rejected, | 169 | _ => Some(InResponse::Rejected), |
| 157 | } | 170 | } |
| 158 | } | 171 | } |
| 159 | } | 172 | } |
| @@ -162,17 +175,12 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | |||
| 162 | /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For | 175 | /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For |
| 163 | /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. | 176 | /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. |
| 164 | pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, max_packet_size: u16) -> Self { | 177 | pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, max_packet_size: u16) -> Self { |
| 165 | let control = state.control.write(Control { shared: &state.shared }); | ||
| 166 | |||
| 167 | let control_shared = &state.shared; | ||
| 168 | |||
| 169 | assert!(builder.control_buf_len() >= 7); | 178 | assert!(builder.control_buf_len() >= 7); |
| 170 | 179 | ||
| 171 | let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE); | 180 | let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE); |
| 172 | 181 | ||
| 173 | // Control interface | 182 | // Control interface |
| 174 | let mut iface = func.interface(); | 183 | let mut iface = func.interface(); |
| 175 | iface.handler(control); | ||
| 176 | let comm_if = iface.interface_number(); | 184 | let comm_if = iface.interface_number(); |
| 177 | let data_if = u8::from(comm_if) + 1; | 185 | let data_if = u8::from(comm_if) + 1; |
| 178 | let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE, None); | 186 | let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE, None); |
| @@ -213,6 +221,16 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | |||
| 213 | let read_ep = alt.endpoint_bulk_out(max_packet_size); | 221 | let read_ep = alt.endpoint_bulk_out(max_packet_size); |
| 214 | let write_ep = alt.endpoint_bulk_in(max_packet_size); | 222 | let write_ep = alt.endpoint_bulk_in(max_packet_size); |
| 215 | 223 | ||
| 224 | drop(func); | ||
| 225 | |||
| 226 | let control = state.control.write(Control { | ||
| 227 | shared: &state.shared, | ||
| 228 | comm_if, | ||
| 229 | }); | ||
| 230 | builder.handler(control); | ||
| 231 | |||
| 232 | let control_shared = &state.shared; | ||
| 233 | |||
| 216 | CdcAcmClass { | 234 | CdcAcmClass { |
| 217 | _comm_ep: comm_ep, | 235 | _comm_ep: comm_ep, |
| 218 | _data_if: data_if, | 236 | _data_if: data_if, |
diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index d6c7d37e6..262499ccb 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs | |||
| @@ -17,10 +17,10 @@ | |||
| 17 | use core::intrinsics::copy_nonoverlapping; | 17 | use core::intrinsics::copy_nonoverlapping; |
| 18 | use core::mem::{size_of, MaybeUninit}; | 18 | use core::mem::{size_of, MaybeUninit}; |
| 19 | 19 | ||
| 20 | use crate::control::{self, ControlHandler, InResponse, OutResponse, Request}; | 20 | use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; |
| 21 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; | 21 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; |
| 22 | use crate::types::*; | 22 | use crate::types::*; |
| 23 | use crate::Builder; | 23 | use crate::{Builder, Handler}; |
| 24 | 24 | ||
| 25 | pub mod embassy_net; | 25 | pub mod embassy_net; |
| 26 | 26 | ||
| @@ -117,8 +117,7 @@ fn byteify<T>(buf: &mut [u8], data: T) -> &[u8] { | |||
| 117 | 117 | ||
| 118 | /// Internal state for the CDC-NCM class. | 118 | /// Internal state for the CDC-NCM class. |
| 119 | pub struct State<'a> { | 119 | pub struct State<'a> { |
| 120 | comm_control: MaybeUninit<CommControl<'a>>, | 120 | control: MaybeUninit<Control<'a>>, |
| 121 | data_control: MaybeUninit<DataControl>, | ||
| 122 | shared: ControlShared, | 121 | shared: ControlShared, |
| 123 | } | 122 | } |
| 124 | 123 | ||
| @@ -126,8 +125,7 @@ impl<'a> State<'a> { | |||
| 126 | /// Create a new `State`. | 125 | /// Create a new `State`. |
| 127 | pub fn new() -> Self { | 126 | pub fn new() -> Self { |
| 128 | Self { | 127 | Self { |
| 129 | comm_control: MaybeUninit::uninit(), | 128 | control: MaybeUninit::uninit(), |
| 130 | data_control: MaybeUninit::uninit(), | ||
| 131 | shared: Default::default(), | 129 | shared: Default::default(), |
| 132 | } | 130 | } |
| 133 | } | 131 | } |
| @@ -144,29 +142,55 @@ impl Default for ControlShared { | |||
| 144 | } | 142 | } |
| 145 | } | 143 | } |
| 146 | 144 | ||
| 147 | struct CommControl<'a> { | 145 | struct Control<'a> { |
| 148 | mac_addr_string: StringIndex, | 146 | mac_addr_string: StringIndex, |
| 149 | shared: &'a ControlShared, | 147 | shared: &'a ControlShared, |
| 150 | mac_addr_str: [u8; 12], | 148 | mac_addr_str: [u8; 12], |
| 149 | comm_if: InterfaceNumber, | ||
| 150 | data_if: InterfaceNumber, | ||
| 151 | } | 151 | } |
| 152 | 152 | ||
| 153 | impl<'d> ControlHandler for CommControl<'d> { | 153 | impl<'d> Handler for Control<'d> { |
| 154 | fn control_out(&mut self, req: control::Request, _data: &[u8]) -> OutResponse { | 154 | fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) { |
| 155 | if iface != self.data_if { | ||
| 156 | return; | ||
| 157 | } | ||
| 158 | |||
| 159 | match alternate_setting { | ||
| 160 | ALTERNATE_SETTING_ENABLED => info!("ncm: interface enabled"), | ||
| 161 | ALTERNATE_SETTING_DISABLED => info!("ncm: interface disabled"), | ||
| 162 | _ => unreachable!(), | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | fn control_out(&mut self, req: control::Request, _data: &[u8]) -> Option<OutResponse> { | ||
| 167 | if (req.request_type, req.recipient, req.index) | ||
| 168 | != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) | ||
| 169 | { | ||
| 170 | return None; | ||
| 171 | } | ||
| 172 | |||
| 155 | match req.request { | 173 | match req.request { |
| 156 | REQ_SEND_ENCAPSULATED_COMMAND => { | 174 | REQ_SEND_ENCAPSULATED_COMMAND => { |
| 157 | // We don't actually support encapsulated commands but pretend we do for standards | 175 | // We don't actually support encapsulated commands but pretend we do for standards |
| 158 | // compatibility. | 176 | // compatibility. |
| 159 | OutResponse::Accepted | 177 | Some(OutResponse::Accepted) |
| 160 | } | 178 | } |
| 161 | REQ_SET_NTB_INPUT_SIZE => { | 179 | REQ_SET_NTB_INPUT_SIZE => { |
| 162 | // TODO | 180 | // TODO |
| 163 | OutResponse::Accepted | 181 | Some(OutResponse::Accepted) |
| 164 | } | 182 | } |
| 165 | _ => OutResponse::Rejected, | 183 | _ => Some(OutResponse::Rejected), |
| 166 | } | 184 | } |
| 167 | } | 185 | } |
| 168 | 186 | ||
| 169 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { | 187 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> { |
| 188 | if (req.request_type, req.recipient, req.index) | ||
| 189 | != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) | ||
| 190 | { | ||
| 191 | return None; | ||
| 192 | } | ||
| 193 | |||
| 170 | match req.request { | 194 | match req.request { |
| 171 | REQ_GET_NTB_PARAMETERS => { | 195 | REQ_GET_NTB_PARAMETERS => { |
| 172 | let res = NtbParameters { | 196 | let res = NtbParameters { |
| @@ -187,9 +211,9 @@ impl<'d> ControlHandler for CommControl<'d> { | |||
| 187 | max_datagram_count: 1, // We only decode 1 packet per NTB | 211 | max_datagram_count: 1, // We only decode 1 packet per NTB |
| 188 | }, | 212 | }, |
| 189 | }; | 213 | }; |
| 190 | InResponse::Accepted(byteify(buf, res)) | 214 | Some(InResponse::Accepted(byteify(buf, res))) |
| 191 | } | 215 | } |
| 192 | _ => InResponse::Rejected, | 216 | _ => Some(InResponse::Rejected), |
| 193 | } | 217 | } |
| 194 | } | 218 | } |
| 195 | 219 | ||
| @@ -214,18 +238,6 @@ impl<'d> ControlHandler for CommControl<'d> { | |||
| 214 | } | 238 | } |
| 215 | } | 239 | } |
| 216 | 240 | ||
| 217 | struct DataControl {} | ||
| 218 | |||
| 219 | impl ControlHandler for DataControl { | ||
| 220 | fn set_alternate_setting(&mut self, alternate_setting: u8) { | ||
| 221 | match alternate_setting { | ||
| 222 | ALTERNATE_SETTING_ENABLED => info!("ncm: interface enabled"), | ||
| 223 | ALTERNATE_SETTING_DISABLED => info!("ncm: interface disabled"), | ||
| 224 | _ => unreachable!(), | ||
| 225 | } | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | /// CDC-NCM class | 241 | /// CDC-NCM class |
| 230 | pub struct CdcNcmClass<'d, D: Driver<'d>> { | 242 | pub struct CdcNcmClass<'d, D: Driver<'d>> { |
| 231 | _comm_if: InterfaceNumber, | 243 | _comm_if: InterfaceNumber, |
| @@ -253,11 +265,6 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { | |||
| 253 | // Control interface | 265 | // Control interface |
| 254 | let mut iface = func.interface(); | 266 | let mut iface = func.interface(); |
| 255 | let mac_addr_string = iface.string(); | 267 | let mac_addr_string = iface.string(); |
| 256 | iface.handler(state.comm_control.write(CommControl { | ||
| 257 | mac_addr_string, | ||
| 258 | shared: &state.shared, | ||
| 259 | mac_addr_str: [0; 12], | ||
| 260 | })); | ||
| 261 | let comm_if = iface.interface_number(); | 268 | let comm_if = iface.interface_number(); |
| 262 | let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE, None); | 269 | let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE, None); |
| 263 | 270 | ||
| @@ -307,13 +314,23 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { | |||
| 307 | 314 | ||
| 308 | // Data interface | 315 | // Data interface |
| 309 | let mut iface = func.interface(); | 316 | let mut iface = func.interface(); |
| 310 | iface.handler(state.data_control.write(DataControl {})); | ||
| 311 | let data_if = iface.interface_number(); | 317 | let data_if = iface.interface_number(); |
| 312 | let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); | 318 | let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); |
| 313 | let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); | 319 | let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); |
| 314 | let read_ep = alt.endpoint_bulk_out(max_packet_size); | 320 | let read_ep = alt.endpoint_bulk_out(max_packet_size); |
| 315 | let write_ep = alt.endpoint_bulk_in(max_packet_size); | 321 | let write_ep = alt.endpoint_bulk_in(max_packet_size); |
| 316 | 322 | ||
| 323 | drop(func); | ||
| 324 | |||
| 325 | let control = state.control.write(Control { | ||
| 326 | mac_addr_string, | ||
| 327 | shared: &state.shared, | ||
| 328 | mac_addr_str: [0; 12], | ||
| 329 | comm_if, | ||
| 330 | data_if, | ||
| 331 | }); | ||
| 332 | builder.handler(control); | ||
| 333 | |||
| 317 | CdcNcmClass { | 334 | CdcNcmClass { |
| 318 | _comm_if: comm_if, | 335 | _comm_if: comm_if, |
| 319 | comm_ep, | 336 | comm_ep, |
diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 0283c1124..974268c62 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs | |||
| @@ -9,9 +9,10 @@ use ssmarshal::serialize; | |||
| 9 | #[cfg(feature = "usbd-hid")] | 9 | #[cfg(feature = "usbd-hid")] |
| 10 | use usbd_hid::descriptor::AsInputReport; | 10 | use usbd_hid::descriptor::AsInputReport; |
| 11 | 11 | ||
| 12 | use crate::control::{ControlHandler, InResponse, OutResponse, Request, RequestType}; | 12 | use crate::control::{InResponse, OutResponse, Recipient, Request, RequestType}; |
| 13 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; | 13 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; |
| 14 | use crate::Builder; | 14 | use crate::types::InterfaceNumber; |
| 15 | use crate::{Builder, Handler}; | ||
| 15 | 16 | ||
| 16 | const USB_CLASS_HID: u8 = 0x03; | 17 | const USB_CLASS_HID: u8 = 0x03; |
| 17 | const USB_SUBCLASS_NONE: u8 = 0x00; | 18 | const USB_SUBCLASS_NONE: u8 = 0x00; |
| @@ -100,17 +101,11 @@ fn build<'d, D: Driver<'d>>( | |||
| 100 | config: Config<'d>, | 101 | config: Config<'d>, |
| 101 | with_out_endpoint: bool, | 102 | with_out_endpoint: bool, |
| 102 | ) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) { | 103 | ) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) { |
| 103 | let control = state.control.write(Control::new( | ||
| 104 | config.report_descriptor, | ||
| 105 | config.request_handler, | ||
| 106 | &state.out_report_offset, | ||
| 107 | )); | ||
| 108 | |||
| 109 | let len = config.report_descriptor.len(); | 104 | let len = config.report_descriptor.len(); |
| 110 | 105 | ||
| 111 | let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); | 106 | let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); |
| 112 | let mut iface = func.interface(); | 107 | let mut iface = func.interface(); |
| 113 | iface.handler(control); | 108 | let if_num = iface.interface_number(); |
| 114 | let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None); | 109 | let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None); |
| 115 | 110 | ||
| 116 | // HID descriptor | 111 | // HID descriptor |
| @@ -139,6 +134,16 @@ fn build<'d, D: Driver<'d>>( | |||
| 139 | None | 134 | None |
| 140 | }; | 135 | }; |
| 141 | 136 | ||
| 137 | drop(func); | ||
| 138 | |||
| 139 | let control = state.control.write(Control::new( | ||
| 140 | if_num, | ||
| 141 | config.report_descriptor, | ||
| 142 | config.request_handler, | ||
| 143 | &state.out_report_offset, | ||
| 144 | )); | ||
| 145 | builder.handler(control); | ||
| 146 | |||
| 142 | (ep_out, ep_in, &state.out_report_offset) | 147 | (ep_out, ep_in, &state.out_report_offset) |
| 143 | } | 148 | } |
| 144 | 149 | ||
| @@ -400,6 +405,7 @@ pub trait RequestHandler { | |||
| 400 | } | 405 | } |
| 401 | 406 | ||
| 402 | struct Control<'d> { | 407 | struct Control<'d> { |
| 408 | if_num: InterfaceNumber, | ||
| 403 | report_descriptor: &'d [u8], | 409 | report_descriptor: &'d [u8], |
| 404 | request_handler: Option<&'d dyn RequestHandler>, | 410 | request_handler: Option<&'d dyn RequestHandler>, |
| 405 | out_report_offset: &'d AtomicUsize, | 411 | out_report_offset: &'d AtomicUsize, |
| @@ -408,11 +414,13 @@ struct Control<'d> { | |||
| 408 | 414 | ||
| 409 | impl<'d> Control<'d> { | 415 | impl<'d> Control<'d> { |
| 410 | fn new( | 416 | fn new( |
| 417 | if_num: InterfaceNumber, | ||
| 411 | report_descriptor: &'d [u8], | 418 | report_descriptor: &'d [u8], |
| 412 | request_handler: Option<&'d dyn RequestHandler>, | 419 | request_handler: Option<&'d dyn RequestHandler>, |
| 413 | out_report_offset: &'d AtomicUsize, | 420 | out_report_offset: &'d AtomicUsize, |
| 414 | ) -> Self { | 421 | ) -> Self { |
| 415 | Control { | 422 | Control { |
| 423 | if_num, | ||
| 416 | report_descriptor, | 424 | report_descriptor, |
| 417 | request_handler, | 425 | request_handler, |
| 418 | out_report_offset, | 426 | out_report_offset, |
| @@ -438,88 +446,100 @@ impl<'d> Control<'d> { | |||
| 438 | } | 446 | } |
| 439 | } | 447 | } |
| 440 | 448 | ||
| 441 | impl<'d> ControlHandler for Control<'d> { | 449 | impl<'d> Handler for Control<'d> { |
| 442 | fn reset(&mut self) { | 450 | fn reset(&mut self) { |
| 443 | self.out_report_offset.store(0, Ordering::Release); | 451 | self.out_report_offset.store(0, Ordering::Release); |
| 444 | } | 452 | } |
| 445 | 453 | ||
| 446 | fn get_descriptor<'a>(&'a mut self, req: Request, _buf: &'a mut [u8]) -> InResponse<'a> { | 454 | fn control_out(&mut self, req: Request, data: &[u8]) -> Option<OutResponse> { |
| 447 | match (req.value >> 8) as u8 { | 455 | if (req.request_type, req.recipient, req.index) |
| 448 | HID_DESC_DESCTYPE_HID_REPORT => InResponse::Accepted(self.report_descriptor), | 456 | != (RequestType::Class, Recipient::Interface, self.if_num.0 as u16) |
| 449 | HID_DESC_DESCTYPE_HID => InResponse::Accepted(&self.hid_descriptor), | 457 | { |
| 450 | _ => InResponse::Rejected, | 458 | return None; |
| 451 | } | 459 | } |
| 452 | } | ||
| 453 | 460 | ||
| 454 | fn control_out(&mut self, req: Request, data: &[u8]) -> OutResponse { | ||
| 455 | trace!("HID control_out {:?} {=[u8]:x}", req, data); | 461 | trace!("HID control_out {:?} {=[u8]:x}", req, data); |
| 456 | if let RequestType::Class = req.request_type { | 462 | match req.request { |
| 457 | match req.request { | 463 | HID_REQ_SET_IDLE => { |
| 458 | HID_REQ_SET_IDLE => { | 464 | if let Some(handler) = self.request_handler { |
| 459 | if let Some(handler) = self.request_handler { | 465 | let id = req.value as u8; |
| 460 | let id = req.value as u8; | 466 | let id = (id != 0).then(|| ReportId::In(id)); |
| 461 | let id = (id != 0).then(|| ReportId::In(id)); | 467 | let dur = u32::from(req.value >> 8); |
| 462 | let dur = u32::from(req.value >> 8); | 468 | let dur = if dur == 0 { u32::MAX } else { 4 * dur }; |
| 463 | let dur = if dur == 0 { u32::MAX } else { 4 * dur }; | 469 | handler.set_idle_ms(id, dur); |
| 464 | handler.set_idle_ms(id, dur); | ||
| 465 | } | ||
| 466 | OutResponse::Accepted | ||
| 467 | } | 470 | } |
| 468 | HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler) { | 471 | Some(OutResponse::Accepted) |
| 469 | (Ok(id), Some(handler)) => handler.set_report(id, data), | 472 | } |
| 470 | _ => OutResponse::Rejected, | 473 | HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler) { |
| 471 | }, | 474 | (Ok(id), Some(handler)) => Some(handler.set_report(id, data)), |
| 472 | HID_REQ_SET_PROTOCOL => { | 475 | _ => Some(OutResponse::Rejected), |
| 473 | if req.value == 1 { | 476 | }, |
| 474 | OutResponse::Accepted | 477 | HID_REQ_SET_PROTOCOL => { |
| 475 | } else { | 478 | if req.value == 1 { |
| 476 | warn!("HID Boot Protocol is unsupported."); | 479 | Some(OutResponse::Accepted) |
| 477 | OutResponse::Rejected // UNSUPPORTED: Boot Protocol | 480 | } else { |
| 478 | } | 481 | warn!("HID Boot Protocol is unsupported."); |
| 482 | Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol | ||
| 479 | } | 483 | } |
| 480 | _ => OutResponse::Rejected, | ||
| 481 | } | 484 | } |
| 482 | } else { | 485 | _ => Some(OutResponse::Rejected), |
| 483 | OutResponse::Rejected // UNSUPPORTED: SET_DESCRIPTOR | ||
| 484 | } | 486 | } |
| 485 | } | 487 | } |
| 486 | 488 | ||
| 487 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { | 489 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> { |
| 488 | trace!("HID control_in {:?}", req); | 490 | if req.index != self.if_num.0 as u16 { |
| 489 | match req.request { | 491 | return None; |
| 490 | HID_REQ_GET_REPORT => { | 492 | } |
| 491 | let size = match ReportId::try_from(req.value) { | 493 | |
| 492 | Ok(id) => self.request_handler.and_then(|x| x.get_report(id, buf)), | 494 | match (req.request_type, req.recipient) { |
| 493 | Err(_) => None, | 495 | (RequestType::Standard, Recipient::Interface) => match req.request { |
| 494 | }; | 496 | Request::GET_DESCRIPTOR => match (req.value >> 8) as u8 { |
| 495 | 497 | HID_DESC_DESCTYPE_HID_REPORT => Some(InResponse::Accepted(self.report_descriptor)), | |
| 496 | if let Some(size) = size { | 498 | HID_DESC_DESCTYPE_HID => Some(InResponse::Accepted(&self.hid_descriptor)), |
| 497 | InResponse::Accepted(&buf[0..size]) | 499 | _ => Some(InResponse::Rejected), |
| 498 | } else { | 500 | }, |
| 499 | InResponse::Rejected | 501 | |
| 500 | } | 502 | _ => Some(InResponse::Rejected), |
| 501 | } | 503 | }, |
| 502 | HID_REQ_GET_IDLE => { | 504 | (RequestType::Class, Recipient::Interface) => { |
| 503 | if let Some(handler) = self.request_handler { | 505 | trace!("HID control_in {:?}", req); |
| 504 | let id = req.value as u8; | 506 | match req.request { |
| 505 | let id = (id != 0).then(|| ReportId::In(id)); | 507 | HID_REQ_GET_REPORT => { |
| 506 | if let Some(dur) = handler.get_idle_ms(id) { | 508 | let size = match ReportId::try_from(req.value) { |
| 507 | let dur = u8::try_from(dur / 4).unwrap_or(0); | 509 | Ok(id) => self.request_handler.and_then(|x| x.get_report(id, buf)), |
| 508 | buf[0] = dur; | 510 | Err(_) => None, |
| 509 | InResponse::Accepted(&buf[0..1]) | 511 | }; |
| 510 | } else { | 512 | |
| 511 | InResponse::Rejected | 513 | if let Some(size) = size { |
| 514 | Some(InResponse::Accepted(&buf[0..size])) | ||
| 515 | } else { | ||
| 516 | Some(InResponse::Rejected) | ||
| 517 | } | ||
| 512 | } | 518 | } |
| 513 | } else { | 519 | HID_REQ_GET_IDLE => { |
| 514 | InResponse::Rejected | 520 | if let Some(handler) = self.request_handler { |
| 521 | let id = req.value as u8; | ||
| 522 | let id = (id != 0).then(|| ReportId::In(id)); | ||
| 523 | if let Some(dur) = handler.get_idle_ms(id) { | ||
| 524 | let dur = u8::try_from(dur / 4).unwrap_or(0); | ||
| 525 | buf[0] = dur; | ||
| 526 | Some(InResponse::Accepted(&buf[0..1])) | ||
| 527 | } else { | ||
| 528 | Some(InResponse::Rejected) | ||
| 529 | } | ||
| 530 | } else { | ||
| 531 | Some(InResponse::Rejected) | ||
| 532 | } | ||
| 533 | } | ||
| 534 | HID_REQ_GET_PROTOCOL => { | ||
| 535 | // UNSUPPORTED: Boot Protocol | ||
| 536 | buf[0] = 1; | ||
| 537 | Some(InResponse::Accepted(&buf[0..1])) | ||
| 538 | } | ||
| 539 | _ => Some(InResponse::Rejected), | ||
| 515 | } | 540 | } |
| 516 | } | 541 | } |
| 517 | HID_REQ_GET_PROTOCOL => { | 542 | _ => None, |
| 518 | // UNSUPPORTED: Boot Protocol | ||
| 519 | buf[0] = 1; | ||
| 520 | InResponse::Accepted(&buf[0..1]) | ||
| 521 | } | ||
| 522 | _ => InResponse::Rejected, | ||
| 523 | } | 543 | } |
| 524 | } | 544 | } |
| 525 | } | 545 | } |
