0e1ec25759051ab8d124352a4f116c9250832d29
[vuplus_dvbapp] / lib / python / Plugins / SystemPlugins / FirmwareUpgrade / plugin.py
1 import os, urllib
2 from urllib import urlretrieve
3
4 from Plugins.Plugin import PluginDescriptor
5
6 from Components.config import config, getConfigListEntry, ConfigSubsection, ConfigText, ConfigSelection, ConfigYesNo,ConfigText
7 from Components.ConfigList import ConfigListScreen
8 from Components.ActionMap import ActionMap
9 from Components.Sources.StaticText import StaticText
10 from Components.Pixmap import Pixmap
11 from Components.Label import Label
12
13 from Components.FileList import FileList 
14 from Components.Slider import Slider
15
16 from Screens.Screen import Screen
17 from Screens.MessageBox import MessageBox
18
19 from enigma import ePoint, eConsoleAppContainer, eTimer
20 from Tools.Directories import resolveFilename, SCOPE_PLUGINS
21
22 fwlist = None
23 fwdata = None
24 if os.path.exists("/proc/stb/info/vumodel"):
25         vumodel = open("/proc/stb/info/vumodel")
26         info = vumodel.read().strip()
27         vumodel.close()
28
29         if info == "ultimo":
30                 fwlist= [
31                          ("fpga", _("FPGA"))
32                         ,("fp", _("Front Processor"))
33                         ]
34                 fwdata= { 
35                          "fpga" : ["http://archive.vuplus.com/download/fpga", "fpga.files", "/dev/fpga_dp;/dev/misc/dp;"]
36                         ,"fp"   : ["http://archive.vuplus.com/download/fp", "fp.files", "/dev/bcm_mu;"]
37                         }
38         elif info == "uno":
39                 fwlist= [
40                         ("fpga", _("FPGA"))
41                         ]
42                 fwdata= { 
43                         "fpga" : ["http://archive.vuplus.com/download/fpga", "fpga.file", "/dev/fpga_dp;/dev/misc/dp;"]
44                         }
45
46 class UpgradeStatus(Screen):
47         skin =  """
48                 <screen position="center,center" size="450,100" title=" ">
49                         <widget name="name" position="10,0" size="430,20" font="Regular;18" halign="left" valign="bottom"/>
50                         <widget name="slider" position="10,25" size="430,30" backgroundColor="white"/>
51                         <widget name="status" position="10,25" zPosition="1" size="430,30" font="Regular;18" halign="center" valign="center" foregroundColor="black" backgroundColor="black" transparent="1"/>
52                         <widget source="info" render="Label" position="10,70" zPosition="1" size="430,30" font="Regular;22" halign="center" valign="center" backgroundColor="#a08500" transparent="1"/>
53                 </screen>
54                 """
55
56         def __init__(self, session, parent, firmware, datafile, device):
57                 Screen.__init__(self,session)
58                 self.session = session
59
60                 self["actions"] = ActionMap(["OkCancelActions"],
61                 {
62                         "ok": self.keyExit,
63                 }, -1)
64
65                 self.firmware = firmware
66                 self.datafile = datafile
67                 #print "[FirmwareUpgrade] - [%s][%s][%s]" % (self.datafile, firmware, device)
68
69                 self["name"] = Label(_(" "))
70                 self["info"] = StaticText(_("Can't cancel during upgrade!!"))
71
72                 self["status"] = Label(_("Status : 0%"))
73
74                 self.slider = Slider(0, 100)
75                 self["slider"] = self.slider
76
77                 self.callback = None
78
79                 self.setTitle(firmware.upper() + " Upgrade Status")
80
81                 import fu
82                 self.FU = fu.FU()
83
84                 self.old_status   = 0
85                 self.status_exit  = None
86                 self.check_status = eTimer()
87                 self.check_status.callback.append(self.cbCheckStatus)
88                 self.check_status.start(self.FU.getInterval())
89
90                 self.exitTimerCallCount = 0;
91                 self.upgradeLock = True
92                 self.FU.startUpgrade(self.datafile, device, firmware)
93
94         def cbCheckStatus(self):
95                 errmsg = ""
96                 errno  = self.FU.checkError()
97                 if errno:
98                         self.check_status.stop()
99                         errmsg = self.FU.getErrorMessage(errno, errmsg)
100                         print "[FirmwareUpgrade] - ERROR : [%d][%s]" % (errno, errmsg)
101                         self.session.open(MessageBox, _(errmsg), MessageBox.TYPE_INFO, timeout = 10)
102                         self.cbConfirmExit(False)
103                         return
104                 status = self.FU.getStatus()
105                 if self.old_status > status and status != -1:
106                         self.session.open(MessageBox, _("Fail to upgrade!! Retry!!"), MessageBox.TYPE_INFO, timeout = 10)
107                 self.slider.setValue(status)
108                 self["status"].setText(_("%d / 100" % (status)))
109                 if status == 100:
110                         self.check_status.stop()
111                         self["status"].setText(_("Success. Press OK to exit."))
112                         self.status_exit = eTimer()
113                         self.status_exit.callback.append(self.cbTimerExit)
114                         self.status_exit.start(1000)
115                         self.upgradeLock = False
116                 self.old_status = status
117
118         def setCallback(self, cb):
119                 self.callback = cb
120
121         def cbTimerExit(self):
122                 if self.exitTimerCallCount < 10: # exit after 10 sec.
123                         self.exitTimerCallCount = self.exitTimerCallCount + 1
124                         self.setTitle("%s Upgrade Status (%d)" % (self.firmware.upper(), 10-self.exitTimerCallCount))
125                         return
126                 if self.status_exit is not None:
127                         self.status_exit.stop()
128                 self.keyExit()
129
130         def cbConfirmExit(self, ret):
131                 if ret:
132                         os.system("rm -f %s %s.md5" % (self.datafile, self.datafile))
133                 self.close()
134
135         def keyExit(self):
136                 if self.upgradeLock:
137                         return
138                 if self.callback is not None:
139                         self.callback("Reboot now for a successful upgrade.", True)
140                 self.session.openWithCallback(self.cbConfirmExit, MessageBox, _("Do you want to remove binary data?"), MessageBox.TYPE_YESNO, timeout = 10, default = False)
141
142 class Filebrowser(Screen):
143         skin =  """
144                 <screen position="center,center" size="500,260" title="File Browser" >
145                         <ePixmap pixmap="Vu_HD/buttons/blue.png" position="5,7" size="80,40" alphatest="blend" />
146                         <widget source="key_blue" render="Label" position="40,0" zPosition="1" size="300,40" font="Regular;20" halign="left" valign="center" transparent="1"/>
147                         <widget name="file_list" position="0,50" size="500,160" scrollbarMode="showOnDemand" />
148
149                         <widget source="status" render="Label" position="0,220" zPosition="1" size="500,40" font="Regular;18" halign="center" valign="center" backgroundColor="#a08500" transparent="1" />
150                 </screen>
151                 """
152
153         def __init__(self, session, parent, firmware):
154                 Screen.__init__(self, session)
155                 self.session = session 
156                 
157                 self["key_blue"] = StaticText(_("Download  the  firmware (latest)"))
158
159                 self["status"]    = StaticText(_(" "))
160                 self["file_list"] = FileList("/", matchingPattern = "^.*")
161
162                 self["actions"] = ActionMap(["OkCancelActions", "ShortcutActions", "WizardActions", "ColorActions", ],
163                 {
164                         "ok":     self.onClickOk,
165                         "cancel": self.onClickCancel,
166                         "blue":   self.onClickBlue,
167                         "up":     self.onClickUp,
168                         "down":   self.onClickDown,
169                         "left":   self.onClickLeft,
170                         "right":  self.onClickRight,
171                 }, -1) 
172
173                 self.resetGUI()
174                 self.firmware = firmware
175
176                 self.callback = None
177                 self.timer_downloading = None
178
179                 self.downloadLock = False
180                 self.setTitle(firmware.upper() + " File Browser")
181
182         def resetGUI(self):
183                 self["status"].setText("Select to press OK, Exit to press Cancel.")
184
185         def setCallback(self, func):
186                 self.callback = func
187
188         def onClickOk(self):
189                 if self.downloadLock:
190                         return
191
192                 if self["file_list"].canDescent() : # isDir
193                         self["file_list"].descent()
194                         return
195
196                 # verify data
197                 self.gbin = self["file_list"].getCurrentDirectory() + self["file_list"].getFilename()
198                 if not os.path.exists(self.gbin):
199                         self.session.open(MessageBox, _("Can't found binary file."), MessageBox.TYPE_INFO, timeout = 10)
200                         return
201                 if not os.path.exists(self.gbin+".md5"):
202                         self.session.open(MessageBox, _("Can't found MD5 file."), MessageBox.TYPE_INFO, timeout = 10)
203                         return
204                 try:
205                         def checkExt(ext):
206                                 name_ext = os.path.splitext(self["file_list"].getFilename())
207                                 return len(name_ext)==2 and ext.startswith(name_ext[1])
208                         self.check_ext = False
209                         if (self.firmware == "fp" and checkExt(".bin")) or (self.firmware == "fpga" and checkExt(".dat")):
210                                 self.check_ext = True
211                         if self.check_ext == False:
212                                 self.session.open(MessageBox, _("You chose the incorrect file."), MessageBox.TYPE_INFO)
213                                 return
214                 except:
215                         self.session.open(MessageBox, _("You chose the incorrect file."), MessageBox.TYPE_INFO)
216                         return
217
218                 if os.path.exists("/usr/bin/md5sum") == False:
219                         self.session.open(MessageBox, _("Can't find /usr/bin/md5sum"), MessageBox.TYPE_INFO, timeout = 10)
220                         return
221                 md5sum_A = os.popen("md5sum %s | awk \'{print $1}\'"%(self.gbin)).readline().strip()
222                 md5sum_B = os.popen("cat %s.md5 | awk \'{print $1}\'"%(self.gbin)).readline().strip()
223                 #print "[FirmwareUpgrade] - Verify : file[%s], md5[%s]"%(md5sum_A,md5sum_B)
224
225                 if md5sum_A != md5sum_B:
226                         self.session.open(MessageBox, _("Fail to verify data file. \nfile[%s]\nmd5[%s]"%(md5sum_A,md5sum_B)), MessageBox.TYPE_INFO, timeout = 10)
227                         return 
228
229                 if self.callback is not None:
230                         self.callback(_(self.gbin))
231                 self.close()
232
233         def onClickCancel(self):
234                 self.close()
235
236         # uri : source file url(string)
237         # tf  : target file name(string)
238         # bd  : target base directory(string)
239         # cbfunc(string) : callback function(function)
240         def doDownload(self, uri, tf, bd='/tmp', cbfunc=None, errmsg="Fail to download."):
241                 tar = bd + "/" + tf
242                 #print "[FirmwareUpgrade] - Download Info : [%s][%s]" % (uri, tar)
243                 def doHook(blockNumber, blockSize, totalSize) :
244                         if blockNumber*blockSize > totalSize and cbfunc is not None:
245                                 cbfunc(tar)
246                 opener = urllib.URLopener()
247                 try:
248                         opener.open(uri)
249                 except:
250                         #self.session.open(MessageBox, _("File not found in this URL:\n%s"%(uri)), MessageBox.TYPE_INFO, timeout = 10)
251                         print "[FirmwareUpgrade] - Fail to download. URL :",uri
252                         self.session.open(MessageBox, _(errmsg), MessageBox.TYPE_INFO, timeout = 10)
253                         del opener
254                         return False
255                 try :
256                         f, h = urlretrieve(uri, tar, doHook)
257                 except IOError, msg:
258                         #self.session.open(MessageBox, _(str(msg)), MessageBox.TYPE_INFO, timeout = 10)
259                         print "[FirmwareUpgrade] - Fail to download. ERR_MSG :",str(msg)
260                         self.session.open(MessageBox, _(errmsg), MessageBox.TYPE_INFO, timeout = 10)
261                         del opener
262                         return False
263                 del opener
264                 return True
265
266         def runDownloading(self) :
267                 self.timer_downloading.stop()
268                 machine = str(open("/proc/stb/info/vumodel").read().strip())
269
270                 def cbDownloadDone(tar):
271                         try:
272                                 if os.path.splitext(tar)[1] != ".files":
273                                         self["status"].setText("Downloaded : %s\nSelect to press OK, Exit to press Cancel."%(tar))
274                         except:
275                                 pass
276                 # target
277                 global fwdata
278                 root_uri  = fwdata[self.firmware][0]
279                 root_file = fwdata[self.firmware][1]
280                 if not self.doDownload("%s/%s"%(root_uri, root_file), root_file, cbfunc=cbDownloadDone):
281                         self.resetGUI()
282                         self.downloadLock = False
283                         return
284
285                 target_path = ""
286                 for l in file("/tmp/"+root_file).readlines():
287                         if l.startswith(machine):
288                                 try:
289                                         target_path = l.split("=")[1].strip()
290                                 except:
291                                         target_path = ""
292                                         pass
293                 if target_path == "":
294                         self.session.open(MessageBox, _("Firmware does not exist."), MessageBox.TYPE_INFO)
295                         self.resetGUI()
296                         self.downloadLock = False
297                         return
298
299                 self.guri = "%s/vu%s/%s"%(root_uri, machine, target_path)
300                 self.gbin = os.path.basename(target_path)
301                 #print "[FirmwareUpgrade] - uri[%s], data[%s], data_path[%s]" % (self.gbin, self.guri, target_path)
302                 os.system("rm -f /tmp/" + root_file)
303
304                 # md5
305                 if not self.doDownload(self.guri+".md5", self.gbin+".md5", cbfunc=cbDownloadDone, errmsg="Can't download the checksum file."):
306                         self.resetGUI()
307                         self.downloadLock = False
308                         return
309                 # data
310                 if not self.doDownload(self.guri, self.gbin, cbfunc=cbDownloadDone, errmsg="Can't download the firmware file."):
311                         self.resetGUI()
312                         self.downloadLock = False
313                         return
314
315                 t = ''
316                 self["file_list"].changeDir("/tmp/")
317                 self["file_list"].moveToIndex(0)
318                 while cmp(self["file_list"].getFilename(), self.gbin) != 0 :
319                         self["file_list"].down()
320                         if cmp(t, self["file_list"].getFilename()) == 0:
321                                 break
322                         t = self["file_list"].getFilename()
323
324                 del self.timer_downloading
325                 self.timer_downloading = None
326                 self.downloadLock = False
327
328         def onClickBlue(self):
329                 if self.downloadLock:
330                         return
331                 self.downloadLock = True
332                 if not os.path.exists("/proc/stb/info/vumodel"):
333                         self.session.open(MessageBox, _("Can't found model name."), MessageBox.TYPE_INFO, timeout = 10)
334                         self.downloadLock = False
335                         return
336                 self["status"].setText("Please wait during download.")
337                 self.timer_downloading = eTimer()
338                 self.timer_downloading.callback.append(self.runDownloading)
339                 self.timer_downloading.start(1000)
340
341         def onClickUp(self):
342                 if self.downloadLock:
343                         return
344                 self.resetGUI()
345                 self["file_list"].up()
346
347         def onClickDown(self):
348                 if self.downloadLock:
349                         return
350                 self.resetGUI()
351                 self["file_list"].down()
352
353         def onClickLeft(self):
354                 if self.downloadLock:
355                         return
356                 self.resetGUI()
357                 self["file_list"].pageUp()
358
359         def onClickRight(self):
360                 if self.downloadLock:
361                         return
362                 self.resetGUI()
363                 self["file_list"].pageDown()
364
365         def keyNone(self):
366                 None
367
368 class FirmwareUpgrade(Screen, ConfigListScreen):
369         skin =  """
370                 <screen position="center,center" size="560,175" title="Firmware Upgrade" >
371                         <ePixmap pixmap="Vu_HD/buttons/red.png" position="125,7" size="80,40" alphatest="blend" />
372                         <ePixmap pixmap="Vu_HD/buttons/green.png" position="330,7" size="80,40" alphatest="blend" />
373
374                         <widget source="key_red" render="Label" position="160,0" zPosition="1" size="155,40" font="Regular;20" halign="left" valign="center" transparent="1" />
375                         <widget source="key_green" render="Label" position="365,0" zPosition="1" size="155,40" font="Regular;20" halign="left" valign="center" transparent="1" />
376
377                         <widget name="config" zPosition="2" position="0,50" itemHeight="36" size="540,40" scrollbarMode="showOnDemand" transparent="1" />
378                         <widget source="status" render="Label" position="0,100" zPosition="1" size="540,75" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1" />
379                 </screen>
380                 """
381
382         def __init__(self, session): 
383                 Screen.__init__(self, session)
384                 self.session = session 
385
386                 self["shortcuts"] = ActionMap(["ShortcutActions", "SetupActions" ],
387                 {
388                         "ok":      self.keyGreen,
389                         "cancel":  self.keyRed,
390                         "red":     self.keyRed,
391                         "green":   self.keyGreen,
392                         "blue":    self.keyBlue,
393                 }, -2)
394
395                 self.list = []
396                 self.updateFilePath = ""
397
398                 self.finishedExit = False
399
400                 self.rebootLock = False
401                 self.rebootMessage = ""
402                 self.cbRebootCallCount = 0;
403
404                 ConfigListScreen.__init__(self, self.list, session=self.session)
405                 self["key_red"] = StaticText(_("Close"))
406
407                 self.logmode = None
408                 self.old_blue_clicked = 0
409                 self.fileopenmode = False
410                 self.upgrade_auto_run_timer = eTimer()
411                 self.upgrade_auto_run_timer.callback.append(self.keyGreen)
412
413                 global fwlist
414                 if fwlist is None:
415                         self["key_green"] = StaticText(_(" "))
416                         self["status"] = StaticText(_("This plugin is supported only the Ultimo/Uno."))
417                 else:
418                         self["key_green"] = StaticText(_("Upgrade"))
419                         self["status"] = StaticText(_(" "))
420                         self.setupUI()
421
422         def setupUI(self):
423                 global fwlist
424                 self.list = []
425                 self._item_firmware  = ConfigSelection(default=fwlist[0][0],  choices=fwlist)
426                 self._entry_firmware = getConfigListEntry(_("Firmware"), self._item_firmware)
427                 self.list.append(self._entry_firmware)
428                 self["config"].list = self.list
429                 self["config"].l.setList(self.list)
430                 self.setupStatus()
431
432         def setupStatus(self,message=None,reboot=False):
433                 self.updateFilePath = ""
434                 if message is not None:
435                         self.rebootLock = reboot
436                         self["status"].setText(message)
437                         if reboot:
438                                 self.rebootMessage = message
439                                 self.reboot_timer = eTimer()
440                                 self.reboot_timer.callback.append(self.cbReboot)
441                                 self.reboot_timer.start(1000)
442                         return
443                 if not self.rebootLock:
444                         self["status"].setText("Press the Green/OK button")
445
446         def doReboot(self):
447                 from Screens.Standby import TryQuitMainloop
448                 self.session.open(TryQuitMainloop, 2)
449
450         def cbReboot(self):
451                 max_call_count = 20
452                 self.finishedExit = True
453                 if self.cbRebootCallCount < max_call_count:
454                         self.cbRebootCallCount = self.cbRebootCallCount + 1
455                         #self["status"].setText("%s (%d)"%(self.rebootMessage, max_call_count-self.cbRebootCallCount))
456                         self["status"].setText("Reboot after %d seconds. Press the OK to reboot now."%(max_call_count-self.cbRebootCallCount))
457                         return
458                 self.doReboot()
459
460         # filebrowser window callback function
461         def cbSetStatus(self, data=None):
462                 if data is not None:
463                         self["status"].setText("Press the Green/OK button, if you want to upgrade to this file:\n%s\n" % (data))
464                         self.updateFilePath = data
465                         if self.fileopenmode == False:
466                                 self.upgrade_auto_run_timer.start(1000)
467
468         # upgrade window callback function
469         def cbFinishedUpgrade(self,message=None,reboot=False):
470                 self.setupStatus(message=message,reboot=reboot)
471
472         def cbRunUpgrade(self, ret):
473                 if ret == False:
474                         return
475
476                 if self.updateFilePath == "":
477                         self.session.open(MessageBox, _("No selected binary data!!"), MessageBox.TYPE_INFO, timeout = 10)
478                         return
479                 device = None
480                 for d in fwdata[self._item_firmware.value][2].split(';'):
481                         if os.path.exists(d):
482                                 device = d                      
483                 if device is None:
484                         self.session.open(MessageBox, _("Can't found device file!!"), MessageBox.TYPE_INFO, timeout = 10)
485                         return
486                 fbs = self.session.open(UpgradeStatus, self, self._item_firmware.value, self.updateFilePath, device)
487                 fbs.setCallback(self.cbFinishedUpgrade)
488         
489         def doFileOpen(self):
490                 fbs = self.session.open(Filebrowser, self, self._item_firmware.value)
491                 fbs.setCallback(self.cbSetStatus)
492
493         def keyLeft(self):
494                 if self.rebootLock:
495                         return
496                 global fwlist
497                 if fwlist is None:
498                         return
499                 ConfigListScreen.keyLeft(self)
500                 self.setupStatus()
501
502         def keyRight(self):
503                 global fwlist
504                 if fwlist is None:
505                         return
506                 ConfigListScreen.keyRight(self)
507                 self.setupStatus()
508
509         def keyGreen(self):
510                 if self.finishedExit:
511                         self.doReboot()
512                         return
513                 self.upgrade_auto_run_timer.stop()
514                 if self.rebootLock:
515                         return
516                 global fwlist
517                 if fwlist is None:
518                         return
519                 if self.updateFilePath == "":
520                         #self.session.open(MessageBox, _("No selected binary data!!"), MessageBox.TYPE_INFO)
521                         self.doFileOpen()
522                         return
523                 msg = "You should not be stop during the upgrade.\nDo you want to upgrade?"
524                 self.session.openWithCallback(self.cbRunUpgrade, MessageBox, _(msg), MessageBox.TYPE_YESNO, timeout = 15, default = True)
525                 self.fileopenmode = False
526
527         def keyYellow(self):
528                 if self.rebootLock:
529                         return
530                 global fwlist
531                 if fwlist is None:
532                         return
533                 self.fileopenmode = True
534                 self.doFileOpen()
535
536         def keyRed(self):
537                 if self.rebootLock:
538                         return
539                 self.close()
540
541         def cbLogMode(self):
542                 if self.old_blue_clicked:
543                         return
544                 self.logmode.stop()
545                 if os.path.exists("/tmp/onlogmode"):
546                         return
547                 os.system("touch /tmp/onlogmode")
548
549         def keyBlue(self):
550                 if self.rebootLock:
551                         return
552                 if self.logmode is not None and self.old_blue_clicked == 0:
553                         return
554                 if self.old_blue_clicked:
555                         self.old_blue_clicked = 0
556                         return
557                 self.old_blue_clicked = 1
558                 self.logmode = eTimer()
559                 self.logmode.callback.append(self.cbLogMode)
560                 self.logmode.start(1000)
561                 
562         def keyNone(self):
563                 None
564
565 def main(session, **kwargs):
566         session.open(FirmwareUpgrade)
567                                                            
568 def Plugins(**kwargs):            
569         return PluginDescriptor(name=_("Firmware Upgrade"), description="Upgrade Firmware..", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main)
570