This appendix contains the source listing for a sample disk device driver called /dev/xxx_disk.
/******************************************************************
* xxx_disk.c
*
* This module contains a sample disk device driver.
*
******************************************************************/
/********************** Include Files *****************************/
#include <sys/types.h>
#include <io/common/iotypes.h>
#include <sys/errno.h>
#include <sys/buf.h>
#include <sys/secdefines.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/disklabel.h>
#include <sys/user.h>
#include <sys/sysmacros.h>
#include <sys/syslog.h>
#include <io/common/pt.h>
#include <io/common/devdriver.h>
#include <io/common/devio.h>
#include <io/common/devgetinfo.h>
/********************* External References ************************/
int biodone(struct buf *);
int physio(int (*)(), struct buf *, dev_t, int,
uint (*)(), struct uio *);
void log(/* int, char *, arg0, ..., argX */);
void printf(/* char *, arg0, ..., argX */);
void bzero(char *, int);
int bcopy(caddr_t, caddr_t, int);
char * readdisklabel(dev_t, int (*)(), struct disklabel *);
int setdisklabel(struct disklabel *, struct disklabel *, uint);
int writedisklabel(dev_t, int (*)(), struct disklabel *);
int get_def_partitionmap(DEVGEOMST *, struct pt_tbl *);
struct buf * getnewbuf();
void brelse(struct buf *bp);
uint minphys(struct buf *bp);
/**************** Initialized and Uninitialized Data **************/
/*
* NOTE: This example does not illustrate SMP locking.
*/
/*
* Bit format for minor number -- specific to this driver.
*
* Bits
* 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
* +-------------------------------------------------------------+
* | device number | |Partition|
* +-------------------------------------------------------------+
*
*/
#define PART_MASK 0x7
#define PART_SHIFT 6
#define GET_PARTITION(dev) (getminor(dev) & PART_MASK)
#define GET_DEVICENUM(dev) (getminor(dev) >> PART_SHIFT)
/*
* The device number may be any handle the driver chooses,
* including a physical location.
*/
#define MAX_XXX_DEVICES 8
/*
* This is the maximum transfer size for the xxx devices.
* If your device has an arbitrary transfer size, it may not need
* to define a maximum transfer size. Instead, it can use the
* system minphys routine to validate maximum transfer sizes.
*/
#define XXX_MAX_XFRLEN (64 * 1024) /* 64K bytes */
/*
* One structure for each xxx device maintains the device state.
*/
typedef struct xxx_device {
struct buf *bufhd;
struct device *device;
io_handle_t dev_handle;
DEVGEOMST geometry;
struct disklabel label;
ulong flags;
U32 raw_part_opens;
U32 blk_part_opens;
U32 label_writeable;
U32 media_changes;
U32 soft_err_cnt;
U32 hard_err_cnt;
} xxx_device_t;
/* xxx_device flags. */
#define READ_ONLY 0x00001
/*
* An array contains the device structures for all xxx devices
*
* Note: This sample driver uses a "dumb" device model for locating
* and addressing devices. It assumes each device is given the
* next sequential instance number. (The sample does not show
* the probe/attach sequence that builds the relationship between
* the sequence number and physical device.) Your driver may be
* much more complex and maintain device data structures in a
* different way, such as by physical address.
*/
xxx_device_t *xxx_devices[MAX_XXX_DEVICES];
/*
* In this example, a "dummy" hardware request packet does the
* actual I/O transaction. A real driver would replace this with
* its own mechanisms for processing I/O requests.
*/
typedef struct hw_req_pkt {
/*
* Original I/O context
*/
xxx_device_t *devp; /* Target device */
struct buf *bp; /* Initial I/O request */
enum { READ, /* I/O request type */
WRITE
} dir;
U32 phys_blknum; /* Starting block number */
U32 phys_blkcnt; /* Number of blocks */
caddr_t buff_addr; /* Buffer pointer */
U32 error; /* Completion status */
int residual; /* Number of bytes not
transferred */
} hw_req_pkt_t;
#define PROBE_SUCCESS TRUE
#define PROBE_FAIL FALSE
/******************************************************************
*
* Name:
* xxx_slave
*
* Function:
* Determines the presence of a device.
*
* Formal Parameters:
* device - Address of a device structure
* bus_io_handle - Address of an io_handle_t structure which
* commonly contains a pointer to the device
* registers
*
* Modified Parameters:
* None
*
* Implicit input:
* None
*
* Implicit output:
* None
*
* Return values:
* PROBE_SUCCESS - Successful completion
* PROBE_FAIL - Device could not be probed
*
* Caller:
* Kernel code that configures the system
*
* Side effects:
* None
*
******************************************************************/
int
xxx_slave(struct device *device, io_handle_t bus_io_handle)
{
xxx_device_t *devp;
int device_number;
/* Validate the device number. */
device_number = device->logunit;
if (device_number >= MAX_XXX_DEVICES)
return(PROBE_FAIL);
/* Has the device been probed? */
if (xxx_devices[device_number] != (xxx_device_t *)NULL)
return(PROBE_SUCCESS);
/* Allocate a device structure. */
MALLOC(devp, xxx_device_t *, sizeof(xxx_device_t), M_DEVBUF,
(M_WAIT | M_ZERO));
xxx_devices[device_number] = devp;
/* Initialize device-specific structure. */
devp->device = device;
devp->dev_handle = bus_io_handle;
/*
* Do whatever is needed to ensure the device is present.
* Return PROBE_FAIL if the device does not exist.
*/
return(PROBE_SUCCESS);
}
/******************************************************************
*
* Name:
* xxx_attach
*
* Function:
* Configures the device into the topology.
*
* Formal parameters:
* device - Address of a device structure
*
* Modified parameters:
* None
*
* Implicit input:
* None
*
* Implicit output:
* None
*
* Return values:
* PROBE_SUCCESS - Successful completion
* PROBE_FAIL - Device could not be attached
*
* Caller:
* Kernel code that configures the system
*
* Side effects:
* None
*
******************************************************************/
xxx_attach(struct device *device)
{
xxx_device_t *devp;
int device_number;
/* Validate the device number. */
if ((device_number = device->logunit) >= MAX_XXX_DEVICES)
return(PROBE_FAIL);
devp = xxx_devices[device_number];
/* Has the device been probed? */
if (devp == (xxx_device_t *)NULL)
return(PROBE_FAIL);
/*
* Do any other device initialization that is needed.
*/
return(PROBE_SUCCESS);
}
/******************************************************************
*
* Name:
* xxx_open
*
* Function:
* Makes the device ready to accept requests from the user.
*
* Formal parameters:
* devp - A dev_t for the target device
* flag - Read, write, or read-write flags
* fmt - Constant that indicates block or character mode
*
* Modified parameters:
* None
*
* Implicit input:
* None
*
* Implicit output:
* None
*
* Return value:
* ESUCCESS - Successful completion
* ENODEV - The minor number does not map to a device
*
* Caller:
* Kernel code through the switch tables
*
* Side effects:
* None
*
******************************************************************/
int
xxx_open(dev_t dev, int flag, int fmt)
{
xxx_device_t *devp;
int partmask;
int device_number;
void read_label(xxx_device_t *devp, dev_t dev);
/* Extract the physical device handle from the minor number. */
device_number = GET_DEVICENUM(dev);
/* Validate that the minor number maps to a device. */
if ((device_number >= MAX_XXX_DEVICES) ||
(xxx_devices[device_number] == (xxx_device_t *)NULL))
/* No such device. */
return(ENODEV);
/* Grab the data structure that maintains the device state. */
devp = xxx_devices[device_number];
/*
* Extract the partition number from the minor number, create
* a mask, and set the bit corresponding to the partition.
*/
partmask = 1 << GET_PARTITION(dev);
/*
* Bring the device into a state in which it can be accessed.
*
* Note: If the following condition is true:
* ((flag & (FNDELAY|FNONBLOCK)))
* Then failures that would normally exit with an error
* status should be ignored as much as possible.
*/
/*
* Query the device for capacity and geometry information and
* save the results in devp->geometry.
*
* Try to fill in the geometry structure as completely as
* possible, but at the very least, set the dev_size and
* sector_size members.
*/
devp->geometry.geom_info.attributes = FILLIN( attributes );
devp->geometry.geom_info.nsectors = FILLIN( sectors per track );
devp->geometry.geom_info.interleave = FILLIN( interleave );
devp->geometry.geom_info.trackskew = FILLIN( track skew );
devp->geometry.geom_info.cylskew = FILLIN( cylinder skew );
devp->geometry.geom_info.ncylinders = FILLIN( # cylinders );
devp->geometry.geom_info.ntracks = FILLIN( # heads );
devp->geometry.geom_info.rpm = FILLIN( rotational speed );
devp->geometry.geom_info.sector_size = FILLIN( block size );
devp->geometry.geom_info.dev_size = FILLIN( total # of blocks );
/* Pick relevant values for your hardware. */
devp->geometry.geom_info.min_trans =
devp->geometry.geom_info.sector_size;
devp->geometry.geom_info.max_trans = XXX_MAX_XFRLEN;
devp->geometry.geom_info.prefer_trans = (16 * 1024); /* 16K */
/*
* Query the device for interesting properties, such as whether
* the media is write protected.
*/
/*
* Read the disk label if this is the first open to the physical
* device (not simply the first open on this partition).
*/
if ((devp->raw_part_opens | devp->blk_part_opens) == 0)
read_label(devp, dev);
/* Indicate that this partition is opened. */
switch (fmt) {
case S_IFCHR:
devp->raw_part_opens |= partmask;
break;
case S_IFBLK:
devp->blk_part_opens |= partmask;
break;
}
/*
* If the device has removable media and an option to
* programatically disable media removal, do so. If this
* fails, however, do not generate an error message.
*/
return(ESUCCESS);
}
/******************************************************************
*
* Name:
* read_label
*
* Function:
* Reads the disk label from the disk into memory.
*
* Formal parameters:
* devp - Address of a device-specific structure
* dev - A dev_t for the target device
*
* Modified parameters:
* The routine initializes the device-specific structure with
* either the disk label from the disk or a default disk label.
*
* Implicit input:
* None
*
* Implicit output:
* None
*
* Return values:
* None
*
* Caller:
* xxx_open on the first open to the physical device
*
* Side effects:
* None
*
******************************************************************/
static void
read_label(xxx_device_t *devp, dev_t dev)
{
struct disklabel *lp = &devp->label;
char *statusmsg;
struct pt_tbl ptable;
dev_t tempdev;
int i;
void xxx_strategy(struct buf *bp);
/*
* Create a "dummy" of the driver's disk label so a service
* routine can read the disk label. The service routine will
* invoke the strategy routine, which must execute against a
* valid disk label. As there is no valid partition map yet,
* the dummy label sets partition 0 to span the entire disk
* for this read operation, and the dev_t passed to the service
* routine and strategy indicates that the I/O should be
* executed against partition 0.
*/
lp->d_magic = 0;
lp->d_secsize = devp->geometry.geom_info.sector_size;
lp->d_secperunit = devp->geometry.geom_info.dev_size;
/*
* Set secpercyl. The service routine uses it to set up the
* buf structure for the strategy call.
*/
lp->d_secpercyl = 1;
lp->d_nsectors = lp->d_secperunit;
/*
* Dummy up one partition that spans the entire disk.
*/
lp->d_npartitions = 1;
lp->d_partitions[0].p_offset = 0;
lp->d_partitions[0].p_size = lp->d_secperunit;
/*
* Create the temporary dev_t for the service routine with the
* same device handle but with the partition number set to 0.
*/
tempdev = makedev(getmajor(dev),(getminor(dev) & ~PART_MASK));
/*
* The strategy routine will check to make sure a valid open
* context exists, so temporarily set the open valid bits for
* partition 0.
*/
devp->raw_part_opens |= 1;
devp->blk_part_opens |= 1;
/* Use the service routine to read the label. */
statusmsg = readdisklabel(tempdev, (int (*)())xxx_strategy, lp);
if (statusmsg != NULL) {
/* FAILURE - No disk label. */
/* Optionally, log an error message to the system. */
log(LOG_ERR, "XXX disk %d: error reading disk label -- %s\n",
GET_DEVICENUM(dev), statusmsg);
/*
* Create a default partition map. The driver may use the
* service routine to derive this or use its own algorithm
* and/or table.
*
* At minimum, the "c" partition (index 2) should span the
* entire disk. Digital recommends that the "a" partition
* (index 0) span the entire disk as well.
*/
/*
* Mark the label invalid. It can be set to valid only if
* the driver reads a valid label.
*/
lp->d_magic = 0;
/*
* Call the generic routine that calculates the default
* partition layout.
*/
if (get_def_partitionmap(&devp->geometry, &ptable) == 0) {
/* SUCCESS */
/* Copy the map to the disk label. */
lp->d_npartitions = 8;
for (i=0; i<8; i++) {
lp->d_partitions[i].p_offset =
ptable.d_partitions[i].p_offset;
lp->d_partitions[i].p_size =
ptable.d_partitions[i].p_size;
}
} else {
/* FAILURE
*
* get_def_partitionmap rarely fails, but if it should,
* make partitions "a" and "c" span the entire disk.
*/
bzero((caddr_t)lp->d_partitions,sizeof(lp->d_partitions));
lp->d_npartitions = 8;
lp->d_partitions[0].p_size = lp->d_partitions[2].p_size =
devp->geometry.geom_info.dev_size;
}
}
/* Clear the open valid bits for partition 0. */
devp->raw_part_opens &= ~1;
devp->blk_part_opens &= ~1;
return;
}
/******************************************************************
*
* Name:
* xxx_close
*
* Formal parameters:
* dev - A dev_t for the target device
* flag - Read, write, or read-write flags
* fmt - Constant that indicates character or block mode
*
* Modified parameters:
* None
*
* Implicit input:
* None
*
* Implicit output:
* None
*
* Return values:
* ESUCCESS - Successful completion
* ENODEV - Device number does not map to a physical device
*
* Caller:
* Kernel code through the switch tables
*
* Side effects:
* None
*
******************************************************************/
int
xxx_close(dev_t dev, int flag, int fmt)
{
xxx_device_t *devp;
int partmask;
int device_number;
/* Extract the physical device handle from the minor number. */
device_number = GET_DEVICENUM(dev);
/* Validate that the minor number maps to a device. */
if ((device_number >= MAX_XXX_DEVICES) ||
(xxx_devices[device_number] == (xxx_device_t *)NULL))
/* No such device. */
return(ENODEV);
/* Grab the data structure that maintains the device state. */
devp = xxx_devices[device_number];
/*
* Extract the partition number from the minor number, create
* a mask, and set the bit corresponding to the partition.
*/
partmask = 1 << GET_PARTITION(dev);
switch (fmt) {
case S_IFCHR:
devp->raw_part_opens &= ~partmask;
break;
case S_IFBLK:
devp->blk_part_opens &= ~partmask;
break;
}
/*
* If this is not the last open on the physical device
* (at least one partition is still open), return.
*/
if ((devp->raw_part_opens | devp->blk_part_opens) != 0)
return(ESUCCESS);
/*
* This is the last open on the physical device.
*/
/*
* Reset the label writeable flag so that whoever now opens
* the device has to go through the process of enabling write
* protection if they want to write the label.
*/
devp->label_writeable = FALSE;
/*
* If the device has removable media and it was programatically
* disabled in the xxx_open routine, do whatever it takes to
* re-enable media removal. If this fails, the driver may
* generate an error message.
*/
return(ESUCCESS);
}
/******************************************************************
*
* Name:
* xxx_size
*
* Function:
* Obtains the size of the partition.
*
* Formal parameters:
* dev - A dev_t for the target device
*
* Modified parameters:
* None
*
* Implicit input:
* None
*
* Implicit output:
* None
*
* Return values:
* Partition size for the device on successful completion
* -1 - Device number does not map to a physical device or a valid
* partition map does not exist for this device
* Caller:
* The kernel through the switch tables.
*
* Side effects:
* None
*
******************************************************************/
daddr_t
xxx_size(dev_t dev)
{
xxx_device_t *devp;
int partition;
int device_number;
/* Extract the physical device handle from the minor number. */
device_number = GET_DEVICENUM(dev);
/* Validate that the minor number maps to a device. */
if ((device_number >= MAX_XXX_DEVICES) ||
(xxx_devices[device_number] == (xxx_device_t *)NULL))
/* No such device. */
return(-1);
/* Grab the data structure that maintains the device state. */
devp = xxx_devices[device_number];
/* Extract the partition number from the minor number. */
partition = GET_PARTITION(dev);
/* Does a valid partition map exist for this index? */
if (partition >= devp->label.d_npartitions)
return(-1);
else
return(devp->label.d_partitions[partition].p_size);
}
/******************************************************************
*
* Name:
* xxx_read
*
* Function:
* Performs character-mode read operations.
*
* Formal parameters:
* dev - A dev_t for the target device
* uio - Address of a uio structure
*
* Modified parameters:
* None
*
* Implicit input:
* None
*
* Implicit output:
* None
*
* Return values:
* ENODEV - Minor number does not map to a physical device
* EBADF - The partition is not open
* EIO - The I/O request is not aligned on a block boundary
*
* Caller:
* Kernel code through the switch tables
*
* Side effects:
* None
*
******************************************************************/
int
xxx_read(dev_t dev, struct uio *uio)
{
struct buf *bp;
xxx_device_t *devp;
int status;
int device_number;
void xxx_strategy(struct buf *bp);
void xxx_minphys(struct buf *bp);
/* Extract the physical device handle from the minor number. */
device_number = GET_DEVICENUM(dev);
/* Validate that the minor number maps to a device. */
if ((device_number >= MAX_XXX_DEVICES) ||
(xxx_devices[device_number] == (xxx_device_t *)NULL))
/* No such device. */
return(ENODEV);
devp = xxx_devices[device_number];
/* Is the partition open? */
if (((devp->raw_part_opens | devp->blk_part_opens) &
(1 << GET_PARTITION(dev))) != 0)
return(EBADF);
/* Validate that the request is aligned on a block boundary. */
if ((uio->uio_offset %
devp->geometry.geom_info.sector_size) != 0)
return(EIO);
/*
* Allocate a buf structure for the request.
*
* Note: The getnewbuf service routine blocks until the request
* can succeed.
*
* Alternative implementation: The driver may allocate a single
* buf structure per device or maintain a pool of buffers.
* You may also use generic kernel allocators to allocate
* buf memory, but getnewbuf guarantees that the buf
* structure is properly initialized.
*
* Note: If getnewbuf fails due to allocation errors, return
* ENOMEM.
*/
bp = getnewbuf();
/*
* Call physio with the driver's strategy routine. Physio
* locks down the buffer pages and sends a buf structure
* through strategy for each I/O vector (iov) in the user I/O
* (uio) structure.
*
* If the device supports a large arbitrary transfer length,
* pass the address of the system minphys routine instead of
* supplying an xxx_minphys routine.
*/
status = physio((int (*)())xxx_strategy, bp, dev, B_READ,
(uint (*)())xxx_minphys, uio);
/*
* Return the buf structure to the buffer pool.
*/
brelse(bp);
return(status);
}
/******************************************************************
*
* Name:
* xxx_write
*
* Function:
* Perfoms character-mode write operations.
*
* Formal parameters:
* dev - A dev_t for the target device
* uio - Address of a uio structure
*
* Modified parameters:
* None
*
* Implicit input:
* None
*
* Implicit output:
* None
*
* Return values:
* ENODEV - The minor number does not map to a physical device
* EBADF - The partition is not open
* EIO - The write request is not aligned on a block boundary
*
* Caller:
* Kernel code through the switch tables
*
* Size effects:
* None
*
******************************************************************/
int
xxx_write(dev_t dev, struct uio *uio)
{
struct buf *bp;
xxx_device_t *devp;
int status;
int device_number;
void xxx_strategy(struct buf *bp);
void xxx_minphys(struct buf *bp);
/* Extract the physical device handle from the minor number. */
device_number = GET_DEVICENUM(dev);
/* Validate that the minor number maps to a device. */
if ((device_number >= MAX_XXX_DEVICES) ||
(xxx_devices[device_number] == (xxx_device_t *)NULL))
/* No such device. */
return(ENODEV);
devp = xxx_devices[device_number];
/* Is this partition open? */
if (((devp->raw_part_opens | devp->blk_part_opens) &
(1 << GET_PARTITION(dev))) != 0)
return(EBADF);
/* Validate that the request is aligned on a block boundary. */
if ((uio->uio_offset %
devp->geometry.geom_info.sector_size) != 0)
return(EIO);
/*
* Allocate a buf structure for the I/O request.
*
* Note: The getnewbuf service routine blocks until the request
* can succeed.
*
* Alternative implementation: The driver may allocate a single
* buf structure per device or maintain a pool of buffers.
* You may also use generic kernel allocators to allocate
* buf memory, but getnewbuf guarantees that the buf
* structure is properly initialized.
*
* Note: If getnewbuf fails due to allocation errors, return
* ENOMEM.
*/
bp = getnewbuf();
/*
* Call physio with the driver's strategy routine. Physio locks
* down buffer pages and sends a buf structure through strategy
* for each I/O vector (iov) in the user I/O (uio) structure.
*
* If the device supports a large arbitrary transfer length,
* pass the address of the system minphys routine instead of
* supplying an xxx_minphys routine.
*/
status = physio((int (*)())xxx_strategy, bp, dev, B_WRITE,
(uint (*)())xxx_minphys, uio);
/*
* Return the buf structure to the buffer pool.
*/
brelse(bp);
return(status);
}
/******************************************************************
*
* Name:
* xxx_minphys
*
* Function:
* Passed to physio by the read/write routines to make sure the
* resulting I/O fits within the maximum transfer length for the
* device. If not, the routine adjusts the byte count so that it
* is within the allowable transfer length.
*
* Formal parameters:
* bp - Address of a buf structure
*
* Modified parameters:
* If the length of the data exceeds the maximum transfer length,
* the routines sets the b_count member of the buf structure to
* the device's maximum transfer length.
*
* Implicit input:
* None
*
* Implicit output:
* None
*
* Return value:
* None
*
* Caller:
* The physio routine on return from the driver's strategy routine
*
* Side effects:
* None
*
******************************************************************/
void
xxx_minphys(struct buf *bp)
{
if (bp->b_bcount > XXX_MAX_XFRLEN)
bp->b_bcount = XXX_MAX_XFRLEN;
return;
}
/******************************************************************
*
* Name:
* xxx_strategy
*
* Function:
* Performs all block-oriented read and write operations.
*
* Formal parameters:
* bp - Address of a buf structure
*
* Modified parameters:
* When an error occurs, the routine initializes members of the
* buf structure as follows:
*
* b_resid - Number of bytes not transferred
* b_flags - B_ERROR
* b_error - Error status value
*
* Implicit input:
* None
*
* Implicit output:
* None
*
* Return value:
* None
*
* Caller:
* The physio routine for character I/O operations
* The kernel through the switch tables for block I/O operations
*
* Side effects:
* None
*
******************************************************************/
void
xxx_strategy(struct buf *bp)
{
xxx_device_t *devp;
struct partition *pp;
hw_req_pkt_t *hw_pkt;
int partition;
int device_number;
U32 nblocks;
U32 start_blk;
/*
* Validate that the I/O request is properly block aligned.
*
* Alternatively, the driver could use a read-modify-write
* sequence to transfer data of any length. The driver writer
* must decide if the complexity of this task is worth the
* effort.
*/
if ((bp->b_bcount %
devp->geometry.geom_info.sector_size) != 0) {
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
bp->b_error = EIO;
(void)biodone(bp);
return;
}
/* Extract the physical device handle from the minor number. */
device_number = GET_DEVICENUM(bp->b_dev);
/* Validate that the minor number maps to a device. */
if ((device_number >= MAX_XXX_DEVICES) ||
(xxx_devices[device_number] == (xxx_device_t *)NULL)) {
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
bp->b_error = ENODEV;
(void)biodone(bp);
return;
}
/* Grab the data structure that maintains the device state. */
devp = xxx_devices[device_number];
/* Extract the partition number from the minor number. */
partition = GET_PARTITION(bp->b_dev);
/* Validate the partition. */
if (partition >= devp->label.d_npartitions) {
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
bp->b_error = ENXIO;
(void)biodone(bp);
return;
}
pp = &devp->label.d_partitions[partition];
/* Does the partition have a valid length? */
if (pp->p_size == 0) {
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
bp->b_error = EROFS;
(void)biodone(bp);
return;
}
/* Is the partition open? */
if (((devp->raw_part_opens | devp->blk_part_opens) &
(1 << partition)) != 0) {
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
bp->b_error = EBADF;
(void)biodone(bp);
return;
}
/*
* Is this is a write request for a read-only device?
*/
if ((devp->flags & READ_ONLY) && ((bp->b_flags & B_READ) == 0)) {
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
bp->b_error = EROFS;
(void)biodone(bp);
return;
}
/*
* Perform range checking on the location/length of the I/O
* request.
*/
/* How many blocks does the I/O cross? */
nblocks = (bp->b_bcount +
(devp->geometry.geom_info.sector_size - 1)) /
devp->geometry.geom_info.sector_size;
/* Calculate the physical block where the I/O starts. */
start_blk = bp->b_blkno + pp->p_offset;
/*
* Will the request write over the disk label?
*
* The label can be overwritten if it is a default label (one
* that was not read from the disk), or if the driver's
* label_writeable flag has been set to TRUE by an ioctl
* command.
*/
if ((start_blk <= LABELSECTOR) && /* it's the label area */
((bp->b_flags & B_READ) == 0) && /* it's a write */
(devp->label.d_magic == DISKMAGIC) && /* default label */
(devp->label_writeable == FALSE)) { /* not write-enabled */
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
bp->b_error = EROFS;
(void)biodone(bp);
return;
}
/*
* Validate that the physical block number is within range and
* that the partition is not off the end of the disk. If so, the
* disk label is invalid.
*/
if ((bp->b_blkno < 0) || (bp->b_blkno >= pp->p_size) ||
(pp->p_offset >= devp->geometry.geom_info.dev_size) ) {
/*
* POSIX says do not return an error for reads, but do for
* writes.
*/
if ((bp->b_flags & B_READ) == 0) {
bp->b_flags |= B_ERROR;
bp->b_error = ENOSPC;
}
bp->b_resid = bp->b_bcount;
(void)biodone(bp);
return;
}
/* Transfer up to the end of the partition. */
if ((bp->b_blkno + nblocks) > pp->p_size ) {
/* Save the original count in case of failure. */
bp->b_resid = bp->b_bcount;
/* Replace the count with the number of blocks remaining. */
bp->b_bcount = (pp->p_size - bp->b_blkno) *
devp->geometry.geom_info.sector_size;
} else {
/* Initialize the current residual count. */
bp->b_resid = 0;
}
/* Validation is now complete. */
/*
* For illustration purposes, the sample driver creates a dummy
* hardware packet for the I/O request.
*/
MALLOC(hw_pkt, hw_req_pkt_t *, sizeof(hw_req_pkt_t), M_DEVBUF,
(M_NOWAIT | M_ZERO));
if (hw_pkt == (hw_req_pkt_t *)NULL) {
/*
* A production driver should deal with this error and queue
* the I/O until resources are available. Keep in mind that
* you cannot block in a strategy routine.
*/
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
bp->b_error = ENOMEM;
(void)biodone(bp);
return;
}
/* The actual request: */
hw_pkt->devp = devp;
hw_pkt->bp = bp;
hw_pkt->dir = (bp->b_flags & B_READ) ? READ : WRITE;
hw_pkt->phys_blknum = start_blk;
hw_pkt->phys_blkcnt = ((U32)bp->b_bcount /
devp->geometry.geom_info.sector_size);
hw_pkt->buff_addr = bp->b_un.b_addr;
/* Kick off the I/O request on the hardware. */
return;
}
/******************************************************************
*
* Name:
* xxx_hw-pkt_complete
*
* Function:
* This routine handles the completion of a hardware packet.
* It would typically be called from the device driver's
* interrupt routine.
*
* The routine finishes up all completion for the original I/O
* request. This example will not concern itself with retries
* and the like.
*
* Formal parameters:
* hw_pkt - Address of the data packet to be sent to the device
*
* Modified parameters:
* The routine sets the b_resid member of the buf structure to the
* number of bytes that were not transferred, or 0 if all were
* transferred.
*
* Implicit input:
* None
*
* Implicit output:
* None
*
* Return value:
* None
*
* Caller:
* Driver's interrupt routine
*
* Side effects:
* None
*
******************************************************************/
static void
xxx_hw_pkt_complete(hw_req_pkt_t *hw_pkt)
{
xxx_device_t *devp;
struct buf *bp;
int trunc_resid;
/* Restore the I/O context. */
devp = hw_pkt->devp;
bp = hw_pkt->bp;
/*
* If the transfer length was greater than the maximum allowed,
* find out how many bytes were not transferred and restore the
* original byte count.
*/
if (bp->b_resid) {
/* How much was chopped off by the end of the partition? */
trunc_resid = bp->b_resid - bp->b_bcount;
/* Restore the original transfer count. */
bp->b_bcount = bp->b_resid;
} else {
trunc_resid = 0;
}
/* It's all or nothing for disks. */
bp->b_resid = trunc_resid + hw_pkt->residual;
/* Was there an error? */
if (hw_pkt->error) {
bp->b_flags |= B_ERROR;
bp->b_error = EIO;
}
biodone(bp);
/* Return the hardware request packet to the pool. */
FREE(hw_pkt, M_DEVBUF);
return;
}
/******************************************************************
*
* Name:
* xxx_ioctl
*
* Function:
* Handles all nonread and nonwrite I/O requests.
*
* Formal parameters:
* dev - A dev_t for the target device
* cmd - The ioctl command
* data - Address of a user data buffer
* flag - Flags from the file handle
*
* Modified parameters:
* Some ioctl commands pass data back to the caller through the
* data buffer.
*
* Implicit input:
* None
*
* Implicit output:
* None
*
* Return value:
* ENODEV - Minor number does not map to a physical device
* EBADF - The partition was not open
* EINVAL - Disk label is invalid
* EBADF - Command needs an open context, buf can't find one
* EACCESS - User doesn't have the necessary priviledges
*
* Caller:
* Kernel code through the switch tables
*
* Side effects:
* None
*
******************************************************************/
int
xxx_ioctl(dev_t dev, U32 cmd, caddr_t data, int flag)
{
xxx_device_t *devp;
struct disklabel *lp;
int retval = ESUCCESS;
struct partition *pp;
int partition;
int device_number;
U32 current_opens;
/* Extract the physical device handle from the minor number. */
device_number = GET_DEVICENUM(dev);
/* Validate that the minor number maps to a device. */
if ((device_number >= MAX_XXX_DEVICES) ||
(xxx_devices[device_number] == (xxx_device_t *)NULL))
/* No such device. */
return(ENODEV);
/* Grab the data structure that maintains the device state. */
devp = xxx_devices[device_number];
lp = &devp->label;
current_opens = (devp->raw_part_opens | devp->blk_part_opens);
/* Extract the partition number from the minor number. */
partition = GET_PARTITION(dev);
pp = &lp->d_partitions[partition];
/* Is the partition open? */
if (((devp->raw_part_opens | devp->blk_part_opens) &
(1 << GET_PARTITION(dev))) != 0) {
/*
* There are only 3 ioctls that do not require a valid open
* context - DEVGETINFO, DEVIOCGET, and DEVROOT.
*/
if ((cmd != DEVGETINFO) &&
(cmd != DEVIOCGET))
return(EBADF);
}
switch (cmd) {
/*
* Return a pointer to a disklabel structure.
*/
case DIOCGDINFO:
{
/* If the disk label is invalid, don't return success. */
if (lp->d_magic != DISKMAGIC)
return(EINVAL);
/* Copy the disk label. */
*(struct disklabel *)data = *lp;
break;
}
/*
* Return information about a partition.
*
* Note: This is a kernel-level routine, so passing pointers
* is okay.
*/
case DIOCGPART:
{
/* If the disk label is invalid, don't return success. */
if ((lp->d_magic != DISKMAGIC) ||
(partition >= lp->d_npartitions))
return(EINVAL);
/* Assign the disk label and partition to the data buffer */
((struct partinfo *)data)->disklab = lp;
((struct partinfo *)data)->part = pp;
break;
}
/*
* Set a disklabel structure.
*/
case DIOCSDINFO:
{
if ((flag & FWRITE) == 0)
return(EBADF);
/*
* Call a service routine to validate label changes and
* update the in-memory copy of the disk label.
*/
if (lp->d_magic == DISKMAGIC) {
/* Label is valid, so pass a valid partition mask. */
retval = setdisklabel(lp, (struct disklabel *)data,
current_opens);
} else {
/*
* When using a default label, don't worry about open
* partitions.
*/
retval = setdisklabel(lp, (struct disklabel *)data, 0);
}
break;
}
/*
* Set a write-enable label.
*/
case DIOCWLABEL:
{
if ((flag & FWRITE) == 0)
return(EBADF);
/*
* Assign the value in the data buffer to the label
* writeable flag.
*/
devp->label_writeable = *(int *)data;
break;
}
/*
* Write a disklabel.
*/
case DIOCWDINFO:
{
struct disklabel *new_lp = (struct disklabel *)data;
U32 current_label_wrtbl;
if ((flag & FWRITE) == 0)
return(EBADF);
/*
* If the partition in the new label that maps to this dev_t
* does not start at block 0, the driver may not be able to
* write the label to disk.
*/
if ((partition >= lp->d_npartitions) ||
(new_lp->d_partitions[partition].p_offset != 0))
return(EINVAL);
/*
* Call a service routine to validate label changes and to
* update the in-memory version of the label.
*/
if (lp->d_magic == DISKMAGIC) {
/* Label is valid, so pass an open partition mask. */
retval = setdisklabel(lp, new_lp, current_opens);
} else {
/*
* Using a default label, so don't worry about open
* partitions.
*/
retval = setdisklabel(lp, new_lp, 0);
}
if (retval != ESUCCESS)
/* FAILURE - break out of switch statement. */
break;
/*
* Patch the label writeable flag so the driver can write
* the label.
*/
current_label_wrtbl = devp->label_writeable;
devp->label_writeable = TRUE;
/*
* Use the service routine to write the disk label via our
* strategy routine.
*
* Note: If the partition does not start at block 0, the write
* will succeed but will not replace the old disk label.
* It will be written to the wrong place on the disk.
*/
retval = writedisklabel(dev, (int (*)())xxx_strategy, lp);
/* Restore the label writeable flag. */
devp->label_writeable = current_label_wrtbl;
break;
}
/*
* Create a default partition map.
*/
case DIOCGDEFPT:
{
struct pt_tbl *ptable = (struct pt_tbl *)data;
/*
* Call the service routine to create a default partition
* map.
*/
if (get_def_partitionmap(&devp->geometry, ptable) != 0) {
/*
* If get_def_partitionmap fails, create "a" and "c"
* partitions that span the entire disk.
*/
bzero((caddr_t)ptable, sizeof(struct pt_tbl));
ptable->d_partitions[0].p_size =
ptable->d_partitions[2].p_size =
devp->geometry.geom_info.dev_size;
}
break;
}
/*
* Return the current partition map.
*/
case DIOCGCURPT:
{
/* Assign the current partition map to the data buffer. */
*(struct pt_tbl *)data = *(struct pt_tbl *)lp->d_partitions;
break;
}
/*
* Return device geometry.
*/
case DEVGETGEOM:
{
/* Check that the disk geometry is known. */
if ((devp->geometry.geom_info.ntracks == 0) ||
(devp->geometry.geom_info.nsectors == 0) ||
(devp->geometry.geom_info.ncylinders == 0))
return(EIO);
/*
* If the device can dynamically change its geometry, query
* the device for the latest status. This example copies
* the geometry values that were set up on open.
*/
*(DEVGEOMST *)data = *(&devp->geometry);
break;
}
/*
* Return device status
*
* Note: This ioctl must be able to operate without having a
* previous open on the device as it is used by the
* kernel at startup.
*/
case DEVGETINFO:
{
struct device *device;
v1_device_info_t *devi_p;
v1_bustype_info_t *busp;
v1_disk_dev_info_t *diskp;
device = devp->device;
devi_p = (v1_device_info_t *)data;
bzero((caddr_t)devi_p,sizeof(*devi_p));
/****************************************************
* Fill in generic information.
****************************************************/
devi_p->version = VERSION_1;
devi_p->category = DEV_DISK;
devi_p->bus = FILLIN( whatever is appropriate );
bcopy("XXX", devi_p->interface, 3);
bcopy("xxxdev", devi_p->device, 6);
bcopy("xx", devi_p->dev_name, 2);
devi_p->soft_count = devp->soft_err_cnt;
devi_p->hard_count = devp->hard_err_cnt;
/****************************************************
* Fill in (topology) bus-generic information.
****************************************************/
busp = &devi_p->businfo;
if (device != (struct device *)NULL) {
/*
* This is the bus slot (e.g., pci slot) of the adaptor.
*/
busp->nexus_num = device->ctlr_hd->slot;
/*
* This is the instance of the device relative to the
* adaptor. For example, an adaptor may have two
* controller chips. Chip A would be instance 0, and
* chip B instance 1. This example assumes only one
* instance on the adaptor.
*/
busp->adpt_num = 0;
/*
* This is the logical bus number that the adaptor is
* plugged into.
*/
busp->bus_num = device->ctlr_hd->bus_hd->bus_num;
/*
* This is the system-wide instance number of the adaptor.
* For example, 2 adaptors with 2 controller chips would
* result in instance 0, instance 1, instance 2, instance
* 3, which correspond to adaptor 0 chip A, adaptor 0
* chip B, adaptor 1 chip A, adaptor 1 chip B.
*/
busp->ctlr_num = device->ctlr_num;
/*
* This is the remote id for the adaptor. Except for
* specialized hardware, this will almost always be 0.
*/
busp->rctlr_num = device->ctlr_hd->rctlr;
/*
* This is the device instance relative to the controlling
* hardware. For example, if there were 2 disk devices
* off of chip A, one would be slave 0, the other slave 1.
* This example assumes only 1 slave.
*/
busp->slave_num = 0;
/*
* This is the system-wide device instance number.
*/
busp->unit_num = device_number;
} else {
busp->nexus_num = -1;
busp->adpt_num = -1;
busp->bus_num = -1;
busp->ctlr_num = -1;
busp->rctlr_num = 0;
busp->slave_num = 0;
busp->unit_num = device_number;
}
/****************************************************
* Fill in bus-specific information.
****************************************************/
/* Fill in whatever is relevant for your hardware. */
/****************************************************
* Fill in category-specific information.
****************************************************/
diskp = (v1_disk_dev_info_t *)&devi_p->devinfo;
diskp->class = DKDEV_CLS_HARDDISK;
diskp->part_num = partition;
diskp->blocksz = devp->geometry.geom_info.sector_size;
diskp->capacity = devp->geometry.geom_info.dev_size;
/*
* Fill in fields appropriate for your device. Be aware
* of attributes for online/offline, write protect,
* removable media, media changes, and so on. Also set
* subclasses, such as cdrom or floppy, if applicable.
*/
/*****************************************************
* Fill in category-specific architecture information.
*****************************************************/
/* Fill in whatever is relevant for your hardware. */
break;
}
/*
* Return device status.
*
* This ioctl has been replaced by the DEVGETINFO ioctl, but
* you may want to implement it for backward compatibility.
*/
case DEVIOCGET:
{
struct device *device;
struct devget *devget;
device = devp->device;
devget = (struct devget *)data;
bzero((caddr_t)devget,sizeof(struct devget));
devget->category = DEV_DISK;
devget->bus = FILLIN( whatever is appropriate );
bcopy("XXX", devget->interface, 3);
bcopy("xxxdev", devget->device, 6);
bcopy("xx", devget->dev_name, 2);
devget->soft_count = devp->soft_err_cnt;
devget->hard_count = devp->hard_err_cnt;
if (device != (struct device *)NULL) {
/*
* This is the bus slot (e.g., pci slot) for the adaptor.
*/
devget->nexus_num = device->ctlr_hd->slot;
/*
* This is the instance of the device relative to the
* adaptor. For example, an adaptor may have two
* controller chips. Chip A would be instance 0, and
* chip B instance 1. This example assumes only one
* instance on this adaptor.
*/
devget->adpt_num = 0;
/*
* This is the logical bus number that the adaptor is
* plugged into.
*/
devget->bus_num = device->ctlr_hd->bus_hd->bus_num;
/*
* This is the system-wide instance number of the adaptor.
* For example, 2 adaptors with 2 controller chips would
* result in instance 0, instance 1, instance 2, instance
* 3, which correspond to adaptor 0 chip A, adaptor 0
* chip B, adaptor 1 chip A, adaptor 1 chip B.
*/
devget->ctlr_num = device->ctlr_num;
/*
* This is the remote id for the adaptor. Except for
* specialized hardware, this will almost always be 0.
*/
devget->rctlr_num = device->ctlr_hd->rctlr;
/*
* This is the device instance relative to the controlling
* hardware. For example, if there were 2 disk devices
* off of chip A, one would be slave 0, the other slave 1.
* This example assumes only 1 slave.
*/
devget->slave_num = 0;
/*
* This is the system-wide device instance number.
*/
devget->unit_num = device_number;
} else {
devget->nexus_num = -1;
devget->adpt_num = -1;
devget->bus_num = -1;
devget->ctlr_num = -1;
devget->rctlr_num = 0;
devget->slave_num = 0;
devget->unit_num = device_number;
}
devget->category_stat = partition;
/*
* Fill in fields appropriate for your device. Be aware
* of attributes for online/offline, write protect,
* removable media, media changes, and so on. Also set
* subclasses, such as cdrom or floppy, if applicable.
*
* If reporting media change count, do so as follows:
* devget->category_stat |= DEV_MC_COUNT;
* devget->category_stat |=
* (devp->media_changes << 16);
*
*/
break;
}
default:
retval = EINVAL;
break;
} /* switch(cmd) */
return(retval);