bm1397_protocol/command.rs
1use crate::crc::{crc16, crc5};
2use crate::registers::Register;
3use byteorder::{BigEndian, ByteOrder, LittleEndian};
4
5/// Some command can be send to All chip in the chain or to a specific one
6pub enum Destination {
7 All,
8 Chip(u8),
9}
10
11pub struct Command;
12
13pub type Midstate = [u8; 32];
14
15impl Command {
16 const CMD_ALL_CHIP: u8 = 0x10;
17 const CMD_SEND_JOB: u8 = 0x21;
18 const CMD_SET_CHIP_ADDR: u8 = 0x40;
19 const CMD_WRITE_REGISTER: u8 = 0x41;
20 const CMD_READ_REGISTER: u8 = 0x42;
21 const CMD_CHAIN_INACTIVE: u8 = 0x43;
22
23 /// # Chain Inactive Command
24 ///
25 /// This disable the relay ability of every chip on the chain (CI signal is no more relayed to CO pin).
26 /// Usually done before setting each chip address individually using `Command::set_chip_addr`.
27 ///
28 /// ## Example
29 ///
30 /// ```
31 /// use bm1397_protocol::Command;
32 ///
33 /// let cmd = Command::chain_inactive();
34 /// assert_eq!(cmd, [0x55, 0xAA, 0x53, 0x05, 0x00, 0x00, 0x03]);
35 /// ```
36 pub fn chain_inactive() -> [u8; 7] {
37 let mut data: [u8; 7] = [
38 0x55,
39 0xAA,
40 Self::CMD_CHAIN_INACTIVE + Self::CMD_ALL_CHIP,
41 5,
42 0,
43 0,
44 0,
45 ];
46 data[6] = crc5(&data[2..6]);
47 data
48 }
49
50 /// # Set Chip Address Command
51 ///
52 /// Give a logical `ChipAddress` to the chip on the chain that does not have one yet.
53 /// Usually done after sending `Command::chain_inactive` so only the first chip in the
54 /// chain will receive it's chip address, then it will start relaying CI signal to
55 /// CO pin and subsequent `Command::set_chip_addr` will be received by the next chip only.
56 /// This way all chips will be addressed one by one and will received differents `ChipAddress`.
57 ///
58 /// This logical `ChipAddress` have 2 utilities :
59 /// - sending command to a specific chip on the chain, using `Destination::Chip(ChipAddress)`.
60 /// - when mining, the nonce space (u32) will be divided evenly according to `ChipAddress` :
61 /// each chip will add it's own `ChipAddress` to the MSB of the starting nonce for a job.
62 ///
63 /// ## Example
64 ///
65 /// ```
66 /// use bm1397_protocol::Command;
67 ///
68 /// let cmd = Command::set_chip_addr(0x00);
69 /// assert_eq!(cmd, [0x55, 0xAA, 0x40, 0x05, 0x00, 0x00, 0x1C]);
70 ///
71 /// let cmd = Command::set_chip_addr(0x08);
72 /// assert_eq!(cmd, [0x55, 0xAA, 0x40, 0x05, 0x08, 0x00, 0x07]);
73 /// ```
74 pub fn set_chip_addr(addr: u8) -> [u8; 7] {
75 let mut data: [u8; 7] = [0x55, 0xAA, Self::CMD_SET_CHIP_ADDR, 5, addr, 0, 0];
76 data[6] = crc5(&data[2..6]);
77 data
78 }
79
80 /// # Read Register Command
81 ///
82 /// Used to send a Read Register command on the chain.
83 ///
84 /// All chips on the chain or only a specific one can be addressed by this command
85 /// using the `dest` parameter.
86 ///
87 /// Usually the first command sent by a driver to the chain is the Read Register
88 /// command of `Register::ChipAddress` to all chips on the chain, this is usefull to
89 /// enumerate all chips on the chain.
90 ///
91 /// ## Example
92 ///
93 /// ```
94 /// use bm1397_protocol::{Command, Destination, Register};
95 ///
96 /// // Enumerate the chain
97 /// let cmd = Command::read_reg(Register::ChipAddress, Destination::All);
98 /// assert_eq!(cmd, [0x55, 0xAA, 0x52, 0x05, 0x00, 0x00, 0x0A]);
99 ///
100 /// // Read I2CControl on chip with ChipAddress@64
101 /// let cmd = Command::read_reg(Register::I2CControl, Destination::Chip(64));
102 /// assert_eq!(cmd, [0x55, 0xAA, 0x42, 0x05, 0x40, 0x1C, 0x0B]);
103 /// ```
104 pub fn read_reg(reg: Register, dest: Destination) -> [u8; 7] {
105 let mut data: [u8; 7] = [0x55, 0xAA, Self::CMD_READ_REGISTER, 5, 0, reg as u8, 0];
106 match dest {
107 Destination::All => data[2] += Self::CMD_ALL_CHIP,
108 Destination::Chip(c) => data[4] = c,
109 }
110 data[6] = crc5(&data[2..6]);
111 data
112 }
113
114 /// # Write Register Command
115 ///
116 /// Used to send a Write Register command on the chain.
117 ///
118 /// All chips on the chain or only a specific one can be addressed by this command
119 /// using the `dest` parameter.
120 ///
121 /// ## Example
122 ///
123 /// ```
124 /// use bm1397_protocol::{Command, Destination, Register};
125 ///
126 /// // Write ClockOrderControl0 value 0x0000_0000 on All chip of the chain
127 /// let cmd = Command::write_reg(Register::ClockOrderControl0, Destination::All, 0x0000_0000);
128 /// assert_eq!(cmd, [0x55, 0xAA, 0x51, 0x09, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1C]);
129 ///
130 /// // Write MiscControl value 0x0000_7A31 on chip with ChipAddress@64
131 /// let cmd = Command::write_reg(Register::MiscControl, Destination::Chip(64), 0x0000_7A31);
132 /// assert_eq!(cmd, [0x55, 0xAA, 0x41, 0x09, 0x40, 0x18, 0x00, 0x00, 0x7A, 0x31, 0x11]);
133 /// ```
134 pub fn write_reg(reg: Register, dest: Destination, val: u32) -> [u8; 11] {
135 let mut data: [u8; 11] = [
136 0x55,
137 0xAA,
138 Self::CMD_WRITE_REGISTER,
139 9,
140 0,
141 reg as u8,
142 0,
143 0,
144 0,
145 0,
146 0,
147 ];
148 match dest {
149 Destination::All => data[2] += 0x10,
150 Destination::Chip(c) => data[4] = c,
151 }
152 BigEndian::write_u32(&mut data[6..], val);
153 data[10] = crc5(&data[2..10]);
154 data
155 }
156
157 /// # Job with 1 Midstate Command
158 ///
159 /// ## Example
160 ///
161 /// ```
162 /// use bm1397_protocol::{Command, Midstate};
163 ///
164 /// let midstates: [&Midstate; 1] = [
165 /// &[
166 /// 0xDE, 0x60, 0x4A, 0x09, 0xE9, 0x30, 0x1D, 0xE1, 0x25, 0x6D, 0x7E, 0xB8, 0x0E, 0xA1,
167 /// 0xE6, 0x43, 0x82, 0xDF, 0x61, 0x14, 0x15, 0x03, 0x96, 0x6C, 0x18, 0x5F, 0x50, 0x2F,
168 /// 0x55, 0x74, 0xD4, 0xBA,
169 /// ],
170 /// ];
171 /// let cmd = Command::job_1_midstate(0, 0x1707_9E15, 0x638E_3275, 0x706A_B3A2, midstates);
172 /// assert_eq!(
173 /// cmd,
174 /// [
175 /// 0x55, 0xAA, 0x21, 0x36, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x15, 0x9E, 0x07, 0x17,
176 /// 0x75, 0x32, 0x8E, 0x63, 0xA2, 0xB3, 0x6A, 0x70, 0xDE, 0x60, 0x4A, 0x09, 0xE9, 0x30,
177 /// 0x1D, 0xE1, 0x25, 0x6D, 0x7E, 0xB8, 0x0E, 0xA1, 0xE6, 0x43, 0x82, 0xDF, 0x61, 0x14,
178 /// 0x15, 0x03, 0x96, 0x6C, 0x18, 0x5F, 0x50, 0x2F, 0x55, 0x74, 0xD4, 0xBA, 0xD3, 0xDC
179 /// ]
180 /// );
181 /// ```
182 pub fn job_1_midstate(
183 job_id: u8,
184 n_bits: u32,
185 n_time: u32,
186 merkle_root: u32,
187 midstates: [&Midstate; 1],
188 ) -> [u8; 56] {
189 let mut data: [u8; 56] = [0; 56];
190 data[0] = 0x55;
191 data[1] = 0xAA;
192 data[2] = Self::CMD_SEND_JOB;
193 // data[3] = 22 + (midstates.len() * 32) as u8;
194 data[3] = data.len() as u8 - 2;
195 data[4] = job_id;
196 data[5] = midstates.len() as u8;
197 // LittleEndian::write_u32(&mut data[6..], 0u32); // starting_nonce ?
198 LittleEndian::write_u32(&mut data[10..], n_bits);
199 LittleEndian::write_u32(&mut data[14..], n_time);
200 LittleEndian::write_u32(&mut data[18..], merkle_root);
201 let mut offset = 22;
202 for ms in midstates.into_iter() {
203 data[offset..offset + ms.len()].clone_from_slice(ms);
204 offset += ms.len();
205 }
206 let crc = crc16(&data[2..offset]);
207 BigEndian::write_u16(&mut data[offset..], crc);
208 data
209 }
210
211 /// # Job with 4 Midstate Command
212 ///
213 /// ## Example
214 ///
215 /// ```
216 /// use bm1397_protocol::{Command, Midstate};
217 ///
218 /// let midstates: [&Midstate; 4] = [
219 /// &[
220 /// 0xDE, 0x60, 0x4A, 0x09, 0xE9, 0x30, 0x1D, 0xE1, 0x25, 0x6D, 0x7E, 0xB8, 0x0E, 0xA1,
221 /// 0xE6, 0x43, 0x82, 0xDF, 0x61, 0x14, 0x15, 0x03, 0x96, 0x6C, 0x18, 0x5F, 0x50, 0x2F,
222 /// 0x55, 0x74, 0xD4, 0xBA,
223 /// ],
224 /// &[
225 /// 0xAE, 0x2F, 0x3F, 0xC6, 0x02, 0xD9, 0xCD, 0x3B, 0x9E, 0x39, 0xAD, 0x97, 0x9C, 0xFD,
226 /// 0xFF, 0x3A, 0x40, 0x49, 0x4D, 0xB6, 0xD7, 0x8D, 0xA4, 0x51, 0x34, 0x99, 0x29, 0xD1,
227 /// 0xAD, 0x36, 0x66, 0x1D,
228 /// ],
229 /// &[
230 /// 0xDF, 0xFF, 0xC1, 0xCC, 0x89, 0x33, 0xEA, 0xF3, 0xE8, 0x3A, 0x91, 0x58, 0xA6, 0xD6,
231 /// 0xFA, 0x02, 0x0D, 0xCF, 0x60, 0xF8, 0xC1, 0x0E, 0x99, 0x36, 0xDE, 0x71, 0xDB, 0xD3,
232 /// 0xF7, 0xD2, 0x86, 0xAF,
233 /// ],
234 /// &[
235 /// 0xAD, 0x62, 0x59, 0x3A, 0x8D, 0xA3, 0x28, 0xAF, 0xEC, 0x09, 0x6D, 0x86, 0xB9, 0x8E,
236 /// 0x30, 0xE5, 0x79, 0xAE, 0xA4, 0x35, 0xE1, 0x4B, 0xB5, 0xD7, 0x09, 0xCC, 0xE1, 0x74,
237 /// 0x04, 0x3A, 0x7C, 0x2D,
238 /// ],
239 /// ];
240 /// let cmd = Command::job_4_midstate(0, 0x1707_9E15, 0x638E_3275, 0x706A_B3A2, midstates);
241 /// assert_eq!(
242 /// cmd,
243 /// [
244 /// 0x55, 0xAA, 0x21, 0x96, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x15, 0x9E, 0x07, 0x17,
245 /// 0x75, 0x32, 0x8E, 0x63, 0xA2, 0xB3, 0x6A, 0x70, 0xDE, 0x60, 0x4A, 0x09, 0xE9, 0x30,
246 /// 0x1D, 0xE1, 0x25, 0x6D, 0x7E, 0xB8, 0x0E, 0xA1, 0xE6, 0x43, 0x82, 0xDF, 0x61, 0x14,
247 /// 0x15, 0x03, 0x96, 0x6C, 0x18, 0x5F, 0x50, 0x2F, 0x55, 0x74, 0xD4, 0xBA, 0xAE, 0x2F,
248 /// 0x3F, 0xC6, 0x02, 0xD9, 0xCD, 0x3B, 0x9E, 0x39, 0xAD, 0x97, 0x9C, 0xFD, 0xFF, 0x3A,
249 /// 0x40, 0x49, 0x4D, 0xB6, 0xD7, 0x8D, 0xA4, 0x51, 0x34, 0x99, 0x29, 0xD1, 0xAD, 0x36,
250 /// 0x66, 0x1D, 0xDF, 0xFF, 0xC1, 0xCC, 0x89, 0x33, 0xEA, 0xF3, 0xE8, 0x3A, 0x91, 0x58,
251 /// 0xA6, 0xD6, 0xFA, 0x02, 0x0D, 0xCF, 0x60, 0xF8, 0xC1, 0x0E, 0x99, 0x36, 0xDE, 0x71,
252 /// 0xDB, 0xD3, 0xF7, 0xD2, 0x86, 0xAF, 0xAD, 0x62, 0x59, 0x3A, 0x8D, 0xA3, 0x28, 0xAF,
253 /// 0xEC, 0x09, 0x6D, 0x86, 0xB9, 0x8E, 0x30, 0xE5, 0x79, 0xAE, 0xA4, 0x35, 0xE1, 0x4B,
254 /// 0xB5, 0xD7, 0x09, 0xCC, 0xE1, 0x74, 0x04, 0x3A, 0x7C, 0x2D, 0x1B, 0x5C
255 /// ]
256 /// );
257 /// ```
258 pub fn job_4_midstate(
259 job_id: u8,
260 n_bits: u32,
261 n_time: u32,
262 merkle_root: u32,
263 midstates: [&Midstate; 4],
264 ) -> [u8; 152] {
265 let mut data: [u8; 152] = [0; 152];
266 data[0] = 0x55;
267 data[1] = 0xAA;
268 data[2] = Self::CMD_SEND_JOB;
269 // data[3] = 22 + (midstates.len() * 32) as u8;
270 data[3] = data.len() as u8 - 2;
271 data[4] = job_id;
272 data[5] = midstates.len() as u8;
273 // LittleEndian::write_u32(&mut data[6..], 0u32); // starting_nonce ?
274 LittleEndian::write_u32(&mut data[10..], n_bits);
275 LittleEndian::write_u32(&mut data[14..], n_time);
276 LittleEndian::write_u32(&mut data[18..], merkle_root);
277 let mut offset = 22;
278 for ms in midstates.into_iter() {
279 data[offset..offset + ms.len()].clone_from_slice(ms);
280 offset += ms.len();
281 }
282 let crc = crc16(&data[2..offset]);
283 BigEndian::write_u16(&mut data[offset..], crc);
284 data
285 }
286}