iso14229_1/response/
request_file_transfer.rs

1//! response of Service 38
2
3
4use std::collections::HashSet;
5use lazy_static::lazy_static;
6use crate::{ByteOrder, Configuration, DataFormatIdentifier, error::Error, LengthFormatIdentifier, ModeOfOperation, Placeholder, response::Code, ResponseData, utils};
7
8lazy_static!(
9    pub static ref REQUEST_FILE_TRANSFER_NEGATIVES: HashSet<Code> = HashSet::from([
10        Code::IncorrectMessageLengthOrInvalidFormat,
11        Code::ConditionsNotCorrect,
12        #[cfg(any(feature = "std2020"))]
13        Code::RequestSequenceError, // ResumeFile only
14        Code::RequestOutOfRange,
15        Code::SecurityAccessDenied,
16        #[cfg(any(feature = "std2020"))]
17        Code::AuthenticationRequired,
18        Code::UploadDownloadNotAccepted,
19    ]);
20);
21
22/*
23// Specifies the length (number of bytes) of the maxNumberOfBlockLength parameter.
24// If the modeOfOperation parameter equals to 02 (DeleteFile) this parameter shall not be included in the
25// response message.
26// */
27// lengthFormatIdentifier
28/*
29This parameter is used by the requestFileTransfer positive response message to inform the client how many
30data bytes (maxNumberOfBlockLength) to include in each TransferData request message from the client or how
31many data bytes the server will include in a TransferData positive response when uploading data. This length
32reflects the complete message length, including the service identifier and the data parameters present in the
33TransferData request message or positive response message. This parameter allows either the client to adapt to
34the receive buffer size of the server before it starts transferring data to the server or to indicate how many data
35bytes will be included in each TransferData positive response in the event that data is uploaded. A server is
36required to accept transferData requests that are equal in length to its reported maxNumberOfBlockLength. It is
37server specific what transferData request lengths less than maxNumberOfBlockLength are accepted (if any).
38NOTE The last transferData request within a given block can be required to be less than
39maxNumberOfBlockLength. It is not allowed for a server to write additional data bytes (i.e. pad bytes) not
40contained within the transferData message (either in a compressed or uncompressed format), as this would
41affect the memory address of where the subsequent transferData request data would be written.
42If the modeOfOperation parameter equals to 02 (DeleteFile) this parameter shall be not be included in the
43response message.
44*/
45// maxNumberOfBlockLength
46/*
47This is parameter echoes the value of the request.
48If the modeOfOperation parameter equals to 02 (DeleteFile) this parameter shall not be included in the
49response message.)
50If the modeOfOperation parameter equals to 05 (ReadDir) the value of this parameter shall be equal to 00.
51*/
52// dataFormatIdentifier
53/*
54Specifies the length in bytes for both parameters fileSizeUncompressedOrDirInfoLength and fileSizeCompressed.
55If the modeOfOperation parameter equals to 01 (AddFile), 02 (DeleteFile), 03 (ReplaceFile) or 06 (ResumeFile)
56this parameter shall not be included in the response message.
57*/
58// fileSizeOrDirInfoParameterLength
59/*
60Specifies the length in bytes for both parameters fileSizeUncompressedOrDirInfoLength and fileSizeCompressed.
61If the modeOfOperation parameter equals to 01 (AddFile), 02 (DeleteFile), 03 (ReplaceFile) or 06 ResumeFile)
62this parameter shall not be included in the response message.
63*/
64// fileSizeUncompressedOrDirInfoLength
65/*
66Specifies the size of the compressed file in bytes.
67If the modeOfOperation parameter equals to 01 (AddFile), 02 (DeleteFile, 03 (ReplaceFile)), 05 (ReadDir)
68or 06 (ResumeFile) this parameter shall not be included in the response message.
69*/
70// fileSizeCompressed
71/*
72Specifies the byte position within the file at which the Tester will resume downloading after an initial download
73is suspended. A download is suspended when the ECU stops receiving TransferData requests and does not
74receive the RequestTransferExit request before returning to the defaultSession.
75The filePosition is relative to the compressed size or uncompressed size, depending if the file was originally sent
76compressed or uncompressed during the initiating ModeOfOperation = AddFile or ReplaceFile.
77If the modeOfOperation parameter equals to 01 (AddFile), 02 (DeleteFile), 03 (ReplaceFile), 04 (ReadFile),
78or 05 (ReadDir) this parameter shall not be included in the request.
79*/
80// filePosition
81#[derive(Debug, Clone)]
82pub enum RequestFileTransferData {
83    AddFile {       // 1 modeOfOperation
84        lfi: u8,
85        max_block_len: u128,
86        dfi: DataFormatIdentifier,
87    },
88    DeleteFile,     // 2 modeOfOperation
89    ReplaceFile {   // 3 modeOfOperation
90        lfi: u8,
91        max_block_len: u128,
92        dfi: DataFormatIdentifier,
93    },
94    ReadFile {      // 4 modeOfOperation
95        lfi: u8,
96        max_block_len: u128,
97        dfi: DataFormatIdentifier,
98        filesize_or_dir_param_len: u16,
99        uncompressed_size_or_dir_len: u128,
100        compressed_size: u128,
101    },
102    ReadDir {       // 5 modeOfOperation
103        lfi: u8,
104        max_block_len: u128,
105        dfi: DataFormatIdentifier,  // always 0x00
106        filesize_or_dir_param_len: u16,
107        uncompressed_size_or_dir_len: u128,
108    },
109    ResumeFile {    // 6 modeOfOperation
110        lfi: u8,
111        max_block_len: u128,
112        dfi: DataFormatIdentifier,
113        file_pos: [u8; 8],
114    },
115}
116
117impl ResponseData for RequestFileTransferData {
118    type SubFunc = ModeOfOperation;
119    fn try_parse(data: &[u8], sub_func: Option<Self::SubFunc>, _: &Configuration) -> Result<Self, Error> {
120        match sub_func {
121            Some(mode) => {
122                let data_len = data.len();
123                utils::data_length_check(data_len, 1, false)?;
124                let mut offset = 0;
125
126                match mode {
127                    ModeOfOperation::AddFile => {
128                        utils::data_length_check(data_len, offset + 1, false)?;
129
130                        let lfi = data[offset];
131                        offset += 1;
132                        utils::data_length_check(data_len, offset + lfi as usize + 1, false)?;
133
134                        let max_block_len = utils::slice_to_u128(&data[offset..offset + lfi as usize], ByteOrder::Big);
135                        offset += lfi as usize;
136                        let dfi = DataFormatIdentifier::from(data[offset]);
137                        Ok(Self::AddFile {
138                            lfi, max_block_len, dfi
139                        })
140                    },
141                    ModeOfOperation::DeleteFile => Ok(Self::DeleteFile),
142                    ModeOfOperation::ReplaceFile => {
143                        utils::data_length_check(data_len, offset + 1, false)?;
144
145                        let lfi = data[offset];
146                        offset += 1;
147                        utils::data_length_check(data_len, offset + lfi as usize + 1, false)?;
148
149                        let max_block_len = utils::slice_to_u128(&data[offset..offset + lfi as usize], ByteOrder::Big);
150                        offset += lfi as usize;
151                        let dfi = DataFormatIdentifier::from(data[offset]);
152                        Ok(Self::ReplaceFile {
153                            lfi, max_block_len, dfi
154                        })
155                    },
156                    ModeOfOperation::ReadFile => {
157                        utils::data_length_check(data_len, offset + 1, false)?;
158
159                        let lfi = data[offset];
160                        offset += 1;
161                        utils::data_length_check(data_len, offset + lfi as usize + 4, false)?;
162
163                        let max_block_len = utils::slice_to_u128(&data[offset..offset + lfi as usize], ByteOrder::Big);
164                        offset += lfi as usize;
165
166                        let dfi = DataFormatIdentifier::from(data[offset]);
167                        offset += 1;
168
169                        let filesize_or_dir_param_len = u16::from_be_bytes([data[offset], data[offset + 1]]);
170                        offset += 2;
171
172                        utils::data_length_check(data_len, offset + filesize_or_dir_param_len as usize + 1, false)?;
173
174                        let uncompressed_size_or_dir_len = utils::slice_to_u128(&data[offset..offset + filesize_or_dir_param_len as usize], ByteOrder::Big);
175                        offset += filesize_or_dir_param_len as usize;
176
177                        let compressed_size = utils::slice_to_u128(&data[offset..], ByteOrder::Big);
178
179                        Ok(Self::ReadFile {
180                            lfi,
181                            max_block_len,
182                            dfi,
183                            filesize_or_dir_param_len,
184                            uncompressed_size_or_dir_len,
185                            compressed_size,
186                        })
187                    },
188                    ModeOfOperation::ReadDir => {
189                        utils::data_length_check(data_len, offset + 1, false)?;
190
191                        let lfi = data[offset];
192                        offset += 1;
193                        utils::data_length_check(data_len, offset + lfi as usize + 4, false)?;
194
195                        let max_block_len = utils::slice_to_u128(&data[offset..offset + lfi as usize], ByteOrder::Big);
196                        offset += lfi as usize;
197
198                        let dfi = data[offset];
199                        offset += 1;
200                        if dfi != 0x00 {
201                            return Err(Error::InvalidData(hex::encode(data)));
202                        }
203                        let dfi = DataFormatIdentifier(dfi);
204
205                        let filesize_or_dir_param_len = u16::from_be_bytes([data[offset], data[offset + 1]]);
206                        offset += 2;
207                        let uncompressed_size_or_dir_len = utils::slice_to_u128(&data[offset..offset + filesize_or_dir_param_len as usize], ByteOrder::Big);
208
209                        Ok(Self::ReadDir {
210                            lfi,
211                            max_block_len,
212                            dfi,
213                            filesize_or_dir_param_len,
214                            uncompressed_size_or_dir_len,
215                        })
216                    },
217                    ModeOfOperation::ResumeFile => {
218                        utils::data_length_check(data_len, offset + 1, false)?;
219
220                        let lfi = data[offset];
221                        offset += 1;
222                        utils::data_length_check(data_len, offset + lfi as usize + 9, false)?;
223
224                        let max_block_len = utils::slice_to_u128(&data[offset..offset + lfi as usize], ByteOrder::Big);
225                        offset += lfi as usize;
226                        let dfi = DataFormatIdentifier::from(data[offset]);
227                        offset += 1;
228                        let file_pos = <[u8; 8]>::try_from(&data[offset..])
229                            .map_err(|_| Error::InvalidData(hex::encode(data)))?;
230
231                        Ok(Self::ResumeFile {
232                            lfi,
233                            max_block_len,
234                            dfi,
235                            file_pos,
236                        })
237                    },
238                }
239            },
240            None => panic!("Sub-function required"),
241        }
242    }
243    #[inline]
244    fn to_vec(self, _: &Configuration) -> Vec<u8> {
245        self.into()
246    }
247}
248
249impl Into<Vec<u8>> for RequestFileTransferData {
250    fn into(self) -> Vec<u8> {
251        let mut result = Vec::new();
252        match self {
253            Self::AddFile { lfi, max_block_len, dfi } => {
254                result.push(lfi);
255                result.extend(utils::u128_to_vec_fix(max_block_len, ByteOrder::Big));
256                result.push(dfi.into());
257            },
258            Self::DeleteFile => {},
259            Self::ReplaceFile { lfi, max_block_len, dfi } => {
260                result.push(lfi);
261                result.extend(utils::u128_to_vec_fix(max_block_len, ByteOrder::Big));
262                result.push(dfi.into());
263            },
264            Self::ReadFile {
265                lfi,
266                max_block_len,
267                dfi,
268                filesize_or_dir_param_len,
269                uncompressed_size_or_dir_len,
270                compressed_size
271            } => {
272                result.push(lfi);
273                result.extend(utils::u128_to_vec_fix(max_block_len, ByteOrder::Big));
274                result.push(dfi.into());
275                result.extend(filesize_or_dir_param_len.to_be_bytes());
276                result.extend(utils::u128_to_vec_fix(uncompressed_size_or_dir_len, ByteOrder::Big));
277                result.extend(utils::u128_to_vec_fix(compressed_size, ByteOrder::Big));
278            },
279            Self::ReadDir {
280                lfi,
281                max_block_len,
282                dfi,
283                filesize_or_dir_param_len,
284                uncompressed_size_or_dir_len,
285            } => {
286                result.push(lfi);
287                result.extend(utils::u128_to_vec_fix(max_block_len, ByteOrder::Big));
288                result.push(dfi.into());
289                result.extend(filesize_or_dir_param_len.to_be_bytes());
290                result.extend(utils::u128_to_vec_fix(uncompressed_size_or_dir_len, ByteOrder::Big));
291            },
292            Self::ResumeFile {
293                lfi,
294                max_block_len,
295                dfi,
296                file_pos,
297            } => {
298                result.push(lfi);
299                result.extend(utils::u128_to_vec_fix(max_block_len, ByteOrder::Big));
300                result.push(dfi.into());
301                result.extend(file_pos);
302            },
303        }
304
305        result
306    }
307}