Skip to content

Commit 5db5a89

Browse files
committed
Initial commit
0 parents  commit 5db5a89

File tree

12 files changed

+1068
-0
lines changed

12 files changed

+1068
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
target/
2+
Cargo.lock

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[package]
2+
name = "usb-device"
3+
version = "0.1.0"
4+
authors = ["Matti Virkkunen <[email protected]>"]

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2018 Matti Virkkunen
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
usb-device
2+
==========
3+
4+
Experimental device-side USB framework for microcontrollers in Rust.
5+
6+
This crate is still under development and should not be considered production ready or even USB
7+
compliant.
8+
9+
The UsbDevice object represents a composite USB device and is the most important object for
10+
end-users. Most of the other items are for implementing new USB classes or device-specific drivers.
11+
12+
The UsbClass trait can be used to implemented USB classes such as HID devices or serial ports.
13+
Pre-made class implementations will be provided in separate crates.
14+
15+
The UsbBus trait is intended to be implemented by device-specific crates to provide a driver for
16+
each device specific USB peripheral.
17+
18+
Related crates
19+
--------------
20+
21+
* [stm32f103xx-usb](https://github.com/mvirkkunen/stm32f103xx-usb) - device-driver implementation
22+
for STM32F103 microcontrollers. Also contains runnable examples.
23+
24+
TODO
25+
----
26+
27+
Features planned but not implemented yet:
28+
29+
- Documentation
30+
- Detecting bus state (connected/not connected)
31+
- Suspend and resume
32+
- Interface alternate settings
33+
- A safer DescriptorWriter
34+
- Extra string descriptors
35+
- Multilingual string descriptors
36+
- Isochronous endpoints
37+
- Standard requests
38+
- GET_STATUS
39+
- CLEAR_FEATURE
40+
- SET_FEATURE
41+
- GET_CONFIGURATION
42+
- SYNCH_FRAME
43+
- Optimize interrupt driven operation (maybe UsbDevice::poll should return which device has data
44+
available)
45+
46+
Features not planning to support at the moment:
47+
48+
- More than one configuration descriptor
49+
- Control transfers on other than endpoint 0

src/bus.rs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
use ::Result;
2+
3+
pub trait UsbBus {
4+
fn enable(&self);
5+
fn reset(&self);
6+
fn configure_ep(&self, ep_addr: u8, ep_type: EndpointType, max_packet_size: u16) -> Result<()>;
7+
fn set_device_address(&self, addr: u8);
8+
fn write(&self, ep_addr: u8, buf: &[u8]) -> Result<usize>;
9+
fn read(&self, ep_addr: u8, buf: &mut [u8]) -> Result<usize>;
10+
fn stall(&self, ep_addr: u8);
11+
fn unstall(&self, ep_addr: u8);
12+
fn poll(&self) -> PollResult;
13+
}
14+
15+
#[repr(u8)]
16+
#[derive(Copy, Clone, Debug)]
17+
pub enum EndpointType {
18+
Control = 0b00,
19+
Isochronous = 0b01,
20+
Bulk = 0b10,
21+
Interrupt = 0b11,
22+
}
23+
24+
// bEndpointAddress:
25+
// D7: Direction 0 = OUT, 1 = IN
26+
27+
pub struct EndpointPair<'a, B: 'a + UsbBus> {
28+
bus: &'a B,
29+
address: u8,
30+
}
31+
32+
impl<'a, B: UsbBus> EndpointPair<'a, B> {
33+
pub fn new(bus: &'a B, address: u8) -> EndpointPair<'a, B> {
34+
EndpointPair { bus, address }
35+
}
36+
37+
pub fn split(self, ep_type: EndpointType, max_packet_size: u16) -> (EndpointOut<'a, B>, EndpointIn<'a, B>) {
38+
let ep_out = EndpointOut {
39+
bus: self.bus,
40+
address: self.address,
41+
ep_type: ep_type,
42+
max_packet_size,
43+
interval: 1,
44+
};
45+
46+
let ep_in = EndpointIn {
47+
bus: self.bus,
48+
address: self.address | 0x80,
49+
ep_type: ep_type,
50+
max_packet_size,
51+
interval: 1,
52+
};
53+
54+
(ep_out, ep_in)
55+
}
56+
}
57+
58+
pub trait Endpoint {
59+
fn address(&self) -> u8;
60+
fn ep_type(&self) -> EndpointType;
61+
fn max_packet_size(&self) -> u16;
62+
fn interval(&self) -> u8;
63+
}
64+
65+
pub struct EndpointOut<'a, B: 'a + UsbBus> {
66+
bus: &'a B,
67+
address: u8,
68+
ep_type: EndpointType,
69+
max_packet_size: u16,
70+
interval: u8,
71+
}
72+
73+
impl<'a, B: UsbBus> EndpointOut<'a, B> {
74+
pub fn configure(&self) -> Result<()> {
75+
self.bus.configure_ep(self.address, self.ep_type, self.max_packet_size)
76+
}
77+
78+
pub fn read(&self, data: &mut [u8]) -> Result<usize> {
79+
self.bus.read(self.address, data)
80+
}
81+
82+
pub fn stall(&self) {
83+
self.bus.stall(self.address);
84+
}
85+
86+
pub fn unstall(&self) {
87+
self.bus.unstall(self.address);
88+
}
89+
}
90+
91+
impl<'a, B: UsbBus> Endpoint for EndpointOut<'a, B> {
92+
fn address(&self) -> u8 { self.address }
93+
fn ep_type(&self) -> EndpointType { self.ep_type }
94+
fn max_packet_size(&self) -> u16 { self.max_packet_size }
95+
fn interval(&self) -> u8 { self.interval }
96+
}
97+
98+
pub struct EndpointIn<'a, B: 'a + UsbBus> {
99+
bus: &'a B,
100+
address: u8,
101+
ep_type: EndpointType,
102+
max_packet_size: u16,
103+
interval: u8,
104+
}
105+
106+
impl<'a, B: UsbBus> EndpointIn<'a, B> {
107+
pub fn configure(&self) -> Result<()> {
108+
self.bus.configure_ep(self.address, self.ep_type, self.max_packet_size)
109+
}
110+
111+
pub fn write(&self, data: &[u8]) -> Result<usize> {
112+
self.bus.write(self.address, data)
113+
}
114+
115+
pub fn stall(&self) {
116+
self.bus.stall(self.address);
117+
}
118+
119+
pub fn unstall(&self) {
120+
self.bus.unstall(self.address);
121+
}
122+
}
123+
124+
impl<'a, B: UsbBus> Endpoint for EndpointIn<'a, B> {
125+
fn address(&self) -> u8 { self.address }
126+
fn ep_type(&self) -> EndpointType { self.ep_type }
127+
fn max_packet_size(&self) -> u16 { self.max_packet_size }
128+
fn interval(&self) -> u8 { self.interval }
129+
}
130+
131+
#[derive(Default)]
132+
pub struct PollResult {
133+
pub reset: bool,
134+
pub setup: bool,
135+
pub ep_in_complete: u16,
136+
pub ep_out: u16,
137+
}

src/class.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use ::Result;
2+
pub use device::{ControlOutResult, ControlInResult};
3+
pub use descriptor::DescriptorWriter;
4+
use control;
5+
6+
pub trait UsbClass {
7+
fn reset(&self) -> Result<()> {
8+
Ok(())
9+
}
10+
11+
fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
12+
let _ = writer;
13+
Ok (())
14+
}
15+
16+
fn control_out(&self, req: &control::Request, data: &[u8]) -> ControlOutResult {
17+
let _ = (req, data);
18+
ControlOutResult::Ignore
19+
}
20+
21+
fn control_in(&self, req: &control::Request, data: &mut [u8]) -> ControlInResult {
22+
let _ = (req, data);
23+
ControlInResult::Ignore
24+
}
25+
26+
fn endpoint_out(&self, addr: u8) {
27+
let _ = addr;
28+
}
29+
30+
fn endpoint_in_complete(&self, addr: u8) {
31+
let _ = addr;
32+
}
33+
}

src/control.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use core::mem;
2+
use ::{Result, UsbError};
3+
4+
#[repr(u8)]
5+
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
6+
pub enum Direction {
7+
HostToDevice = 0,
8+
DeviceToHost = 1,
9+
}
10+
11+
#[repr(u8)]
12+
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
13+
pub enum RequestType {
14+
Standard = 0,
15+
Class = 1,
16+
Vendor = 2,
17+
Reserved = 3,
18+
}
19+
20+
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
21+
pub enum Recipient {
22+
Device = 0,
23+
Interface = 1,
24+
Endpoint = 2,
25+
Other = 3,
26+
Reserved = 4,
27+
}
28+
29+
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
30+
pub struct Request {
31+
pub direction: Direction,
32+
pub request_type: RequestType,
33+
pub recipient: Recipient,
34+
pub request: u8,
35+
pub value: u16,
36+
pub index: u16,
37+
pub length: u16,
38+
}
39+
40+
impl Request {
41+
pub(crate) fn parse(buf: &[u8]) -> Result<Request> {
42+
if buf.len() != 8 {
43+
return Err(UsbError::InvalidSetupPacket);
44+
}
45+
46+
let rt = buf[0];
47+
let recipient = rt & 0b11111;
48+
49+
Ok(Request {
50+
direction: unsafe { mem::transmute(rt >> 7) },
51+
request_type: unsafe { mem::transmute((rt >> 5) & 0b11) },
52+
recipient:
53+
if recipient <= 3 { unsafe { mem::transmute(recipient) } }
54+
else { Recipient::Reserved },
55+
request: buf[1],
56+
value: (buf[2] as u16) | ((buf[3] as u16) << 8),
57+
index: (buf[4] as u16) | ((buf[5] as u16) << 8),
58+
length: (buf[6] as u16) | ((buf[7] as u16) << 8),
59+
})
60+
}
61+
62+
pub fn descriptor_type_index(&self) -> (u8, u8) {
63+
((self.value >> 8) as u8, self.value as u8)
64+
}
65+
}
66+
67+
// TODO: Maybe move parsing standard requests here altogether
68+
69+
pub mod standard_request {
70+
pub const GET_STATUS: u8 = 0;
71+
pub const CLEAR_FEATURE: u8 = 1;
72+
pub const SET_FEATURE: u8 = 3;
73+
pub const SET_ADDRESS: u8 = 5;
74+
pub const GET_DESCRIPTOR: u8 = 6;
75+
pub const SET_DESCRIPTOR: u8 = 7;
76+
pub const GET_CONFIGURATION: u8 = 8;
77+
pub const SET_CONFIGURATION: u8 = 9;
78+
pub const GET_INTERFACE: u8 = 10;
79+
pub const SET_INTERFACE: u8 = 11;
80+
pub const SYNCH_FRAME: u8 = 12;
81+
}

0 commit comments

Comments
 (0)