Skip to content

Commit d35bdb0

Browse files
committed
Reorder times so that they are 8-byte aligned
1 parent b6816cc commit d35bdb0

4 files changed

+90
-33
lines changed

README.rst

+5-4
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ The header is four fixed entries and a variable length path:
160160
* 1 Byte reserved for padding.
161161
* Path length: 16-bit number encoding the encoded length of the path string.
162162
* Offset: 32-bit number encoding the starting offset to write.
163-
* Total size: 32-bit number encoding the total length of the file contents.
164163
* Current time: 64-bit number encoding nanoseconds since January 1st, 1970. Used as the file modification time. Not all system will support the full resolution. Use the truncated time response value for caching.
164+
* Total size: 32-bit number encoding the total length of the file contents.
165165
* Path: UTF-8 encoded string that is *not* null terminated. (We send the length instead.)
166166

167167
The server will repeatedly respond until the total length has been transferred with:
@@ -170,8 +170,8 @@ The server will repeatedly respond until the total length has been transferred w
170170
* Status: Single byte. ``0x01`` if OK. ``0x02`` if any parent directory is missing or a file.
171171
* 2 Bytes reserved for padding.
172172
* Offset: 32-bit number encoding the starting offset to write. (Should match the offset from the previous 0x20 or 0x22 message)
173-
* Free space: 32-bit number encoding the amount of data the client can send.
174173
* Truncated time: 64-bit number encoding nanoseconds since January 1st, 1970 as stored by the file system. The resolution may be less that the protocol. It is sent back for use in caching on the host side.
174+
* Free space: 32-bit number encoding the amount of data the client can send.
175175

176176
The client will repeatedly respond until the total length has been transferred with:
177177

@@ -216,14 +216,15 @@ The header is two fixed entries and a variable length path:
216216
* Command: Single byte. Always ``0x40``.
217217
* 1 Byte reserved for padding.
218218
* Path length: 16-bit number encoding the encoded length of the path string.
219+
* 4 Bytes reserved for padding.
219220
* Current time: 64-bit number encoding nanoseconds since January 1st, 1970. Used as the file modification time. Not all system will support the full resolution. Use the truncated time response value for caching.
220221
* Path: UTF-8 encoded string that is *not* null terminated. (We send the length instead.)
221222

222223
The server will reply with:
223224

224225
* Command: Single byte. Always ``0x41``.
225226
* Status: Single byte. ``0x01`` if the directory(s) were created or ``0x02`` if any parent of the path is an existing file.
226-
* 2 Bytes reserved for padding.
227+
* 6 Bytes reserved for padding.
227228
* Truncated time: 64-bit number encoding nanoseconds since January 1st, 1970 as stored by the file system. The resolution may be less that the protocol. It is sent back for use in caching on the host side.
228229

229230
``0x50`` - List a directory
@@ -250,8 +251,8 @@ The server will reply with n+1 entries for a directory with n files:
250251
- Bit 0: Set when the entry is a directory
251252
- Bits 1-7: Reserved
252253

253-
* File size: 32-bit number encoding the size of the file. Ignore for directories. Value may change.
254254
* Modification time: 64-bit number of nanoseconds since January 1st, 1970. *However*, files modifiers may not have an accurate clock so do *not* assume it is correct. Instead, only use it to determine cacheability vs a local copy.
255+
* File size: 32-bit number encoding the size of the file. Ignore for directories. Value may change.
255256
* Path: UTF-8 encoded string that is *not* null terminated. (We send the length instead.) These paths are relative so they won't contain ``/`` at all.
256257

257258
The transaction is complete when the final entry is sent from the server. It will have entry number == total entries and zeros for flags, file size and path length.

adafruit_ble_file_transfer.py

+14-12
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class FileTransferClient:
120120
def __init__(self, service):
121121
self._service = service
122122

123-
if service.version != 4:
123+
if service.version < 3:
124124
raise RuntimeError("Service on other device too old")
125125

126126
def _write(self, buffer):
@@ -221,21 +221,21 @@ def write(self, path, contents, *, offset=0, modification_time=None):
221221
modification_time = int(time.time() * 1_000_000_000)
222222
encoded = (
223223
struct.pack(
224-
"<BxHIIQ",
224+
"<BxHIQI",
225225
FileTransferService.WRITE,
226226
len(path),
227227
offset,
228-
total_length,
229228
modification_time,
229+
total_length,
230230
)
231231
+ path
232232
)
233233
self._write(encoded)
234-
b = bytearray(struct.calcsize("<BBxxIIQ"))
234+
b = bytearray(struct.calcsize("<BBxxIQI"))
235235
written = 0
236236
while written < len(contents):
237237
self._readinto(b)
238-
cmd, status, current_offset, free_space, _ = struct.unpack("<BBxxIIQ", b)
238+
cmd, status, current_offset, _, free_space = struct.unpack("<BBxxIQI", b)
239239
if status != FileTransferService.OK:
240240
print("write error", status)
241241
raise RuntimeError()
@@ -268,7 +268,7 @@ def write(self, path, contents, *, offset=0, modification_time=None):
268268

269269
# Wait for confirmation that everything was written ok.
270270
self._readinto(b)
271-
cmd, status, offset, free_space, truncated_time = struct.unpack("<BBxxIIQ", b)
271+
cmd, status, offset, truncated_time, free_space = struct.unpack("<BBxxIQI", b)
272272
if cmd != FileTransferService.WRITE_PACING or offset != total_length:
273273
raise ProtocolError()
274274
return truncated_time
@@ -280,15 +280,15 @@ def mkdir(self, path, modification_time=None):
280280
modification_time = int(time.time() * 1_000_000_000)
281281
encoded = (
282282
struct.pack(
283-
"<BxHQ", FileTransferService.MKDIR, len(path), modification_time
283+
"<BxHxxxxQ", FileTransferService.MKDIR, len(path), modification_time
284284
)
285285
+ path
286286
)
287287
self._write(encoded)
288288

289-
b = bytearray(struct.calcsize("<BBxxQ"))
289+
b = bytearray(struct.calcsize("<BBxxxxxxQ"))
290290
self._readinto(b)
291-
cmd, status, truncated_time = struct.unpack("<BBxxQ", b)
291+
cmd, status, truncated_time = struct.unpack("<BBxxxxxxQ", b)
292292
if cmd != FileTransferService.MKDIR_STATUS:
293293
raise ProtocolError()
294294
if status != FileTransferService.OK:
@@ -305,7 +305,7 @@ def listdir(self, path):
305305
b = bytearray(self._service.raw.incoming_packet_length)
306306
i = 0
307307
total = 10 # starting value that will be replaced by the first response
308-
header_size = struct.calcsize("<BBHIIIIQ")
308+
header_size = struct.calcsize("<BBHIIIQI")
309309
path_length = 0
310310
encoded_path = b""
311311
file_size = 0
@@ -329,9 +329,9 @@ def listdir(self, path):
329329
i,
330330
total,
331331
flags,
332-
file_size,
333332
modification_time,
334-
) = struct.unpack_from("<BBHIIIIQ", b, offset=offset)
333+
file_size,
334+
) = struct.unpack_from("<BBHIIIQI", b, offset=offset)
335335
offset += header_size
336336
encoded_path = b""
337337
if cmd != FileTransferService.LISTDIR_ENTRY:
@@ -362,6 +362,8 @@ def delete(self, path):
362362

363363
def move(self, old_path, new_path):
364364
"""Moves the file or directory from old_path to new_path."""
365+
if self._service.version < 4:
366+
raise RuntimeError("Service on other device too old")
365367
old_path = old_path.encode("utf-8")
366368
new_path = new_path.encode("utf-8")
367369
encoded = (
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# SPDX-FileCopyrightText: 2020 ladyada for Adafruit Industries
2+
# SPDX-License-Identifier: MIT
3+
4+
"""
5+
Used with ble_uart_echo_test.py. Transmits "echo" to the UARTService and receives it back.
6+
"""
7+
8+
import sys
9+
10+
from adafruit_ble import BLERadio
11+
from adafruit_ble.advertising.standard import (
12+
ProvideServicesAdvertisement,
13+
Advertisement,
14+
)
15+
import adafruit_ble_file_transfer
16+
17+
# Connect to a file transfer device
18+
ble = BLERadio()
19+
connection = None
20+
print("disconnected, scanning")
21+
for advertisement in ble.start_scan(
22+
ProvideServicesAdvertisement, Advertisement, timeout=1
23+
):
24+
# print(advertisement.address, advertisement.address.type)
25+
if (
26+
not hasattr(advertisement, "services")
27+
or adafruit_ble_file_transfer.FileTransferService not in advertisement.services
28+
):
29+
continue
30+
connection = ble.connect(advertisement)
31+
peer_address = advertisement.address
32+
print("connected to", advertisement.address)
33+
break
34+
ble.stop_scan()
35+
36+
if not connection:
37+
print("No advertisement found")
38+
sys.exit(1)
39+
40+
# Prep the connection
41+
if adafruit_ble_file_transfer.FileTransferService not in connection:
42+
print("Connected device missing file transfer service")
43+
sys.exit(1)
44+
if not connection.paired:
45+
print("pairing")
46+
connection.pair()
47+
print("paired")
48+
print()
49+
service = connection[adafruit_ble_file_transfer.FileTransferService]
50+
client = adafruit_ble_file_transfer.FileTransferClient(service)
51+
52+
# Do the file operations
53+
print(client.listdir("/"))
54+
print(client.listdir("/lib/"))

examples/ble_file_transfer_stub_server.py

+17-17
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,10 @@ def read_complete_path(starting_path, total_length):
113113
(
114114
path_length,
115115
start_offset,
116-
content_length,
117116
modification_time,
118-
) = struct.unpack_from("<xHIIQ", p, offset=1)
119-
path_start = struct.calcsize("<BxHIIQ")
117+
content_length,
118+
) = struct.unpack_from("<xHIQI", p, offset=1)
119+
path_start = struct.calcsize("<BxHIQI")
120120
path = read_complete_path(p[path_start:], path_length)
121121

122122
d = find_dir(path)
@@ -144,12 +144,12 @@ def read_complete_path(starting_path, total_length):
144144
while contents_read < content_length and ok:
145145
next_amount = min(CHUNK_SIZE, content_length - contents_read)
146146
header = struct.pack(
147-
"<BBxxIIQ",
147+
"<BBxxIQI",
148148
FileTransferService.WRITE_PACING,
149149
FileTransferService.OK,
150150
contents_read,
151-
next_amount,
152151
truncated_time,
152+
next_amount,
153153
)
154154
write_packets(header)
155155
read = read_packets(
@@ -164,12 +164,12 @@ def read_complete_path(starting_path, total_length):
164164
if cmd != FileTransferService.WRITE_DATA:
165165
write_packets(
166166
struct.pack(
167-
"<BBxxIIQ",
167+
"<BBxxIQI",
168168
FileTransferService.WRITE_PACING,
169169
FileTransferService.ERROR_PROTOCOL,
170170
0,
171-
0,
172171
truncated_time,
172+
0,
173173
)
174174
)
175175
print("protocol error, resetting")
@@ -186,12 +186,12 @@ def read_complete_path(starting_path, total_length):
186186
disconnect_after = time.monotonic() + 0.7
187187
write_packets(
188188
struct.pack(
189-
"<BBxxIIQ",
189+
"<BBxxIQI",
190190
FileTransferService.WRITE_PACING,
191191
FileTransferService.OK,
192192
content_length,
193-
0,
194193
truncated_time,
194+
0,
195195
)
196196
)
197197
elif command == adafruit_ble_file_transfer.FileTransferService.READ:
@@ -267,11 +267,11 @@ def read_complete_path(starting_path, total_length):
267267
print("mismatched offset")
268268
break
269269
elif command == adafruit_ble_file_transfer.FileTransferService.MKDIR:
270-
path_length, modification_time = struct.unpack_from("<xHQ", p, offset=1)
270+
path_length, modification_time = struct.unpack_from("<xHxxxxQ", p, offset=1)
271271
# Trucate to the nearest 3 seconds.
272272
truncation = 3 * 1_000_000_000
273273
truncated_time = (modification_time // truncation) * truncation
274-
path_start = struct.calcsize("<BxHQ")
274+
path_start = struct.calcsize("<BxHxxxxQ")
275275
path = read_complete_path(p[path_start:], path_length)
276276
pieces = path.split("/")[1:-1]
277277
parent = stored_data
@@ -288,15 +288,15 @@ def read_complete_path(starting_path, total_length):
288288

289289
if ok:
290290
header = struct.pack(
291-
"<BBxxQ",
291+
"<BBxxxxxxQ",
292292
FileTransferService.MKDIR_STATUS,
293293
FileTransferService.OK,
294294
truncated_time,
295295
)
296296
stored_timestamps[path] = truncated_time
297297
else:
298298
header = struct.pack(
299-
"<BBxxQ",
299+
"<BBxxxxxxQ",
300300
FileTransferService.MKDIR_STATUS,
301301
FileTransferService.ERR,
302302
0,
@@ -312,7 +312,7 @@ def read_complete_path(starting_path, total_length):
312312
d = find_dir(path)
313313
if d is None:
314314
error = struct.pack(
315-
"<BBHIIIIQ",
315+
"<BBHIIIQI",
316316
FileTransferService.LISTDIR_ENTRY,
317317
FileTransferService.ERROR,
318318
0,
@@ -341,21 +341,21 @@ def read_complete_path(starting_path, total_length):
341341
full_file_path += "/"
342342
timestamp = stored_timestamps[full_file_path]
343343
header = struct.pack(
344-
"<BBHIIIIQ",
344+
"<BBHIIIQI",
345345
FileTransferService.LISTDIR_ENTRY,
346346
FileTransferService.OK,
347347
len(encoded_filename),
348348
i,
349349
total_files,
350350
flags,
351-
content_length,
352351
timestamp,
352+
content_length,
353353
)
354354
packet = header + encoded_filename
355355
write_packets(packet)
356356

357357
header = struct.pack(
358-
"<BBHIIIIQ",
358+
"<BBHIIIQI",
359359
FileTransferService.LISTDIR_ENTRY,
360360
FileTransferService.OK,
361361
0,

0 commit comments

Comments
 (0)