iPXE
fbcon.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 /** @file
00027  *
00028  * Frame buffer console
00029  *
00030  */
00031 
00032 #include <string.h>
00033 #include <errno.h>
00034 #include <assert.h>
00035 #include <byteswap.h>
00036 #include <ipxe/ansiesc.h>
00037 #include <ipxe/image.h>
00038 #include <ipxe/pixbuf.h>
00039 #include <ipxe/umalloc.h>
00040 #include <ipxe/console.h>
00041 #include <ipxe/fbcon.h>
00042 
00043 /**
00044  * Calculate raw colour value
00045  *
00046  * @v fbcon             Frame buffer console
00047  * @v rgb               24-bit RGB value
00048  * @ret raw             Raw colour
00049  */
00050 static uint32_t fbcon_colour ( struct fbcon *fbcon, uint32_t rgb ) {
00051         struct fbcon_colour_map *map = fbcon->map;
00052         uint8_t red = ( rgb >> 16 );
00053         uint8_t green = ( rgb >> 8 );
00054         uint8_t blue = ( rgb >> 0 );
00055         uint32_t mapped;
00056 
00057         mapped = ( ( ( red >> map->red_scale ) << map->red_lsb ) |
00058                    ( ( green >> map->green_scale ) << map->green_lsb ) |
00059                    ( ( blue >> map->blue_scale ) << map->blue_lsb ) );
00060         return cpu_to_le32 ( mapped );
00061 }
00062 
00063 /**
00064  * Calculate ANSI font colour
00065  *
00066  * @v fbcon             Frame buffer console
00067  * @v ansicol           ANSI colour value (0-based)
00068  * @ret colour          Raw colour
00069  */
00070 static uint32_t fbcon_ansi_colour ( struct fbcon *fbcon,
00071                                     unsigned int ansicol ) {
00072         uint32_t rgb;
00073 
00074         /* Treat ansicol as 3-bit BGR with intensity 0xaa */
00075         rgb = ( ( ( ansicol & ( 1 << 0 ) ) ? 0xaa0000 : 0 ) |
00076                 ( ( ansicol & ( 1 << 1 ) ) ? 0x00aa00 : 0 ) |
00077                 ( ( ansicol & ( 1 << 2 ) ) ? 0x0000aa : 0 ) );
00078 
00079         return fbcon_colour ( fbcon, rgb );
00080 }
00081 
00082 /**
00083  * Set default foreground colour
00084  *
00085  * @v fbcon             Frame buffer console
00086  */
00087 static void fbcon_set_default_foreground ( struct fbcon *fbcon ) {
00088 
00089         /* Default to non-bold white foreground */
00090         fbcon->foreground = fbcon_ansi_colour ( fbcon, 0x7 );
00091         fbcon->bold = 0;
00092 }
00093 
00094 /**
00095  * Set default background colour
00096  *
00097  * @v fbcon             Frame buffer console
00098  */
00099 static void fbcon_set_default_background ( struct fbcon *fbcon ) {
00100 
00101         /* Default to transparent background */
00102         fbcon->background = FBCON_TRANSPARENT;
00103 }
00104 
00105 /**
00106  * Clear rows of characters
00107  *
00108  * @v fbcon             Frame buffer console
00109  * @v ypos              Starting Y position
00110  */
00111 static void fbcon_clear ( struct fbcon *fbcon, unsigned int ypos ) {
00112         struct fbcon_text_cell cell = {
00113                 .foreground = fbcon->foreground,
00114                 .background = fbcon->background,
00115                 .character = ' ',
00116         };
00117         size_t offset;
00118         unsigned int xpos;
00119 
00120         /* Clear stored character array */
00121         for ( ; ypos < fbcon->character.height ; ypos++ ) {
00122                 offset = ( ypos * fbcon->character.width * sizeof ( cell ) );
00123                 for ( xpos = 0 ; xpos < fbcon->character.width ; xpos++ ) {
00124                         copy_to_user ( fbcon->text.start, offset, &cell,
00125                                        sizeof ( cell ) );
00126                         offset += sizeof ( cell );
00127                 }
00128         }
00129 }
00130 
00131 /**
00132  * Store character at specified position
00133  *
00134  * @v fbcon             Frame buffer console
00135  * @v cell              Text cell
00136  * @v xpos              X position
00137  * @v ypos              Y position
00138  */
00139 static void fbcon_store ( struct fbcon *fbcon, struct fbcon_text_cell *cell,
00140                           unsigned int xpos, unsigned int ypos ) {
00141         size_t offset;
00142 
00143         /* Store cell */
00144         offset = ( ( ( ypos * fbcon->character.width ) + xpos ) *
00145                    sizeof ( *cell ) );
00146         copy_to_user ( fbcon->text.start, offset, cell, sizeof ( *cell ) );
00147 }
00148 
00149 /**
00150  * Draw character at specified position
00151  *
00152  * @v fbcon             Frame buffer console
00153  * @v cell              Text cell
00154  * @v xpos              X position
00155  * @v ypos              Y position
00156  */
00157 static void fbcon_draw ( struct fbcon *fbcon, struct fbcon_text_cell *cell,
00158                          unsigned int xpos, unsigned int ypos ) {
00159         uint8_t glyph[fbcon->font->height];
00160         size_t offset;
00161         size_t pixel_len;
00162         size_t skip_len;
00163         unsigned int row;
00164         unsigned int column;
00165         uint8_t bitmask;
00166         int transparent;
00167         void *src;
00168 
00169         /* Get font character */
00170         fbcon->font->glyph ( cell->character, glyph );
00171 
00172         /* Calculate pixel geometry */
00173         offset = ( fbcon->indent +
00174                    ( ypos * fbcon->character.stride ) +
00175                    ( xpos * fbcon->character.len ) );
00176         pixel_len = fbcon->pixel->len;
00177         skip_len = ( fbcon->pixel->stride - fbcon->character.len );
00178 
00179         /* Check for transparent background colour */
00180         transparent = ( cell->background == FBCON_TRANSPARENT );
00181 
00182         /* Draw character rows */
00183         for ( row = 0 ; row < fbcon->font->height ; row++ ) {
00184 
00185                 /* Draw background picture, if applicable */
00186                 if ( transparent ) {
00187                         if ( fbcon->picture.start ) {
00188                                 memcpy_user ( fbcon->start, offset,
00189                                               fbcon->picture.start, offset,
00190                                               fbcon->character.len );
00191                         } else {
00192                                 memset_user ( fbcon->start, offset, 0,
00193                                               fbcon->character.len );
00194                         }
00195                 }
00196 
00197                 /* Draw character row */
00198                 for ( column = FBCON_CHAR_WIDTH, bitmask = glyph[row] ;
00199                       column ; column--, bitmask <<= 1, offset += pixel_len ) {
00200                         if ( bitmask & 0x80 ) {
00201                                 src = &cell->foreground;
00202                         } else if ( ! transparent ) {
00203                                 src = &cell->background;
00204                         } else {
00205                                 continue;
00206                         }
00207                         copy_to_user ( fbcon->start, offset, src, pixel_len );
00208                 }
00209 
00210                 /* Move to next row */
00211                 offset += skip_len;
00212         }
00213 }
00214 
00215 /**
00216  * Redraw all characters
00217  *
00218  * @v fbcon             Frame buffer console
00219  */
00220 static void fbcon_redraw ( struct fbcon *fbcon ) {
00221         struct fbcon_text_cell cell;
00222         size_t offset = 0;
00223         unsigned int xpos;
00224         unsigned int ypos;
00225 
00226         /* Redraw characters */
00227         for ( ypos = 0 ; ypos < fbcon->character.height ; ypos++ ) {
00228                 for ( xpos = 0 ; xpos < fbcon->character.width ; xpos++ ) {
00229                         copy_from_user ( &cell, fbcon->text.start, offset,
00230                                          sizeof ( cell ) );
00231                         fbcon_draw ( fbcon, &cell, xpos, ypos );
00232                         offset += sizeof ( cell );
00233                 }
00234         }
00235 }
00236 
00237 /**
00238  * Scroll screen
00239  *
00240  * @v fbcon             Frame buffer console
00241  */
00242 static void fbcon_scroll ( struct fbcon *fbcon ) {
00243         size_t row_len;
00244 
00245         /* Sanity check */
00246         assert ( fbcon->ypos == fbcon->character.height );
00247 
00248         /* Scroll up character array */
00249         row_len = ( fbcon->character.width * sizeof ( struct fbcon_text_cell ));
00250         memmove_user ( fbcon->text.start, 0, fbcon->text.start, row_len,
00251                        ( row_len * ( fbcon->character.height - 1 ) ) );
00252         fbcon_clear ( fbcon, ( fbcon->character.height - 1 ) );
00253 
00254         /* Update cursor position */
00255         fbcon->ypos--;
00256 
00257         /* Redraw all characters */
00258         fbcon_redraw ( fbcon );
00259 }
00260 
00261 /**
00262  * Draw character at cursor position
00263  *
00264  * @v fbcon             Frame buffer console
00265  * @v show_cursor       Show cursor
00266  */
00267 static void fbcon_draw_cursor ( struct fbcon *fbcon, int show_cursor ) {
00268         struct fbcon_text_cell cell;
00269         size_t offset;
00270 
00271         offset = ( ( ( fbcon->ypos * fbcon->character.width ) + fbcon->xpos ) *
00272                    sizeof ( cell ) );
00273         copy_from_user ( &cell, fbcon->text.start, offset, sizeof ( cell ) );
00274         if ( show_cursor ) {
00275                 cell.background = fbcon->foreground;
00276                 cell.foreground = ( ( fbcon->background == FBCON_TRANSPARENT ) ?
00277                                     0 : fbcon->background );
00278         }
00279         fbcon_draw ( fbcon, &cell, fbcon->xpos, fbcon->ypos );
00280 }
00281 
00282 /**
00283  * Handle ANSI CUP (cursor position)
00284  *
00285  * @v ctx               ANSI escape sequence context
00286  * @v count             Parameter count
00287  * @v params[0]         Row (1 is top)
00288  * @v params[1]         Column (1 is left)
00289  */
00290 static void fbcon_handle_cup ( struct ansiesc_context *ctx,
00291                                unsigned int count __unused, int params[] ) {
00292         struct fbcon *fbcon = container_of ( ctx, struct fbcon, ctx );
00293         int cx = ( params[1] - 1 );
00294         int cy = ( params[0] - 1 );
00295 
00296         fbcon_draw_cursor ( fbcon, 0 );
00297         fbcon->xpos = cx;
00298         if ( fbcon->xpos >= fbcon->character.width )
00299                 fbcon->xpos = 0;
00300         fbcon->ypos = cy;
00301         if ( fbcon->ypos >= fbcon->character.height )
00302                 fbcon->ypos = 0;
00303         fbcon_draw_cursor ( fbcon, fbcon->show_cursor );
00304 }
00305 
00306 /**
00307  * Handle ANSI ED (erase in page)
00308  *
00309  * @v ctx               ANSI escape sequence context
00310  * @v count             Parameter count
00311  * @v params[0]         Region to erase
00312  */
00313 static void fbcon_handle_ed ( struct ansiesc_context *ctx,
00314                               unsigned int count __unused,
00315                               int params[] __unused ) {
00316         struct fbcon *fbcon = container_of ( ctx, struct fbcon, ctx );
00317 
00318         /* We assume that we always clear the whole screen */
00319         assert ( params[0] == ANSIESC_ED_ALL );
00320 
00321         /* Clear character array */
00322         fbcon_clear ( fbcon, 0 );
00323 
00324         /* Redraw all characters */
00325         fbcon_redraw ( fbcon );
00326 
00327         /* Reset cursor position */
00328         fbcon->xpos = 0;
00329         fbcon->ypos = 0;
00330         fbcon_draw_cursor ( fbcon, fbcon->show_cursor );
00331 }
00332 
00333 /**
00334  * Handle ANSI SGR (set graphics rendition)
00335  *
00336  * @v ctx               ANSI escape sequence context
00337  * @v count             Parameter count
00338  * @v params            List of graphic rendition aspects
00339  */
00340 static void fbcon_handle_sgr ( struct ansiesc_context *ctx, unsigned int count,
00341                                int params[] ) {
00342         struct fbcon *fbcon = container_of ( ctx, struct fbcon, ctx );
00343         uint32_t *custom = NULL;
00344         uint32_t rgb;
00345         unsigned int end;
00346         unsigned int i;
00347         int aspect;
00348 
00349         for ( i = 0 ; i < count ; i++ ) {
00350 
00351                 /* Process aspect */
00352                 aspect = params[i];
00353                 if ( aspect == 0 ) {
00354                         fbcon_set_default_foreground ( fbcon );
00355                         fbcon_set_default_background ( fbcon );
00356                 } else if ( aspect == 1 ) {
00357                         fbcon->bold = fbcon_colour ( fbcon, FBCON_BOLD );
00358                 } else if ( aspect == 22 ) {
00359                         fbcon->bold = 0;
00360                 } else if ( ( aspect >= 30 ) && ( aspect <= 37 ) ) {
00361                         fbcon->foreground =
00362                                 fbcon_ansi_colour ( fbcon, aspect - 30 );
00363                 } else if ( aspect == 38 ) {
00364                         custom = &fbcon->foreground;
00365                 } else if ( aspect == 39 ) {
00366                         fbcon_set_default_foreground ( fbcon );
00367                 } else if ( ( aspect >= 40 ) && ( aspect <= 47 ) ) {
00368                         fbcon->background =
00369                                 fbcon_ansi_colour ( fbcon, aspect - 40 );
00370                 } else if ( aspect == 48 ) {
00371                         custom = &fbcon->background;
00372                 } else if ( aspect == 49 ) {
00373                         fbcon_set_default_background ( fbcon );
00374                 }
00375 
00376                 /* Process custom RGB colour, if applicable
00377                  *
00378                  * We support the xterm-compatible
00379                  * "<ESC>[38;2;<red>;<green>;<blue>m" and
00380                  * "<ESC>[48;2;<red>;<green>;<blue>m" sequences.
00381                  */
00382                 if ( custom ) {
00383                         rgb = 0;
00384                         end = ( i + 5 );
00385                         for ( ; ( i < count ) && ( i < end ) ; i++ )
00386                                 rgb = ( ( rgb << 8 ) | params[i] );
00387                         *custom = fbcon_colour ( fbcon, rgb );
00388                         custom = NULL;
00389                 }
00390         }
00391 }
00392 
00393 /**
00394  * Handle ANSI DECTCEM set (show cursor)
00395  *
00396  * @v ctx               ANSI escape sequence context
00397  * @v count             Parameter count
00398  * @v params            List of graphic rendition aspects
00399  */
00400 static void fbcon_handle_dectcem_set ( struct ansiesc_context *ctx,
00401                                        unsigned int count __unused,
00402                                        int params[] __unused ) {
00403         struct fbcon *fbcon = container_of ( ctx, struct fbcon, ctx );
00404 
00405         fbcon->show_cursor = 1;
00406         fbcon_draw_cursor ( fbcon, 1 );
00407 }
00408 
00409 /**
00410  * Handle ANSI DECTCEM reset (hide cursor)
00411  *
00412  * @v ctx               ANSI escape sequence context
00413  * @v count             Parameter count
00414  * @v params            List of graphic rendition aspects
00415  */
00416 static void fbcon_handle_dectcem_reset ( struct ansiesc_context *ctx,
00417                                          unsigned int count __unused,
00418                                          int params[] __unused ) {
00419         struct fbcon *fbcon = container_of ( ctx, struct fbcon, ctx );
00420 
00421         fbcon->show_cursor = 0;
00422         fbcon_draw_cursor ( fbcon, 0 );
00423 }
00424 
00425 /** ANSI escape sequence handlers */
00426 static struct ansiesc_handler fbcon_ansiesc_handlers[] = {
00427         { ANSIESC_CUP, fbcon_handle_cup },
00428         { ANSIESC_ED, fbcon_handle_ed },
00429         { ANSIESC_SGR, fbcon_handle_sgr },
00430         { ANSIESC_DECTCEM_SET, fbcon_handle_dectcem_set },
00431         { ANSIESC_DECTCEM_RESET, fbcon_handle_dectcem_reset },
00432         { 0, NULL }
00433 };
00434 
00435 /**
00436  * Print a character to current cursor position
00437  *
00438  * @v fbcon             Frame buffer console
00439  * @v character         Character
00440  */
00441 void fbcon_putchar ( struct fbcon *fbcon, int character ) {
00442         struct fbcon_text_cell cell;
00443 
00444         /* Intercept ANSI escape sequences */
00445         character = ansiesc_process ( &fbcon->ctx, character );
00446         if ( character < 0 )
00447                 return;
00448 
00449         /* Handle control characters */
00450         switch ( character ) {
00451         case '\r':
00452                 fbcon_draw_cursor ( fbcon, 0 );
00453                 fbcon->xpos = 0;
00454                 break;
00455         case '\n':
00456                 fbcon_draw_cursor ( fbcon, 0 );
00457                 fbcon->xpos = 0;
00458                 fbcon->ypos++;
00459                 break;
00460         case '\b':
00461                 fbcon_draw_cursor ( fbcon, 0 );
00462                 if ( fbcon->xpos ) {
00463                         fbcon->xpos--;
00464                 } else if ( fbcon->ypos ) {
00465                         fbcon->xpos = ( fbcon->character.width - 1 );
00466                         fbcon->ypos--;
00467                 }
00468                 break;
00469         default:
00470                 /* Print character at current cursor position */
00471                 cell.foreground = ( fbcon->foreground | fbcon->bold );
00472                 cell.background = fbcon->background;
00473                 cell.character = character;
00474                 fbcon_store ( fbcon, &cell, fbcon->xpos, fbcon->ypos );
00475                 fbcon_draw ( fbcon, &cell, fbcon->xpos, fbcon->ypos );
00476 
00477                 /* Advance cursor */
00478                 fbcon->xpos++;
00479                 if ( fbcon->xpos >= fbcon->character.width ) {
00480                         fbcon->xpos = 0;
00481                         fbcon->ypos++;
00482                 }
00483                 break;
00484         }
00485 
00486         /* Scroll screen if necessary */
00487         if ( fbcon->ypos >= fbcon->character.height )
00488                 fbcon_scroll ( fbcon );
00489 
00490         /* Show cursor */
00491         fbcon_draw_cursor ( fbcon, fbcon->show_cursor );
00492 }
00493 
00494 /**
00495  * Initialise background picture
00496  *
00497  * @v fbcon             Frame buffer console
00498  * @v pixbuf            Background picture
00499  * @ret rc              Return status code
00500  */
00501 static int fbcon_picture_init ( struct fbcon *fbcon,
00502                                 struct pixel_buffer *pixbuf ) {
00503         struct fbcon_geometry *pixel = fbcon->pixel;
00504         struct fbcon_picture *picture = &fbcon->picture;
00505         size_t len;
00506         size_t pixbuf_stride;
00507         size_t indent;
00508         size_t pixbuf_indent;
00509         size_t offset;
00510         size_t pixbuf_offset;
00511         uint32_t rgb;
00512         uint32_t raw;
00513         unsigned int x;
00514         unsigned int y;
00515         unsigned int width;
00516         unsigned int height;
00517         int xgap;
00518         int ygap;
00519         int rc;
00520 
00521         /* Allocate buffer */
00522         len = ( pixel->height * pixel->stride );
00523         picture->start = umalloc ( len );
00524         if ( ! picture->start ) {
00525                 DBGC ( fbcon, "FBCON %p could not allocate %zd bytes for "
00526                        "picture\n", fbcon, len );
00527                 rc = -ENOMEM;
00528                 goto err_umalloc;
00529         }
00530 
00531         /* Centre picture on console */
00532         pixbuf_stride = ( pixbuf->width * sizeof ( rgb ) );
00533         xgap = ( ( ( int ) ( pixel->width - pixbuf->width ) ) / 2 );
00534         ygap = ( ( ( int ) ( pixel->height - pixbuf->height ) ) / 2 );
00535         indent = ( ( ( ( ygap >= 0 ) ? ygap : 0 ) * pixel->stride ) +
00536                    ( ( ( xgap >= 0 ) ? xgap : 0 ) * pixel->len ) );
00537         pixbuf_indent = ( ( ( ( ygap < 0 ) ? -ygap : 0 ) * pixbuf_stride ) +
00538                           ( ( ( xgap < 0 ) ? -xgap : 0 ) * sizeof ( rgb ) ) );
00539         width = pixbuf->width;
00540         if ( width > pixel->width )
00541                 width = pixel->width;
00542         height = pixbuf->height;
00543         if ( height > pixel->height )
00544                 height = pixel->height;
00545         DBGC ( fbcon, "FBCON %p picture is pixel %dx%d at [%d,%d),[%d,%d)\n",
00546                fbcon, width, height, xgap, ( xgap + pixbuf->width ), ygap,
00547                ( ygap + pixbuf->height ) );
00548 
00549         /* Convert to frame buffer raw format */
00550         memset_user ( picture->start, 0, 0, len );
00551         for ( y = 0 ; y < height ; y++ ) {
00552                 offset = ( indent + ( y * pixel->stride ) );
00553                 pixbuf_offset = ( pixbuf_indent + ( y * pixbuf_stride ) );
00554                 for ( x = 0 ; x < width ; x++ ) {
00555                         copy_from_user ( &rgb, pixbuf->data, pixbuf_offset,
00556                                          sizeof ( rgb ) );
00557                         raw = fbcon_colour ( fbcon, rgb );
00558                         copy_to_user ( picture->start, offset, &raw,
00559                                        pixel->len );
00560                         offset += pixel->len;
00561                         pixbuf_offset += sizeof ( rgb );
00562                 }
00563         }
00564 
00565         return 0;
00566 
00567         ufree ( picture->start );
00568  err_umalloc:
00569         return rc;
00570 }
00571 
00572 /**
00573  * Initialise frame buffer console
00574  *
00575  * @v fbcon             Frame buffer console
00576  * @v start             Start address
00577  * @v pixel             Pixel geometry
00578  * @v map               Colour mapping
00579  * @v font              Font definition
00580  * @v config            Console configuration
00581  * @ret rc              Return status code
00582  */
00583 int fbcon_init ( struct fbcon *fbcon, userptr_t start,
00584                  struct fbcon_geometry *pixel,
00585                  struct fbcon_colour_map *map,
00586                  struct fbcon_font *font,
00587                  struct console_configuration *config ) {
00588         int width;
00589         int height;
00590         unsigned int xgap;
00591         unsigned int ygap;
00592         unsigned int left;
00593         unsigned int right;
00594         unsigned int top;
00595         unsigned int bottom;
00596         int rc;
00597 
00598         /* Initialise data structure */
00599         memset ( fbcon, 0, sizeof ( *fbcon ) );
00600         fbcon->start = start;
00601         fbcon->pixel = pixel;
00602         assert ( pixel->len <= sizeof ( uint32_t ) );
00603         fbcon->map = map;
00604         fbcon->font = font;
00605         fbcon->ctx.handlers = fbcon_ansiesc_handlers;
00606         fbcon->show_cursor = 1;
00607 
00608         /* Derive overall length */
00609         fbcon->len = ( pixel->height * pixel->stride );
00610         DBGC ( fbcon, "FBCON %p at [%08lx,%08lx)\n", fbcon,
00611                user_to_phys ( fbcon->start, 0 ),
00612                user_to_phys ( fbcon->start, fbcon->len ) );
00613 
00614         /* Calculate margin.  If the actual screen size is larger than
00615          * the requested screen size, then update the margins so that
00616          * the margin remains relative to the requested screen size.
00617          * (As an exception, if a zero margin was specified then treat
00618          * this as meaning "expand to edge of actual screen".)
00619          */
00620         xgap = ( pixel->width - config->width );
00621         ygap = ( pixel->height - config->height );
00622         left = ( xgap / 2 );
00623         right = ( xgap - left );
00624         top = ( ygap / 2 );
00625         bottom = ( ygap - top );
00626         fbcon->margin.left = ( config->left + ( config->left ? left : 0 ) );
00627         fbcon->margin.right = ( config->right + ( config->right ? right : 0 ) );
00628         fbcon->margin.top = ( config->top + ( config->top ? top : 0 ) );
00629         fbcon->margin.bottom =
00630                 ( config->bottom + ( config->bottom ? bottom : 0 ) );
00631 
00632         /* Expand margin to accommodate whole characters */
00633         width = ( pixel->width - fbcon->margin.left - fbcon->margin.right );
00634         height = ( pixel->height - fbcon->margin.top - fbcon->margin.bottom );
00635         if ( ( width < FBCON_CHAR_WIDTH ) ||
00636              ( height < ( ( int ) font->height ) ) ) {
00637                 DBGC ( fbcon, "FBCON %p has unusable character area "
00638                        "[%d-%d),[%d-%d)\n", fbcon, fbcon->margin.left,
00639                        ( pixel->width - fbcon->margin.right ),
00640                        fbcon->margin.top,
00641                        ( pixel->height - fbcon->margin.bottom ) );
00642                 rc = -EINVAL;
00643                 goto err_margin;
00644         }
00645         xgap = ( width % FBCON_CHAR_WIDTH );
00646         ygap = ( height % font->height );
00647         fbcon->margin.left += ( xgap / 2 );
00648         fbcon->margin.top += ( ygap / 2 );
00649         fbcon->margin.right += ( xgap - ( xgap / 2 ) );
00650         fbcon->margin.bottom += ( ygap - ( ygap / 2 ) );
00651         fbcon->indent = ( ( fbcon->margin.top * pixel->stride ) +
00652                           ( fbcon->margin.left * pixel->len ) );
00653 
00654         /* Derive character geometry from pixel geometry */
00655         fbcon->character.width = ( width / FBCON_CHAR_WIDTH );
00656         fbcon->character.height = ( height / font->height );
00657         fbcon->character.len = ( pixel->len * FBCON_CHAR_WIDTH );
00658         fbcon->character.stride = ( pixel->stride * font->height );
00659         DBGC ( fbcon, "FBCON %p is pixel %dx%d, char %dx%d at "
00660                "[%d-%d),[%d-%d)\n", fbcon, fbcon->pixel->width,
00661                fbcon->pixel->height, fbcon->character.width,
00662                fbcon->character.height, fbcon->margin.left,
00663                ( fbcon->pixel->width - fbcon->margin.right ),
00664                fbcon->margin.top,
00665                ( fbcon->pixel->height - fbcon->margin.bottom ) );
00666 
00667         /* Set default colours */
00668         fbcon_set_default_foreground ( fbcon );
00669         fbcon_set_default_background ( fbcon );
00670 
00671         /* Allocate and initialise stored character array */
00672         fbcon->text.start = umalloc ( fbcon->character.width *
00673                                       fbcon->character.height *
00674                                       sizeof ( struct fbcon_text_cell ) );
00675         if ( ! fbcon->text.start ) {
00676                 rc = -ENOMEM;
00677                 goto err_text;
00678         }
00679         fbcon_clear ( fbcon, 0 );
00680 
00681         /* Set framebuffer to all black (including margins) */
00682         memset_user ( fbcon->start, 0, 0, fbcon->len );
00683 
00684         /* Generate pixel buffer from background image, if applicable */
00685         if ( config->pixbuf &&
00686              ( ( rc = fbcon_picture_init ( fbcon, config->pixbuf ) ) != 0 ) )
00687                 goto err_picture;
00688 
00689         /* Draw background picture (including margins), if applicable */
00690         if ( fbcon->picture.start ) {
00691                 memcpy_user ( fbcon->start, 0, fbcon->picture.start, 0,
00692                               fbcon->len );
00693         }
00694 
00695         /* Update console width and height */
00696         console_set_size ( fbcon->character.width, fbcon->character.height );
00697 
00698         return 0;
00699 
00700         ufree ( fbcon->picture.start );
00701  err_picture:
00702         ufree ( fbcon->text.start );
00703  err_text:
00704  err_margin:
00705         return rc;
00706 }
00707 
00708 /**
00709  * Finalise frame buffer console
00710  *
00711  * @v fbcon             Frame buffer console
00712  */
00713 void fbcon_fini ( struct fbcon *fbcon ) {
00714 
00715         ufree ( fbcon->text.start );
00716         ufree ( fbcon->picture.start );
00717 }