commit 7aa98c0c683b5561c6d11caf20b24c3d1926d836 from: Benjamin Stürz date: Fri Feb 07 20:56:17 2025 UTC write a working demo driver commit - b7e0bc374d935a50b7e60e741ba7c99efcf123dd commit + 7aa98c0c683b5561c6d11caf20b24c3d1926d836 blob - ea8c4bf7f35f6f77f75d92ad8ce8349f6e81ddba blob + ef44ab21fd8f7d4ae212f791f16a02dfa1dec7e0 --- .gitignore +++ .gitignore @@ -1 +1,2 @@ -/target +target +ppa6 blob - e950f4221ea673013612454faad769ce61e482a6 blob + 30df70e6c521ba0775bc976ccecc6cad4a099904 --- src/main.rs +++ src/main.rs @@ -1,80 +1,174 @@ // Very helpful doc for USB: https://www.beyondlogic.org/usbnutshell/usb1.shtml -use rusb::{Direction, TransferType, UsbContext}; +use anyhow::{Result, Context as _}; +use std::{iter::repeat_n, time::Duration}; +use rusb::{Direction, TransferType, UsbContext, Context, DeviceHandle}; const VENDOR_ID: u16 = 0x09c5; const PRODUCT_ID: u16 = 0x0200; -fn main() { - println!("libusb version: {:?}", rusb::version()); - println!("Kernel supports detaching driver: {}", rusb::supports_detach_kernel_driver()); +struct Peripage { + handle: DeviceHandle, + ep: u8, +} - - let ctx = rusb::Context::new().expect("cannot connect to libusb"); +impl Peripage { + fn find(ctx: &Context) -> Result { + let dev = ctx + .devices() + .context("cannot read list of devices")? + .iter() + .find(|dev| { + let Ok(desc) = dev.device_descriptor() else { + eprintln!("cannot get device descriptor for Bus {dev:?}"); + return false + }; - // Find Peripage A6 - let dev = ctx - .devices() - .expect("cannot read list of devices") - .iter() - .find(|dev| { - let Ok(desc) = dev.device_descriptor() else { - eprintln!("cannot get device descriptor for Bus {dev:?}"); - return false - }; + desc.vendor_id() == VENDOR_ID && desc.product_id() == PRODUCT_ID + }) + .context("no Peripage A6 found")?; - desc.vendor_id() == VENDOR_ID && desc.product_id() == PRODUCT_ID - }) - .expect("No Peripage A6 found") - ; + let handle = dev.open().context("cannot open usb device")?; + Self::connect(handle) + } - let handle = dev.open().expect("cannot open usb device"); + fn connect(handle: DeviceHandle) -> Result { + let dev = handle.device(); + let _ = handle.set_auto_detach_kernel_driver(false); - let dd = dev.device_descriptor().unwrap(); - println!("Device Descriptor = {dd:#?}"); - assert_eq!(dd.vendor_id(), VENDOR_ID); - assert_eq!(dd.product_id(), PRODUCT_ID); - if let Ok(s) = handle.read_manufacturer_string_ascii(&dd) { - println!("Vendor: {s}"); + let dd = dev.device_descriptor().unwrap(); + println!("Device Descriptor = {dd:#?}"); + assert_eq!(dd.vendor_id(), VENDOR_ID); + assert_eq!(dd.product_id(), PRODUCT_ID); + if let Ok(s) = handle.read_manufacturer_string_ascii(&dd) { + println!("Vendor: {s}"); + } + if let Ok(s) = handle.read_product_string_ascii(&dd) { + println!("Product: {s}"); + } + if let Ok(s) = handle.read_serial_number_string_ascii(&dd) { + println!("Serial: {s}"); + } + + assert_eq!(dd.num_configurations(), 1); + let cd = dev.config_descriptor(0).unwrap(); + println!("Config Descriptor = {cd:#?}"); + + assert_eq!(cd.num_interfaces(), 1); + let int = cd.interfaces().next().unwrap(); + let id = int.descriptors().next().unwrap(); + println!("Interface Descriptor = {id:#?}"); + if let Some(sid) = id.description_string_index() { + println!("Interface: {}", handle.read_string_descriptor_ascii(sid).unwrap()); + } + let kactive = handle.kernel_driver_active(0); + println!("Is kernel driver attached: {kactive:?}"); + + assert_eq!(id.class_code(), 7); // Printer + assert_eq!(id.sub_class_code(), 1); // Printer + assert_eq!(id.protocol_code(), 2); // Bi-directional + assert_eq!(id.num_endpoints(), 2); + + let mut epds = id.endpoint_descriptors(); + let epd0 = epds.next().unwrap(); + let epd1 = epds.next().unwrap(); + println!("Endpoint Descriptor 0: {epd0:#?}"); + println!("Endpoint Descriptor 1: {epd1:#?}"); + + assert_eq!(epd0.address(), 129); // IN (128) + 1 + assert_eq!(epd0.direction(), Direction::In); + assert_eq!(epd0.transfer_type(), TransferType::Bulk); + + assert_eq!(epd1.address(), 2); // OUT (0) + 2 + assert_eq!(epd1.direction(), Direction::Out); + assert_eq!(epd1.transfer_type(), TransferType::Bulk); + + let ep = epd1.address(); + + if let Ok(true) = kactive { + handle + .detach_kernel_driver(0) + .context("failed to detach kernel driver")?; + } + + handle + .claim_interface(0) + .context("failed to claim interface 0")?; + + Ok(Self { + handle, + ep, + }) } - if let Ok(s) = handle.read_product_string_ascii(&dd) { - println!("Product: {s}"); + + fn write(&mut self, buf: &[u8], timeout: u64) -> Result<()> { + self.handle.write_bulk(self.ep, buf, Duration::from_secs(timeout))?; + Ok(()) } - if let Ok(s) = handle.read_serial_number_string_ascii(&dd) { - println!("Serial: {s}"); + + fn newline(&mut self) -> Result<()> { + let buf = &[0x10, 0xff, 0xfe, 0x01]; + self.write(buf, 1) } - assert_eq!(dd.num_configurations(), 1); - let cd = dev.config_descriptor(0).unwrap(); - println!("Config Descriptor = {cd:#?}"); + fn confirm(&mut self) -> Result<()> { + let buf = &[0x10, 0xff, 0xfe, 0x45]; + self.write(buf, 1) + } +} - assert_eq!(cd.num_interfaces(), 1); - let int = cd.interfaces().next().unwrap(); - let id = int.descriptors().next().unwrap(); - println!("Interface Descriptor = {id:#?}"); - if let Some(sid) = id.description_string_index() { - println!("Interface: {}", handle.read_string_descriptor_ascii(sid).unwrap()); +impl Drop for Peripage { + fn drop(&mut self) { + let _ = self.handle.release_interface(0); } - println!("Is kernel driver attached: {:?}", handle.kernel_driver_active(0)); +} - assert_eq!(id.class_code(), 7); - assert_eq!(id.sub_class_code(), 1); - assert_eq!(id.protocol_code(), 2); - assert_eq!(id.num_endpoints(), 2); +fn main() { + println!("libusb version: {:?}", rusb::version()); + println!("Kernel supports detaching driver: {}", rusb::supports_detach_kernel_driver()); - let mut epds = id.endpoint_descriptors(); - let epd0 = epds.next().unwrap(); - let epd1 = epds.next().unwrap(); - println!("Endpoint Descriptor 0: {epd0:#?}"); - println!("Endpoint Descriptor 1: {epd1:#?}"); + + let ctx = rusb::Context::new().expect("cannot connect to libusb"); - assert_eq!(epd0.address(), 129); // IN (128) + 1 - assert_eq!(epd0.direction(), Direction::In); - assert_eq!(epd0.transfer_type(), TransferType::Bulk); + let mut pp = Peripage::find(&ctx).unwrap(); - assert_eq!(epd1.address(), 2); // OUT (0) + 2 - assert_eq!(epd1.direction(), Direction::Out); - assert_eq!(epd1.transfer_type(), TransferType::Bulk); + let mut img = vec![0u8; 5 * 48 * 24]; - handle.claim_interface(0).expect("failed to claim interface 0"); - handle.release_interface(0).expect("failed to release interface 0"); + img + .iter_mut() + .enumerate() + .filter(|(i, _)| i % 2 == 0 && (i / 48 / 4) % 2 == 0) + .for_each(|(_, b)| *b = 0xff); + + let header = &[ + 0x10, 0xff, 0xfe, 0x01, + 0x1b, 0x40, 0x00, 0x1b, + 0x4a, 0x60, + ]; + + let mut packet = Vec::new(); + packet.extend_from_slice(header); + + const HEIGHT: u16 = 512; + const BPC: usize = 48 * HEIGHT as usize; + img + .chunks(BPC) + .chain(std::iter::repeat_n(&[0u8; BPC] as &[u8], 0)) + .for_each(|chunk| { + packet.extend_from_slice(&[ + 0x1d, 0x76, 0x30, 0x00, 0x30, 0x00, + ]); + packet.extend_from_slice(&HEIGHT.to_le_bytes()); + packet.extend_from_slice(chunk); + if chunk.len() < BPC { + let z = repeat_n(0u8, BPC - chunk.len()); + packet.extend(z); + } + }); + + // send image + pp.write(&packet, 30).unwrap(); + pp.confirm().unwrap(); + + pp.write(&packet, 30).unwrap(); + pp.confirm().unwrap(); } blob - /dev/null blob + 75a553c8506f6eca35b1bec8a56e5cbecdf9cca1 (mode 644) --- /dev/null +++ Makefile @@ -0,0 +1,14 @@ +SRC != find src -name '*.rs' + +all: ppa6 + +clean: + rm -rf target + rm -f ppa6 + +run: ppa6 + doas ./ppa6 + +ppa6: ${SRC} + cargo build + cp -f target/debug/ppa6 .