Buttplug sex toy control library
1// Buttplug Rust Source Code File - See https://buttplug.io for more info.
2//
3// Copyright 2016-2023 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 crate::device::{
9 hardware::{Hardware, HardwareCommand, HardwareReadCmd, HardwareWriteCmd},
10 protocol::{ProtocolHandler, ProtocolIdentifier, ProtocolInitializer},
11};
12use async_trait::async_trait;
13use buttplug_core::errors::ButtplugDeviceError;
14use buttplug_server_device_config::Endpoint;
15use buttplug_server_device_config::{
16 ProtocolCommunicationSpecifier,
17 ServerDeviceDefinition,
18 UserDeviceIdentifier,
19};
20use std::sync::{
21 Arc,
22 atomic::{AtomicU8, Ordering},
23};
24use uuid::{Uuid, uuid};
25
26pub mod setup {
27 use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory};
28 #[derive(Default)]
29 pub struct MonsterPubIdentifierFactory {}
30
31 impl ProtocolIdentifierFactory for MonsterPubIdentifierFactory {
32 fn identifier(&self) -> &str {
33 "monsterpub"
34 }
35
36 fn create(&self) -> Box<dyn ProtocolIdentifier> {
37 Box::new(super::MonsterPubIdentifier::default())
38 }
39 }
40}
41
42const MONSTERPUB_PROTOCOL_UUID: Uuid = uuid!("c7fe6c69-e7c2-4fa9-822a-6bb337dece1a");
43
44#[derive(Default)]
45pub struct MonsterPubIdentifier {}
46
47#[async_trait]
48impl ProtocolIdentifier for MonsterPubIdentifier {
49 async fn identify(
50 &mut self,
51 hardware: Arc<Hardware>,
52 _: ProtocolCommunicationSpecifier,
53 ) -> Result<(UserDeviceIdentifier, Box<dyn ProtocolInitializer>), ButtplugDeviceError> {
54 let read_resp = hardware
55 .read_value(&HardwareReadCmd::new(
56 MONSTERPUB_PROTOCOL_UUID,
57 Endpoint::RxBLEModel,
58 32,
59 500,
60 ))
61 .await;
62 let ident = match read_resp {
63 Ok(data) => std::str::from_utf8(data.data())
64 .map_err(|_| {
65 ButtplugDeviceError::ProtocolSpecificError(
66 "monsterpub".to_owned(),
67 "MonsterPub device name is non-UTF8 string.".to_owned(),
68 )
69 })?
70 .replace("\0", "")
71 .to_owned(),
72 Err(_) => "Unknown".to_string(),
73 };
74 return Ok((
75 UserDeviceIdentifier::new(hardware.address(), "monsterpub", &Some(ident)),
76 Box::new(MonsterPubInitializer::default()),
77 ));
78 }
79}
80
81#[derive(Default)]
82pub struct MonsterPubInitializer {}
83
84#[async_trait]
85impl ProtocolInitializer for MonsterPubInitializer {
86 async fn initialize(
87 &mut self,
88 hardware: Arc<Hardware>,
89 def: &ServerDeviceDefinition,
90 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
91 if hardware.endpoints().contains(&Endpoint::Rx) {
92 let value = hardware
93 .read_value(&HardwareReadCmd::new(
94 MONSTERPUB_PROTOCOL_UUID,
95 Endpoint::Rx,
96 16,
97 200,
98 ))
99 .await?;
100 let keys = [
101 [
102 0x32u8, 0x49, 0x50, 0x4f, 0x32, 0x49, 0x50, 0x4f, 0x32, 0x49, 0x50, 0x4f, 0x32, 0x49,
103 0x50,
104 ],
105 [
106 0x4c, 0x53, 0x42, 0x42, 0x4c, 0x53, 0x42, 0x42, 0x4c, 0x53, 0x42, 0x42, 0x4c, 0x53, 0x42,
107 ],
108 [
109 0x53, 0x49, 0x53, 0x36, 0x53, 0x49, 0x53, 0x36, 0x53, 0x49, 0x53, 0x36, 0x53, 0x49, 0x53,
110 ],
111 [
112 0x54, 0x41, 0x4c, 0x4b, 0x54, 0x41, 0x4c, 0x4b, 0x54, 0x41, 0x4c, 0x4b, 0x54, 0x41, 0x4c,
113 ],
114 ];
115
116 let auth = value.data()[1..16]
117 .iter()
118 .zip(keys[value.data()[0] as usize].iter())
119 .map(|(&x1, &x2)| x1 ^ x2)
120 .collect();
121
122 trace!(
123 "Got {:?} XOR with key {} to get {:?}",
124 value.data(),
125 value.data()[0],
126 auth
127 );
128
129 hardware
130 .write_value(&HardwareWriteCmd::new(
131 &[MONSTERPUB_PROTOCOL_UUID],
132 Endpoint::Rx,
133 auth,
134 true,
135 ))
136 .await?;
137 }
138 let output_count = def
139 .features()
140 .iter()
141 .filter(|x| x.output().is_some())
142 .count();
143
144 Ok(Arc::new(MonsterPub::new(
145 if hardware.endpoints().contains(&Endpoint::TxVibrate) {
146 Endpoint::TxVibrate
147 } else if hardware.endpoints().contains(&Endpoint::Tx) {
148 Endpoint::Tx
149 } else {
150 Endpoint::Generic0 // tracy's dog 3 vibe
151 },
152 output_count as u32,
153 )))
154 }
155}
156
157pub struct MonsterPub {
158 tx: Endpoint,
159 speeds: Vec<AtomicU8>,
160}
161
162impl MonsterPub {
163 pub fn new(tx: Endpoint, num_outputs: u32) -> Self {
164 let speeds: Vec<AtomicU8> = std::iter::repeat_with(AtomicU8::default)
165 .take(num_outputs as usize)
166 .collect();
167 Self { tx, speeds }
168 }
169
170 fn form_command(&self) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
171 let mut data = vec![];
172 let mut stop = true;
173
174 if self.tx == Endpoint::Generic0 {
175 data.push(3u8);
176 }
177 for cmd in self.speeds.iter() {
178 let speed = cmd.load(Ordering::Relaxed);
179 data.push(speed);
180 if speed != 0 {
181 stop = false;
182 }
183 }
184 let tx = if self.tx == Endpoint::Tx && stop {
185 Endpoint::TxMode
186 } else {
187 self.tx
188 };
189 Ok(vec![
190 HardwareWriteCmd::new(
191 &[MONSTERPUB_PROTOCOL_UUID],
192 tx,
193 data,
194 tx == Endpoint::TxMode,
195 )
196 .into(),
197 ])
198 }
199}
200
201impl ProtocolHandler for MonsterPub {
202 fn handle_output_vibrate_cmd(
203 &self,
204 feature_index: u32,
205 _feature_id: Uuid,
206 speed: u32,
207 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
208 self.speeds[feature_index as usize].store(speed as u8, Ordering::Relaxed);
209 self.form_command()
210 }
211
212 fn handle_output_oscillate_cmd(
213 &self,
214 feature_index: u32,
215 _feature_id: Uuid,
216 speed: u32,
217 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
218 self.speeds[feature_index as usize].store(speed as u8, Ordering::Relaxed);
219 self.form_command()
220 }
221}