Buttplug sex toy control library
1// Buttplug Rust Source Code File - See https://buttplug.io for more info.
2//
3// Copyright 2016-2024 Nonpolynomial Labs LLC. All rights reserved.
4//
5// Licensed under the BSD 3-Clause license. See LICENSE file in the project root
6// for full license information.
7
8use buttplug_core::errors::ButtplugDeviceError;
9use buttplug_server_device_config::Endpoint;
10use buttplug_server_device_config::{
11 ProtocolCommunicationSpecifier,
12 ServerDeviceDefinition,
13 UserDeviceIdentifier,
14};
15
16use crate::device::{
17 hardware::{Hardware, HardwareCommand, HardwareWriteCmd},
18 protocol::{ProtocolHandler, ProtocolIdentifier, ProtocolInitializer},
19};
20use async_trait::async_trait;
21use std::sync::{
22 Arc,
23 atomic::{AtomicU8, Ordering},
24};
25use uuid::Uuid;
26
27pub mod setup {
28 use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory};
29 #[derive(Default)]
30 pub struct YououIdentifierFactory {}
31
32 impl ProtocolIdentifierFactory for YououIdentifierFactory {
33 fn identifier(&self) -> &str {
34 "youou"
35 }
36
37 fn create(&self) -> Box<dyn ProtocolIdentifier> {
38 Box::new(super::YououIdentifier::default())
39 }
40 }
41}
42
43#[derive(Default)]
44pub struct YououIdentifier {}
45
46#[async_trait]
47impl ProtocolIdentifier for YououIdentifier {
48 async fn identify(
49 &mut self,
50 hardware: Arc<Hardware>,
51 _: ProtocolCommunicationSpecifier,
52 ) -> Result<(UserDeviceIdentifier, Box<dyn ProtocolInitializer>), ButtplugDeviceError> {
53 Ok((
54 UserDeviceIdentifier::new(hardware.address(), "Youou", &Some("VX001_".to_owned())),
55 Box::new(YououInitializer::default()),
56 ))
57 }
58}
59
60#[derive(Default)]
61pub struct YououInitializer {}
62
63#[async_trait]
64impl ProtocolInitializer for YououInitializer {
65 async fn initialize(
66 &mut self,
67 _: Arc<Hardware>,
68 _: &ServerDeviceDefinition,
69 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
70 Ok(Arc::new(Youou::default()))
71 }
72}
73
74#[derive(Default)]
75pub struct Youou {
76 packet_id: AtomicU8,
77}
78
79impl ProtocolHandler for Youou {
80 fn handle_output_vibrate_cmd(
81 &self,
82 _feature_index: u32,
83 feature_id: Uuid,
84 speed: u32,
85 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
86 // Byte 2 seems to be a monotonically increasing packet id of some kind
87 //
88 // Speed seems to be 0-247 or so.
89 //
90 // Anything above that sets a pattern which isn't what we want here.
91 let state = u8::from(speed > 0);
92
93 // Scope the packet id set so we can unlock ASAP.
94 let mut data = vec![
95 0xaa,
96 0x55,
97 self.packet_id.load(Ordering::Relaxed),
98 0x02,
99 0x03,
100 0x01,
101 speed as u8,
102 state,
103 ];
104 self.packet_id.store(
105 self.packet_id.load(Ordering::Relaxed).wrapping_add(1),
106 Ordering::Relaxed,
107 );
108 let mut crc: u8 = 0;
109
110 // Simple XOR of everything up to the 9th byte for CRC.
111 for b in data.clone() {
112 crc ^= b;
113 }
114
115 let mut data2 = vec![crc, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
116 data.append(&mut data2);
117
118 Ok(vec![
119 HardwareWriteCmd::new(&[feature_id], Endpoint::Tx, data, false).into(),
120 ])
121 }
122}