Issue 3-25, June 24, 1998

Be Engineering Insights: My Address? In What Space?

By Igor Eydelnant

It's a trivial observation that today's high-tech life often draws upon ideas and terms from yesterday's fiction. Welcome to the parallel universe of address spaces. (Eerie music in the background.)

Recently I needed a piece of code to provide bus-to- virtual and virtual-to-bus address conversions for my network device driver.

Here's the problem. DMA-based hardware devices use bus address space for transfers, while software normally operates with virtual memory. Kernel device drivers live in between. Luckily, the bus address is the same as the physical RAM address. (Well, as with all generalizations, this one is not entirely true. But it still holds for the currently supported set of BeOS platforms. BTW, can I get an N64 for testing?)

Here's the plan. Create a locked contiguous area and map it, and then it's only a matter of offsets! I find areas very powerful and flexible. The gain: we don't have to worry about crossing physical page boundaries, because the memory chunk is physically contiguous. The disadvantage: memory cannot be moved or swapped out until it's released. The Be Book describes this feature as "justifiable excess":

TheKernelKit_Areas.html

It was indeed justifiable for my driver. But recently I got a fortune cookie indicating: "If you don't conserve memory, the eco-police will get you!" Signed: Paranoid Anonymous.

The code snippet snip-snips...umh, follows.

/* * * * * * * * * * * * * * * * * * * * * *
 * Kernel-mode only
 * virt2phys(), phys2virt()
 */

#include <KernelExport.h>

#define RNDUP(x, y) (((x) + (y) - 1) & ~((y) - 1))

#define FOO_SIZE   0x2000      // 2 phys pages
#define AREA_NAME  "foo_area"

area_id foo_area;
static uchar *  phys_start_area;
static uchar *  virt_start_area;


/* init_driver()
 * Gets called when the driver is being loaded
 */

status_t
init_driver()
{
  uchar *  foo_buf;

  // driver init essentials...

  foo_buf = get_area(FOO_SIZE, &foo_area);
  if (!foo_buf) {
    kprintf("init: can't get_area()\n");
    return B_NO_MEMORY;
  }

  return (init_area(foo_buf));
}


/* uninit_driver()
 * Gets called when the driver is being unloaded
 */

status_t
uninit_driver()
{
  // driver uninit essentials...

  return (delete_area(foo_area));
}


/* get_area()
 * Create locked contiguous area
 */
static uchar *
get_area(size_t size, area_id *areap)
{
  uchar * base;
  area_id id;

  size = RNDUP(size, B_PAGE_SIZE);

  id = create_area(AREA_NAME,
          &base,
          B_ANY_KERNEL_ADDRESS,
          size,
          B_FULL_LOCK | B_CONTIGUOUS,
          B_READ_AREA | B_WRITE_AREA);

  if (id < B_NO_ERROR) {
    kprintf("get_area: Can't create area\n");
    return (NULL);
  }

  memset(base, NULL, size);   // zero it out
  *areap = id;
  return (base);
}


/* init_area()
 * Map the area, set global ptrs
 */

status_t
init_area(uchar * base)
{
  physical_entry  area_phys_addr[2];  // only the start + 0

  // Get location & size of phys blocks: use the 1st byte of
  // the 1st page to find the start of the area

  get_memory_map(base,     // vbuf to translate
                 1,        // size of vbuf
                 &area_phys_addr[0],    // tbl to fill out
                 1);       // #of entries

  // we're relying on the fact that both virt area
  // and it's phys map are contiguous as advertised.

  virt_start_area = base;
  phys_start_area = (uchar *) (area_phys_addr[0].address);

  return B_NO_ERROR;
}


/* phys2virt()
 * Find the offset, ret virt addr
 * or panic
 */

void *
phys2virt(ulong ph_addr)
{
  ulong  offset;

  offset = ph_addr - (ulong) phys_start_area;

  if ( (ph_addr < (ulong) phys_start_area) ||
       (offset > AREA_SIZE) ) {
    kprintf("Out of bounds: PA=0x%08x\n", ph_addr);
    kernel_debugger("p2v");
  }

  return (virt_start_area + offset);
}


/* virt2phys()
 * Find the offset, ret phys address
 * or panic
 */

ulong
virt2phys(void * v_addr)
{
  ulong  offset;

  offset = (ulong) v_addr - (ulong) virt_start_area;

  if ( (v_addr < virt_start_area) ||
       (offset > AREA_SIZE) ) {
    kprintf("Out of bounds: VA=0x%08x\n", v_addr);
    kernel_debugger("v2p");
  }

  return ((ulong) phys_start_area + offset);
}

As always, the most interesting issues (memory protection, exception handling) are beyond the scope of this short article. See you next time here on the pages of the "Menlo Park Bee."

Recommended literature:
http://www.be.com/documentation/be_book/index.html
http://www.be.com/developers/BeDC/March98/presentations/WritingDeviceDrivers/index.html

Linux Device Drivers, by A. Rubini, O'Reilly
Linux Kernel Internals, by M. Beck et. al., 2nd Ed., Addison-Wesley


Developers' Workshop: BeOS Programming Basics: Part 4

By Eric Shepherd

In part three of this series, we investigated messaging on the BeOS by adding support for a window registry to our application. This week, we'll take the first step on the road to changing what was originally "Hello World" into a real, live, useful BeOS application, by adding a scrollable text editing view to our windows.

As always, if you haven't read the previous three parts of this series, you might want to take a moment to do so:

Developers' Workshop: BeOS Programming Basics: Part 1
Developers' Workshop: BeOS Programming Basics, Part 2
Developers' Workshop: BeOS Programming Basics: Part 3

You can download the source code for this week's project from the Be FTP site:

ftp://ftp.be.com/pub/samples/intro/obsolete/TextWorld.zip

We begin with the source from Part 3, messageworld.cpp, and perform a little clean-up work in preparation for this week. First, we need to include some additional files, so we can use the BTextView class for editing:

#include <TextView.h>
#include <ScrollView.h>

Let's also fix the application signature to be unique:

const char *APP_SIGNATURE = "application/x-vnd.BeTextWorld";

Then let's remove the Options menu and the old HelloView class, since they won't be needed anymore. So long, simple demo view! We'll miss you!

Also, we can remove the following constants, which are near the top of the MessageWorld source:

constuint32 MENU_OPT_HELLO = 'MOhl';
const char   *STRING_HELLO = "Hello World!";
const char   *STRING_GOODBYE = "Goodbye World!";

The HelloView class and its member functions can be removed as well. Don't forget to remove the helloview variable from the HelloWindow class.

From the HelloWindow constructor, remove the following code, so we don't keep adding the Options menu to the menu bar:

menu = new BMenu("Options");
item=new BMenuItem("Say Hello", new BMessage(MENU_OPT_HELLO));
item->SetMarked(true);
menu->AddItem(item);
menubar->AddItem(menu);

Also, remove the code that adds the HelloView to the window:

r.top = menubar->Bounds().bottom+1;
AddChild(helloview = new HelloView(r));

And, from HelloWindow::MessageReceived(), remove the case handler for MENU_OPT_HELLO from the switch block.

Now let's do a quick find-and-replace operation to change all occurrences of "HelloWindow" to "TextWindow" and all occurrences of "HelloApp" to "TextApp".

Now that we've finished our initial tidying up of the code, let's add the BTextView. The BTextView class is used to present (optionally) editable, (optionally) formatted text in a view. This is great for creating simple text editors (StyledEdit, for example, uses a BTextView for the editing area of the window).

First, add to the TextWindow class a new private member variable:

BTextView    *textview;

Now, let's look at the revised TextWindow constructor:

TextWindow::TextWindow(BRect frame)
      : BWindow(frame, "Untitled ", B_TITLED_WINDOW,
        B_NOT_RESIZABLE|B_NOT_ZOOMABLE) {
  BRect r;
  BMenu *menu;

  // Add the menu bar

  r = Bounds();
  menubar = new BMenuBar(r, "menu_bar");

  // Add File menu to menu bar

  menu = new BMenu("File");
  menu->AddItem(new BMenuItem("New",
          new BMessage(MENU_FILE_NEW), 'N'));
  menu->AddItem(new BMenuItem("Open" B_UTF8_ELLIPSIS,
          new BMessage(MENU_FILE_OPEN), 'O'));
  menu->AddItem(new BMenuItem("Close",
          new BMessage(MENU_FILE_CLOSE), 'W'));
  menu->AddSeparatorItem();
  menu->AddItem(new BMenuItem("Save",
          new BMessage(MENU_FILE_SAVE), 'S'));
  menu->AddItem(new BMenuItem("Save as" B_UTF8_ELLIPSIS,
          new BMessage(MENU_FILE_SAVEAS)));
  menu->AddSeparatorItem();
  menu->AddItem(new BMenuItem("Page Setup" B_UTF8_ELLIPSIS,
          new BMessage(MENU_FILE_PAGESETUP)));
  menu->AddItem(new BMenuItem("Print" B_UTF8_ELLIPSIS,
          new BMessage(MENU_FILE_PRINT), 'P'));
  menu->AddSeparatorItem();
  menu->AddItem(new BMenuItem("Quit",
          new BMessage(MENU_FILE_QUIT), 'Q'));
  menubar->AddItem(menu);
  AddChild(menubar);

  // Add the text view

  BRect textframe = Bounds();
  textframe.top = menubar->Bounds().bottom + 1.0;
  BRect textrect = textframe;
  textrect.OffsetTo(B_ORIGIN);
  r.InsetBy(3.0,3.0);
  AddChild(textview =
    new BTextView(textframe, "text_view", textrect,
      B_FOLLOW_ALL_SIDES, B_WILL_DRAW|B_PULSE_NEEDED));

  // Tell the application that there's one more window
  // and get the number for this untitled window.

  Register(true);
  Show();
}

There are two changes here worth noting. First, the AddChild() call to add the menu bar to the window now occurs only after all the menus are added. This is critical (and fixes a minor bug in the existing code): although the BMenuBar class adjusts the height of the menu bar given the height of the text it contains, if it doesn't have any text in it when we add it to the window, it won't calculate the height. So it was assuming that the height of the menu bar is zero (which is true given what it knows about the menu bar at the time).

By including the AddChild(menubar) after the menus are all in place, the height of the menu bar's frame rectangle is correctly adjusted. This is necessary because the code that computes the frame rectangle for the BTextView relies on the menu bar's frame rectangle (so that our code will work regardless of the user's settings in the Fonts preferences application).

The other change is that instead of adding a HelloView to the code, we now add a BTextView. Its frame rectangle is computed by taking the bounds of the entire window and moving the top edge down below the bottom of the menu bar.

A BTextView actually requires two rectangles as input. The first represents the actual frame rectangle of the text view itself. The other, the text rectangle, represents the area inside the view that the text is actually drawn in. This lets you establish borders around the edges of the text, and determines how wide the lines of the text are. The bottom edge of this rectangle is ignored, and will change as the text gets longer or shorter while being edited.

The text rectangle is simply the frame rectangle, inset by three pixels on every side. We use OffsetTo(B_ORIGIN) to offset the text rectangle so its top-left corner is at (0,0)—the text rectangle is local to the text view, so we need to make this adjustment.

Finally, we instantiate the BTextView object and add it to the window.

If you compile and run the application now, you have a very simple "notepad-style" text editor, where you can create multiple windows using the New option in the File menu, and enter text in them all.

But there are some obvious shortcomings. Let's go through them, one by one, and correct them.

First, you have to click in the BTextView before you can start editing. This is easy to fix. The problem is that, by default, the text view doesn't have the focus. "Focus" is a fancy way of saying "gets dibs on keys the user presses." So let's give the BTextView the default focus. Add the following line of code, right after the AddChild() call that adds the BTextView to the window:

textview->MakeFocus(true);

This establishes that keystrokes should, by default, be sent to the BTextView. Try compiling and running the application again to see that this is, in fact, the case.

The next shortcoming is that the window is a fixed size. This is pretty dull. Let's go ahead and make the window resizable. As you see, our current TextWindow constructor begins like this:

TextWindow::TextWindow(BRect frame)
      : BWindow(frame, "Untitled ", B_TITLED_WINDOW,
        B_NOT_RESIZABLE|B_NOT_ZOOMABLE) {

Just change this to:

TextWindow::TextWindow(BRect frame)
      : BWindow(frame, "Untitled ", B_DOCUMENT_WINDOW, 0) {

Now run this. Well, the window's resizable, but the resize box is leaving droppings behind when you use it, and it'd be really nice if there were a scroll bar in there.

So now we introduce the BScrollView class. This is a type of view that contains either a horizontal or vertical scroll bar (or both), as well as any other view. The scroll bars then let the user scroll the child view.

In our case, we'll create a BScrollView and add the BTextView to it, so the user can then use scroll bars to scroll through the BTextView. This changes the code that creates and adds the BTextView (and BScrollView) to look like this:

BRect textframe = Bounds();
textframe.top = menubar->Bounds().bottom + 1.0;
textframe.right -= B_V_SCROLL_BAR_WIDTH;
BRect textrect = textframe;
textrect.OffsetTo(B_ORIGIN);
r.InsetBy(3.0,3.0);

textview = new BTextView(textframe, "text_view", textrect,
  B_FOLLOW_ALL_SIDES, B_WILL_DRAW|B_PULSE_NEEDED);

AddChild(scrollview =
  new BScrollView("scroll_view", textview,
      B_FOLLOW_ALL_SIDES, 0, false, true, B_NO_BORDER));

textview->MakeFocus(true);

Note that we're subtracting B_V_SCROLL_BAR_WIDTH from the right edge of the BTextView's frame rectangle. This makes room for the vertical scroll bar.

The BTextView is now allocated and saved, without actually adding it to the window.

Then the BScrollView is created, with the name "scroll_view." Note the reference to textview; the BScrollView constructor automatically makes the specified view (our BTextView in this case) the target of the scroll bars in the BScrollView.

We specify that the view should have no horizontal scroll bar (false), but should have a vertical scroll bar (true). Finally, we indicate that we don't need a border, by specifying the B_NO_BORDER value. Be sure to read over the BScrollView section in the Be Book for a more in-depth look at exactly what's happening here.

Note also that we've added:

BScrollView  *scrollview;

To the TextWindow class's private members.

If you now compile and run this, you should have a very familiar-looking editing window—completely resizable and scrollable. But you'll notice that word wrapping is odd, because the width of the text rectangle isn't being changed as you make the window wider and narrower. Let's fix that by adding a FrameResized() function to our window:

Add the following line to the TextWindow's public member functions:

virtual void  FrameResized(float width, float height);

Then add the code for TextWindow::FrameResized():

void TextWindow::FrameResized(float width, float height) {
  BRect textrect = textview->TextRect();

  textrect.right = textrect.left +
    (width - B_V_SCROLL_BAR_WIDTH - 3.0);
  textview->SetTextRect(textrect);
}

This code gets the current text rectangle from the BTextView, adjusts the right edge, and updates the text rectangle by calling SetTextRect().

The right edge is corrected by taking the left edge, adding the window's new width, and subtracting the width of the scroll bar and the number 3.0, which, as you recall, is the amount by which we inset the text rectangle from the BTextView's frame rectangle.

If you compile and run this application, you now have an editor which always keeps the text wrapped inside the edit window.

That's all for this time. There are obviously many things we can do to make this application more useful, and you can probably figure most of them out yourself by browsing through the BTextView and BScrollView sections of the Be Book.

Next time (in about six weeks), we'll add an Edit menu to add the standard editing features, and we'll add code to open existing text files, and save documents to disk. Until then, play with this code and see what you can do with it.


After PC Expo

By Jean-Louis Gassée

It is tempting to compare our PC Expo experience to our participation in last fall's Comdex. In Las Vegas, graciously hosted by our long-time partners Umax, we showed the released PowerPC version of the BeOS and a pre-release Intel implementation.

Most visitors there first gave us quizzical looks: Who are you, what are you doing here? Things improved after the demonstration, but we didn't have a product for this mostly Wintel crowd. Our positioning was unclear and we couldn't show many applications.

At PC Expo, we had our own booth. We had an Intel product, complete with a fresh 3.1 upgrade. We stated and demonstrated our position as a specialized OS for A/V applications, coexisting with Windows, the general purpose OS. We also had Be Developers showing and selling applications.

One rewarding moment that repeated itself over and over came at the conclusion of our demos—a wave of people walking to the Be Developer stations to look at their applications.

Another interesting pattern was the reaction of visitors whose badges identified them as IS managers. We thought they couldn't possibly be interested in anything but Microsoft platforms. Many reminded us, however, that they had a life besides their day job and they were interested in our media capabilities. Some even ventured the opinion that they saw possible uses for our technology in their companies if or when we proved capable and solid enough.

Overall, we enjoyed good traffic in spite of being in the "other" section. Unlike at Comdex, most visitors had already heard about us and they seemed pleased with the time they spent on our booth.

At one point, I spotted Michael Dell walking by—taking time out from the executive suite—and we chatted for a few moments. He amiably expressed an interest in trying the BeOS on one of his machines, and we'll do our best to give him a first good impression.

I won't belabor the point, but it would be nice, when you order your Dell System on their Web site, to have—alongside your choice of modems and applications software—a button to click for a factory install of the BeOS and choice of applications as an option.

This said, we have much work ahead of us, focusing on one goal: to bring customers to our developers. From improvements to our product to better e-commerce capabilities, evangelism, marketing, PR, OEM agreements, to cite but a few building blocks, with BeOS applications now emerging, the next steps become pretty obvious.

Our gratitude goes to all the people inside and outside Be who made this first PC Expo a success. I'll take the risk of singling out Sylvie Pelaprat for masterminding the operation, not least of all because she'd have been criticized if things had gone awry.

In an earlier column I mentioned NYC cab drivers and Parisian maître d's. I just sampled the former, and found them reasonably friendly and efficient. Now I'll go and refresh my memories of the latter.

Creative Commons License
Legal Notice
This work is licensed under a Creative Commons Attribution-Non commercial-No Derivative Works 3.0 License.