Writing Device Drivers

Entry Points Used by Device Power Management

The Power Management framework uses the following entry points:

power()

	int xxpower(dev_info_t *dip, int component, int level);

The system calls the power(9E) entry point (either directly or as a result of a call to ddi_dev_is_needed(9F)) when it determines that a component's current power level needs to be changed. The action taken by this entry point is device driver specific. In the example of the SCSI target disk driver mentioned previously, setting the power level of the component to 0 results in sending a SCSI command to spin down the disk, while setting the power level to the normal power level results in sending a SCSI command to spin up the disk. Example 8-1 shows a sample power(9E) routine.


Example 8-1 power(9E) Routine

int
xxpower(dev_info_t *dip, int component, int level)
{
  	struct xxstate *xsp;
	   int	instance;
	   instance = ddi_get_instance(dip);
  	xsp = ddi_get_soft_state(statep, instance);

	   /*
	   * Make sure that the request is valid
  	*/
 	if (xx_valid_power_level(component, level))
	    	return (DDI_FAILURE);

 	mutex_enter(&xsp->mu);
 	if (xsp->xx_power_level[component] != level) {
	    	device- and component-specific setting of power level.
	    	xsp->xx_power_level[component] = level;
 	}
 	mutex_exit(&xsp->mu);
 	return (DDI_SUCCESS);
}

detach()

	int detach(dev_info_t *dip, ddi_detach_cmd_t cmd);

Before the system sets component 0 (entire device) to power level 0, it calls the driver's detach(9E) entry point with a detach command of DDI_PM_SUSPEND to allow the driver to save all hardware state to memory.

If the device is busy and has outstanding operations, it should fail the detach(9E) call. The framework will try again later after the device has been idle for its threshold time. Otherwise, the driver must arrange to block all subsequent accesses to the hardware until the device has been resumed (which the driver can initiate by calling ddi_dev_is_needed(9F)), and save all hardware state to memory.

Example 8-2 shows an example of a detach(9E) routine with DDI_PM_SUSPEND implemented.


Example 8-2 detach(9E) Routine Showing the Use of DDI_PM_SUSPEND

int
xxdetach(devinfo_t *dip, ddi_detach_cmd_t cmd)
{
 	struct xxstate *xsp;
 	int	instance;
 	instance = ddi_get_instance(dip);
 	xsp = ddi_get_soft_state(statep, instance);

 	switch (cmd) {
 	case DDI_DETACH:
	   	see chapter 5, Autoconfiguration, for discussion

		case DDI_SUSPEND:
	    	see Example 8-4		case DDI_PM_SUSPEND:
		   /*
		    * We won't be called with DDI_PM_SUSPEND when already called
		    * with DDI_SUSPEND.
		    */
	    	 mutex_enter(&xsp->mu);
	    	 if (xsp->xx_busy) {
			      mutex_exit(&xsp->mu);
			      return(DDI_FAILURE);
		    }

	    	 xsp->xx_pm_suspended = 1; 
	    	 Save device register contents into xsp->xx_device_state
	
	    	this section is optional, only needed if the driver maintains a running 	
	        	timeout (but be sure to drop the  mutex in any case)
	    	 /* cancel timeouts */
	    	 if (xsp->xx_timeout_id) {
			     timeout_id_t temp_timeout_id = xsp->xx_timeout_id;

			     xsp->xx_timeout_id = 0;
			     mutex_exit(&xsp->mu);
			     untimeout(temp_timeout_id);
	    	 } else {
			     mutex_exit(&xsp->mu);
	    	 }
	   	 return(DDI_SUCCESS);

 	default:
	    	 return(DDI_FAILURE);
 	}
}

attach()

	int attach(dev_info_t *dip, ddi_attach_cmd_t cmd);

When a device that has been suspended is needed again, its power(9E) entry point is called to restore the power level of component 0 (entire device) to its normal power. The driver's attach(9E) entry point is then called with an attach command value of DDI_PM_RESUME to restore the device hardware state saved in the detach(9E) routine and unblock any pending operations. Example 8-3 shows an attach(9E) routine with DDI_PM_RESUME implemented.


Example 8-3 attach(9E) Showing the Use of DDI_PM_RESUME

int
xxattach(devinfo_t *dip, ddi_attach_cmd_t cmd)
{
 	struct xxstate *xsp;
 	int	instance;

 	instance = ddi_get_instance(dip);
 	xsp = ddi_get_soft_state(statep, instance);

 	switch (cmd) {
 	case DDI_ATTACH:
	   	see chapter 5, Autoconfiguration for discussion

 	case DDI_RESUME:
	   	see Example 8-5 for DDI_RESUME implementation

 	case DDI_PM_RESUME:
		   /*
		    * We won't be DDI_PM_RESUMEd while DDI_SUSPENDed
		    */
	   	 mutex_enter(&xsp->mu);
	   	 Restore device register contents from xsp->xx_device_state
		
	      	this section is optional, only needed if the driver maintains a running timeout
	   	 /* restart timeouts */
	   	 xsp->xx_timeout_id = timeout({...});
		
	   	 xsp->xx_pm_suspended = 0;	/* allow new operations */
	   	 cv_broadcast(&xsp->cv);
	   	 mutex_exit(&xsp->mu);
	   	 return(DDI_SUCCESS);

 	default:
	   	return(DDI_FAILURE);
 	}
}