blob: c66a1338e0f46311b9ab23dc18aaf20fe69eaa21 [file] [log] [blame]
Serge Bazanski81981362021-03-06 13:08:00 +01001/// USB Device Class for I2C transactions.
2//
3// It's not very good, and the API is weird. Someone with more USB device design experience could
4// easily come up with something better.
5//
6// Control OUT transactions are used to perform I2C transfers to/from an internal buffer.
7// Bulk IN/OUT transactions are used to transfer contents of the buffer to the host. It has not
8// been optimized for speed or pipelining.
9//
10// To perform an I2C read:
11// 1) Control OUT: ReadI2C(Address: 0xAA, Length: N)
12// (0xAA is the device address, N is the amount of bytes to read. Cannot be larger than
13// BUFFER_SIZE).
14// This performs an I2C read of N bytes into the inne buffer of the device, starting at
15// address 0.
16// 2) Control IN: GetStatus()
17// The host ensures that the transaction was either ACK or NACK by getting one byte of status
18// from the device.
19// 3) Control OUT: ReadBuffer(Address: X, Length: N)
20// (X is the address within the buffer, N is the amount of bytes to transfer to the host. N
21// cannot be larger than PACKET_SIZE).
22// 4) Bulk IN: Read PACKET_SIZE bytes.
23// Steps 3/4 can be skipped for scanning (the device won't mind the inner buffer not being read).
24//
25// To perform an I2C write:
26// 1) Control OUT: SetWritePointer(Addrss: X)
27// 2) Bulk OUT: Write at most PACKET_SIZE bytes.
28// Repeat steps 1/2 to fill buffer with an I2c transaction.
29// 3) Control OUT: WriteI2C(Address: 0x00, Length: N)
30// (0xAA is the device address, N is the amount of bytes to write. Cannot be larger than
31// BUFFER_SIZE).
32// 4) Control IN: GetStatus()
33// The host ensures that the transaction was either ACK or NACK by getting one byte of status
34// from the device.
35
36use embedded_hal::digital::v2::OutputPin;
37use usb_device::class_prelude::*;
38use nb::Error as NbError;
39use stm32f1xx_hal::{
40 gpio::{gpiob::*, Alternate, OpenDrain},
41 i2c::{BlockingI2c, Error as I2CError},
42 pac::I2C1,
43 prelude::*,
44};
45
46use num_derive::FromPrimitive;
47use num_traits::FromPrimitive;
48
49use crate::{hprint, hprintln};
50
51// Size of buffer within class, in bytes. Dictates maximum I2C transaction size.
52const BUFFER_SIZE: usize = 1024;
53// Size of bulk packets.
54const PACKET_SIZE: usize = 64;
55
56// All IN/OUT references bellow conform to typical USB naming, where IN: from device to host; OUT:
57// from host to device.
58
59/// Request number passed within Control IN requests to the I2C interface (ie. 'gets' from device).
60#[derive(FromPrimitive)]
61#[repr(u8)]
62enum ControlInRequest {
63 /// Write the current status as a single byte in response.
64 GetStatus = 1,
65}
66
67/// Request number passed within Control OUT requests to the I2C interface (ie. 'sets' from the
68/// host).
69#[derive(FromPrimitive)]
70#[repr(u8)]
71enum ControlOutRequest {
72 /// Set LED on or off (value == 0 -> off; on otherwise).
73 SetLED = 1,
74
75 /// Perform I2C bus read of a given length from a given I2C address.
76 /// I2C Address: lower 8 bits of value.
77 /// Read Length: upper 8 bits of value.
78 ReadI2C = 2,
79
80 /// Schedule a BULK IN transaction on the USB bus with the contents of the inner buffer.
81 /// Buffer start address: lower 8 bits of value
82 /// Read Length: upper 8 bits of value.
83 ReadBuffer = 3,
84
85 /// Perform I2C bus write of a given length to a given I2C address.
86 /// I2C Address: lower 8 bits of value.
87 /// Read Length: upper 8 bits of value.
88 WriteI2C = 4,
89
90 /// Set inner buffer write pointer. Any subsequent BULK OUT will write to the buffer at that
91 /// address (but will not auto advance the pointer).
92 SetWritePointer = 5,
93}
94
95/// Status of the I2C class. Combines information about requested transactions and I2C bus
96/// responses.
97#[derive(Copy, Clone)]
98#[repr(u8)]
99enum Status {
100 /// Last request okay.
101 OK = 0,
102 /// Last request contained an invalid argument.
103 InvalidArgument = 1,
104 /// Last request okay, resulted in a successful I2C transaction.
105 Ack = 2,
106 /// Last request okay, resulted in a NACKd I2C transaction.
107 Nack = 3,
108 /// Last request okay, resulted in a fully failed I2C transaction.
109 BusError = 4,
110}
111
112pub struct I2CClass<'a, B: UsbBus, LED> {
113 interface: InterfaceNumber,
114 /// Bulk IN endpoint for buffer transfers to host.
115 ep_in: EndpointIn<'a, B>,
116 /// Bulk OUT endpoint for buffer transfers from host.
117 ep_out: EndpointOut<'a, B>,
118
119 /// LED used for debugging.
120 led: LED,
121
122 /// The underlying I2C device.
123 i2c_dev: BlockingI2c<I2C1, (PB6<Alternate<OpenDrain>>, PB7<Alternate<OpenDrain>>)>,
124
125 /// Marker that is true when the host requested a BULK OUT via ReadBuffer.
126 expect_bulk_out: bool,
127
128 /// The underlying buffer and its write pointer.
129 buffer: [u8; BUFFER_SIZE],
130 write_pointer: usize,
131
132 /// The device's main status byte, used by host to check whether operations were succesful.
133 status: Status,
134}
135
136impl<B: UsbBus, LED: OutputPin> I2CClass<'_, B, LED> {
137 pub fn new(
138 alloc: &UsbBusAllocator<B>,
139 led: LED,
140 i2c_dev: BlockingI2c<I2C1, (PB6<Alternate<OpenDrain>>, PB7<Alternate<OpenDrain>>)>,
141 ) -> I2CClass<'_, B, LED> {
142 I2CClass {
143 interface: alloc.interface(),
144 ep_in: alloc.bulk(PACKET_SIZE as u16),
145 ep_out: alloc.bulk(PACKET_SIZE as u16),
146 led, i2c_dev,
147
148 expect_bulk_out: false,
149
150 buffer: [0; BUFFER_SIZE],
151 write_pointer: 0usize,
152 status: Status::OK,
153 }
154 }
155}
156
157impl<'a, B: UsbBus, LED: OutputPin> UsbClass<B> for I2CClass<'a, B, LED> {
158 fn reset(&mut self) {
159 self.expect_bulk_out = false;
Serge Bazanskic8b14e72021-03-27 11:52:44 +0000160 self.status = Status::OK;
Serge Bazanski81981362021-03-06 13:08:00 +0100161 }
162
163 fn control_in(&mut self, xfer: ControlIn<B>) {
164 let req = xfer.request();
165
166 if req.request_type != control::RequestType::Vendor
167 || req.recipient != control::Recipient::Interface
168 || req.index != u8::from(self.interface) as u16 {
169 return
170 }
171
172 match FromPrimitive::from_u8(req.request) {
173 /// Serve GetStatus: return this.status.
174 Some(ControlInRequest::GetStatus) => {
175 let status = self.status.clone() as u8;
176 xfer.accept(|buf| {
177 buf[0] = status;
178 Ok(1usize)
179 }).ok();
180 },
181 _ => {
182 hprintln!("Unhandled control in on iface: {:?}", req).unwrap();
183 },
184 }
185 }
186
187 fn control_out(&mut self, xfer: ControlOut<B>) {
188 let req = xfer.request();
189
190 if req.request_type != control::RequestType::Vendor
191 || req.recipient != control::Recipient::Interface
192 || req.index != u8::from(self.interface) as u16 {
193 return
194 }
195
196 match FromPrimitive::from_u8(req.request) {
197 // Serve SetLED.
198 Some(ControlOutRequest::SetLED) => {
199 let on: bool = req.value > 0;
200 match on {
201 true => self.led.set_low(),
202 false => self.led.set_high(),
203 }.ok();
204 xfer.accept().ok();
205 },
206
207 // Serve ReadI2C: read len bytes from I2C addr into internal buffer.
208 Some(ControlOutRequest::ReadI2C) => {
209 let addr: u8 = (req.value & 0xff) as u8;
210 let len: u8 = (req.value >> 8) as u8;
211 if len as usize > BUFFER_SIZE || len < 1u8 {
212 self.status = Status::InvalidArgument;
213 xfer.accept().ok();
214 return
215 }
216 if addr > 127u8 {
217 self.status = Status::InvalidArgument;
218 xfer.accept().ok();
219 return
220 }
221 match self.i2c_dev.read(addr, &mut self.buffer[0usize..(len as usize)]) {
222 Ok(_) => {
223 self.status = Status::Ack;
224 },
225 Err(NbError::Other(I2CError::Acknowledge)) => {
226 self.status = Status::Nack;
227 },
228 Err(e) => {
229 hprintln!("When reading I2C (addr {}, {} bytes): {:?}", addr, len, e).ok();
230 self.status = Status::BusError;
231 },
232 }
233 xfer.accept().ok();
234 },
235
236 // Serve ReadBuffer: send BULK IN with slice of buffer.
237 Some(ControlOutRequest::ReadBuffer) => {
238 let addr: u8 = (req.value & 0xff) as u8;
239 let len: u8 = (req.value >> 8) as u8;
240
241 if len as usize > PACKET_SIZE || len < 1u8 {
242 self.status = Status::InvalidArgument;
243 xfer.accept().ok();
244 return
245 }
246
247 let start = addr as usize;
248 let end = (addr + len) as usize;
249 if end as usize > BUFFER_SIZE {
250 self.status = Status::InvalidArgument;
251 xfer.accept().ok();
252 return
253 }
254
255 hprintln!("READ BUFFER, addr: {}, len: {}", addr, len).ok();
256
257 self.status = Status::OK;
258 xfer.accept().ok();
259 match self.ep_in.write(&self.buffer[start..end]) {
260 Ok(count) => {
261 },
262 Err(UsbError::WouldBlock) => {},
263 Err(err) => {
264 hprintln!("bulk write failed: {:?}", err).ok();
265 },
266 }
267 },
268
269 // Serve WriteI2C: write len bytes to I2C bus at addr from internal buffer.
270 Some(ControlOutRequest::WriteI2C) => {
271 let addr: u8 = (req.value & 0xff) as u8;
272 let len: u8 = (req.value >> 8) as u8;
273 if len as usize > BUFFER_SIZE || len < 1u8 {
274 self.status = Status::InvalidArgument;
275 xfer.accept().ok();
276 return
277 }
278 if addr > 127u8 {
279 self.status = Status::InvalidArgument;
280 xfer.accept().ok();
281 return
282 }
283
284 hprintln!("WRITE I2C, addr: {}, len: {}", addr, len).ok();
285 match self.i2c_dev.write(addr, &self.buffer[0usize..(len as usize)]) {
286 Ok(_) => {
287 self.status = Status::Ack;
288 },
289 Err(NbError::Other(I2CError::Acknowledge)) => {
290 self.status = Status::Nack;
291 },
292 Err(e) => {
293 hprintln!("When writing I2C (addr {}, {} bytes): {:?}", addr, len, e).ok();
294 self.status = Status::BusError;
295 },
296 }
297 xfer.accept().ok();
298 },
299
300 // Serve SetWritePointer: set start address at which bytes from a BULK OUT will be
301 // written to. The write pointer does _not_ increment on every write, so will need to
302 // be manually controler after every BULK transfer.
303 Some(ControlOutRequest::SetWritePointer) => {
304 let pointer = req.value;
305 if (pointer as usize) >= BUFFER_SIZE {
306 self.status = Status::InvalidArgument;
307 xfer.accept().ok();
308 return
309 }
310 hprintln!("SET WRITE PTR, pointer: {}", pointer).ok();
311 self.write_pointer = pointer as usize;
312 self.status = Status::OK;
313 xfer.accept().ok();
314 },
315 _ => {
316 hprintln!("Unhandled control out on iface: {:?}", req).ok();
317 },
318 }
319 }
320
321 fn get_configuration_descriptors(
322 &self,
323 writer: &mut DescriptorWriter,
324 ) -> usb_device::Result<()> {
325 writer.interface(
326 self.interface,
327 0xff,
328 21, 37,
329 )?;
330 writer.endpoint(&self.ep_in)?;
331 writer.endpoint(&self.ep_out)?;
332
333 Ok(())
334 }
335
336 fn poll(&mut self) {
337 let mut temp_buf = [0; PACKET_SIZE];
338 // Serve BULK OUT writes - copy bytes into internal buffer.
339 match self.ep_out.read(&mut temp_buf) {
340 Ok(count) => {
341 if self.expect_bulk_out {
342 self.expect_bulk_out = false;
343 } else {
344 panic!("unexpectedly read data from bulk out endpoint");
345 }
346 hprintln!("SET BUFFER: ptr {}, {} bytes", self.write_pointer, count).ok();
347 for (i, c) in temp_buf.iter().enumerate() {
348 let ptr = self.write_pointer + i;
349 // Silently drop bytes that do not fit in buffer.
350 if ptr >= BUFFER_SIZE {
351 continue;
352 }
353 self.buffer[ptr] = c.clone();
354 }
355 },
356 Err(UsbError::WouldBlock) => {},
357 Err(err) => panic!("bulk read {:?}", err),
358 }
359 }
360
361 fn endpoint_out(&mut self, addr: EndpointAddress) {
362 if addr == self.ep_out.address() {
363 self.expect_bulk_out = true;
364 }
365 }
366
367 fn endpoint_in_complete(&mut self, addr: EndpointAddress) {
368 if addr == self.ep_in.address() {
369 // TODO(q3k): should we be doing something here?
370 }
371 }
372}