I lied, there are actually *six* parameters defined for the mount()
prototype in /boot/develop/headers/posix/unistd.be.h
but the last three are currently not used and should be set to 0.
Mounting a volume under the BeOS requires identifying and passing three
parameters to the mount()
function. The first parameter is the file
system that the volume was initialized with (for the mount to succeed, a
file system add-on of the same name must exist in the
/boot/beos/system/add-ons/kernel/file_systems
directory).
The second parameter is the mount point—the location that the newly mounted volume will be referenced from (typically this is at root level but in theory can be anywhere).
The last parameter is the device driver used to access the volume.
I lied, there are actually *six* parameters defined for the mount()
prototype in /boot/develop/headers/posix/unistd.be.h
but the last three are currently not used and should be set to 0.
Device drivers for accessing volumes come in two flavors: physical and logical. A physical driver allows complete and total access to all blocks on the device. A physical driver can only be used to mount a device that does not contain a partition map or has multiple sessions. A logical driver allows only indirect access to a range of blocks on the device and is used for mounting individual partitions and sessions.
Physical (raw) drivers are installed by the system at boot time for all recognizable devices. To look for and install raw drivers for devices after boot, there are two "rescan" drivers, /dev/disk/scsi/rescan and /dev/disk/ide/rescan...
if ((dev
=open
("/dev/disk/ide/rescan",0)) >=0) {ioctl
(dev
,B_SCSI_SCAN_FOR_DEVICES
);close
(dev
); }
Logical drivers are constructed by creating a new device and using an ioctl to set the range of accessible blocks...
charpath
[256]; chartmp
[256]; int32dev
; partition_infopartition
;strncpy
(tmp
,DEVICE_PATH
,strlen
(DEVICE_PATH
) -strlen
("/raw"));sprintf
(path
, "%s/%d_%d",tmp
,session
,partition
); if ((dev
=create
(path
, 0666)) >= 0) {partition
.offset
=offset
; /* offset from start of physical device in bytes */partition
.size
=size
; /* length of partition in bytes */partition
.logical_block_size
=block_size
; /* physical block size*/partition
.session
=session
; /* session ID */partition
.partition
=partition
; /* partition ID */strcpy
(partition
.device
,DEVICE_PATH
);ioctl
(dev
,B_SET_PARTITION
, &partition
);close
(dev
); } else { // error creating logical device }
The BeOS comes with a number of add-ons to help determine session
offsets, what type of partition map, if any, and what types of file
systems exist on a device. These add-ons are located in the
/boot/beos/system/add-ons/drive_setup/
directory. To use these add-ons,
you first need to determine if the device is a CD. To do this, use the
B_GET_GEOMETRY
ioctl call and test against B_CD...
boolis_cd
; int32dev
; device_geometrygeometry
; if ((dev
=open
(DEVICE_PATH
, 0)) >= 0) { if (ioctl
(dev
,B_GET_GEOMETRY
, &geometry
) ==B_NO_ERROR
) {is_cd
= (geometry
.device_type
==B_CD
); } else { // media not loaded }close
(dev
); } else { // error accessing device }
If the device is indeed a CD, use the session add-on to locate the offset, length, and type of each session...
#defineDS_SESSION_ADDONS
"drive_setup/session/" #defineDS_GET_NTH_SESSION
"ds_get_nth_session" typedef struct { uint64offset
; /* in device blocks */ uint64blocks
; /* number of blocks in session */ booldata
; /* true: data session, session */ } session_data; boolhave_session
=false
; int32block_size
; int32dev
; int32index
= 0;BDirectory
dir
;BEntry
entry
;BPath
path
; device_geometrygeometry
; image_idimage
; session_datasession
; status_t (*ds_get_nth_session
)(int32, int32, int32, session_data*); if (ioctl
(dev
,B_GET_GEOMETRY
, &geometry
) ==B_NO_ERROR
) {block_size
=geometry
.bytes_per_sector
;find_directory
(B_BEOS_ADDONS_DIRECTORY
, &path
);dir
.SetTo
(path
.Path
());dir
.FindEntry
(DS_SESSION_ADDONS
, &entry
);dir
.SetTo
(&entry
); if (dir
.InitCheck
() ==B_NO_ERROR
) {dir
.Rewind
(); while (!have_session
) { /* try loading the next add-on */ if (dir
.GetNextEntry
(&entry
) >= 0) {entry
.GetPath
(&path
); if ((image
= load_add_on(path
.Path
())) >= 0) { if (get_image_symbol
(image
,DS_GET_NTH_SESSION
,B_SYMBOL_TYPE_TEXT
, &ds_get_nth_session
) >= 0) {have_session
=true
; while ((*ds_get_nth_session
)(dev
,index
,block_size
,session
) ==B_NO_ERROR
) { ...index
++; } }unload_add_on
(image
); } } else break; } } }
If the device is not a CD or for each session of a multi-session CD, use the partition add-ons to determine if there is a partition map and, if so, the offset and length for each individual partition...
#defineDS_PARTITION_ID
"ds_partition_id" #defineDS_PARTITION_MAP
"ds_get_nth_map" typedef struct { charpartition_name
[B_FILE_NAME_LENGTH
]; charpartition_type
[B_FILE_NAME_LENGTH
]; charfile_system_short_name
[B_FILE_NAME_LENGTH
]; charfile_system_long_name
[B_FILE_NAME_LENGTH
]; charvolume_name
[B_FILE_NAME_LENGTH
]; charmounted_at
[B_FILE_NAME_LENGTH
]; uint32logical_block_size
; uint64offset
; /* in logical blocks from start of session */ uint64blocks
; /* in logical blocks */ boolhidden
; /* non-file system partition */ boolreserved1
; uint32reserved2
; } partition_data; boolhave_map
=false
; bool (*ds_partition_id
)(uchar *, int32); uchar *block
; int32index
= 0;BDirectory
dir
;BEntry
add_on
;BEntry
entry
;BPath
path
; image_idimage
; partition_datapartition
; status_t (*ds_get_nth_map
)(int32, uchar*, uint64, int32, int32, partition_data*);block
= (uchar *)malloc
(block_size
);lseek
(dev
,session
->offset
*block_size
, 0); if (read
(dev
,block
,block_size
) ==block_size
) {find_directory
(B_BEOS_ADDONS_DIRECTORY
, &path
);dir
.SetTo
(path
.Path()
);dir
.FindEntry
(DS_PART_ADDONS
, &entry
);dir
.SetTo
(&entry
); if (dir
.InitCheck
() ==B_NO_ERROR
) {dir
.Rewind
(); while (!have_map
) { /* try loading the next add-on */ if (dir
.GetNextEntry
(&add_on
) >= 0) {add_on
.GetPath
(&path
); if ((image
=load_add_on
(path
.Path
())) >= 0) { if (get_image_symbol
(image
,DS_PARTITION_ID
,B_SYMBOL_TYPE_TEXT
, &ds_partition_id
) >= 0) { /* add-on recognize this map? */ if ((*ds_partition_id
)(block
,block_size
)) {have_map
=true
; if (get_image_symbol
(image
,DS_PARTITION_MAP
,B_SYMBOL_TYPE_TEXT
, &ds_get_nth_map
) >= 0) { /* gather info about each partition */ while ((*ds_get_nth_map
)(dev
,block
,session
->offset
,fBlockSize
,index
, &partition
) ==B_NO_ERROR
) { ...index
++; } } } } }unload_add_on
(image
); } else break; } }free
(block
); }
The file system add-ons are used to determine the type of file system a partition, session or raw device was initialized with...
#defineDS_FS_ADDONS
"drive_setup/fs/" #defineDS_FS_ID
"ds_fs_id" bool (*ds_fs_id
)(partition_data*, int32, uint64, int32); boolhave_fs
=FALSE
;BDirectory
dir
;BEntry
entry
;BPath
path
; image_idimage
;find_directory
(B_BEOS_ADDONS_DIRECTORY
, &path
);dir
.SetTo
(path
.Path
());dir
.FindEntry
(DS_FS_ADDONS
, &entry
);dir
.SetTo
(&entry
); if (dir
.InitCheck
() ==B_NO_ERROR
) {dir
.Rewind
(); while (!have_fs
) { /* try loading next add-on */ if (dir
.GetNextEntry
(&entry
) >= 0) {entry
.GetPath
(&path
); if ((image
=load_add_on
(path
.Path
())) >= 0) { if (get_image_symbol
(image
,DS_FS_ID
,B_SYMBOL_TYPE_TEXT
, &ds_fs_id
) = 0) { /* add-on recognize this file system? */ if ((*ds_fs_id
)(partition
,dev
,session
->offset
,block_size
)) { ...have_fs
=TRUE
; } }unload_add_on
(image
); } } else break; } }
If the file system was successfully identified by the add-on, the file_system_short_name, file_system_long_name and volume_name fields of the partition_data structure will be filled in with the correct values. The volume name can be used to create the mount point and the file_system_short_name is used for the mount function...
charmount_point
[B_FILE_NAME_LENGTH
]; chartmp
[B_FILE_NAME_LENGTH
]; int32index
= 1; int32loop
;BDirectory
directory; /* if there is no volume name, use a default */ if (!strlen
(partition
.volume_name
))sprintf
(tmp
, "/disk"); else { /* create mount point at root */sprintf
(tmp
, "/%s",partition
.volume_name
); /* convert any forward slashes to ':' */ for (loop
= 1;loop
<strlen
(partition
.volume_name
);loop
++) if (tmp
[loop
] == '/')tmp
[loop
] = ':'; } } /* make sure name is unique or if not unique, */ /* make sure existing directory is empty */strcpy
(mount_point
,tmp
); while (1) { if (mkdir
(mount_point
, 0777) >= 0) break;directory
.SetTo
(mount_point
); if ((!directory
.InitCheck
()) && (!directory
.CountEntries
())) break;sprintf
(mount_point
, "%s%d",tmp
,index
++); } /* try mounting */ if (mount
(partition
.file_system_short_name
,mount_point
,device_path
, 0,NULL
, 0) < 0) { /* error mounting device */ }
"Developers' Workshop" is a new weekly feature that provides answers to our developers' questions. Each week, a Be technical support or documentation professional will choose a question (or two) sent in by an actual developer and provide an answer.
We've created a new section on our web site. Please send us your Newsletter topic suggestions by visiting the web site at: http://www.be.com/developers/developers_test/suggestion_box.html.
Q: Having just read a book on baroque counterpoint I'm now ready to create masterpieces (in MIDI format) that are indistinguishable from those of Bach, Handel, and Cannabich (my favorite!). However, I can't play a musical instrument and I'm not that great at reading music, either. Can I create MIDI files by typing them in?—Dick "Really Scary Spice" Wagner, Bayreuth, Germany
A. Well, Dick, you've dropped in at the right moment. I just popped a little MIDI-generating language out of the oven. Mmmmm, smell the aroma of copy-and-paste composition churned out by tone-deaf hobbyists whose musical education ended right after they learned that tritones sound really creepy.
Before we go any further, let's do some weeding. If you don't need to generate MIDI, or if you'd rather use a GUI to enter data, leave the room. Or just skip down to the final paragraph where we pay our respects to one of the creators of the Headspace General MIDI synthesizer.
For the rest of you, you'll soon be generating chromatic scales as easily as...
Xchannel(1); Xvoice(B_BANJO); for (int i = 0; i < 12; i++ ) { Xnote(c4+1, 1.0, 0.5, 0.0); Xinc(1.0); }
Talk about fun. How about this:
Xchannel(1); Xvoice(B_OBOE); Xnote(c5, 4.0, 0.5, 0.0); // play a note XskewA(0.0); // start a pitch bend trajectory XampA(1.0); // start an amplitude trajectory Xinc(4.0); // wait for beats XskewZ(-1.0); // stop the pitch bend trajectory XampZ(0.0); // stop the amplitude trajectory
This generates an oboe tone on c5 (the c above middle c), and then, over the next four beats (seconds, by default) bends the note down to bf4 and fades the note out. One more; this produces a cloud of swirly metal for about 20 seconds:
Xvoice(B_REVERSE_CYMBAL); for (int i = 0; i < 100; i++) { Xabs(1.5); Xinc((rand()%100)/5.0); Xnote((rand()%30)+40, (rand()%100)/40.0+.2, ((rand()%100)/200.0)+.3, 0.); }
Everything you need to scare your dog is contained in this tar file:
ftp://ftp.be.com/pub/samples/r3/midi_kit/EdMidi.tar
When you unpack the tar file, it will create a directory called "EdMidi" that contains the following seven files in three parts:
Part 1: edmidi.cpp, p2kn.h, axe_names.h These file create the "edmidi" application. Throw all three files into a new BeIDE project, add libmidi and libmedia, and compile the app.
Part 2: lbjtest.cpp, langMacros.h, pitches.h lbjtest.cpp is an example of a file that, when compiled and run, creates new data that you feed to edmidi (the reverse cymbal code above is taken from lbjtest.cpp). The file includes langMacros.h and pitches.h; the latter defines an array of human-parsable note names, such as c4 (middle c), cs4 (c-sharp above), bff3 (b double-flat below), and so on.
Part 3: edmidi.lang This is a text file that describes the data format that edmidi expects, and that explains the macros that are defined in langMacros.h. It's not the most robust documentation, but it will do for now.
The process goes something like this:
Copy and modify lbjtest.cpp, mixing and matching the Xmumble() macros that are listed in langMacros.h (and described in edmidi.lang).
Compile lbjtest.cpp as, say, LbjTest.
Run LbjTest and capture the output (for convenience, I use the extension ".lbj", but it isn't required): $ LbjTest > test1.lbj
If you want, you can edit the test1.lbj file. You can even create such files by hand by using the language format described in Part 1 of edmidi.lang.
Pass test1.lbj to edmidi: $ edmidi test1.lbj edmidi will think for a little bit, and then will produce the file "test.midi" (the filename is declared in the lbjtest.cpp file). It will also play the file, spitting out statistics as it does, and prompting you to replay:
$ edmidi test1.lbj Last timestamp: 23.225000 Max notes: 74 Writing midi test.midi Setting sampling rate 44100 Replay [y]?
If you pass it a trailing "r" argument, edmidi will record the playback to a sound file:
$ edmidi lbjtest.lbj r Recording to Last timestamp: 23.225000 Max notes: 74 Writing midi test.midi Writing sound lbjtest.snd Setting sampling rate 44100 Replay [y]?
edmidi will also let you mute and solo individual channels. It also has a number of other options that you can dump by typing 'edmidi' with no argument.
Now to the serious and unfortunate part of the column. Last Friday, Jim Nitchalls, one of the creators of the Headspace synthesizer, died. I met Jim eons ago when we both were working in or around Electronic Arts. Every couple of years since then, our paths would cross with us on them at the same time; one particularly cogent collision is when he called me up two years ago to see if Be could use a General MIDI synthesizer.
You may have seen Jim, possibly even chatted with him, at one of the Be Developer Conferences. If you've never actually used the Headspace synthesizer, you should try it sometime. Utter Jim's name and generate a burst of voice #126 (0-based).
If you haven't already visited the Developer section of our Web site, I'd like to direct your attention to
http://www.be.com/developers/developer_program/devprogram_overview.html. As the end of the URL implies, it contains an overview of our new tiered developer programs, as well as pointers to more detailed information.
This should provide you with the information you need in order to decide which of the three programs is right for you. For my part, I'd like to review the philosophy behind these changes, as well as what we are trying to accomplish for the Be community.
As obvious as we think it is, it bears repeating that an operating system is useless in itself. Developers make it useful with the applications they build for it. Only after we have applications that proclaim the strengths of the BeOS (and we believe we have some already) can we think of the other constituencies in the Be community—our customers, shareholders, business partners, and employees.
So, why do we charge for developer programs if we believe developers are so important? Don't we risk turning away the next Mitch Kapor, or even the next Paul Allen?
As you'll see in looking through the new programs, we are trying to juggle a contradiction. We want to make sure enthusiasts can try their hand at writing code for the BeOS in the largest possible numbers at the least possible expense. That requires access to a computer and to a copy of the BeOS. There is no annual fee at this level. Our Web site and newsgroups provide technical documentation, and access to libraries and newsletters. Your creations will be featured in the BeWare section of our Web site.
For people or companies who make a business of writing BeOS commercial applications we want to offer more support. These developers get more technical and marketing assistance, their CodeWarrior tools are included in the $299 annual fee, and we'll enthusiastically distribute your commercial software through BeDepot.com.
Developers with a large organization can use the $599 Corporate program with even more technical and marketing support, along with one copy of the BeOS and all upgrades released during the subscription year, a copy of the current IDE, CodeWarrior, and PackageBuilder (which lets developers build packages that can be installed with SoftwareValet). Corporate developers can also buy additional seats (BeOS, CodeWarrior, and PackageBuilder tools) "wholesale" at $99 each.
Further provisions ease the transition for existing developers, especially CodeWarrior customers. It's that simple.
We've already received some feedback on the letter we sent to existing developers. One suggestion was to use Silver, Gold, and Platinum labels for the three tiers. Personally, I prefer the suggestion from a Be executive (who requested anonymity): Geek, Midnight Programmer, and Commercial, if I remember correctly.
Our hope is that the no-fee program will continue to attract individuals who are intrigued with the BeOS and want to try writing an application. If our product and our company do a good job for them, they'll go up one level, and make a business out of their experiment. If we all do what we're supposed to do, they'll go to the top—and us with them. Or else they'll happily make shareware because that's what they like to do.
We learned a long time ago it's not our place to tell a developer what to do. It works much better the other way around when programmers who actually struggle with the platform and the marketplace tell us what to do with our APIs and our marketing programs. Their suggestions are always colorful and useful.
Speaking of hue and abuse, we're off to NYC next week for PC Expo. If you're in town, look for us at the friendly Jacob Javits Convention Center. We'll see if New York City unions and waiters are a match for French ones. More exciting still, we'll be showing off the newest BeOS applications running on Release 3.1.