Driver Program Example

0 views
Skip to first unread message

Latarsha Dorrance

unread,
Aug 4, 2024, 6:43:24 PM8/4/24
to ocancari
Thefirst example is a simple interrupt-per-character line printer driver. The driver is unidirectional--it has no read-side processing. It demonstrates some differences between module and driver programming, including the following:

Most of the STREAMS processing in the driver is independent of the actual printer hardware; in this example, actual interaction with the printer is limited to the lpoutchar function, which prints one character at a time. For purposes of demonstration, the "printer hardware"is actually the system console, accessed through cmn_err(9F). Since there's no actual hardware to generate a genuine hardware interrupt, lpoutchar simulates interrupts using ddi_trigger_softintr(9F). For a real printer, the lpoutchar functionis rewritten to send a character to the printer, which should generate a hardware interrupt.


The driver declarations follow. After specifying header files (include and as the last two header files), the driver declares a per-printer structure, struct lp. This structure contains members that enable the driver to keep track of each instanceof the driver, such as flags (what the driver is doing), msg (the current STREAMS print message), qptr (pointer to the stream's write queue), dip (the instance's device information handle), iblock_cookie (forregistering an interrupt handler), siid (the handle of the soft interrupt), and lp_lock (a mutex to protect the data structure from multithreaded race conditions). The driver next defines the bits for the flags member of struct lp;the driver defines only one flag, BUSY.


Following function prototypes, the driver provides some standard STREAMS declarations: a module_info(9S) structure (minfo),a qinit(9S) structure for the read side (rinit) that is initialized by the driver's open and closeentry points, a qinit(9S) structure for the write side (winit) that is initialized by the write put procedure,and a streamtab(9S) that points to rinit and winit. The values in the module name and ID fields in the module_info(9S) structure must be unique in the system. Because the driver is unidirectional, there is no read side put or service procedure. The flow control limits for use on the write side are 50 bytes for the low-watermark and 150 bytes for the high-watermark.


The driver next declares lp_state. This is an anchor on which the various DDK - provided "soft-state" functions operate. The ddi_soft_state(9F) manual page describes how to maintain multiple instances of a driver.


The driver next declares acb_ops(9S) structure, which is required in all device drivers. In non-STREAMS device drivers, cb_ops(9S) contains vectors to the table-driven entry points. For STREAMS drivers, however, cb_ops(9S) contains mostly nodev entries. The cb_stream field, however, is initialized with a pointer to the driver's streamtab(9S) structure. This indicates to the kernel that this driver is a STREAMS driver.


Next, the driver declares a dev_ops(9S) structure, which points to the various initialization entry points as well as to the cb_ops(9S) structure. Finally, the driver declares a struct moldrv and a struct modlinkage for use by the kernel linker when the driver isdynamically loaded. Struct moldrv contains a pointer to mod_driverops (a significant difference between a STREAMS driver and a STREAMS module--a STREAMS module would contain a pointer to mod_strops instead).


lpattach first uses ddi_soft_state_zalloc(9F) to allocate a per-instance structure for the printer being attached.Next it creates a node in the device tree for the printer using ddi_create_minor_node(9F); user programs use the node to access thedevice. lpattach then registers the driver interrupt handler; because the sample is driver pseudo-hardware, the driver uses soft interrupts. A driver for a real printer would use ddi_add_intr(9F) instead of ddi_add_softintr(9F). A driver for a real printer would alsoneed to perform any other required hardware initialization in lpattach. Finally, lpattach initializes the per-instance mutex.


The STREAMS mechanism allows only one stream per minor device. The driver open routine is called whenever a STREAMS device is opened. open matches the correct private data structure with the stream using ddi_get_soft_state(9F). The driver open, lpopen in Example 9-4, has the same interface as the module open.


The stream flag, sflag, must have the value 0, indicating a normal driver open. devp pointers to the major/minor device number for the port. After checking sflag, lpopen uses devp to find the correct soft-statestructure.


The next check, if (q->q_ptr)..., determines if the printer is already open. q_ptr is a driver or module private data pointer. It can be used by the driver for any purpose and is initialized to zero by STREAMS before the first open. In thisexample, the driver sets the value of q_ptr, in both the read and write queue structures, to point to the device's per-instance data structure. If the pointer is non-NULL, it means the printer is already open, so lpopen returns EBUSY to avoid mergingprintouts from multiple users.


There are no physical pointers between the read and write queue of a pair. WR(9F) is a queue pointer function. WR(9F) generates the write pointer from the read pointer. RD(9F)and OTHERQ(9F) are additional queue pointer functions. RD(9F) generates the read pointer from the write pointer, and OTHERQ(9F) generates themate pointer from either.


In most drivers, if FLUSHR is set, the read queue is flushed. However, in this example, no messages are ever placed on the read queue, so it is not necessary to flush it. The FLUSHW bit is cleared and the message is sent upstream using qreply(9F). If FLUSHR is not set, the message is discarded.


The stream head always performs the following actions on flush requests received on the read side from downstream. If FLUSHR is set, messages waiting to be sent to user space are flushed. If FLUSHW is set, the stream head clears the FLUSHRbit and sends the M_FLUSH message downstream. In this manner, a single M_FLUSH message sent from the driver can reach all queues in a stream. A module must send two M_FLUSH messages to have the same effect.


lpout takes a single character from the queue and sends it to the printer. For convenience, the message currently being output is stored in lp->msg in the per-instance structure. It is assumed that this is called with the mutex held.


lpoutchar sends a single character to the printer (in this case the system console using cmn_err(9F)) and interrupts whencomplete. Of course, hardware would generate a hard interrupt, so the call to ddi_trigger_softintr(9F) would be unnecessary.


When the message is queued, putq(9F) increments the value of q_count by the size of the message and compares the result tothe driver's write high-watermark (q_hiwat) value. If the count reaches q_hiwat, putq(9F) sets the internal FULL indicator for the driver write queue. This causes messages from upstream to be halted (canputnext(9F) returns FALSE) until thewrite queue count drops below q_lowat. The driver messages waiting to be output through lpout are dequeued by the driver output interrupt routine with getq(9F), which decrements the count. If the resulting count is below q_lowat, getq(9F) back-enablesany upstream queue that had been blocked.


To some extent, a driver or a module can control when its upstream transmission becomes blocked. Control is available through the M_SETOPTS message (see Appendix A, Message Types) to modify the stream head read-side flow control limits.


A device driver is defined as a software program without a user interface (UI) that manages hardware components or peripherals attached to a computer and enables them to function with the computer smoothly. This article explains the working of device drivers, their various types, and five critical applications.


A device driver is a software program without a user interface (UI) that manages hardware components or peripherals attached to a computer and enables them to function with the computer smoothly.


The hardware is linked to a computer bus/communication subsystem via which device drivers interact with the device. They are hardware-dependent and operating-system-specific (OS). They offer the interrupt processing essential for any time-dependent asynchronous hardware interface.


Device drivers enable peripheral devices, such as printers or keyboards, to interact with the computer. The following outlines the steps developers or programmers can take while developing device drivers for operating systems such as Windows, Linux, or macOS.


If the primary device is a system on chip, developers should know how the driver interacts with its firmware and command protocols. Additionally, developers should be prepared for documentation to fall short when handling a new type of hardware. Thus, they should be ready to perform more tests than usual.


If a device is incorrectly designed, drivers running in user mode may cause a system crash. Similarly, if anything goes wrong when drivers are operating in highly privileged settings, operational concerns may occur. Thus, developers should take advantage of the information in the driver development documentation available for the selected operating system, be it Windows or Linux.


The first device driver functions that are developed are the load and unload functions. When the operating system starts and stops, these functions are called. One of the primary responsibilities of the load/unload functions is to detect whether the hardware is plugged into the system or not. Users can detect hardware by using the device ID specified by the specific bus. If the hardware is plugged in, then the load function is successful. If not, call the unload function.


Once the device can detect the hardware, the next step is initializing it. The type of initialization required may differ depending on the kind of hardware. Initialization can range from writing to the device register to downloading a microcode onto the device and communicating on a long-term basis using proprietary command protocols.

3a8082e126
Reply all
Reply to author
Forward
0 new messages