blob: f3300cb1df8701a55d48874197f815e55ad3fea0 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use byteorder::{ByteOrder, LittleEndian};
use sys;
use sys::{device_get_protocol, usb_device_descriptor_t, usb_protocol_t, usb_request_type_t, USB_DIR_IN, USB_DIR_MASK, USB_DIR_OUT, USB_DT_DEVICE, USB_DT_STRING, USB_REQ_GET_DESCRIPTOR, USB_REQ_GET_STATUS};
use std::char::decode_utf16;
use std::mem::size_of;
use std::slice;
use zircon::{Status, Time};
use into_result;
use Device;
pub struct UsbProtocol(usb_protocol_t);
unsafe fn as_slice<T: Sized>(s: &mut T) -> &mut [u8] {
slice::from_raw_parts_mut(s as *mut T as *mut u8, size_of::<T>())
}
impl UsbProtocol {
/// Get a new instance of UsbProtocol for a given device, if it is available.
pub fn get(device: &Device) -> Result<UsbProtocol, Status> {
let mut protocol: usb_protocol_t = Default::default();
let status = unsafe {
device_get_protocol(device.device, sys::ZX_PROTOCOL_USB,
&mut protocol as *mut usb_protocol_t as *mut u8)
};
into_result(status, || UsbProtocol(protocol))
}
/// Send a USB control message.
pub fn control(&mut self, request_type: usb_request_type_t, request: u8,
value: u16, index: u16, data: &mut [u8], timeout: Time) -> Result<usize, Status>
{
let mut out_length: usize = 0;
let status = unsafe {
((*self.0.ops).control)(self.0.ctx, request_type, request, value, index,
data.as_mut_ptr(), data.len(), timeout.nanos(), &mut out_length)
};
into_result(status, || out_length)
}
pub fn control_struct<T: Sized>(&mut self, request_type: usb_request_type_t, request: u8,
value: u16, index: u16, data: &mut T, timeout: Time) -> Result<usize, Status>
{
let mut out_length: usize = 0;
let status = unsafe {
((*self.0.ops).control)(self.0.ctx, request_type, request, value, index,
data as *mut T as *mut u8, size_of::<T>(), timeout.nanos(), &mut out_length)
};
into_result(status, || out_length)
}
/// Send a USB control message which only sends data, doesn't receive any.
pub fn control_out(&mut self, request_type: usb_request_type_t, request: u8,
value: u16, index: u16, data: &[u8], timeout: Time) -> Result<usize, Status>
{
// Force request type to be an out request, so data doesn't need to be mutable.
let request_type_out = (request_type & !USB_DIR_MASK) | USB_DIR_OUT;
let mut out_length: usize = 0;
let status = unsafe {
((*self.0.ops).control)(self.0.ctx, request_type_out, request, value, index,
data.as_ptr() as *mut u8, data.len(), timeout.nanos(), &mut out_length)
};
into_result(status, || out_length)
}
/// Get any sort of USB descriptor.
fn get_descriptor(&mut self, descriptor_type: u8, descriptor_index: u8, language_id: u16,
data: &mut [u8], timeout: Time) -> Result<usize, Status>
{
self.control(USB_DIR_IN, USB_REQ_GET_DESCRIPTOR,
(descriptor_type as u16) << 8 | descriptor_index as u16, language_id, data, timeout)
}
/// Get the USB device descriptor.
pub fn get_device_descriptor(&mut self, descriptor: &mut usb_device_descriptor_t, timeout: Time)
-> Result<(), Status>
{
let data = unsafe { as_slice(descriptor) };
let length = self.get_descriptor(USB_DT_DEVICE, 0, 0, data, timeout)?;
if length != size_of::<usb_device_descriptor_t>() {
return Err(Status::IO);
}
Ok(())
}
/// Get the given USB string descriptor.
pub fn get_string_descriptor(&mut self, descriptor_index: u8, language_id: u16, timeout: Time)
-> Result<String, Status>
{
let mut byte_buffer = [0; 254];
let length = self.get_descriptor(USB_DT_STRING, descriptor_index, language_id,
&mut byte_buffer, timeout)?;
// Must be an even number of bytes to be valid UTF-16-LE
if length < 2 || length % 2 != 0 {
return Err(Status::IO);
}
// Skip the first two bytes, which are the length and descriptor type.
decode_utf16(byte_buffer[2..length].chunks(2).map(LittleEndian::read_u16))
.collect::<Result<String, _>>().or(Err(Status::IO))
}
/// Get the list of available languages for string descriptors.
pub fn get_string_descriptor_languages(&mut self, timeout: Time) -> Result<Vec<u16>, Status> {
let mut byte_buffer = [0; 254];
// Descriptor index 0, language ID 0 should contain a list of all supported language IDs.
let length = self.get_descriptor(USB_DT_STRING, 0, 0, &mut byte_buffer, timeout)?;
// Should be an even number of bytes, starting with the length and descriptor type (which we
// ignore).
if length < 2 || length % 2 != 0 {
return Err(Status::IO);
}
Ok(byte_buffer[2..length].chunks(2).map(LittleEndian::read_u16).collect::<Vec<u16>>())
}
pub fn get_status(&mut self, request_type: usb_request_type_t, index: u16, data: &mut [u8],
timeout: Time) -> Result<usize, Status>
{
self.control(request_type | USB_DIR_IN, USB_REQ_GET_STATUS, 0, index,
data, timeout)
}
}