stlinkv2.py now works with newer PyUSB
[gnuk/gnuk.git] / tool / dfuse.py
1 #! /usr/bin/python
2
3 """
4 dfuse.py - DFU (Device Firmware Upgrade) tool for STM32 Processor.
5 "SE" in DfuSe stands for "STmicroelectronics Extention".
6
7 Copyright (C) 2010, 2011 Free Software Initiative of Japan
8 Author: NIIBE Yutaka <gniibe@fsij.org>
9
10 This file is a part of Gnuk, a GnuPG USB Token implementation.
11
12 Gnuk is free software: you can redistribute it and/or modify it
13 under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 Gnuk is distributed in the hope that it will be useful, but WITHOUT
18 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
20 License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 """
25
26 from intel_hex import *
27 import sys, time, struct
28
29 # INPUT: intel hex file
30
31 # As of October 2010 (DfuSe V3.0.1 - 06/18/2010), it seems that
32 # following features are not supported by DfuSe implementation on
33 # target:
34 #
35 #     unprotect
36 #     leave_dfu_mode
37 #     write to option bytes
38 #     erase for mass erase
39
40
41 # See: AN3156 by STMicroelectronics
42
43 import usb
44
45 # check string descriptor in interrface descriptor in config descriptor: iInterface
46
47 # USB DFU class, subclass, protocol
48 DFU_CLASS = 0xFE
49 DFU_SUBCLASS = 0x01
50 DFU_STM32PROTOCOL_0 = 0
51 DFU_STM32PROTOCOL_2 = 2
52
53 # DFU request
54 DFU_DETACH    = 0x00
55 DFU_DNLOAD    = 0x01
56 DFU_UPLOAD    = 0x02
57 DFU_GETSTATUS = 0x03
58 DFU_CLRSTATUS = 0x04
59 DFU_GETSTATE  = 0x05
60 DFU_ABORT     = 0x06
61
62 # DFU status
63 DFU_STATUS_OK                 = 0x00
64 DFU_STATUS_ERROR_TARGET       = 0x01
65 DFU_STATUS_ERROR_FILE         = 0x02
66 DFU_STATUS_ERROR_WRITE        = 0x03
67 DFU_STATUS_ERROR_ERASE        = 0x04
68 DFU_STATUS_ERROR_CHECK_ERASED = 0x05
69 DFU_STATUS_ERROR_PROG         = 0x06
70 DFU_STATUS_ERROR_VERIFY       = 0x07
71 DFU_STATUS_ERROR_ADDRESS      = 0x08
72 DFU_STATUS_ERROR_NOTDONE      = 0x09
73 DFU_STATUS_ERROR_FIRMWARE     = 0x0a
74 DFU_STATUS_ERROR_VENDOR       = 0x0b
75 DFU_STATUS_ERROR_USBR         = 0x0c
76 DFU_STATUS_ERROR_POR          = 0x0d
77 DFU_STATUS_ERROR_UNKNOWN      = 0x0e
78 DFU_STATUS_ERROR_STALLEDPKT   = 0x0f
79
80 # DFU state
81 STATE_APP_IDLE                = 0x00
82 STATE_APP_DETACH              = 0x01
83 STATE_DFU_IDLE                = 0x02
84 STATE_DFU_DOWNLOAD_SYNC       = 0x03
85 STATE_DFU_DOWNLOAD_BUSY       = 0x04
86 STATE_DFU_DOWNLOAD_IDLE       = 0x05
87 STATE_DFU_MANIFEST_SYNC       = 0x06
88 STATE_DFU_MANIFEST            = 0x07
89 STATE_DFU_MANIFEST_WAIT_RESET = 0x08
90 STATE_DFU_UPLOAD_IDLE         = 0x09
91 STATE_DFU_ERROR               = 0x0a
92
93 # Return tuple of 4-bytes from integer
94 def get_four_bytes (v):
95     return [ v % 256, (v >> 8)%256, (v >> 16)%256, (v >> 24) ]
96
97 class DFU_STM32(object):
98     def __init__(self, device, configuration, interface):
99         """
100         __init__(device, configuration, interface) -> None
101         Initialize the device.
102         device: usb.Device object.
103         configuration: configuration number.
104         interface: usb.Interface object representing the interface and altenate setting.
105         """
106         if interface.interfaceClass != DFU_CLASS:
107             raise ValueError, "Wrong interface class"
108         if interface.interfaceSubClass != DFU_SUBCLASS:
109             raise ValueError, "Wrong interface sub class"
110         self.__protocol = interface.interfaceProtocol
111         self.__devhandle = device.open()
112         self.__devhandle.setConfiguration(configuration)
113         self.__devhandle.claimInterface(interface)
114         self.__devhandle.setAltInterface(0)
115
116         self.__intf = interface.interfaceNumber
117         self.__alt = interface.alternateSetting
118         self.__conf = configuration
119         # Initialize members
120         self.__blocknum = 0
121
122     def ll_getdev(self):
123         return self.__devhandle
124
125     def ll_get_string(self, index):
126         # specify buffer length for 80
127         return self.__devhandle.getString(index, 80)
128
129     def ll_get_status(self):
130         # Status, PollTimeout[3], State, String
131         return self.__devhandle.controlMsg(requestType = 0xa1,
132                                            request = DFU_GETSTATUS,
133                                            value = 0,
134                                            index = self.__intf,
135                                            buffer = 6,
136                                            timeout = 3000000)
137
138     def ll_clear_status(self):
139         return self.__devhandle.controlMsg(requestType = 0x21,
140                                            request = DFU_CLRSTATUS,
141                                            value = 0,
142                                            index = self.__intf,
143                                            buffer = None)
144
145     # Upload: TARGET -> HOST
146     def ll_upload_block(self, block_num):
147         return self.__devhandle.controlMsg(requestType = 0xa1,
148                                            request = DFU_UPLOAD,
149                                            value = block_num,
150                                            index = self.__intf,
151                                            buffer = 1024,
152                                            timeout = 3000000)
153
154     # Download: HOST -> TARGET
155     def ll_download_block(self, block_num, block):
156         return self.__devhandle.controlMsg(requestType = 0x21,
157                                            request = DFU_DNLOAD,
158                                            value = block_num,
159                                            index = self.__intf,
160                                            buffer = block)
161
162     def dfuse_read_memory(self):
163         blocknum = self.__blocknum
164         self.__blocknum = self.__blocknum + 1
165         try:
166             block = self.ll_upload_block(blocknum)
167             return block
168         except:
169             s = self.ll_get_status()
170             while s[4] == STATE_DFU_DOWNLOAD_BUSY:
171                 time.sleep(0.1)
172                 s = self.ll_get_status()
173             raise ValueError, "Read memory failed (%d)" % s[0]
174
175     def dfuse_set_address_pointer(self, address):
176         bytes = get_four_bytes (address)
177         self.__blocknum = 2
178         self.ll_download_block(0, [0x21] + bytes)
179         s = self.ll_get_status()
180         while s[4] == STATE_DFU_DOWNLOAD_BUSY:
181             time.sleep(0.1)
182             s = self.ll_get_status()
183         if s[4] != STATE_DFU_DOWNLOAD_IDLE:
184             raise ValueError, "Set Address Pointer failed"
185
186     def dfuse_erase(self, address):
187         bytes = get_four_bytes (address)
188         self.ll_download_block(0, [0x41] + bytes)
189         s = self.ll_get_status()
190         while s[4] == STATE_DFU_DOWNLOAD_BUSY:
191             time.sleep(0.1)
192             s = self.ll_get_status()
193         if s[4] != STATE_DFU_DOWNLOAD_IDLE:
194             raise ValueError, "Erase failed"
195
196     def dfuse_write_memory(self, block):
197         blocknum = self.__blocknum
198         self.__blocknum = self.__blocknum + 1
199         self.ll_download_block(blocknum, block)
200         s = self.ll_get_status()
201         while s[4] == STATE_DFU_DOWNLOAD_BUSY:
202             time.sleep(0.1)
203             s = self.ll_get_status()
204         if s[4] != STATE_DFU_DOWNLOAD_IDLE:
205             raise ValueError, "Write memory failed"
206
207     def download(self, ih):
208         # First, erase pages
209         sys.stdout.write("Erasing: ")
210         sys.stdout.flush()
211         last_addr = 0
212         for start_addr in sorted(ih.memory.keys()):
213             data = ih.memory[start_addr]
214             end_addr = start_addr + len(data)
215             addr = start_addr & 0xfffffc00
216             if not last_addr == 0:
217                 i = 0
218                 if last_addr > addr:
219                     addr = last_addr
220                 else:
221                     while last_addr < addr:
222                         self.dfuse_erase(last_addr)
223                         if i & 0x03 == 0x03:
224                             sys.stdout.write(".")
225                             sys.stdout.flush()
226                             last_addr += 1024
227                         i += 1
228             i = 0
229             while addr < end_addr:
230                 self.dfuse_erase(addr)
231                 if i & 0x03 == 0x03:
232                     sys.stdout.write("#")
233                     sys.stdout.flush()
234                 addr += 1024
235                 i += 1
236             last_addr = addr
237         sys.stdout.write("\n")
238         sys.stdout.flush()
239         # Then, write pages
240         sys.stdout.write("Writing: ")
241         sys.stdout.flush()
242         last_addr = 0
243         for start_addr in sorted(ih.memory.keys()):
244             data = ih.memory[start_addr]
245             end_addr = start_addr + len(data)
246             addr = start_addr & 0xfffffc00
247             if not last_addr == 0:
248                 i = 0
249                 while last_addr < addr:
250                     if i & 0x03 == 0x03:
251                         sys.stdout.write(".")
252                         sys.stdout.flush()
253                     last_addr += 1024
254                     i += 1
255             i = 0
256             if addr != start_addr:
257                 self.dfuse_set_address_pointer(start_addr)
258                 self.dfuse_write_memory(data[0:(addr + 1024 - start_addr)])
259                 data = data[(addr + 1024 - start_addr):]
260                 addr += 1024
261             self.dfuse_set_address_pointer(addr)
262             while addr < end_addr:
263                 self.dfuse_write_memory(data[i*1024:(i+1)*1024])
264                 if i & 0x03 == 0x03:
265                     sys.stdout.write("#")
266                     sys.stdout.flush()
267                 addr += 1024
268                 i += 1
269             last_addr = addr
270         if self.__protocol == DFU_STM32PROTOCOL_0:
271             # 0-length write at the end
272             self.ll_download_block(self.__blocknum, None)
273             s = self.ll_get_status()
274             if s[4] == STATE_DFU_MANIFEST:
275                 time.sleep(1)
276                 try:
277                     s = self.ll_get_status()
278                 except:
279                     self.__devhandle.reset()
280             elif s[4] == STATE_DFU_MANIFEST_WAIT_RESET:
281                 self.__devhandle.reset()
282             elif s[4] != STATE_DFU_IDLE:
283                 raise ValueError, "write failed (%d)." % s[4]
284         else:
285             self.ll_clear_status()
286             self.ll_clear_status()
287         sys.stdout.write("\n")
288         sys.stdout.flush()
289
290     def verify(self, ih):
291         s = self.ll_get_status()
292         if s[4] != STATE_DFU_IDLE:
293             self.ll_clear_status()
294         # Read pages
295         sys.stdout.write("Reading: ")
296         sys.stdout.flush()
297         last_addr = 0
298         for start_addr in sorted(ih.memory.keys()):
299             data = ih.memory[start_addr]
300             end_addr = start_addr + len(data)
301             addr = start_addr & 0xfffffc00
302             if not last_addr == 0:
303                 i = 0
304                 while last_addr < addr:
305                     if i & 0x03 == 0x03:
306                         sys.stdout.write(".")
307                         sys.stdout.flush()
308                     last_addr += 1024
309                     i += 1
310             if addr != start_addr:
311                 self.dfuse_set_address_pointer(addr)
312                 self.ll_clear_status()
313                 self.ll_clear_status()
314                 block = self.dfuse_read_memory()
315                 j = 0
316                 for c in data[0:(addr + 1024 - start_addr)]:
317                     if (ord(c)&0xff) != block[j + start_addr - addr]:
318                         raise ValueError, "verify failed at %08x" % (addr + i*1024+j)
319                     j += 1
320                 data = data[(addr + 1024 - start_addr):]
321                 addr += 1024
322                 self.ll_clear_status()
323                 self.ll_clear_status()
324             self.dfuse_set_address_pointer(addr)
325             self.ll_clear_status()
326             self.ll_clear_status()
327             i = 0
328             while addr < end_addr:
329                 block = self.dfuse_read_memory()
330                 j = 0
331                 for c in data[i*1024:(i+1)*1024]:
332                     if (ord(c)&0xff) != block[j]:
333                         raise ValueError, "verify failed at %08x" % (addr + i*1024+j)
334                     j += 1
335                 if i & 0x03 == 0x03:
336                     sys.stdout.write("#")
337                     sys.stdout.flush()
338                 addr += 1024
339                 i += 1
340             last_addr = addr
341             self.ll_clear_status()
342             self.ll_clear_status()
343         self.ll_clear_status()
344         sys.stdout.write("\n")
345         sys.stdout.flush()
346
347 busses = usb.busses()
348
349 # 0483: SGS Thomson Microelectronics
350 # df11: DfuSe
351 USB_VENDOR_STMICRO=0x0483
352 USB_PRODUCT_DFUSE=0xdf11
353
354 def get_device():
355     for bus in busses:
356         devices = bus.devices
357         for dev in devices:
358             if dev.idVendor != USB_VENDOR_STMICRO:
359                 continue
360             if dev.idProduct != USB_PRODUCT_DFUSE:
361                 continue
362             for config in dev.configurations:
363                 for intf in config.interfaces:
364                     for alt in intf:
365                         if alt.interfaceClass == DFU_CLASS and \
366                                 alt.interfaceSubClass == DFU_SUBCLASS and \
367                                 (alt.interfaceProtocol == DFU_STM32PROTOCOL_0 or \
368                                      alt.interfaceProtocol == DFU_STM32PROTOCOL_2):
369                             return dev, config, alt
370     raise ValueError, "Device not found"
371
372 def main(filename):
373     dev, config, intf = get_device()
374     print "Device:", dev.filename
375     print "Configuration", config.value
376     print "Interface", intf.interfaceNumber
377     dfu = DFU_STM32(dev, config, intf)
378     print dfu.ll_get_string(intf.iInterface)
379     s = dfu.ll_get_status()
380     if s[4] == STATE_DFU_ERROR:
381         dfu.ll_clear_status()
382     s = dfu.ll_get_status()
383     print s
384     if s[4] == STATE_DFU_IDLE:
385         exit
386     transfer_size = 1024
387     if s[0] != DFU_STATUS_OK:
388         print s
389         exit
390     ih = intel_hex(filename)
391     dfu.download(ih)
392     dfu.verify(ih)
393
394 if __name__ == '__main__':
395     main(sys.argv[1])