commit - b7e0bc374d935a50b7e60e741ba7c99efcf123dd
commit + 7aa98c0c683b5561c6d11caf20b24c3d1926d836
blob - ea8c4bf7f35f6f77f75d92ad8ce8349f6e81ddba
blob + ef44ab21fd8f7d4ae212f791f16a02dfa1dec7e0
--- .gitignore
+++ .gitignore
-/target
+target
+ppa6
blob - e950f4221ea673013612454faad769ce61e482a6
blob + 30df70e6c521ba0775bc976ccecc6cad4a099904
--- src/main.rs
+++ src/main.rs
// 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<Context>,
+ ep: u8,
+}
-
- let ctx = rusb::Context::new().expect("cannot connect to libusb");
+impl Peripage {
+ fn find(ctx: &Context) -> Result<Self> {
+ 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<Context>) -> Result<Self> {
+ 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
+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 .