OLD | NEW |
1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 | 5 |
6 # DESCRIPTION : | 6 # DESCRIPTION : |
7 # | 7 # |
8 # Intended for use during manufacturing to validate Developer mode | 8 # Intended for use during manufacturing to validate Developer mode |
9 # switch and Recovery button function properly. This program will | 9 # switch and Recovery button function properly. This program will |
10 # display an image of the d-housing with Developer switch and Recovery | 10 # display an image of the d-housing with Developer switch and Recovery |
11 # button. Operator will then be instructed via text and visually to | 11 # button. Operator will then be instructed via text and visually to |
12 # switch and restore the Developer switch and press/release the | 12 # switch and restore the Developer switch and press/release the |
13 # Recovery button. Success at each step resets a 20 second countdown timer. | 13 # Recovery button. Success at each step resets a 20 second countdown timer. |
14 | 14 |
| 15 |
| 16 import cairo |
| 17 import gobject |
| 18 import gtk |
| 19 import sys |
| 20 import time |
| 21 import os |
| 22 import math |
| 23 |
| 24 from autotest_lib.client.bin import factory |
| 25 from autotest_lib.client.bin import factory_ui_lib as ful |
15 from autotest_lib.client.bin import test | 26 from autotest_lib.client.bin import test |
16 from autotest_lib.client.common_lib import error | 27 from autotest_lib.client.common_lib import error |
17 from autotest_lib.client.common_lib import factory_test | 28 |
18 | 29 |
19 import gtk | 30 class DevRecTest(): |
20 import cairo | 31 |
21 import os | 32 gpio_info = { |
22 | 33 'developer' : {'type' : 'switch', |
23 import DevRecTest | 34 'cx' : 355, |
| 35 'cy' : 175, |
| 36 'size' : 30, |
| 37 'arrow' : {'x' : 305, |
| 38 'y' : 175, |
| 39 'width' : 15, |
| 40 'length' : 100, |
| 41 # in degrees starts as rt arrow |
| 42 'direction' : 0, |
| 43 }, |
| 44 }, |
| 45 'recovery' : {'type' : 'button', |
| 46 'cx' : 635, |
| 47 'cy' : 425, |
| 48 'size' : 30, |
| 49 'arrow' : {'x' : 580, |
| 50 'y' : 425, |
| 51 'width' : 15, |
| 52 'length' : 100, |
| 53 'direction' : 270, |
| 54 } |
| 55 }, |
| 56 } |
| 57 |
| 58 # How long DevRecTest allows in seconds until failing |
| 59 timeout = 20 |
| 60 |
| 61 # How long to display the success message in seconds before exit. |
| 62 pass_msg_timeout = 2 |
| 63 |
| 64 # Background color and alpha for the final result message. |
| 65 bg_rgba_fail = (0.7, 0, 0, 0.9) |
| 66 bg_rgba_pass = ( 0, 0.7, 0, 0.9) |
| 67 |
| 68 rgba_state = [(0.0, 1.0, 0.0, 0.9), |
| 69 (0.9, 0.9, 0.0, 0.6), |
| 70 (0.9, 0.0, 0.0, 0.6)] |
| 71 |
| 72 def __init__(self, autodir, devrec_image): |
| 73 self._devrec_image = devrec_image |
| 74 self._successful = set() |
| 75 self._deadline = None |
| 76 self._success = None |
| 77 self.gpios = DevRecGpio(autodir) |
| 78 self.gpios.cfg() |
| 79 |
| 80 def show_arrow(self, context, cx, cy, headx, heady, awidth, length, |
| 81 degrees): |
| 82 |
| 83 '''Draw a simple arrow in given context. |
| 84 ''' |
| 85 |
| 86 context.save() |
| 87 |
| 88 # rotation transform |
| 89 matrix = cairo.Matrix(1, 0, 0, 1, 0, 0) |
| 90 context.set_source_rgba(0, 0, 0, 1) |
| 91 cairo.Matrix.translate(matrix, cx, cy) |
| 92 cairo.Matrix.rotate(matrix, math.radians(degrees)) |
| 93 cairo.Matrix.translate(matrix, -cx, -cy) |
| 94 context.transform(matrix) |
| 95 |
| 96 # right arrow default |
| 97 context.set_line_width(5) |
| 98 context.move_to(headx, heady) |
| 99 context.rel_line_to(-awidth, -awidth/2.0) |
| 100 context.rel_line_to(0, awidth) |
| 101 context.rel_line_to(awidth, -awidth/2.0) |
| 102 context.fill_preserve() |
| 103 context.rel_line_to(-length, 0) |
| 104 context.stroke_preserve() |
| 105 context.set_source_rgba(0, 0, 0, 0.5) |
| 106 context.stroke_preserve() |
| 107 context.restore() |
| 108 |
| 109 def start_countdown(self, duration): |
| 110 self._deadline = int(time.time()) + duration |
| 111 |
| 112 def request_action(self, widget, context, name): |
| 113 '''Determine action required by gpio state and show |
| 114 ''' |
| 115 |
| 116 gpio_default = self.gpios.table[name][1] |
| 117 gpio_state = self.gpios.table[name][2] |
| 118 gpio_val = self.gpios.gpio_read(name) |
| 119 |
| 120 # state transitions based on current value |
| 121 if (gpio_state == 2) and (gpio_val != gpio_default): |
| 122 gpio_state-=1 |
| 123 # refresh countdown |
| 124 self.start_countdown(self.timeout) |
| 125 elif (gpio_state == 1) and (gpio_val == gpio_default): |
| 126 gpio_state-=1 |
| 127 self._successful.add(name) |
| 128 if self.gpio_info.__len__() is self._successful.__len__(): |
| 129 self._success = True |
| 130 self.start_countdown(self.pass_msg_timeout) |
| 131 |
| 132 # store state change |
| 133 self.gpios.table[name][2] = gpio_state |
| 134 |
| 135 widget_width, widget_height = widget.get_size_request() |
| 136 context.save() |
| 137 ginfo = self.gpio_info[name] |
| 138 |
| 139 context.set_source_rgba(0, 0, 0, 1) |
| 140 |
| 141 if (ginfo['type'] == 'button'): |
| 142 text = ['Done', 'Release', 'Press'] |
| 143 context.arc(ginfo['cx'], ginfo['cy'], ginfo['size'], |
| 144 0, math.radians(360)) |
| 145 context.stroke() |
| 146 context.arc(ginfo['cx'], ginfo['cy'], ginfo['size'], |
| 147 0, math.radians(360)) |
| 148 elif (ginfo['type'] == 'switch'): |
| 149 text = ['Done', 'Restore', 'Move'] |
| 150 # two rects one outline of switch body the other |
| 151 # representing the position |
| 152 rect_x = ginfo['cx'] - ginfo['size'] |
| 153 rect_y = ginfo['cy'] - ginfo['size'] / 2.0 |
| 154 context.rectangle(rect_x, rect_y, ginfo['size'] * 2, |
| 155 ginfo['size']) |
| 156 |
| 157 context.stroke() |
| 158 |
| 159 if gpio_state == 1: |
| 160 rect_x = rect_x + ginfo['size'] |
| 161 context.rectangle(rect_x, rect_y, ginfo['size'], |
| 162 ginfo['size']) |
| 163 else: |
| 164 raise |
| 165 |
| 166 context.set_source_rgba(*self.rgba_state[gpio_state]) |
| 167 context.fill() |
| 168 |
| 169 if ginfo['arrow'] is not None: |
| 170 arrow_x = ginfo['arrow']['x'] |
| 171 arrow_y = ginfo['arrow']['y'] |
| 172 arrow_l = ginfo['arrow']['length'] |
| 173 arrow_w = ginfo['arrow']['width'] |
| 174 |
| 175 arrow_dir = ginfo['arrow']['direction'] |
| 176 if (gpio_state == 1) and (ginfo['type'] == 'switch'): |
| 177 arrow_dir =+ 180 |
| 178 |
| 179 self.show_arrow(context, ginfo['cx'], ginfo['cy'], |
| 180 arrow_x, arrow_y, arrow_w, arrow_l, arrow_dir) |
| 181 |
| 182 context.scale(widget_width / 1.0, widget_height / 1.0) |
| 183 context.set_source_rgba(0.1, 0.1, 0.1, 0.95) |
| 184 context.select_font_face( |
| 185 'Verdana', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) |
| 186 context.set_font_size(.05) |
| 187 |
| 188 if gpio_state > 0: |
| 189 dtext = "%s %s %s now [ %d ] " % \ |
| 190 (text[gpio_state], name, ginfo['type'], |
| 191 (self._deadline - int(time.time()))) |
| 192 else: |
| 193 dtext = "%s with %s %s" % (text[gpio_state], name, ginfo['type']) |
| 194 |
| 195 x_bearing, y_bearing, width, height = context.text_extents(dtext)[:4] |
| 196 context.move_to(0.5 - (width / 2) - x_bearing, |
| 197 0.5 - (height / 2) - y_bearing) |
| 198 context.show_text(dtext) |
| 199 context.restore() |
| 200 return True |
| 201 |
| 202 def expose_event(self, widget, event): |
| 203 context = widget.window.cairo_create() |
| 204 |
| 205 context.set_source_surface(self._devrec_image, 0, 0) |
| 206 context.paint() |
| 207 |
| 208 if self._success is None: |
| 209 for key in self.gpio_info: |
| 210 if key not in self._successful: |
| 211 self.request_action(widget, context, key) |
| 212 break |
| 213 return True |
| 214 |
| 215 def timer_event(self, window): |
| 216 if not self._deadline: |
| 217 # Ignore timer events with no countdown in progress. |
| 218 return True |
| 219 if self._deadline <= time.time(): |
| 220 self._deadline = None |
| 221 if self._success is None: |
| 222 self._success = False |
| 223 elif self._success: |
| 224 sys.exit(0) |
| 225 window.queue_draw() |
| 226 return True |
| 227 |
| 228 |
| 229 class DevRecGpio: |
| 230 ''' |
| 231 Borrowed from site_tests/hardware_GPIOSwitches. Will replace |
| 232 iotools implementation with successor chromium-os issue id=3119 |
| 233 ''' |
| 234 |
| 235 def __init__(self, autodir): |
| 236 self._autodir = autodir |
| 237 self.gpio_read = None |
| 238 self.table = None |
| 239 |
| 240 def cfg(self): |
| 241 self.sku_table = { |
| 242 # SKU: gpio_read, recovery GPIO, developer mode, |
| 243 # firmware writeprotect |
| 244 'atom-proto': {'gpio_read': self.pinetrail_gpio_read, |
| 245 # name : [<bit>, <type>, <default>, |
| 246 # <assert>, <state>] |
| 247 # <type> == button || switch || ro (read-only) |
| 248 # <default> == 0 || 1 |
| 249 # <state> == number counts down 0 |
| 250 'developer': [7, 1, 2], |
| 251 |
| 252 'recovery': [6, 1, 2], |
| 253 }, |
| 254 } |
| 255 |
| 256 # TODO(nsanders): Detect actual system type here by HWQual ID (?) |
| 257 # and redirect to the correct check. |
| 258 # We're just checking for any Atom here, and hoping for the best. |
| 259 if not os.system('cat /proc/cpuinfo | grep "model name" | ' |
| 260 'grep -qe "N4[0-9][0-9]"'): |
| 261 systemsku = 'atom-proto' |
| 262 else: |
| 263 systemsku = 'unknown' |
| 264 |
| 265 # Look up hardware configuration. |
| 266 if systemsku in self.sku_table: |
| 267 table = self.sku_table[systemsku] |
| 268 self.table = table |
| 269 self.gpio_read = table['gpio_read'] |
| 270 else: |
| 271 raise KeyError('System settings not defined for board %s' % |
| 272 systemsku) |
| 273 |
| 274 def pinetrail_gpio_read(self, name): |
| 275 if not self.table.__contains__(name): |
| 276 raise |
| 277 |
| 278 # Tigerpoint LPC Interface. |
| 279 tp_device = (0, 31, 0) |
| 280 # TP io port location of GPIO registers. |
| 281 tp_GPIOBASE = 0x48 |
| 282 # IO offset to check GPIO levels. |
| 283 tp_GP_LVL_off = 0xc |
| 284 |
| 285 try: |
| 286 tp_gpio_iobase_str = os.popen('pci_read32 %s %s %s %s' % ( |
| 287 tp_device[0], tp_device[1], tp_device[2], |
| 288 tp_GPIOBASE)).readlines()[0] |
| 289 except: |
| 290 factory.log("ERROR: reading gpio iobase") |
| 291 |
| 292 |
| 293 # Bottom bit of GPIOBASE is a flag indicating io space. |
| 294 tp_gpio_iobase = long(tp_gpio_iobase_str, 16) & ~1 |
| 295 |
| 296 try: |
| 297 tp_gpio_mask_str = os.popen('io_read32 %s' % ( |
| 298 tp_gpio_iobase + tp_GP_LVL_off)).readlines()[0] |
| 299 except: |
| 300 factory.log("ERROR: reading gpio value") |
| 301 |
| 302 tp_gpio_mask = long(tp_gpio_mask_str, 16) |
| 303 |
| 304 gpio_val = int((tp_gpio_mask >> self.table[name][0]) & 1) |
| 305 return gpio_val |
| 306 |
24 | 307 |
25 class factory_DeveloperRecovery(test.test): | 308 class factory_DeveloperRecovery(test.test): |
26 version = 1 | 309 version = 1 |
27 preserve_srcdir = True | 310 preserve_srcdir = True |
28 | 311 |
29 def key_release_callback(self, widget, event): | 312 def key_release_callback(self, widget, event): |
30 char = event.keyval in range(32,127) and chr(event.keyval) or None | 313 self._ft_state.exit_on_trigger(event) |
31 factory_test.XXX_log('key_release_callback %s(%s)' % | |
32 (event.keyval, char)) | |
33 if event.keyval == self.quit_key: | |
34 factory_test.XXX_log('%s exiting...' % self.tagged_testname) | |
35 gtk.main_quit() | |
36 factory_test.test_switch_on_trigger(event) | |
37 return True | 314 return True |
38 | 315 |
39 def register_callbacks(self, window): | 316 def register_callbacks(self, window): |
40 window.connect('key-release-event', self.key_release_callback) | 317 window.connect('key-release-event', self.key_release_callback) |
41 window.add_events(gtk.gdk.KEY_RELEASE_MASK) | 318 window.add_events(gtk.gdk.KEY_RELEASE_MASK) |
42 | 319 |
43 def run_once(self, test_widget_size=None, trigger_set=None, layout='devrec', | 320 def run_once(self, |
44 result_file_path=None, quit_key=ord('Q'), | 321 test_widget_size=None, |
45 msg='factory_DeveloperRecovery'): | 322 trigger_set=None, |
46 | 323 result_file_path=None, |
47 factory_test.XXX_log(self.tagged_testname) | 324 layout=None): |
48 | 325 |
49 self.quit_key = quit_key | 326 factory.log('%s run_once' % self.__class__) |
50 | 327 |
51 factory_test.init(trigger_set=trigger_set, | 328 self._ft_state = ful.State( |
52 result_file_path=result_file_path) | 329 trigger_set=trigger_set, |
| 330 result_file_path=result_file_path) |
53 | 331 |
54 os.chdir(self.srcdir) | 332 os.chdir(self.srcdir) |
55 dr_image = cairo.ImageSurface.create_from_png('%s.png' % layout) | 333 dr_image = cairo.ImageSurface.create_from_png('%s.png' % layout) |
56 | 334 image_size = (dr_image.get_width(), dr_image.get_height()) |
57 test_widget = DevRecTest.make_test_widget(self.autodir, dr_image) | 335 |
58 | 336 test = DevRecTest(autodir, dr_image) |
59 factory_test.run_test_widget( | 337 |
60 test_widget=test_widget, | 338 drawing_area = gtk.DrawingArea() |
| 339 drawing_area.set_size_request(*image_size) |
| 340 drawing_area.connect('expose_event', test.expose_event) |
| 341 drawing_area.add_events(gtk.gdk.EXPOSURE_MASK) |
| 342 gobject.timeout_add(200, test.timer_event, drawing_area) |
| 343 |
| 344 test.start_countdown(test.timeout) |
| 345 |
| 346 self._ft_state.run_test_widget( |
| 347 test_widget=drawing_area, |
61 test_widget_size=test_widget_size, | 348 test_widget_size=test_widget_size, |
62 window_registration_callback=self.register_callbacks) | 349 window_registration_callback=self.register_callbacks) |
63 | 350 |
64 factory_test.XXX_log('exiting %s' % self.tagged_testname) | 351 factory.log('%s run_once finished' % self.__class__) |
OLD | NEW |