Drivers for Character Devices
Character devices are devices that do not have physically addressable storage media, such as tape drives or serial ports, where I/O is normally performed in a byte stream. This chapter describes the structure of a character device driver, focusing in particular on character driver entry points. In addition, this chapter describes the use of physio(9F) (in read(9E) and write(9E)) and aphysio(9F) (in aread(9E) and awrite(9E)) in the context of synchronous and asynchronous I/O transfers.
This chapter provides information on the following subjects:
Character Driver Structure Overview
Figure 10-1 shows data structures and routines that define the structure of a character device driver. Device drivers typically include the following:
Device-loadable driver section
Device configuration section
Character driver entry points
The shaded device access section in Figure 10-1 illustrates character driver entry points.
Figure 10-1 Character Driver Roadmap
Associated with each device driver is a dev_ops(9S) structure, which in turn refers to a cb_ops(9S) structure. These structures contain pointers to the driver entry points. Note that some of these entry points can be replaced with nodev(9F) or nulldev(9F) as appropriate.
Character Device Autoconfiguration
The attach(9E) routine should perform the common initialization tasks that all devices require. Typically, these tasks include:
Allocating per-instance state structures
Registering device interrupts
Mapping the device's registers
Initializing mutex and condition variables
Creating power-manageable components
Creating minor nodes
See "The attach() Entry Point" for code examples of these tasks.
Character device drivers create minor nodes of type S_IFCHR. This causes a character special file representing the node to eventually appear in the /devices hierarchy.
Example 10-1 shows a sample attach(9E) routine. It is common to declare any properties associated with the device in an attach() routine; in this example, it is the predefined Size property. Size is the equivalent of the Nblocks property used for getting the size of partition in a block device. If, for example, you are doing character I/O on a disk device, you might use Size to get the size of a partition. Since Size is a 64-bit property--the 32-bit version is size--you must use a 64-bit property interface, in this case ddi_prop_updtate_int64(9E) See "Device Properties" for more on properties.
Example 10-1 Character Driver attach(9E) Routine
static int xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int instance = ddi_get_instance(dip); switch (cmd) { case DDI_ATTACH: allocate a state structure and initialize it. map the device's registers. add the device driver's interrupt handler(s). initialize any mutexes and condition variables. create power manageable components. /* * Create the device's minor node. Note that the node_type * argument is set to DDI_NT_TAPE. */ if (ddi_create_minor_node(dip, "minor_name", S_IFCHR, instance, DDI_NT_TAPE, 0) == DDI_FAILURE) { free resources allocated so far. /* Remove any previously allocated minor nodes */ ddi_remove_minor_node(dip, NULL); return (DDI_FAILURE); } /* * Create driver properties like "Size." Use "Size" * instead of "size" to ensure the property works * for large bytecounts. */ xsp->Size = size of device in bytes; maj_number = ddi_driver_major(dip); if (ddi_prop_update_int64(makedevice(maj_number, instance), dip, "Size", xsp->Size) != DDI_PROP_SUCCESS) { cmn_err(CE_CONT, "%s: cannot create Size property\n", ddi_get_name(dip)); free resources allocated so far return (DDI_FAILURE); } ... return (DDI_SUCCESS); case DDI_RESUME: For information, see Chapter 9, Power Management default: return (DDI_FAILURE); } } |