Last update: Sep. 20, 2000
This article describes how to write a BeOS joystick driver. It assumes knowledge on BeOS device drivers (kernel drivers and modules).
Be discloses little information on device driver development for minor fields, one of which is joysticks/gameports; a little information available is sonic_vibes driver source codes, BJoystick in Be Book and Be Developer Newsletter article. Thus I have investigated how to write a joystick driver and gained information enough to write a narrowly working one, as described below.
The contents of this article is based on my own investigation and Be does not publish nor guarantee them except some. I do not guarantee their correctness. Also beware that there are many unknown issues left.
First we will have a glance at BeOS joystick API (the BJoystick class). For details, refer to Be Book and Be Developer Newsletter article.
BJoystick has two operation modes, standard mode and enhanced mode. Standard mode is obsolete, based on BeBox gameports, limited to 2-axes and 2-buttons regardless of joystick models, and is seldom used. Enhanced mode is added in R4 and allows you to make best use of joystick features. In addition, the Joysticks preference is effective under enhanced mode only. Thus you will usually use enhanced mode. You can enter enhanced mode from standard mode, but it is done by default when the gameport is opened. In order to use standard mode, you have to explicity specify not to enter enhanced mode.
Applications will have access to joysticks as follows (function names represent the member functions in the BJoystick class). To use enhanced mode, you have to configure your joystick in the Joystick preference in advance.
BeOS joystick support framework is constructed as follows:
applications Joysticks preference------------+ | | | | |BJoystick joystick definition files | | | | operating system----------------+ | | |kernel driver API | | gameport drivers--------+ | | | |module API | | | joystick modules | | gameport hardware | | joystick hardware
Functionality of main components is show below:
A kernel driver to manipulate the gameport and input joystick data. Dependent on the gameport hardware (ISA sound card, PCI sound card, USB, proprietary interface etc).
The driver publishes device files under /dev/joystick/. When the app calls BJoystick::Update(), a read request will occur to the device file, and the driver's read() will be called. In reply to this, the driver inputs joystick data, interprets it and returns it. The returned data format is the joystick structure in standard mode and the extended_joystick structure in enhanced mode (both of which are defined in joystick_driver.h).
Under standard mode the gameport driver itself also performs data interpretation, but under enhanced mode the driver only performs raw data input and delegates interpretation to the appropriate joystick module, assuming possibility that data interpretation may vary depending on joystick models. This will allow you to separate gameport hardware dependent part from joystick model dependent part.
However it is not clear about details of how they share their roles. Unlike other buses and ports, the gameport does not necessarily allow you clear separation, and its necessity is relatively low. Therefore you can also implement them so that the joystick module performs most process including raw data input. As this article assumes that the gameport driver always performs raw data input, some differences will arise if you do it in the joystick module. In case of USB, you ought to always do it in the gameport driver because interface to the USB bus manager can be complicated.
Besides, if the joystick has force feedback capability, force feedback should occur by writing the extended_joystick structure to the device file under enhanced mode. However details are not clear.
A module to interpret raw data by the gameport driver and convert it in the format the operating system can understand (the extended_joystick structure). Dependent on joystick models. The API is defined by the joystick_module structure in joystick_driver.h. Called by the gameport driver under enhanced mode. Note that the operating system does not call joystick modules (except std_ops()).
A text file describing characteristics (product name, numbers and names of axes and buttons, joystick module name etc.) for each joystick model. Joysticks preference presents a list of product names obtained from the definition files and the user chooses among them the one connected to the game port. This information (the joystick_module_info structure) will be passed by the operating system to the gameport driver when the gameport enters enhanced mode. Based on it the gameport driver will choose the appropriate joystick module.
BTW, your choice in the Joysticks preference is saved under ~/config/settings/joysticks/, where there are symbolic links driver_name/port_number, which point to joystick definition files selected for each port.
Each file resides in the following locations:
Gameport drivers | /system/add-ons/kernel/drivers/dev/joystick/* |
Joystick modules | /system/add-ons/kernel/media/joy/* |
Joystick definition files | /etc/joysticks/* |
Gameport device files | /dev/joysticks/*/* |
Besides them, there is a thing called the "generic gameport module" (/system/add-ons/kernel/generic/gameport, the generic_gameport_module structure in joystick_driver.h) which is used in the sonic_vibes driver, but its role and usage are not clear. I will not take it into account here since you can write a driver without using it.
Developing a joystick driver implies writing a gameport driver, writing a joystick module and writing a joystick definition file.
I will breifly discuss processes the gameport driver API should implement. Refer to joystick_driver.h in the sonic_vibes driver for details about structures etc.
If your gameport is PCI or ISA, you can detect and initialize it. But the driver will be unloaded soon after init_hardware(), so it makes no sense here to store obtained information into global variables. If you'd like to do it, you must allocate an area and store into it. In case of PCI, devices are usually mapped into the physical memory space, so you'll need an area anyway.
The driver initializes internal variables etc. Also you can detect and initialize your gameport here every time instead of doing in init_hardware(). In case of USB you will only obtain the bus manager and register the driver with it here and do actual process in device_added()/device_removed().
The driver finalizes. In case of USB you will deregister the driver and release the bus manager.
The driver publishes device names. Device names are usually made as "joystick/driver_name/port_number" (e.g. joystick/gameport/201, joystick/ymf724/1). In case of USB, they will change dynamically by plugging and unplugging joysticks. There's nothing special.
The driver returns the device hooks. Nothing special too.
If the supplied device name is applicable to the driver, the driver obtains the port number from it and examines the port. If a joystick is attached, the driver allocates and initializes a cookie. A cookie typically contains following information:
Obtains the port number from the cookie and inputs data from the gameport. Under standard mode the driver interprets the input data, converts it into the joystick structure to return. Under enhanced mode the driver calls read() in the joystick module, tell him to convert the input data into the extended_joystick structure to return.
Under enhanced mode the driver passes the write data (the extended_joystick structure) to force() in the joystick module and tell him to perform force feedback. Detailed process is not clear.
The driver performs ioctls (B_JOYSTICK_*) defined in joystick_driver.h. B_JOYSTICK_SET_DEVICE_MODULE and B_JOYSTICK_GET_DEVICE_MODULE are mandatory.
When the gameport will enter enhanced mode by the app's instruction, B_JOYSTICK_SET_DEVICE_MODULE will be called along with the joystick information configured in the Joystick preference. The argument is a joystick_module_info structure, which includes the joystick module name, the product name, number of axes and buttons etc. If the given information is compatible with the actually connected joystick, the driver saves joystick_module_info, obtain the joystick module (get_module()), calls configure() in the module to tell him to initialize and enters enhanced mode. BTW, when you do "Probe" in the Joystick preference, it calls this ioctl for every joystick definition file and it assumes the one(s) with no error may be connected.
B_JOYSTICK_GET_DEVICE_MODULE only returns the joystick_module_info structure given by B_JOYSTICK_SET_DEVICE_MODULE.
Nothing to do.
The driver releases the cookie. Under enhanced mode the driver calls crumble() in the joystick module to tell him to finalize, and release the module (put_module()).
I will breifly discuss processes the joystick module API should implement. There are still many unknown issues left about joystick modules compared to gameport drivers.
The module allocates and initializes a cookie according to the given port number and the joystick_module_info structure. Unlike the gameport driver's cookie to be used for communication between the operating system and the gameport driver, this cookie is to be used for communication between the joystick and the gameport driver, The contents of a cookie might be the raw data the gameport driver inputs.
If the module can detect joystick model automatically, it may rewrite the joystick_module_info structure with the detected information. In particular, full automatic detection is desired in case of USB since USB allows you to obtain all necessary information from the joystick without relying on the joystick_module_info structure (and its source, the joystick definition file). That is, it'd be ideal that you provide only one (virtually dummy) joystick definition file (e.g. "Generic USB HID Joystick) for all USB joysticks and that users can choose it regardless of his USB joystick model.
Called from read() in the gameport driver, the module interprets the raw data given by the driver and stores it into the extended_joystick structure.
Called from write() in the gameport driver, the module performs force feedback based on the extended_joystick structure given by the driver.
The module releases the cookie.
B_MODULE_INIT and B_MODULE_UNINIT will be called but no process is required.
Publishes the joystick_module structure.
Refer to my sample source codes.
Many left.