f3c28a7f06c980306b8966230d57dec3c5e7cadf
[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.rebootLock = False
399                 self.rebootMessage = ""
400                 self.cbRebootCallCount = 0;
401
402                 ConfigListScreen.__init__(self, self.list, session=self.session)
403                 self["key_red"] = StaticText(_("Close"))
404
405                 self.logmode = None
406                 self.old_blue_clicked = 0
407                 self.fileopenmode = False
408                 self.upgrade_auto_run_timer = eTimer()
409                 self.upgrade_auto_run_timer.callback.append(self.keyGreen)
410
411                 global fwlist
412                 if fwlist is None:
413                         self["key_green"] = StaticText(_(" "))
414                         self["status"] = StaticText(_("This plugin is supported only the Ultimo/Uno."))
415                 else:
416                         self["key_green"] = StaticText(_("Upgrade"))
417                         self["status"] = StaticText(_(" "))
418                         self.setupUI()
419
420         def setupUI(self):
421                 global fwlist
422                 self.list = []
423                 self._item_firmware  = ConfigSelection(default=fwlist[0][0],  choices=fwlist)
424                 self._entry_firmware = getConfigListEntry(_("Firmware"), self._item_firmware)
425                 self.list.append(self._entry_firmware)
426                 self["config"].list = self.list
427                 self["config"].l.setList(self.list)
428                 self.setupStatus()
429
430         def setupStatus(self,message=None,reboot=False):
431                 self.updateFilePath = ""
432                 if message is not None:
433                         self.rebootLock = reboot
434                         self["status"].setText(message)
435                         if reboot:
436                                 self.rebootMessage = message
437                                 self.reboot_timer = eTimer()
438                                 self.reboot_timer.callback.append(self.cbReboot)
439                                 self.reboot_timer.start(500)
440                         return
441                 if not self.rebootLock:
442                         self["status"].setText("Press the Green/OK button")
443
444         def cbReboot(self):
445                 if self.cbRebootCallCount < 6:
446                         self.cbRebootCallCount = self.cbRebootCallCount + 1
447                         self["status"].setText("%s (%d)"%(self.rebootMessage, 6-self.cbRebootCallCount))
448                         return
449                 from Screens.Standby import TryQuitMainloop
450                 self.session.open(TryQuitMainloop, 2)
451
452         # filebrowser window callback function
453         def cbSetStatus(self, data=None):
454                 if data is not None:
455                         self["status"].setText("Press the Green/OK button, if you want to upgrade to this file:\n%s\n" % (data))
456                         self.updateFilePath = data
457                         if self.fileopenmode == False:
458                                 self.upgrade_auto_run_timer.start(1000)
459
460         # upgrade window callback function
461         def cbFinishedUpgrade(self,message=None,reboot=False):
462                 self.setupStatus(message=message,reboot=reboot)
463
464         def cbRunUpgrade(self, ret):
465                 if ret == False:
466                         return
467
468                 if self.updateFilePath == "":
469                         self.session.open(MessageBox, _("No selected binary data!!"), MessageBox.TYPE_INFO, timeout = 10)
470                         return
471                 device = None
472                 for d in fwdata[self._item_firmware.value][2].split(';'):
473                         if os.path.exists(d):
474                                 device = d                      
475                 if device is None:
476                         self.session.open(MessageBox, _("Can't found device file!!"), MessageBox.TYPE_INFO, timeout = 10)
477                         return
478                 fbs = self.session.open(UpgradeStatus, self, self._item_firmware.value, self.updateFilePath, device)
479                 fbs.setCallback(self.cbFinishedUpgrade)
480         
481         def doFileOpen(self):
482                 fbs = self.session.open(Filebrowser, self, self._item_firmware.value)
483                 fbs.setCallback(self.cbSetStatus)
484
485         def keyLeft(self):
486                 if self.rebootLock:
487                         return
488                 global fwlist
489                 if fwlist is None:
490                         return
491                 ConfigListScreen.keyLeft(self)
492                 self.setupStatus()
493
494         def keyRight(self):
495                 global fwlist
496                 if fwlist is None:
497                         return
498                 ConfigListScreen.keyRight(self)
499                 self.setupStatus()
500
501         def keyGreen(self):
502                 self.upgrade_auto_run_timer.stop()
503                 if self.rebootLock:
504                         return
505                 global fwlist
506                 if fwlist is None:
507                         return
508                 if self.updateFilePath == "":
509                         #self.session.open(MessageBox, _("No selected binary data!!"), MessageBox.TYPE_INFO)
510                         self.doFileOpen()
511                         return
512                 msg = "You should not be stop during the upgrade.\nDo you want to upgrade?"
513                 self.session.openWithCallback(self.cbRunUpgrade, MessageBox, _(msg), MessageBox.TYPE_YESNO, timeout = 15, default = True)
514                 self.fileopenmode = False
515
516         def keyYellow(self):
517                 if self.rebootLock:
518                         return
519                 global fwlist
520                 if fwlist is None:
521                         return
522                 self.fileopenmode = True
523                 self.doFileOpen()
524
525         def keyRed(self):
526                 if self.rebootLock:
527                         return
528                 self.close()
529
530         def cbLogMode(self):
531                 if self.old_blue_clicked:
532                         return
533                 self.logmode.stop()
534                 if os.path.exists("/tmp/onlogmode"):
535                         return
536                 os.system("touch /tmp/onlogmode")
537
538         def keyBlue(self):
539                 if self.rebootLock:
540                         return
541                 if self.logmode is not None and self.old_blue_clicked == 0:
542                         return
543                 if self.old_blue_clicked:
544                         self.old_blue_clicked = 0
545                         return
546                 self.old_blue_clicked = 1
547                 self.logmode = eTimer()
548                 self.logmode.callback.append(self.cbLogMode)
549                 self.logmode.start(1000)
550                 
551         def keyNone(self):
552                 None
553
554 def main(session, **kwargs):
555         session.open(FirmwareUpgrade)
556                                                            
557 def Plugins(**kwargs):            
558         return PluginDescriptor(name=_("Firmware Upgrade"), description="Upgrade Firmware..", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main)
559