1use anyhow::{Result, bail, ensure};
6use std::cell::Cell;
7use std::cmp;
8use std::rc::Rc;
9use std::time::Duration;
10use zerocopy::{FromBytes, Immutable, IntoBytes};
11
12use crate::io::gpio::GpioPin;
13use crate::io::i2c::{self, Bus, DeviceStatus, DeviceTransfer, I2cError, ReadStatus, Transfer};
14use crate::transport::hyperdebug::{BulkInterface, Inner};
15use crate::transport::{TransportError, TransportInterfaceType};
16
17pub struct HyperdebugI2cBus {
18 inner: Rc<Inner>,
19 interface: BulkInterface,
20 cmsis_encapsulation: bool,
21 supports_i2c_device: bool,
22 bus_idx: u8,
23 mode: Cell<Mode>,
24 max_write_size: usize,
25 max_read_size: usize,
26 default_addr: Cell<Option<u8>>,
27}
28
29#[derive(Copy, Clone, Debug, Eq, PartialEq)]
30pub enum Mode {
31 Host,
32 Device,
33}
34
35const USB_MAX_SIZE: usize = 64;
36
37#[derive(Immutable, IntoBytes, FromBytes, Debug)]
40#[allow(dead_code)] #[repr(C, packed)]
42struct CmdTransferShort {
43 encapsulation_header: u8,
44 port: u8,
45 addr: u8,
46 write_count: u8,
47 read_count: u8,
48 data: [u8; USB_MAX_SIZE - 4],
49}
50
51#[derive(Immutable, IntoBytes, FromBytes, Debug)]
54#[allow(dead_code)] #[repr(C, packed)]
56struct CmdTransferLong {
57 encapsulation_header: u8,
58 port: u8,
59 addr: u8,
60 write_count: u8,
61 read_count: u8,
62 read_count1: u8,
63 flags: u8,
64 data: [u8; USB_MAX_SIZE - 6],
65}
66
67#[derive(Immutable, IntoBytes, FromBytes, Debug)]
69#[allow(dead_code)] #[repr(C, packed)]
71struct RspTransfer {
72 encapsulation_header: u8,
73 status_code: u16,
74 reserved: u16,
75 data: [u8; USB_MAX_SIZE],
76}
77impl RspTransfer {
78 fn new() -> Self {
79 Self {
80 encapsulation_header: 0,
81 status_code: 0,
82 reserved: 0,
83 data: [0; USB_MAX_SIZE],
84 }
85 }
86}
87
88#[derive(Immutable, IntoBytes, FromBytes, Debug)]
89#[allow(dead_code)] #[repr(C, packed)]
91struct CmdGetDeviceStatus {
92 encapsulation_header: u8,
93 port: u8,
94 device_cmd: u8,
95 timeout_ms: u16,
96}
97
98const I2C_DEVICE_CMD_GET_DEVICE_STATUS: u8 = 0x00;
100const I2C_DEVICE_CMD_PREPARE_READ_DATA: u8 = 0x01;
101
102const I2C_DEVICE_FLAG_STICKY: u8 = 0x80;
104
105#[derive(Immutable, IntoBytes, FromBytes, Debug)]
106#[repr(C, packed)]
107struct RspGetDeviceStatus {
108 encapsulation_header: u8,
109 struct_size: u16,
110 read_status: u8,
111 blocked_read_addr: u8,
112 transcript_size: u16,
113 data: [u8; USB_MAX_SIZE],
114}
115impl RspGetDeviceStatus {
116 fn new() -> Self {
117 Self {
118 encapsulation_header: 0,
119 struct_size: 0,
120 read_status: 0,
121 blocked_read_addr: 0,
122 transcript_size: 0,
123 data: [0u8; USB_MAX_SIZE],
124 }
125 }
126}
127
128#[derive(Immutable, IntoBytes, FromBytes, Debug)]
129#[allow(dead_code)] #[repr(C, packed)]
131struct CmdPrepareReadData {
132 encapsulation_header: u8,
133 port: u8,
134 device_cmd: u8,
135 data_size: u16,
136 data: [u8; USB_MAX_SIZE - 4],
137}
138
139impl HyperdebugI2cBus {
140 const CMSIS_DAP_CUSTOM_COMMAND_I2C: u8 = 0x81;
144
145 const CMSIS_DAP_CUSTOM_COMMAND_I2C_DEVICE: u8 = 0x82;
147
148 pub fn open(
149 inner: &Rc<Inner>,
150 i2c_interface: &BulkInterface,
151 cmsis_encapsulation: bool,
152 supports_i2c_device: bool,
153 idx: u8,
154 mode: Mode,
155 ) -> Result<Self> {
156 ensure!(
157 idx < 16,
158 TransportError::InvalidInstance(TransportInterfaceType::I2c, idx.to_string())
159 );
160 let mut usb_handle = inner.usb_device.borrow_mut();
161
162 usb_handle.claim_interface(i2c_interface.interface)?;
164
165 Ok(Self {
166 inner: Rc::clone(inner),
167 interface: *i2c_interface,
168 cmsis_encapsulation,
169 supports_i2c_device,
170 bus_idx: idx,
171 mode: Cell::new(mode),
172 max_read_size: 0x8000,
173 max_write_size: 0x1000,
174 default_addr: Cell::new(None),
175 })
176 }
177
178 fn transmit_then_receive(
180 &self,
181 addr: u8,
182 wbuf: &[u8],
183 rbuf: &mut [u8],
184 gsc_ready: bool,
185 ) -> Result<()> {
186 ensure!(
187 rbuf.len() < self.max_read_size,
188 I2cError::InvalidDataLength(rbuf.len())
189 );
190 ensure!(
191 wbuf.len() < self.max_write_size,
192 I2cError::InvalidDataLength(wbuf.len())
193 );
194 let encapsulation_header_size = if self.cmsis_encapsulation { 1 } else { 0 };
195 let mut index = if rbuf.len() < 128 && !gsc_ready {
196 let mut req = CmdTransferShort {
198 encapsulation_header: Self::CMSIS_DAP_CUSTOM_COMMAND_I2C,
199 port: self.bus_idx | (((wbuf.len() & 0x0F00) >> 4) as u8),
200 addr,
201 write_count: (wbuf.len() & 0x00FF) as u8,
202 read_count: rbuf.len() as u8,
203 data: [0; USB_MAX_SIZE - 4],
204 };
205 let databytes = cmp::min(USB_MAX_SIZE - 4 - encapsulation_header_size, wbuf.len());
206 req.data[..databytes].clone_from_slice(&wbuf[..databytes]);
207 self.usb_write_bulk(&req.as_bytes()[1 - encapsulation_header_size..1 + 4 + databytes])?;
208 databytes
209 } else {
210 let mut req = CmdTransferLong {
212 encapsulation_header: Self::CMSIS_DAP_CUSTOM_COMMAND_I2C,
213 port: self.bus_idx | (((wbuf.len() & 0x0F00) >> 4) as u8),
214 addr,
215 write_count: (wbuf.len() & 0x00FF) as u8,
216 read_count: (rbuf.len() & 0x007F | 0x0080) as u8,
217 read_count1: (rbuf.len() >> 7) as u8,
218 flags: if gsc_ready { 0x80 } else { 0x00 },
219 data: [0; USB_MAX_SIZE - 6],
220 };
221 let databytes = cmp::min(USB_MAX_SIZE - 6 - encapsulation_header_size, wbuf.len());
222 req.data[..databytes].clone_from_slice(&wbuf[..databytes]);
223 self.usb_write_bulk(&req.as_bytes()[1 - encapsulation_header_size..1 + 6 + databytes])?;
224 databytes
225 };
226
227 while index < wbuf.len() {
229 let databytes = cmp::min(USB_MAX_SIZE, wbuf.len() - index);
230 self.usb_write_bulk(&wbuf[index..index + databytes])?;
231 index += databytes;
232 }
233
234 let mut resp = RspTransfer::new();
235 let mut bytecount = 0;
236 while bytecount < 4 + encapsulation_header_size {
237 let read_count = self.usb_read_bulk(
238 &mut resp.as_mut_bytes()[1 - encapsulation_header_size + bytecount..][..64],
239 )?;
240 ensure!(
241 read_count > 0,
242 TransportError::CommunicationError("Truncated I2C response".to_string())
243 );
244 bytecount += read_count;
245 }
246 if encapsulation_header_size == 1 {
247 ensure!(
248 resp.encapsulation_header == Self::CMSIS_DAP_CUSTOM_COMMAND_I2C,
249 TransportError::CommunicationError(
250 "Unrecognized CMSIS-DAP response to I2C request".to_string()
251 )
252 );
253 }
254 match resp.status_code {
255 0 => (),
256 1 => bail!(I2cError::Timeout),
257 2 => bail!(I2cError::Busy),
258 n => bail!(TransportError::CommunicationError(format!(
259 "I2C error: {}",
260 n
261 ))),
262 }
263 let databytes = bytecount - 4 - encapsulation_header_size;
264 rbuf[..databytes].clone_from_slice(&resp.data[..databytes]);
265 let mut index = databytes;
266 while index < rbuf.len() {
267 let databytes = self.usb_read_bulk(&mut rbuf[index..])?;
268 ensure!(
269 databytes > 0,
270 TransportError::CommunicationError(
271 "Unrecognized reponse to I2C request".to_string()
272 )
273 );
274 index += databytes;
275 }
276 Ok(())
277 }
278
279 fn usb_write_bulk(&self, buf: &[u8]) -> Result<()> {
281 self.inner
282 .usb_device
283 .borrow()
284 .write_bulk(self.interface.out_endpoint, buf)?;
285 Ok(())
286 }
287
288 fn usb_read_bulk(&self, buf: &mut [u8]) -> Result<usize> {
290 self.inner
291 .usb_device
292 .borrow()
293 .read_bulk(self.interface.in_endpoint, buf)
294 }
295
296 fn usb_read_bulk_timeout(&self, buf: &mut [u8], timeout: Duration) -> Result<usize> {
298 self.inner
299 .usb_device
300 .borrow()
301 .read_bulk_timeout(self.interface.in_endpoint, buf, timeout)
302 }
303}
304
305impl Bus for HyperdebugI2cBus {
306 fn set_mode(&self, mode: i2c::Mode) -> Result<()> {
307 match mode {
308 i2c::Mode::Host => {
310 self.inner
311 .cmd_no_output(&format!("i2c set mode {} host", &self.bus_idx))?;
312 self.mode.set(Mode::Host);
313 }
314 i2c::Mode::Device(addr) => {
316 ensure!(
317 self.supports_i2c_device,
318 TransportError::UnsupportedOperation,
319 );
320 self.inner
321 .cmd_no_output(&format!("i2c set mode {} device {}", &self.bus_idx, addr))?;
322 self.mode.set(Mode::Device);
323 }
324 }
325 Ok(())
326 }
327
328 fn get_max_speed(&self) -> Result<u32> {
330 let mut buf = String::new();
331 let captures = self.inner.cmd_one_line_output_match(
332 &format!("i2c info {}", &self.bus_idx),
333 &super::SPI_REGEX,
334 &mut buf,
335 )?;
336 Ok(captures.get(3).unwrap().as_str().parse().unwrap())
337 }
338
339 fn set_max_speed(&self, max_speed: u32) -> Result<()> {
342 self.inner
343 .cmd_no_output(&format!("i2c set speed {} {}", &self.bus_idx, max_speed))
344 }
345
346 fn set_pins(
347 &self,
348 serial_clock: Option<&Rc<dyn GpioPin>>,
349 serial_data: Option<&Rc<dyn GpioPin>>,
350 gsc_ready: Option<&Rc<dyn GpioPin>>,
351 ) -> Result<()> {
352 if serial_clock.is_some() || serial_data.is_some() {
353 bail!(I2cError::InvalidPin);
354 }
355 if let Some(pin) = gsc_ready {
356 self.inner.cmd_no_output(&format!(
357 "i2c set ready {} {}",
358 &self.bus_idx,
359 pin.get_internal_pin_name().ok_or(I2cError::InvalidPin)?
360 ))?;
361 }
362 Ok(())
363 }
364
365 fn set_default_address(&self, addr: u8) -> Result<()> {
366 self.default_addr.set(Some(addr));
367 Ok(())
368 }
369
370 fn run_transaction(&self, addr: Option<u8>, mut transaction: &mut [Transfer]) -> Result<()> {
371 let addr = addr
372 .or(self.default_addr.get())
373 .ok_or(I2cError::MissingAddress)?;
374 while !transaction.is_empty() {
375 match transaction {
376 [
377 Transfer::Write(wbuf),
378 Transfer::GscReady,
379 Transfer::Read(rbuf),
380 ..,
381 ] => {
382 ensure!(
386 wbuf.len() <= self.max_write_size,
387 I2cError::InvalidDataLength(wbuf.len())
388 );
389 ensure!(
390 rbuf.len() <= self.max_read_size,
391 I2cError::InvalidDataLength(rbuf.len())
392 );
393 self.transmit_then_receive(addr, wbuf, rbuf, true)?;
394 transaction = &mut transaction[3..];
396 }
397 [Transfer::Write(wbuf), Transfer::Read(rbuf), ..] => {
398 ensure!(
402 wbuf.len() <= self.max_write_size,
403 I2cError::InvalidDataLength(wbuf.len())
404 );
405 ensure!(
406 rbuf.len() <= self.max_read_size,
407 I2cError::InvalidDataLength(rbuf.len())
408 );
409 self.transmit_then_receive(addr, wbuf, rbuf, false)?;
410 transaction = &mut transaction[2..];
412 }
413 [Transfer::Write(wbuf), Transfer::GscReady, ..] => {
414 ensure!(
415 wbuf.len() <= self.max_write_size,
416 I2cError::InvalidDataLength(wbuf.len())
417 );
418 self.transmit_then_receive(addr, wbuf, &mut [], true)?;
419 transaction = &mut transaction[2..];
421 }
422 [Transfer::Write(wbuf), ..] => {
423 ensure!(
424 wbuf.len() <= self.max_write_size,
425 I2cError::InvalidDataLength(wbuf.len())
426 );
427 self.transmit_then_receive(addr, wbuf, &mut [], false)?;
428 transaction = &mut transaction[1..];
429 }
430 [Transfer::Read(rbuf), ..] => {
431 ensure!(
432 rbuf.len() <= self.max_read_size,
433 I2cError::InvalidDataLength(rbuf.len())
434 );
435 self.transmit_then_receive(addr, &[], rbuf, false)?;
436 transaction = &mut transaction[1..];
437 }
438 [] => (),
439 _ => bail!(TransportError::UnsupportedOperation),
440 }
441 }
442 Ok(())
443 }
444
445 fn get_device_status(&self, timeout: Duration) -> Result<DeviceStatus> {
446 ensure!(
447 self.cmsis_encapsulation && self.supports_i2c_device,
448 TransportError::UnsupportedOperation
449 );
450 ensure!(self.mode.get() == Mode::Device, I2cError::NotInDeviceMode);
451 let timeout_ms = timeout.as_millis();
452 let req = CmdGetDeviceStatus {
453 encapsulation_header: Self::CMSIS_DAP_CUSTOM_COMMAND_I2C_DEVICE,
454 port: self.bus_idx,
455 device_cmd: I2C_DEVICE_CMD_GET_DEVICE_STATUS,
456 timeout_ms: if timeout_ms > 65535 {
457 65535
458 } else {
459 timeout_ms as u16
460 },
461 };
462 self.usb_write_bulk(req.as_bytes())?;
463
464 let mut resp = RspGetDeviceStatus::new();
465 let mut bytecount = 0;
466 while bytecount < 7 {
467 let read_count = self.usb_read_bulk_timeout(
468 &mut resp.as_mut_bytes()[bytecount..][..64],
469 Duration::from_millis(req.timeout_ms as u64 + 500),
470 )?;
471 ensure!(
472 read_count > 0,
473 TransportError::CommunicationError("Truncated I2C response".to_string())
474 );
475 bytecount += read_count;
476 }
477 ensure!(
478 resp.encapsulation_header == Self::CMSIS_DAP_CUSTOM_COMMAND_I2C_DEVICE,
479 TransportError::CommunicationError(
480 "Unrecognized CMSIS-DAP response to I2C request".to_string()
481 )
482 );
483 let skip_bytes = resp.struct_size - 6;
484
485 let mut databytes: Vec<u8> = Vec::new();
486 databytes.extend_from_slice(&resp.data[..bytecount - 7]);
487
488 while databytes.len() < (skip_bytes + resp.transcript_size) as usize {
489 let original_length = databytes.len();
490 databytes.resize(original_length + 64, 0u8);
491 let c = self.usb_read_bulk(&mut databytes[original_length..])?;
492 databytes.resize(original_length + c, 0u8);
493 }
494
495 let mut transfers = Vec::new();
496 let mut idx = skip_bytes as usize;
497 while idx < databytes.len() {
498 let addr = databytes[idx] >> 1;
499 let is_read = (databytes[idx] & 0x01) != 0;
500 let timeout = (databytes[idx + 1] & 0x01) != 0;
501 let transfer_len = databytes[idx + 2] as usize + ((databytes[idx + 3] as usize) << 8);
502 idx += 4;
503 if is_read {
504 transfers.push(DeviceTransfer::Read {
506 addr,
507 timeout,
508 len: transfer_len,
509 });
510 } else {
511 transfers.push(DeviceTransfer::Write {
513 addr,
514 data: databytes[idx..idx + transfer_len].to_vec(),
515 });
516 idx += (transfer_len + 3) & !3;
517 }
518 }
519
520 let read_status = match resp.read_status {
521 0 => ReadStatus::Idle,
522 1 => ReadStatus::DataPrepared,
523 2 => ReadStatus::WaitingForData(resp.blocked_read_addr >> 1),
524 _ => bail!(TransportError::CommunicationError(
525 "Unrecognized I2C read status".to_string()
526 )),
527 };
528
529 Ok(DeviceStatus {
530 transfers,
531 read_status,
532 })
533 }
534
535 fn prepare_read_data(&self, data: &[u8], sticky: bool) -> Result<()> {
536 ensure!(
537 self.cmsis_encapsulation && self.supports_i2c_device,
538 TransportError::UnsupportedOperation
539 );
540 ensure!(self.mode.get() == Mode::Device, I2cError::NotInDeviceMode);
541 if data.len() > 1024 {
542 bail!(TransportError::CommunicationError(
543 "Data exceeds maximum length".to_string()
544 ))
545 }
546 let flags = if sticky { I2C_DEVICE_FLAG_STICKY } else { 0 };
547 let mut req = CmdPrepareReadData {
548 encapsulation_header: Self::CMSIS_DAP_CUSTOM_COMMAND_I2C_DEVICE,
549 port: self.bus_idx | flags,
550 device_cmd: I2C_DEVICE_CMD_PREPARE_READ_DATA,
551 data_size: data.len() as u16,
552 data: [0; USB_MAX_SIZE - 4],
553 };
554 let mut index = std::cmp::min(64 - 5, data.len());
556 req.data[..index].clone_from_slice(&data[..index]);
557 self.usb_write_bulk(&req.as_bytes()[..5 + index])?;
558
559 while index < data.len() {
560 let packet_len = std::cmp::min(64, data.len() - index);
561 self.usb_write_bulk(&data[index..index + packet_len])?;
562 index += packet_len;
563 }
564
565 let mut resp = 0u8;
566 let c = self.usb_read_bulk(std::slice::from_mut(&mut resp))?;
567 ensure!(
568 c == 1 && resp == Self::CMSIS_DAP_CUSTOM_COMMAND_I2C_DEVICE,
569 TransportError::CommunicationError(
570 "Unrecognized CMSIS-DAP response to I2C request".to_string()
571 )
572 );
573 Ok(())
574 }
575}