blob: 4d827b0f7a8e984d75a9bcf700aab71518b28393 [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.
extern crate fuchsia_ddk;
extern crate fuchsia_zircon;
use fuchsia_zircon::{DurationNum, Status};
use fuchsia_ddk::{DeviceOps, Device, DriverOps, UsbProtocol};
use fuchsia_ddk as ddk;
use fuchsia_ddk::sys::{USB_DIR_IN, USB_DIR_OUT, USB_TYPE_VENDOR, USB_RECIP_DEVICE};
use std::{cmp, fmt, slice, str};
use std::fmt::{Display, Formatter};
use std::mem::size_of;
use std::str::FromStr;
const IOCTL_GETSERIAL: u32 = 1;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u8)]
enum OrbCapabilityFlags { // Does the orb ...
HasGetColor = 0x01, // ... have the GETCOLOR operation implemented ?
HasGetSequence = 0x02, // ... have the GET_SEQUENCE operation implemented?
HasAux = 0x04, // ... have an AUX port and SETAUX implemented ?
HasGammaCorrect = 0x08, // ... have built-in gamma correction ?
HasCurrentLimit = 0x10, // ... behave well on 500mA USB port ?
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u8)]
enum OrbRequest {
OrbSetSequence = 0, // IN orb_sequence_t ; always implemented.
OrbGetCapabilities = 1, // OUT orb_capabilities_t; always implemented.
OrbSetAux = 2, // IN byte ; optional (see flags)
OrbGetColor = 3, // OUT orb_rgb_t ; optional (see flags)
OrbGetSequence = 4, // OUT orb_sequence_t ; optional (see flags)
OrbPokeEeprom = 5, // IN <offset> <bytes> ; optional (Orb4)
}
#[repr(C)]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct orb_rgb_t {
pub red: u8,
pub green: u8,
pub blue: u8,
}
impl Display for orb_rgb_t {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "#{:02x}{:02x}{:02x}", self.red, self.green, self.blue)
}
}
impl FromStr for orb_rgb_t {
type Err = Status;
fn from_str(s: &str) -> Result<Self, Status> {
if s.len() != 7 || &s[0..1] != "#" {
return Err(Status::IO_REFUSED);
}
Ok(orb_rgb_t {
red: u8::from_str_radix(&s[1..3], 16).or(Err(Status::IO_REFUSED))?,
green: u8::from_str_radix(&s[3..5], 16).or(Err(Status::IO_REFUSED))?,
blue: u8::from_str_radix(&s[5..7], 16).or(Err(Status::IO_REFUSED))?,
})
}
}
#[repr(C)]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct orb_color_period_t {
pub color: orb_rgb_t,
pub morph_time: u8,
pub hold_time: u8,
}
impl orb_color_period_t {
fn from_bytes(bytes: &[u8]) -> orb_color_period_t {
assert!(bytes.len() == 5);
orb_color_period_t {
color: orb_rgb_t {
red: bytes[0],
green: bytes[1],
blue: bytes[2],
},
morph_time: bytes[3],
hold_time: bytes[4],
}
}
}
impl Display for orb_color_period_t {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{} {} {}", self.color, self.morph_time, self.hold_time)
}
}
impl FromStr for orb_color_period_t {
type Err = Status;
fn from_str(s: &str) -> Result<Self, Status> {
let mut parts = s.split(" ");
let color = parts.next().ok_or(Status::IO_REFUSED)?.parse()?;
let morph_time = parts.next().ok_or(Status::IO_REFUSED)?.parse().or(Err(Status::IO_REFUSED))?;
let hold_time = parts.next().ok_or(Status::IO_REFUSED)?.parse().or(Err(Status::IO_REFUSED))?;
if parts.next() != None {
return Err(Status::IO_REFUSED);
}
Ok(orb_color_period_t {
color: color,
morph_time: morph_time,
hold_time: hold_time,
})
}
}
struct Sequence {
pub periods: Vec<orb_color_period_t>,
}
impl Display for Sequence {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
for entry in &self.periods {
write!(f, "{}\n", entry)?;
}
Ok(())
}
}
impl FromStr for Sequence {
type Err = Status;
fn from_str(s: &str) -> Result<Self, Status> {
let periods = s.lines().map(|line| line.parse()).collect::<Result<_, _>>()?;
Ok(Sequence {
periods: periods
})
}
}
const ORB_MAX_SEQUENCE: usize = 16;
unsafe fn as_slice<T: Sized>(s: &T) -> &[u8] {
slice::from_raw_parts(s as *const T as *const u8, size_of::<T>())
}
struct MicroOrb {
usb: UsbProtocol,
serial: Option<String>,
}
impl DeviceOps for MicroOrb {
fn name(&self) -> String {
return String::from("microorb")
}
fn unbind(&mut self, device: &mut Device) {
println!("DeviceOps::unbind called");
device.remove();
println!("remove_device returned");
}
fn release(&mut self) {
println!("DeviceOps::release called");
}
fn get_size(&mut self) -> u64 {
println!("get_size");
match self.get_sequence() {
Ok(sequence) => sequence.to_string().len() as u64,
Err(_) => 0,
}
}
fn read(&mut self, buf: &mut [u8], offset: u64) -> Result<usize, Status> {
println!("read {} from {}", buf.len(), offset);
let start = offset as usize;
let sequence = self.get_sequence()?.to_string();
let size = cmp::min(buf.len(), cmp::max(sequence.len() - start, 0));
buf[0..size].copy_from_slice(&sequence.as_bytes()[start..start + size]);
println!("returning {} bytes", size);
Ok(size)
}
fn write(&mut self, buf: &[u8], offset: u64) -> Result<usize, Status> {
// Doesn't make sense to write just part of a sequence.
if offset != 0 {
return Err(Status::NOT_SUPPORTED);
}
let string = str::from_utf8(buf).or(Err(Status::IO_REFUSED))?;
let sequence = string.parse()?;
self.set_sequence(&sequence)?;
Ok(buf.len())
}
fn ioctl(&mut self, op: u32, _in_buf: &[u8], out_buf: &mut [u8]) -> Result<usize, Status> {
match op {
IOCTL_GETSERIAL => {
let serial = self.get_cached_serial()?;
let size = cmp::min(out_buf.len(), serial.len());
out_buf[0..size].copy_from_slice(&serial.as_bytes()[0..size]);
Ok(size)
},
_ => Err(Status::NOT_SUPPORTED)
}
}
}
impl MicroOrb {
fn send(&mut self, command: OrbRequest, input: &[u8]) -> Result<(), Status> {
let timeout = 2.seconds().after_now();
self.usb.control_out(USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, command as u8, 0, 0,
input, timeout)?;
Ok(())
}
fn receive(&mut self, command: OrbRequest, output: &mut [u8]) -> Result<usize, Status> {
let timeout = 2.seconds().after_now();
let length = self.usb.control(USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
command as u8, 0, 0, output, timeout)?;
Ok(length)
}
fn get_serial(&mut self) -> Result<String, Status> {
let timeout = 2.seconds().after_now();
let mut device_descriptor = Default::default();
self.usb.get_device_descriptor(&mut device_descriptor, timeout)?;
let serial_number = device_descriptor.iSerialNumber;
let languages = self.usb.get_string_descriptor_languages(timeout)?;
if languages.len() < 1 {
return Err(Status::IO);
}
let language_id = languages[0];
self.usb.get_string_descriptor(serial_number, language_id, timeout)
}
fn get_cached_serial(&mut self) -> Result<&str, Status> {
match &self.serial {
None => {
self.serial = Some(self.get_serial()?);
Ok(self.serial.as_ref().unwrap())
},
Some(serial) => Ok(serial),
}
}
fn set_sequence(&mut self, sequence: &Sequence) -> Result<(), Status> {
// TODO: trim sequence and limit current for older orbs.
let period_size = size_of::<orb_color_period_t>();
let data_length = size_of::<u8>() + sequence.periods.len() * period_size;
let mut data: Vec<u8> = vec![0; data_length];
data[0] = sequence.periods.len() as u8;
for (color_period, slice) in sequence.periods.iter().zip(data[1..data_length].chunks_mut(period_size)) {
unsafe {
slice.copy_from_slice(as_slice(color_period));
}
}
self.send(OrbRequest::OrbSetSequence, &data)
// TODO: Get sequence back and check that it matches, or else retry.
}
fn set_color(&mut self, color: &orb_rgb_t) -> Result<(), Status> {
let color_period = orb_color_period_t {
color: color.clone(),
morph_time: 0,
hold_time: 1,
};
self.set_sequence(&Sequence { periods: vec![color_period] })
}
fn get_sequence(&mut self) -> Result<Sequence, Status> {
let period_size = size_of::<orb_color_period_t>();
let mut data = vec![0; size_of::<u8>() + ORB_MAX_SEQUENCE * period_size];
let received_length = self.receive(OrbRequest::OrbGetSequence, &mut data)?;
let sequence_length = data[0] as usize;
if received_length != size_of::<u8>() + sequence_length * period_size {
return Err(Status::IO);
}
let mut sequence = Vec::with_capacity(sequence_length);
for i in 0..sequence_length {
let start = 1 + i * period_size;
sequence.push(orb_color_period_t::from_bytes(&data[start..start + period_size]));
}
Ok(Sequence { periods: sequence })
}
}
struct MicroOrbDriver {
}
impl DriverOps for MicroOrbDriver {
fn bind(&mut self, parent: Device) -> Result<Device, Status> {
let usb = UsbProtocol::get(&parent)?;
println!("Got UsbProtocol");
// TODO: Find USB endpoint for our device
// Create now MicroOrb instance, and add the device to the system.
let mut orb = Box::new(MicroOrb {
usb: usb,
serial: None,
});
println!("New MicroOrb, serial {}", orb.get_cached_serial()?);
let color = orb_rgb_t {
red: 64,
green: 32,
blue: 0,
};
orb.set_color(&color)?;
println!("Set color {:?}", color);
Device::add(orb, Some(&parent), ddk::DEVICE_ADD_NONE)
}
}
#[no_mangle]
pub extern "Rust" fn init() -> Result<Box<DriverOps>, Status> {
Ok(Box::new(MicroOrbDriver{}))
}