Initial implementation of OpenWrt mtd writer
[project/luci.git] / libs / sys / luasrc / sys / mtdow.lua
1 --[[
2 LuCI - Lua Configuration Interface
3
4 Copyright 2008 Steven Barth <steven@midlink.org>
5 Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 $Id$
14 ]]--
15
16 local io = require "io"
17 local os = require "os"
18 local fs = require "luci.fs"
19 local util = require "luci.util"
20 local ltn12 = require "luci.ltn12"
21 local posix = require "posix"
22
23 local type, assert, error = type, assert, error
24
25 module "luci.sys.mtdow"
26
27 local WRITE_IMAGE = 0
28 local WRITE_COMBINED = 1
29 local WRITE_EMULATED = 2
30
31 Writer = util.class()
32
33 -- x86
34 EmulatedWriter = util.class(Writer)
35 EmulatedWriter.blocks = {
36         image = {
37                 magic = "eb48",
38                 device = "/dev/hda",
39                 write = WRITE_SEPARATELY
40         }
41 }
42
43 -- Broadcom
44 TRXWriter = util.class(Writer)
45 TRXWriter.blocks = {
46         image = {
47                 magic = {"4844", "5735"},
48                 device = "linux",
49                 write = WRITE_COMBINED
50         }
51 }
52
53 -- Magicbox
54 CommonWriter = util.class(Writer)
55 CommonWriter.blocks = {
56         image = {
57                 device = "linux",
58                 write = WRITE_COMBINED
59         }
60 }
61
62 -- Atheros
63 RedWriter = util.class(Writer)
64 RedWriter.blocks = {
65         kernel = {
66                 device = "vmlinux.bin.l7",
67                 write = WRITE_IMAGE
68         },
69         rootfs = {
70                 device = "rootfs",
71                 write = WRITE_COMBINED
72         } 
73 }
74
75 function EmulatedWriter.write_block(self, name, imagestream, appendpattern)
76         if appendpattern then
77                 os.execute("grep rootfs /proc/mtd >/dev/null || "
78                         .. "{ echo /dev/hda2,65536,rootfs > "
79                         .. "/sys/module/block2mtd/parameters/block2mtd }")
80         end
81         return Writer.write_block(self, name, imagestream, appendpattern)
82 end
83
84
85 Writer.MTD = "/sbin/mtd"
86 Writer.SAFEMTD = "/tmp/mtd"
87 Writer.IMAGEFIFO = "/tmp/mtdimage.fifo"
88
89 function Writer.write_block(self, name, imagestream, appendfile)
90         assert(self.blocks[name], "Undefined block: " % name)
91         local block = self.blocks[name]
92         local device = block.device
93         device = fs.stat(device) and device or self:_find_mtdblock(device)
94         assert(device, "Unable to determine device file")
95         if block.magic then
96                 imagestream = self:_check_magic(imagestream, block.magic)
97         end
98         assert(imagestream, "Invalid image file")
99         
100         if appendfile then
101                 if block.write == WRITE_COMBINED then
102                         return (self:_write_combined(device, imagestream, appendfile) == 0)
103                 elseif block.write == WRITE_EMULATED then
104                         return (self:_write_emulated(device, imagestream, appendfile) == 0)
105                 else
106                         error("Appending is not supported for selected platform.")
107                 end
108         else
109                 return (self:_write_memory(device, imagestream) == 0)
110         end
111 end
112
113 function Writer._check_magic(self, imagestream, magic)
114         magic = type(magic) == "table" and magic or {magic}
115         
116         local block = imagestream()
117         assert(block, "Invalid image stream")
118         local cm = "%x%x" % {block:byte(1), block:byte(2)}
119         
120         if util.contains(magic, cm) then
121                 return ltn12.source.cat(ltn12.source.string(block), imagestream)
122         end
123 end
124
125 function Writer._find_mtdblock(self, mtdname)
126         local k
127         local prefix = "/dev/mtdblock"
128         prefix = prefix .. (fs.stat(prefix) and "/" or "")
129         
130         for l in io.lines("/proc/mtd") do
131                 local k = l:match('([%w-_]+):.*-"%s"' % mtdname)
132                 if k then return prefix..k end
133         end
134 end
135
136 function Write._write_emulated(self, devicename, imagestream, appendfile)
137         local stat = (self:_write_memory(device, imagestream) == 0)
138         stat = stat and (self:_refresh_block("rootfs") == 0)
139         local squash = self:_find_mtdblock("rootfs_data")
140         if squash then
141                 stat = stat and (self:_append("rootfs_data", imagestream, true) == 0)
142         else
143                 stat = stat and (self:_append("rootfs", imagestream) == 0)
144         end
145         return stat
146 end
147
148 function Writer._write_memory(self, devicename, imagestream)
149         local devicestream = ltn12.sink.file(io.open(devicename, "w"))
150         local stat, err = ltn12.pump.all(imagestream, devicestream)
151         if stat then
152                 return os.execute("sync")
153         end
154 end
155
156 function Writer._write_combined(self, devicename, imagestream, appendfile)
157         assert(fs.copy(self.MTD, self.SAFEMTD), "Unable to copy mtd writer")
158         assert(posix.mkfifo(self.IMAGEFIFO), "Unable to create image pipe")
159         
160         local imagefifo = io.open(self.IMAGEFIFO, "w")
161         
162         assert(imagefifo, "Unable to open image pipe")
163         
164         local imageproc = posix.fork()
165         assert(imageproc ~= -1, "Unable to fork()")
166         if imageproc == 0 then
167                 ltn12.pump.all(imagestream, ltn12.sink.file(imagefifo))
168                 os.exit(0)
169         end
170         
171         return os.execute( 
172                 "%s -j '%s' write '%s' '%s'" % {
173                         self.SAFEMTD, appendfile, devicename, self.IMAGEFIFO
174                 }
175         )
176 end
177
178 function Writer._refresh_block(self, devicename)
179         assert(fs.copy(self.MTD, self.SAFEMTD), "Unable to copy mtd writer")
180         return os.execute("%s refresh '%s'" % {self.SAFEMTD, devicename})
181 end
182
183 function Writer._append(self, devicename, appendfile, erase)
184         assert(fs.copy(self.MTD, self.SAFEMTD), "Unable to copy mtd writer")
185         erase = erase and ("-e '%s' " % devicename) or ''
186         
187         return os.execute( 
188                 "%s %s jffs2write '%s' '%s'" % {
189                         self.SAFEMTD, erase, appendfile, devicename
190                 }
191         )
192 end