summaryrefslogtreecommitdiffstats
path: root/movement/movement.h
blob: edb1fabc82675c9cdb32bf810b622671d8a53a5c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
#ifndef MOVEMENT_H_
#define MOVEMENT_H_
#include <stdio.h>
#include <stdbool.h>
#include "watch.h"

// Movement Preferences
// These four 32-bit structs store information about the wearer and their preferences. Tentatively, the plan is
// for Movement to use four 32-bit registers for these preferences and to store them in the RTC's backup registers
// 0-3, leaving registers 4-7 available for third party watch faces to use as they see fit.
// * The movement_settings_t struct is provided to all watch faces in the callback functions, and is stored in the
//   RTC's first backup register (BKUP[0]).
// * The movement_location_t and movement_birthdate_t types are defined here, and are tentatively meant to be
//   stored in BKUP[1] and BKUP[2], respectively.
// * The movement_reserved_t type is here as a placeholder, because I sense there's some other generally useful
//   stuff we'll want to make available to all watch faces and stash in the BKUP[3] register.
// This allows these preferences to be stored before entering BACKUP mode and and restored after waking from reset.

// movement_settings_t contains global settings that cover watch behavior, including preferences around clock and unit
// display, time zones, buzzer behavior, LED color and low energy mode timeouts. These settings are stored in BKUP[0].
// If your watch face changes one of these settings, you should store your changes in either your loop or resign
// function by calling `watch_store_backup_data(settings->reg, 0)`. Otherwise it may not persist after a reset event.
typedef union {
    struct {
        bool button_should_sound : 1;       // if true, pressing a button emits a sound.
        uint8_t to_interval : 2;            // an inactivity interval for asking the active face to resign.
        bool to_always : 1;                 // if true, always time out from the active face to face 0. otherwise only faces that time out will resign (the default).
        uint8_t le_interval : 3;            // 0 to disable low energy mode, or an inactivity interval for going into low energy mode.
        uint8_t led_duration : 2;           // how many seconds to shine the LED for (x2), or 0 to disable it.
        uint8_t led_red_color : 4;          // for general purpose illumination, the red LED value (0-15)
        uint8_t led_green_color : 4;        // for general purpose illumination, the green LED value (0-15)
        uint8_t time_zone : 6;              // an integer representing an index in the time zone table.

        // while Movement itself doesn't implement a clock or display units, it may make sense to include some
        // global settings for watch faces to check. The 12/24 hour preference could inform a clock or a
        // time-oriented complication like a sunrise/sunset timer, and a simple locale preference could tell an
        // altimeter to display feet or meters as easily as it tells a thermometer to display degrees in F or C.
        bool clock_mode_24h : 1;            // indicates whether clock should use 12 or 24 hour mode.
        bool use_imperial_units : 1;        // indicates whether to use metric units (the default) or imperial.
        uint8_t reserved : 7;               // room for more preferences if needed.
    } bit;
    uint32_t reg;
} movement_settings_t;

// movement_location_t is for storing the wearer's location. This will be useful for astronomical calculations such as
// sunrise and sunset, or predictions of visible satellite passes.
// If you create a UI for this register or need to access it, look for it in the RTC's BKUP[1] register.
typedef union {
    struct {
        int16_t latitude : 16;   // signed latutide in hundredths of a degree
        int16_t longitude : 16;  // signed longitude in hundredths of a degree
    } bit;
    uint32_t reg;
} movement_location_t;

// movement_birthdate_t is for storing the user's birth date. This will be useful for calculating the user's age — or
// hey, playing happy birthday at midnight? Fields for birth time (with hour and minute resolution) are also available,
// partly because they fit so nicely, but also because they can be useful for certain astrological calculations.
// If you create a UI for birth date or need to access it, look for it in the RTC's BKUP[2] register.
typedef union {
    struct {
        uint16_t year : 12;  // good through the year 4095
        uint8_t month : 4;
        uint8_t day : 5;
        uint8_t hour : 5;
        uint8_t minute : 6;
    } bit;
    uint32_t reg;
} movement_birthdate_t;

// movement_reserved_t is a placeholder for future use of the BKUP[3] register.
typedef union {
    struct {
        uint32_t reserved : 32;
    } bit;
    uint32_t reg;
} movement_reserved_t;

typedef enum {
    EVENT_NONE = 0,             // There is no event to report.
    EVENT_ACTIVATE,             // Your watch face is entering the foreground.
    EVENT_TICK,                 // Most common event type. Your watch face is being called from the tick callback.
    EVENT_LOW_ENERGY_UPDATE,    // If the watch is in low energy mode and you are in the foreground, you will get a chance to update the display once per minute.
    EVENT_BACKGROUND_TASK,      // Your watch face is being invoked to perform a background task. Don't update the display here; you may not be in the foreground.
    EVENT_TIMEOUT,              // Your watch face has been inactive for a while. You may want to resign, depending on your watch face's intended use case.
    EVENT_LIGHT_BUTTON_DOWN,    // The light button has been pressed, but not yet released.
    EVENT_LIGHT_BUTTON_UP,      // The light button was pressed and released.
    EVENT_LIGHT_LONG_PRESS,     // The light button was held for >2 seconds, and released.
    EVENT_MODE_BUTTON_DOWN,     // The mode button has been pressed, but not yet released.
    EVENT_MODE_BUTTON_UP,       // The mode button was pressed and released.
    EVENT_MODE_LONG_PRESS,      // The mode button was held for >2 seconds, and released.
    EVENT_ALARM_BUTTON_DOWN,    // The alarm button has been pressed, but not yet released.
    EVENT_ALARM_BUTTON_UP,      // The alarm button was pressed and released.
    EVENT_ALARM_LONG_PRESS,     // The alarm button was held for >2 seconds, and released.
} movement_event_type_t;

typedef struct {
    uint8_t event_type;
    uint8_t subsecond;
} movement_event_t;

extern const int16_t movement_timezone_offsets[];
extern const char movement_valid_position_0_chars[];
extern const char movement_valid_position_1_chars[];

/** @brief Perform setup for your watch face.
  * @details It's tempting to say this is 'one-time' setup, but technically this function is called more than
  *          once. When the watch first boots, this function is called with a NULL context_ptr, indicating
  *          that it is the first run. At this time you should set context_ptr to something non-NULL if you
  *          need to keep track of any state in your watch face. If your watch face requires any other setup,
  *          like configuring a pin mode or a peripheral, you may want to do that here too.
  *          This function will be called again after waking from sleep mode, since sleep mode disables all
  *          of the device's pins and peripherals.
  * @param settings A pointer to the global Movement settings. You can use this to inform how you present your
  *                 display to the user (i.e. taking into account whether they have silenced the buttons, or if
  *                 they prefer 12 or 24-hour mode). You can also change these settings if you like.
  * @param watch_face_index The index of this watch face in the global array of watch faces; 0 is the first face,
  *                         1 is the second, etc. You may stash this value in your context if you wish to reference
  *                         it later; your watch face's index is set at launch and will not change.
  * @param context_ptr A pointer to a pointer; at first invocation, this value will be NULL, and you can set it
  *                    to any value you like. Subsequent invocations will pass in whatever value you previously
  *                    set. You may want to check if this is NULL and if so, allocate some space to store any
  *                    data required for your watch face.
  *
  */
typedef void (*watch_face_setup)(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);

/** @brief Prepare to go on-screen.
  * @details This function is called just before your watch enters the foreground. If your watch face has any
  *          segments or text that is always displayed, you may want to set that here. In addition, if your
  *          watch face depends on data from a peripheral (like an I2C sensor), you will likely want to enable
  *          that peripheral here. In addition, if your watch face requires an update frequncy other than 1 Hz,
  *          you may want to request that here using the movement_request_tick_frequency function.
  * @param settings A pointer to the global Movement settings. @see watch_face_setup.
  * @param context A pointer to your watch face's context. @see watch_face_setup.
  *
  */
typedef void (*watch_face_activate)(movement_settings_t *settings, void *context);

/** @brief Handle events and update the display.
  * @details This function is called in response to an event. You should set up a switch statement that handles,
  *          at the very least, the EVENT_TICK and EVENT_MODE_BUTTON_UP event types. The tick event happens once
  *          per second (or more frequently if you asked for a faster tick with movement_request_tick_frequency).
  *          The mode button up event occurs when the user presses the MODE button. **Your loop function SHOULD
  *          call the movement_move_to_next_face function in response to this event.** If you have a good reason
  *          to override this behavior (e.g. your user interface requires all three buttons), your watch face MUST
  *          call the movement_move_to_next_face function in response to the EVENT_MODE_LONG_PRESS event. If you
  *          fail to do this, the user will become stuck on your watch face.
  * @param event A struct containing information about the event, including its type. @see movement_event_type_t
  *              for a list of all possible event types.
  * @param settings A pointer to the global Movement settings. @see watch_face_setup.
  * @param context A pointer to your application's context. @see watch_face_setup.
  * @return true if Movement can enter STANDBY mode; false to keep it awake. You should almost always return true.
  * @note There are two event types that require some extra thought:
          The EVENT_LOW_ENERGY_UPDATE event type is a special case. If you are in the foreground when the watch
          goes into low energy mode, you will receive this tick once a minute (at the top of the minute) so that
          you can update the screen. Great! But! When you receive this event, all pins and peripherals other than
          the RTC will have been disabled to save energy. If your display is clock or calendar oriented, this is
          fine. But if your display requires polling an I2C sensor or reading a value with the ADC, you won't be
          able to do this. You should either display the name of the watch face in response to the low power tick,
          or ensure that you resign before low power mode triggers, (e.g. by calling movement_move_to_face(0)).
          **Your watch face MUST NOT wake up peripherals in response to a low power tick.** The purpose of this
          mode is to consume as little energy as possible during the (potentially long) intervals when it's
          unlikely the user is wearing or looking at the watch.
          EVENT_BACKGROUND_TASK is also a special case. @see watch_face_wants_background_task for details.
  */
typedef bool (*watch_face_loop)(movement_event_t event, movement_settings_t *settings, void *context);

/** @brief Prepare to go off-screen.
  * @details This function is called before your watch face enters the background. If you requested a tick
  *          frequency other than the standard 1 Hz, **you must call movement_request_tick_frequency(1) here**
  *          to reset to 1 Hz. You should also disable any peripherals you enabled when you entered the foreground.
  * @param settings A pointer to the global Movement settings. @see watch_face_setup.
  * @param context A pointer to your application's context. @see watch_face_setup.
  */
typedef void (*watch_face_resign)(movement_settings_t *settings, void *context);

/** @brief OPTIONAL. Request an opportunity to run a background task.
  * @details Most apps will not need this function, but if you provide it, Movement will call it once per minute in
  *          both active and low power modes, regardless of whether your app is in the foreground. You can check the
  *          current time to determine whether you require a background task. If you return true here, Movement will
  *          immediately call your loop function with an EVENT_BACKGROUND_TASK event. Note that it will not call your
  *          activate or deactivate functions, since you are not going on screen.
  *
  *          Examples of background tasks:
  *           - Wake and play a sound when an alarm or timer has been triggered.
  *           - Check the state of an RTC interrupt pin or the timestamp of an RTC interrupt event.
  *           - Log a data point from a sensor, and then return to sleep mode.
  *
  *          Guidelines for background tasks:
  *           - Assume all peripherals and pins other than the RTC will be disabled when you get an EVENT_BACKGROUND_TASK.
  *           - Even if your background task involves only the RTC peripheral, try to request background tasks sparingly.
  *           - If your background task involves an external pin or peripheral, request background tasks no more than once per hour.
  *           - If you need to enable a pin or a peripheral to perform your task, return it to its original state afterwards.
  *
  * @param settings A pointer to the global Movement settings. @see watch_face_setup.
  * @param context A pointer to your application's context. @see watch_face_setup.
  * @return true to request a background task; false otherwise.
  */
typedef bool (*watch_face_wants_background_task)(movement_settings_t *settings, void *context);

typedef struct {
    watch_face_setup setup;
    watch_face_activate activate;
    watch_face_loop loop;
    watch_face_resign resign;
    watch_face_wants_background_task wants_background_task;
} watch_face_t;

typedef struct {
    // properties stored in BACKUP register
    movement_settings_t settings;

    // transient properties
    int16_t current_watch_face;
    int16_t next_watch_face;
    bool watch_face_changed;
    bool fast_tick_enabled;
    int16_t fast_ticks;

    // LED stuff
    int16_t light_ticks;

    // alarm stuff
    int16_t alarm_ticks;
    bool is_buzzing;

    // button tracking for long press
    uint8_t light_down_timestamp;
    uint8_t mode_down_timestamp;
    uint8_t alarm_down_timestamp;

    // background task handling
    bool needs_background_tasks_handled;
    bool has_scheduled_background_task;

    // low energy mode countdown
    int32_t le_mode_ticks;

    // app resignation countdown (TODO: consolidate with LE countdown?)
    int16_t timeout_ticks;

    // stuff for subsecond tracking
    uint8_t tick_frequency;
    uint8_t last_second;
    uint8_t subsecond;
} movement_state_t;

void movement_move_to_face(uint8_t watch_face_index);
void movement_move_to_next_face(void);
void movement_illuminate_led(void);

// note: requesting a tick frequency of 0 will break any scheduled background tasks.
// this will be fixed in a future refactor of the tick mechanism.
void movement_request_tick_frequency(uint8_t freq);

// note: watch faces can only schedule a background task when in the foreground, since
// movement will associate the scheduled task with the currently active face.
void movement_schedule_background_task(watch_date_time date_time);

void movement_play_signal(void);
void movement_play_alarm(void);

#endif // MOVEMENT_H_
DOM0 when it */ /* boots. Any other value, or omitting the char, enables auto-switch */ static unsigned char __read_mostly opt_conswitch[3] = "a"; string_param("conswitch", opt_conswitch); /* sync_console: force synchronous console output (useful for debugging). */ static bool_t __initdata opt_sync_console; boolean_param("sync_console", opt_sync_console); /* console_to_ring: send guest (incl. dom 0) console data to console ring. */ static bool_t __read_mostly opt_console_to_ring; boolean_param("console_to_ring", opt_console_to_ring); /* console_timestamps: include a timestamp prefix on every Xen console line. */ static bool_t __read_mostly opt_console_timestamps; boolean_param("console_timestamps", opt_console_timestamps); /* conring_size: allows a large console ring than default (16kB). */ static uint32_t __initdata opt_conring_size; size_param("conring_size", opt_conring_size); #define _CONRING_SIZE 16384 #define CONRING_IDX_MASK(i) ((i)&(conring_size-1)) static char __initdata _conring[_CONRING_SIZE]; static char *__read_mostly conring = _conring; static uint32_t __read_mostly conring_size = _CONRING_SIZE; static uint32_t conringc, conringp; static int __read_mostly sercon_handle = -1; static DEFINE_SPINLOCK(console_lock); /* * To control the amount of printing, thresholds are added. * These thresholds correspond to the XENLOG logging levels. * There's an upper and lower threshold for non-guest messages and for * guest-provoked messages. This works as follows, for a given log level L: * * L < lower_threshold : always logged * lower_threshold <= L < upper_threshold : rate-limited logging * upper_threshold <= L : never logged * * Note, in the above algorithm, to disable rate limiting simply make * the lower threshold equal to the upper. */ #ifdef NDEBUG #define XENLOG_UPPER_THRESHOLD 2 /* Do not print INFO and DEBUG */ #define XENLOG_LOWER_THRESHOLD 2 /* Always print ERR and WARNING */ #define XENLOG_GUEST_UPPER_THRESHOLD 2 /* Do not print INFO and DEBUG */ #define XENLOG_GUEST_LOWER_THRESHOLD 0 /* Rate-limit ERR and WARNING */ #else #define XENLOG_UPPER_THRESHOLD 4 /* Do not discard anything */ #define XENLOG_LOWER_THRESHOLD 4 /* Print everything */ #define XENLOG_GUEST_UPPER_THRESHOLD 4 /* Do not discard anything */ #define XENLOG_GUEST_LOWER_THRESHOLD 4 /* Print everything */ #endif /* * The XENLOG_DEFAULT is the default given to printks that * do not have any print level associated with them. */ #define XENLOG_DEFAULT 1 /* XENLOG_WARNING */ #define XENLOG_GUEST_DEFAULT 1 /* XENLOG_WARNING */ static int __read_mostly xenlog_upper_thresh = XENLOG_UPPER_THRESHOLD; static int __read_mostly xenlog_lower_thresh = XENLOG_LOWER_THRESHOLD; static int __read_mostly xenlog_guest_upper_thresh = XENLOG_GUEST_UPPER_THRESHOLD; static int __read_mostly xenlog_guest_lower_thresh = XENLOG_GUEST_LOWER_THRESHOLD; static void parse_loglvl(char *s); static void parse_guest_loglvl(char *s); /* * <lvl> := none|error|warning|info|debug|all * loglvl=<lvl_print_always>[/<lvl_print_ratelimit>] * <lvl_print_always>: log level which is always printed * <lvl_print_rlimit>: log level which is rate-limit printed * Similar definitions for guest_loglvl, but applies to guest tracing. * Defaults: loglvl=warning ; guest_loglvl=none/warning */ custom_param("loglvl", parse_loglvl); custom_param("guest_loglvl", parse_guest_loglvl); static atomic_t print_everything = ATOMIC_INIT(0); #define ___parse_loglvl(s, ps, lvlstr, lvlnum) \ if ( !strncmp((s), (lvlstr), strlen(lvlstr)) ) { \ *(ps) = (s) + strlen(lvlstr); \ return (lvlnum); \ } static int __init __parse_loglvl(char *s, char **ps) { ___parse_loglvl(s, ps, "none", 0); ___parse_loglvl(s, ps, "error", 1); ___parse_loglvl(s, ps, "warning", 2); ___parse_loglvl(s, ps, "info", 3); ___parse_loglvl(s, ps, "debug", 4); ___parse_loglvl(s, ps, "all", 4); return 2; /* sane fallback */ } static void __init _parse_loglvl(char *s, int *lower, int *upper) { *lower = *upper = __parse_loglvl(s, &s); if ( *s == '/' ) *upper = __parse_loglvl(s+1, &s); if ( *upper < *lower ) *upper = *lower; } static void __init parse_loglvl(char *s) { _parse_loglvl(s, &xenlog_lower_thresh, &xenlog_upper_thresh); } static void __init parse_guest_loglvl(char *s) { _parse_loglvl(s, &xenlog_guest_lower_thresh, &xenlog_guest_upper_thresh); } static char * __init loglvl_str(int lvl) { switch ( lvl ) { case 0: return "Nothing"; case 1: return "Errors"; case 2: return "Errors and warnings"; case 3: return "Errors, warnings and info"; case 4: return "All"; } return "???"; } /* * ******************************************************** * *************** ACCESS TO CONSOLE RING ***************** * ******************************************************** */ static void putchar_console_ring(int c) { ASSERT(spin_is_locked(&console_lock)); conring[CONRING_IDX_MASK(conringp++)] = c; if ( (uint32_t)(conringp - conringc) > conring_size ) conringc = conringp - conring_size; } long read_console_ring(struct xen_sysctl_readconsole *op) { XEN_GUEST_HANDLE_PARAM(char) str; uint32_t idx, len, max, sofar, c; str = guest_handle_cast(op->buffer, char), max = op->count; sofar = 0; c = conringc; if ( op->incremental && ((int32_t)(op->index - c) > 0) ) c = op->index; while ( (c != conringp) && (sofar < max) ) { idx = CONRING_IDX_MASK(c); len = conringp - c; if ( (idx + len) > conring_size ) len = conring_size - idx; if ( (sofar + len) > max ) len = max - sofar; if ( copy_to_guest_offset(str, sofar, &conring[idx], len) ) return -EFAULT; sofar += len; c += len; } if ( op->clear ) { spin_lock_irq(&console_lock); if ( (uint32_t)(conringp - c) > conring_size ) conringc = conringp - conring_size; else conringc = c; spin_unlock_irq(&console_lock); } op->count = sofar; op->index = c; return 0; } /* * ******************************************************* * *************** ACCESS TO SERIAL LINE ***************** * ******************************************************* */ /* Characters received over the serial line are buffered for domain 0. */ #define SERIAL_RX_SIZE 128 #define SERIAL_RX_MASK(_i) ((_i)&(SERIAL_RX_SIZE-1)) static char serial_rx_ring[SERIAL_RX_SIZE]; static unsigned int serial_rx_cons, serial_rx_prod; static void (*serial_steal_fn)(const char *); int console_steal(int handle, void (*fn)(const char *)) { if ( (handle == -1) || (handle != sercon_handle) ) return 0; if ( serial_steal_fn != NULL ) return -EBUSY; serial_steal_fn = fn; return 1; } void console_giveback(int id) { if ( id == 1 ) serial_steal_fn = NULL; } static void sercon_puts(const char *s) { if ( serial_steal_fn != NULL ) (*serial_steal_fn)(s); else serial_puts(sercon_handle, s); } static void dump_console_ring_key(unsigned char key) { uint32_t idx, len, sofar, c; unsigned int order; char *buf; printk("'%c' pressed -> dumping console ring buffer (dmesg)\n", key); /* create a buffer in which we'll copy the ring in the correct order and NUL terminate */ order = get_order_from_bytes(conring_size + 1); buf = alloc_xenheap_pages(order, 0); if ( buf == NULL ) { printk("unable to allocate memory!\n"); return; } c = conringc; sofar = 0; while ( (c != conringp) ) { idx = CONRING_IDX_MASK(c); len = conringp - c; if ( (idx + len) > conring_size ) len = conring_size - idx; memcpy(buf + sofar, &conring[idx], len); sofar += len; c += len; } buf[sofar] = '\0'; sercon_puts(buf); video_puts(buf); free_xenheap_pages(buf, order); } static struct keyhandler dump_console_ring_keyhandler = { .u.fn = dump_console_ring_key, .desc = "synchronously dump console ring buffer (dmesg)" }; /* CTRL-<switch_char> switches input direction between Xen and DOM0. */ #define switch_code (opt_conswitch[0]-'a'+1) static int __read_mostly xen_rx = 1; /* FALSE => input passed to domain 0. */ static void switch_serial_input(void) { static char *input_str[2] = { "DOM0", "Xen" }; xen_rx = !xen_rx; printk("*** Serial input -> %s", input_str[xen_rx]); if ( switch_code ) printk(" (type 'CTRL-%c' three times to switch input to %s)", opt_conswitch[0], input_str[!xen_rx]); printk("\n"); } static void __serial_rx(char c, struct cpu_user_regs *regs) { if ( xen_rx ) return handle_keypress(c, regs); /* Deliver input to guest buffer, unless it is already full. */ if ( (serial_rx_prod-serial_rx_cons) != SERIAL_RX_SIZE ) serial_rx_ring[SERIAL_RX_MASK(serial_rx_prod++)] = c; /* Always notify the guest: prevents receive path from getting stuck. */ send_global_virq(VIRQ_CONSOLE); } static void serial_rx(char c, struct cpu_user_regs *regs) { static int switch_code_count = 0; if ( switch_code && (c == switch_code) ) { /* We eat CTRL-<switch_char> in groups of 3 to switch console input. */ if ( ++switch_code_count == 3 ) { switch_serial_input(); switch_code_count = 0; } return; } for ( ; switch_code_count != 0; switch_code_count-- ) __serial_rx(switch_code, regs); /* Finally process the just-received character. */ __serial_rx(c, regs); } static void notify_dom0_con_ring(unsigned long unused) { send_global_virq(VIRQ_CON_RING); } static DECLARE_SOFTIRQ_TASKLET(notify_dom0_con_ring_tasklet, notify_dom0_con_ring, 0); static long guest_console_write(XEN_GUEST_HANDLE_PARAM(char) buffer, int count) { char kbuf[128], *kptr; int kcount; while ( count > 0 ) { if ( hypercall_preempt_check() ) return hypercall_create_continuation( __HYPERVISOR_console_io, "iih", CONSOLEIO_write, count, buffer); kcount = min_t(int, count, sizeof(kbuf)-1); if ( copy_from_guest(kbuf, buffer, kcount) ) return -EFAULT; kbuf[kcount] = '\0'; spin_lock_irq(&console_lock); sercon_puts(kbuf); video_puts(kbuf); if ( opt_console_to_ring ) { for ( kptr = kbuf; *kptr != '\0'; kptr++ ) putchar_console_ring(*kptr); tasklet_schedule(&notify_dom0_con_ring_tasklet); } spin_unlock_irq(&console_lock); guest_handle_add_offset(buffer, kcount); count -= kcount; } return 0; } long do_console_io(int cmd, int count, XEN_GUEST_HANDLE_PARAM(char) buffer) { long rc; unsigned int idx, len; rc = xsm_console_io(XSM_OTHER, current->domain, cmd); if ( rc ) return rc; switch ( cmd ) { case CONSOLEIO_write: rc = guest_console_write(buffer, count); break; case CONSOLEIO_read: rc = 0; while ( (serial_rx_cons != serial_rx_prod) && (rc < count) ) { idx = SERIAL_RX_MASK(serial_rx_cons); len = serial_rx_prod - serial_rx_cons; if ( (idx + len) > SERIAL_RX_SIZE ) len = SERIAL_RX_SIZE - idx; if ( (rc + len) > count ) len = count - rc; if ( copy_to_guest_offset(buffer, rc, &serial_rx_ring[idx], len) ) { rc = -EFAULT; break; } rc += len; serial_rx_cons += len; } break; default: rc = -ENOSYS; break; } return rc; } /* * ***************************************************** * *************** GENERIC CONSOLE I/O ***************** * ***************************************************** */ static bool_t console_locks_busted; static void __putstr(const char *str) { int c; ASSERT(spin_is_locked(&console_lock)); sercon_puts(str); video_puts(str); if ( !console_locks_busted ) { while ( (c = *str++) != '\0' ) putchar_console_ring(c); tasklet_schedule(&notify_dom0_con_ring_tasklet); } } static int printk_prefix_check(char *p, char **pp) { int loglvl = -1; int upper_thresh = xenlog_upper_thresh; int lower_thresh = xenlog_lower_thresh; while ( (p[0] == '<') && (p[1] != '\0') && (p[2] == '>') ) { switch ( p[1] ) { case 'G': upper_thresh = xenlog_guest_upper_thresh; lower_thresh = xenlog_guest_lower_thresh; if ( loglvl == -1 ) loglvl = XENLOG_GUEST_DEFAULT; break; case '0' ... '3': loglvl = p[1] - '0'; break; } p += 3; } if ( loglvl == -1 ) loglvl = XENLOG_DEFAULT; *pp = p; return ((atomic_read(&print_everything) != 0) || (loglvl < lower_thresh) || ((loglvl < upper_thresh) && printk_ratelimit())); } static void printk_start_of_line(void) { struct tm tm; char tstr[32]; __putstr("(XEN) "); if ( !opt_console_timestamps ) return; tm = wallclock_time(); if ( tm.tm_mday == 0 ) return; snprintf(tstr, sizeof(tstr), "[%04u-%02u-%02u %02u:%02u:%02u] ", 1900 + tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); __putstr(tstr); } void printk(const char *fmt, ...) { static char buf[1024]; static int start_of_line = 1, do_print; va_list args; char *p, *q; unsigned long flags; /* console_lock can be acquired recursively from __printk_ratelimit(). */ local_irq_save(flags); spin_lock_recursive(&console_lock); va_start(args, fmt); (void)vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); p = buf; while ( (q = strchr(p, '\n')) != NULL ) { *q = '\0'; if ( start_of_line ) do_print = printk_prefix_check(p, &p); if ( do_print ) { if ( start_of_line ) printk_start_of_line(); __putstr(p); __putstr("\n"); } start_of_line = 1; p = q + 1; } if ( *p != '\0' ) { if ( start_of_line ) do_print = printk_prefix_check(p, &p); if ( do_print ) { if ( start_of_line ) printk_start_of_line(); __putstr(p); } start_of_line = 0; } spin_unlock_recursive(&console_lock); local_irq_restore(flags); } void __init console_init_preirq(void) { char *p; int sh; serial_init_preirq(); /* Where should console output go? */ for ( p = opt_console; p != NULL; p = strchr(p, ',') ) { if ( *p == ',' ) p++; if ( !strncmp(p, "vga", 3) ) video_init(); else if ( !strncmp(p, "none", 4) ) continue; else if ( (sh = serial_parse_handle(p)) >= 0 ) sercon_handle = sh; else { char *q = strchr(p, ','); if ( q != NULL ) *q = '\0'; printk("Bad console= option '%s'\n", p); if ( q != NULL ) *q = ','; } } serial_set_rx_handler(sercon_handle, serial_rx); /* HELLO WORLD --- start-of-day banner text. */ spin_lock(&console_lock); __putstr(xen_banner()); spin_unlock(&console_lock); printk("Xen version %d.%d%s (%s@%s) (%s) debug=%c %s\n", xen_major_version(), xen_minor_version(), xen_extra_version(), xen_compile_by(), xen_compile_domain(), xen_compiler(), debug_build() ? 'y' : 'n', xen_compile_date()); printk("Latest ChangeSet: %s\n", xen_changeset()); if ( opt_sync_console ) { serial_start_sync(sercon_handle); add_taint(TAINT_SYNC_CONSOLE); printk("Console output is synchronous.\n"); } } void __init console_init_postirq(void) { char *ring; unsigned int i, order, memflags; serial_init_postirq(); if ( !opt_conring_size ) opt_conring_size = num_present_cpus() << (9 + xenlog_lower_thresh); order = get_order_from_bytes(max(opt_conring_size, conring_size)); memflags = MEMF_bits(crashinfo_maxaddr_bits); while ( (ring = alloc_xenheap_pages(order, memflags)) == NULL ) { BUG_ON(order == 0); order--; } opt_conring_size = PAGE_SIZE << order; spin_lock_irq(&console_lock); for ( i = conringc ; i != conringp; i++ ) ring[i & (opt_conring_size - 1)] = conring[i & (conring_size - 1)]; conring = ring; wmb(); /* Allow users of console_force_unlock() to see larger buffer. */ conring_size = opt_conring_size; spin_unlock_irq(&console_lock); printk("Allocated console ring of %u KiB.\n", opt_conring_size >> 10); } void __init console_endboot(void) { int i, j; printk("Std. Loglevel: %s", loglvl_str(xenlog_lower_thresh)); if ( xenlog_upper_thresh != xenlog_lower_thresh ) printk(" (Rate-limited: %s)", loglvl_str(xenlog_upper_thresh)); printk("\nGuest Loglevel: %s", loglvl_str(xenlog_guest_lower_thresh)); if ( xenlog_guest_upper_thresh != xenlog_guest_lower_thresh ) printk(" (Rate-limited: %s)", loglvl_str(xenlog_guest_upper_thresh)); printk("\n"); if ( opt_sync_console ) { printk("**********************************************\n"); printk("******* WARNING: CONSOLE OUTPUT IS SYNCHRONOUS\n"); printk("******* This option is intended to aid debugging " "of Xen by ensuring\n"); printk("******* that all output is synchronously delivered " "on the serial line.\n"); printk("******* However it can introduce SIGNIFICANT latencies " "and affect\n"); printk("******* timekeeping. It is NOT recommended for " "production use!\n"); printk("**********************************************\n"); for ( i = 0; i < 3; i++ ) { printk("%d... ", 3-i); for ( j = 0; j < 100; j++ ) { process_pending_softirqs(); mdelay(10); } } printk("\n"); } video_endboot(); /* * If user specifies so, we fool the switch routine to redirect input * straight back to Xen. I use this convoluted method so we still print * a useful 'how to switch' message. */ if ( opt_conswitch[1] == 'x' ) xen_rx = !xen_rx; register_keyhandler('w', &dump_console_ring_keyhandler); /* Serial input is directed to DOM0 by default. */ switch_serial_input(); } int __init console_has(const char *device) { char *p; for ( p = opt_console; p != NULL; p = strchr(p, ',') ) { if ( *p == ',' ) p++; if ( strncmp(p, device, strlen(device)) == 0 ) return 1; } return 0; } void console_start_log_everything(void) { serial_start_log_everything(sercon_handle); atomic_inc(&print_everything); } void console_end_log_everything(void) { serial_end_log_everything(sercon_handle); atomic_dec(&print_everything); } void console_force_unlock(void) { spin_lock_init(&console_lock); serial_force_unlock(sercon_handle); console_locks_busted = 1; console_start_sync(); } void console_start_sync(void) { atomic_inc(&print_everything); serial_start_sync(sercon_handle); } void console_end_sync(void) { serial_end_sync(sercon_handle); atomic_dec(&print_everything); } /* * printk rate limiting, lifted from Linux. * * This enforces a rate limit: not more than one kernel message * every printk_ratelimit_ms (millisecs). */ int __printk_ratelimit(int ratelimit_ms, int ratelimit_burst) { static DEFINE_SPINLOCK(ratelimit_lock); static unsigned long toks = 10 * 5 * 1000; static unsigned long last_msg; static int missed; unsigned long flags; unsigned long long now = NOW(); /* ns */ unsigned long ms; do_div(now, 1000000); ms = (unsigned long)now; spin_lock_irqsave(&ratelimit_lock, flags); toks += ms - last_msg; last_msg = ms; if ( toks > (ratelimit_burst * ratelimit_ms)) toks = ratelimit_burst * ratelimit_ms; if ( toks >= ratelimit_ms ) { int lost = missed; missed = 0; toks -= ratelimit_ms; spin_unlock(&ratelimit_lock); if ( lost ) { char lost_str[8]; snprintf(lost_str, sizeof(lost_str), "%d", lost); /* console_lock may already be acquired by printk(). */ spin_lock_recursive(&console_lock); printk_start_of_line(); __putstr("printk: "); __putstr(lost_str); __putstr(" messages suppressed.\n"); spin_unlock_recursive(&console_lock); } local_irq_restore(flags); return 1; } missed++; spin_unlock_irqrestore(&ratelimit_lock, flags); return 0; } /* minimum time in ms between messages */ static int __read_mostly printk_ratelimit_ms = 5 * 1000; /* number of messages we send before ratelimiting */ static int __read_mostly printk_ratelimit_burst = 10; int printk_ratelimit(void) { return __printk_ratelimit(printk_ratelimit_ms, printk_ratelimit_burst); } /* * ************************************************************** * *************** Serial console ring buffer ******************* * ************************************************************** */ #ifdef DEBUG_TRACE_DUMP /* Send output direct to console, or buffer it? */ static volatile int debugtrace_send_to_console; static char *debugtrace_buf; /* Debug-trace buffer */ static unsigned int debugtrace_prd; /* Producer index */ static unsigned int debugtrace_kilobytes = 128, debugtrace_bytes; static unsigned int debugtrace_used; static DEFINE_SPINLOCK(debugtrace_lock); integer_param("debugtrace", debugtrace_kilobytes); static void debugtrace_dump_worker(void) { if ( (debugtrace_bytes == 0) || !debugtrace_used ) return; printk("debugtrace_dump() starting\n"); /* Print oldest portion of the ring. */ ASSERT(debugtrace_buf[debugtrace_bytes - 1] == 0); sercon_puts(&debugtrace_buf[debugtrace_prd]); /* Print youngest portion of the ring. */ debugtrace_buf[debugtrace_prd] = '\0'; sercon_puts(&debugtrace_buf[0]); memset(debugtrace_buf, '\0', debugtrace_bytes); printk("debugtrace_dump() finished\n"); } static void debugtrace_toggle(void) { unsigned long flags; watchdog_disable(); spin_lock_irqsave(&debugtrace_lock, flags); /* * Dump the buffer *before* toggling, in case the act of dumping the * buffer itself causes more printk() invocations. */ printk("debugtrace_printk now writing to %s.\n", !debugtrace_send_to_console ? "console": "buffer"); if ( !debugtrace_send_to_console ) debugtrace_dump_worker(); debugtrace_send_to_console = !debugtrace_send_to_console; spin_unlock_irqrestore(&debugtrace_lock, flags); watchdog_enable(); } void debugtrace_dump(void) { unsigned long flags; watchdog_disable(); spin_lock_irqsave(&debugtrace_lock, flags); debugtrace_dump_worker(); spin_unlock_irqrestore(&debugtrace_lock, flags); watchdog_enable(); } void debugtrace_printk(const char *fmt, ...) { static char buf[1024]; static u32 count; va_list args; char *p; unsigned long flags; if ( debugtrace_bytes == 0 ) return; debugtrace_used = 1; spin_lock_irqsave(&debugtrace_lock, flags); ASSERT(debugtrace_buf[debugtrace_bytes - 1] == 0); snprintf(buf, sizeof(buf), "%u ", ++count); va_start(args, fmt); (void)vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), fmt, args); va_end(args); if ( debugtrace_send_to_console ) { serial_puts(sercon_handle, buf); } else { for ( p = buf; *p != '\0'; p++ ) { debugtrace_buf[debugtrace_prd++] = *p; /* Always leave a nul byte at the end of the buffer. */ if ( debugtrace_prd == (debugtrace_bytes - 1) ) debugtrace_prd = 0; } } spin_unlock_irqrestore(&debugtrace_lock, flags); } static void debugtrace_key(unsigned char key) { debugtrace_toggle(); } static struct keyhandler debugtrace_keyhandler = { .u.fn = debugtrace_key, .desc = "toggle debugtrace to console/buffer" }; static int __init debugtrace_init(void) { int order; unsigned int kbytes, bytes; /* Round size down to next power of two. */ while ( (kbytes = (debugtrace_kilobytes & (debugtrace_kilobytes-1))) != 0 ) debugtrace_kilobytes = kbytes; bytes = debugtrace_kilobytes << 10; if ( bytes == 0 ) return 0; order = get_order_from_bytes(bytes); debugtrace_buf = alloc_xenheap_pages(order, 0); ASSERT(debugtrace_buf != NULL); memset(debugtrace_buf, '\0', bytes); debugtrace_bytes = bytes; register_keyhandler('T', &debugtrace_keyhandler); return 0; } __initcall(debugtrace_init); #endif /* !NDEBUG */ /* * ************************************************************** * *************** Debugging/tracing/error-report *************** * ************************************************************** */ void panic(const char *fmt, ...) { va_list args; unsigned long flags; static DEFINE_SPINLOCK(lock); static char buf[128]; debugtrace_dump(); /* Protects buf[] and ensure multi-line message prints atomically. */ spin_lock_irqsave(&lock, flags); va_start(args, fmt); (void)vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); console_start_sync(); printk("\n****************************************\n"); printk("Panic on CPU %d:\n", smp_processor_id()); printk("%s", buf); printk("****************************************\n\n"); if ( opt_noreboot ) printk("Manual reset required ('noreboot' specified)\n"); else printk("Reboot in five seconds...\n"); spin_unlock_irqrestore(&lock, flags); debugger_trap_immediate(); #ifdef CONFIG_KEXEC kexec_crash(); #endif if ( opt_noreboot ) { machine_halt(); } else { watchdog_disable(); machine_restart(5000); } } void __bug(char *file, int line) { console_start_sync(); printk("Xen BUG at %s:%d\n", file, line); dump_execution_state(); panic("Xen BUG at %s:%d\n", file, line); for ( ; ; ) ; } void __warn(char *file, int line) { printk("Xen WARN at %s:%d\n", file, line); dump_execution_state(); } /* * ************************************************************** * ****************** Console suspend/resume ******************** * ************************************************************** */ static void suspend_steal_fn(const char *str) { } static int suspend_steal_id; int console_suspend(void) { suspend_steal_id = console_steal(sercon_handle, suspend_steal_fn); serial_suspend(); return 0; } int console_resume(void) { serial_resume(); console_giveback(suspend_steal_id); return 0; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */