cregit-Linux how code gets into the kernel

Release 4.11 drivers/scsi/megaraid.c

Directory: drivers/scsi
/*
 *
 *                      Linux MegaRAID device driver
 *
 * Copyright (c) 2002  LSI Logic Corporation.
 *
 *         This program is free software; you can redistribute it and/or
 *         modify it under the terms of the GNU General Public License
 *         as published by the Free Software Foundation; either version
 *         2 of the License, or (at your option) any later version.
 *
 * Copyright (c) 2002  Red Hat, Inc. All rights reserved.
 *        - fixes
 *        - speed-ups (list handling fixes, issued_list, optimizations.)
 *        - lots of cleanups.
 *
 * Copyright (c) 2003  Christoph Hellwig  <hch@lst.de>
 *        - new-style, hotplug-aware pci probing and scsi registration
 *
 * Version : v2.00.4 Mon Nov 14 14:02:43 EST 2005 - Seokmann Ju
 *                                              <Seokmann.Ju@lsil.com>
 *
 * Description: Linux device driver for LSI Logic MegaRAID controller
 *
 * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 471, 490, 493
 *                                      518, 520, 531, 532
 *
 * This driver is supported by LSI Logic, with assistance from Red Hat, Dell,
 * and others. Please send updates to the mailing list
 * linux-scsi@vger.kernel.org .
 *
 */

#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/reboot.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <scsi/scsicam.h>

#include "scsi.h"
#include <scsi/scsi_host.h>

#include "megaraid.h"


#define MEGARAID_MODULE_VERSION "2.00.4"

MODULE_AUTHOR ("sju@lsil.com");
MODULE_DESCRIPTION ("LSI Logic MegaRAID legacy driver");
MODULE_LICENSE ("GPL");

MODULE_VERSION(MEGARAID_MODULE_VERSION);

static DEFINE_MUTEX(megadev_mutex);

static unsigned int max_cmd_per_lun = DEF_CMD_PER_LUN;
module_param(max_cmd_per_lun, uint, 0);
MODULE_PARM_DESC(max_cmd_per_lun, "Maximum number of commands which can be issued to a single LUN (default=DEF_CMD_PER_LUN=63)");


static unsigned short int max_sectors_per_io = MAX_SECTORS_PER_IO;
module_param(max_sectors_per_io, ushort, 0);
MODULE_PARM_DESC(max_sectors_per_io, "Maximum number of sectors per I/O request (default=MAX_SECTORS_PER_IO=128)");



static unsigned short int max_mbox_busy_wait = MBOX_BUSY_WAIT;
module_param(max_mbox_busy_wait, ushort, 0);
MODULE_PARM_DESC(max_mbox_busy_wait, "Maximum wait for mailbox in microseconds if busy (default=MBOX_BUSY_WAIT=10)");


#define RDINDOOR(adapter)	readl((adapter)->mmio_base + 0x20)

#define RDOUTDOOR(adapter)	readl((adapter)->mmio_base + 0x2C)

#define WRINDOOR(adapter,value)	 writel(value, (adapter)->mmio_base + 0x20)

#define WROUTDOOR(adapter,value) writel(value, (adapter)->mmio_base + 0x2C)

/*
 * Global variables
 */


static int hba_count;

static adapter_t *hba_soft_state[MAX_CONTROLLERS];

static struct proc_dir_entry *mega_proc_dir_entry;

/* For controller re-ordering */

static struct mega_hbas mega_hbas[MAX_CONTROLLERS];

static long
megadev_unlocked_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);

/*
 * The File Operations structure for the serial/ioctl interface of the driver
 */

static const struct file_operations megadev_fops = {
	.owner		= THIS_MODULE,
	.unlocked_ioctl	= megadev_unlocked_ioctl,
	.open		= megadev_open,
	.llseek		= noop_llseek,
};

/*
 * Array to structures for storing the information about the controllers. This
 * information is sent to the user level applications, when they do an ioctl
 * for this information.
 */

static struct mcontroller mcontroller[MAX_CONTROLLERS];

/* The current driver version */

static u32 driver_ver = 0x02000000;

/* major number used by the device for character interface */

static int major;


#define IS_RAID_CH(hba, ch)	(((hba)->mega_ch_class >> (ch)) & 0x01)


/*
 * Debug variable to print some diagnostic messages
 */

static int trace_level;

/**
 * mega_setup_mailbox()
 * @adapter - pointer to our soft state
 *
 * Allocates a 8 byte aligned memory for the handshake mailbox.
 */

static int mega_setup_mailbox(adapter_t *adapter) { unsigned long align; adapter->una_mbox64 = pci_alloc_consistent(adapter->dev, sizeof(mbox64_t), &adapter->una_mbox64_dma); if( !adapter->una_mbox64 ) return -1; adapter->mbox = &adapter->una_mbox64->mbox; adapter->mbox = (mbox_t *)((((unsigned long) adapter->mbox) + 15) & (~0UL ^ 0xFUL)); adapter->mbox64 = (mbox64_t *)(((unsigned long)adapter->mbox) - 8); align = ((void *)adapter->mbox) - ((void *)&adapter->una_mbox64->mbox); adapter->mbox_dma = adapter->una_mbox64_dma + 8 + align; /* * Register the mailbox if the controller is an io-mapped controller */ if( adapter->flag & BOARD_IOMAP ) { outb(adapter->mbox_dma & 0xFF, adapter->host->io_port + MBOX_PORT0); outb((adapter->mbox_dma >> 8) & 0xFF, adapter->host->io_port + MBOX_PORT1); outb((adapter->mbox_dma >> 16) & 0xFF, adapter->host->io_port + MBOX_PORT2); outb((adapter->mbox_dma >> 24) & 0xFF, adapter->host->io_port + MBOX_PORT3); outb(ENABLE_MBOX_BYTE, adapter->host->io_port + ENABLE_MBOX_REGION); irq_ack(adapter); irq_enable(adapter); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig22083.33%120.00%
Atul Mukker3212.12%120.00%
Linus Torvalds62.27%120.00%
Alan Cox51.89%120.00%
Linus Torvalds (pre-git)10.38%120.00%
Total264100.00%5100.00%

/* * mega_query_adapter() * @adapter - pointer to our soft state * * Issue the adapter inquiry commands to the controller and find out * information and parameter about the devices attached */
static int mega_query_adapter(adapter_t *adapter) { dma_addr_t prod_info_dma_handle; mega_inquiry3 *inquiry3; u8 raw_mbox[sizeof(struct mbox_out)]; mbox_t *mbox; int retval; /* Initialize adapter inquiry mailbox */ mbox = (mbox_t *)raw_mbox; memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE); memset(&mbox->m_out, 0, sizeof(raw_mbox)); /* * Try to issue Inquiry3 command * if not succeeded, then issue MEGA_MBOXCMD_ADAPTERINQ command and * update enquiry3 structure */ mbox->m_out.xferaddr = (u32)adapter->buf_dma_handle; inquiry3 = (mega_inquiry3 *)adapter->mega_buffer; raw_mbox[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ raw_mbox[2] = NC_SUBOP_ENQUIRY3; /* i.e. 0x0F */ raw_mbox[3] = ENQ3_GET_SOLICITED_FULL; /* i.e. 0x02 */ /* Issue a blocking command to the card */ if ((retval = issue_scb_block(adapter, raw_mbox))) { /* the adapter does not support 40ld */ mraid_ext_inquiry *ext_inq; mraid_inquiry *inq; dma_addr_t dma_handle; ext_inq = pci_alloc_consistent(adapter->dev, sizeof(mraid_ext_inquiry), &dma_handle); if( ext_inq == NULL ) return -1; inq = &ext_inq->raid_inq; mbox->m_out.xferaddr = (u32)dma_handle; /*issue old 0x04 command to adapter */ mbox->m_out.cmd = MEGA_MBOXCMD_ADPEXTINQ; issue_scb_block(adapter, raw_mbox); /* * update Enquiry3 and ProductInfo structures with * mraid_inquiry structure */ mega_8_to_40ld(inq, inquiry3, (mega_product_info *)&adapter->product_info); pci_free_consistent(adapter->dev, sizeof(mraid_ext_inquiry), ext_inq, dma_handle); } else { /*adapter supports 40ld */ adapter->flag |= BOARD_40LD; /* * get product_info, which is static information and will be * unchanged */ prod_info_dma_handle = pci_map_single(adapter->dev, (void *) &adapter->product_info, sizeof(mega_product_info), PCI_DMA_FROMDEVICE); mbox->m_out.xferaddr = prod_info_dma_handle; raw_mbox[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ raw_mbox[2] = NC_SUBOP_PRODUCT_INFO; /* i.e. 0x0E */ if ((retval = issue_scb_block(adapter, raw_mbox))) dev_warn(&adapter->dev->dev, "Product_info cmd failed with error: %d\n", retval); pci_unmap_single(adapter->dev, prod_info_dma_handle, sizeof(mega_product_info), PCI_DMA_FROMDEVICE); } /* * kernel scans the channels from 0 to <= max_channel */ adapter->host->max_channel = adapter->product_info.nchannels + NVIRT_CHAN -1; adapter->host->max_id = 16; /* max targets per channel */ adapter->host->max_lun = 7; /* Up to 7 luns for non disk devices */ adapter->host->cmd_per_lun = max_cmd_per_lun; adapter->numldrv = inquiry3->num_ldrv; adapter->max_cmds = adapter->product_info.max_commands; if(adapter->max_cmds > MAX_COMMANDS) adapter->max_cmds = MAX_COMMANDS; adapter->host->can_queue = adapter->max_cmds - 1; /* * Get the maximum number of scatter-gather elements supported by this * firmware */ mega_get_max_sgl(adapter); adapter->host->sg_tablesize = adapter->sglen; /* use HP firmware and bios version encoding Note: fw_version[0|1] and bios_version[0|1] were originally shifted right 8 bits making them zero. This 0 value was hardcoded to fix sparse warnings. */ if (adapter->product_info.subsysvid == PCI_VENDOR_ID_HP) { sprintf (adapter->fw_version, "%c%d%d.%d%d", adapter->product_info.fw_version[2], 0, adapter->product_info.fw_version[1] & 0x0f, 0, adapter->product_info.fw_version[0] & 0x0f); sprintf (adapter->bios_version, "%c%d%d.%d%d", adapter->product_info.bios_version[2], 0, adapter->product_info.bios_version[1] & 0x0f, 0, adapter->product_info.bios_version[0] & 0x0f); } else { memcpy(adapter->fw_version, (char *)adapter->product_info.fw_version, 4); adapter->fw_version[4] = 0; memcpy(adapter->bios_version, (char *)adapter->product_info.bios_version, 4); adapter->bios_version[4] = 0; } dev_notice(&adapter->dev->dev, "[%s:%s] detected %d logical drives\n", adapter->fw_version, adapter->bios_version, adapter->numldrv); /* * Do we support extended (>10 bytes) cdbs */ adapter->support_ext_cdb = mega_support_ext_cdb(adapter); if (adapter->support_ext_cdb) dev_notice(&adapter->dev->dev, "supports extended CDBs\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig42063.25%111.11%
Atul Mukker11617.47%111.11%
Linus Torvalds9514.31%222.22%
Björn Helgaas274.07%111.11%
Adam Radford30.45%111.11%
Jon Mason10.15%111.11%
Jason Holmes10.15%111.11%
Lucas De Marchi10.15%111.11%
Total664100.00%9100.00%

/** * mega_runpendq() * @adapter - pointer to our soft state * * Runs through the list of pending requests. */
static inline void mega_runpendq(adapter_t *adapter) { if(!list_empty(&adapter->pending_list)) __mega_runpendq(adapter); }

Contributors

PersonTokensPropCommitsCommitProp
Al Viro27100.00%1100.00%
Total27100.00%1100.00%

/* * megaraid_queue() * @scmd - Issue this scsi command * @done - the callback hook into the scsi mid-layer * * The command queuing entry point for the mid-layer. */
static int megaraid_queue_lck(Scsi_Cmnd *scmd, void (*done)(Scsi_Cmnd *)) { adapter_t *adapter; scb_t *scb; int busy=0; unsigned long flags; adapter = (adapter_t *)scmd->device->host->hostdata; scmd->scsi_done = done; /* * Allocate and build a SCB request * busy flag will be set if mega_build_cmd() command could not * allocate scb. We will return non-zero status in that case. * NOTE: scb can be null even though certain commands completed * successfully, e.g., MODE_SENSE and TEST_UNIT_READY, we would * return 0 in that case. */ spin_lock_irqsave(&adapter->lock, flags); scb = mega_build_cmd(adapter, scmd, &busy); if (!scb) goto out; scb->state |= SCB_PENDQ; list_add_tail(&scb->list, &adapter->pending_list); /* * Check if the HBA is in quiescent state, e.g., during a * delete logical drive opertion. If it is, don't run * the pending_list. */ if (atomic_read(&adapter->quiescent) == 0) mega_runpendq(adapter); busy = 0; out: spin_unlock_irqrestore(&adapter->lock, flags); return busy; }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig10975.69%342.86%
Atul Mukker1711.81%114.29%
Linus Torvalds1711.81%228.57%
Jeff Garzik10.69%114.29%
Total144100.00%7100.00%

static DEF_SCSI_QCMD(megaraid_queue) /** * mega_allocate_scb() * @adapter - pointer to our soft state * @cmd - scsi command from the mid-layer * * Allocate a SCB structure. This is the central structure for controller * commands. */ static inline scb_t * mega_allocate_scb(adapter_t *adapter, Scsi_Cmnd *cmd) { struct list_head *head = &adapter->free_list; scb_t *scb; /* Unlink command from Free List */ if( !list_empty(head) ) { scb = list_entry(head->next, scb_t, list); list_del_init(head->next); scb->state = SCB_ACTIVE; scb->cmd = cmd; scb->dma_type = MEGA_DMA_TYPE_NONE; return scb; } return NULL; } /** * mega_get_ldrv_num() * @adapter - pointer to our soft state * @cmd - scsi mid layer command * @channel - channel on the controller * * Calculate the logical drive number based on the information in scsi command * and the channel number. */
static inline int mega_get_ldrv_num(adapter_t *adapter, Scsi_Cmnd *cmd, int channel) { int tgt; int ldrv_num; tgt = cmd->device->id; if ( tgt > adapter->this_id ) tgt--; /* we do not get inquires for initiator id */ ldrv_num = (channel * 15) + tgt; /* * If we have a logical drive with boot enabled, project it first */ if( adapter->boot_ldrv_enabled ) { if( ldrv_num == 0 ) { ldrv_num = adapter->boot_ldrv; } else { if( ldrv_num <= adapter->boot_ldrv ) { ldrv_num--; } } } /* * If "delete logical drive" feature is enabled on this controller. * Do only if at least one delete logical drive operation was done. * * Also, after logical drive deletion, instead of logical drive number, * the value returned should be 0x80+logical drive id. * * These is valid only for IO commands. */ if (adapter->support_random_del && adapter->read_ldidmap ) switch (cmd->cmnd[0]) { case READ_6: /* fall through */ case WRITE_6: /* fall through */ case READ_10: /* fall through */ case WRITE_10: ldrv_num += 0x80; } return ldrv_num; }

Contributors

PersonTokensPropCommitsCommitProp
Al Viro13699.27%150.00%
Jeff Garzik10.73%150.00%
Total137100.00%2100.00%

/** * mega_build_cmd() * @adapter - pointer to our soft state * @cmd - Prepare using this scsi command * @busy - busy flag if no resources * * Prepares a command and scatter gather list for the controller. This routine * also finds out if the commands is intended for a logical drive or a * physical device and prepares the controller command accordingly. * * We also re-order the logical drives and physical devices based on their * boot settings. */
static scb_t * mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy) { mega_ext_passthru *epthru; mega_passthru *pthru; scb_t *scb; mbox_t *mbox; u32 seg; char islogical; int max_ldrv_num; int channel = 0; int target = 0; int ldrv_num = 0; /* logical drive number */ /* * We know what channels our logical drives are on - mega_find_card() */ islogical = adapter->logdrv_chan[cmd->device->channel]; /* * The theory: If physical drive is chosen for boot, all the physical * devices are exported before the logical drives, otherwise physical * devices are pushed after logical drives, in which case - Kernel sees * the physical devices on virtual channel which is obviously converted * to actual channel on the HBA. */ if( adapter->boot_pdrv_enabled ) { if( islogical ) { /* logical channel */ channel = cmd->device->channel - adapter->product_info.nchannels; } else { /* this is physical channel */ channel = cmd->device->channel; target = cmd->device->id; /* * boot from a physical disk, that disk needs to be * exposed first IF both the channels are SCSI, then * booting from the second channel is not allowed. */ if( target == 0 ) { target = adapter->boot_pdrv_tgt; } else if( target == adapter->boot_pdrv_tgt ) { target = 0; } } } else { if( islogical ) { /* this is the logical channel */ channel = cmd->device->channel; } else { /* physical channel */ channel = cmd->device->channel - NVIRT_CHAN; target = cmd->device->id; } } if(islogical) { /* have just LUN 0 for each target on virtual channels */ if (cmd->device->lun) { cmd->result = (DID_BAD_TARGET << 16); cmd->scsi_done(cmd); return NULL; } ldrv_num = mega_get_ldrv_num(adapter, cmd, channel); max_ldrv_num = (adapter->flag & BOARD_40LD) ? MAX_LOGICAL_DRIVES_40LD : MAX_LOGICAL_DRIVES_8LD; /* * max_ldrv_num increases by 0x80 if some logical drive was * deleted. */ if(adapter->read_ldidmap) max_ldrv_num += 0x80; if(ldrv_num > max_ldrv_num ) { cmd->result = (DID_BAD_TARGET << 16); cmd->scsi_done(cmd); return NULL; } } else { if( cmd->device->lun > 7) { /* * Do not support lun >7 for physically accessed * devices */ cmd->result = (DID_BAD_TARGET << 16); cmd->scsi_done(cmd); return NULL; } } /* * * Logical drive commands * */ if(islogical) { switch (cmd->cmnd[0]) { case TEST_UNIT_READY: #if MEGA_HAVE_CLUSTERING /* * Do we support clustering and is the support enabled * If no, return success always */ if( !adapter->has_cluster ) { cmd->result = (DID_OK << 16); cmd->scsi_done(cmd); return NULL; } if(!(scb = mega_allocate_scb(adapter, cmd))) { *busy = 1; return NULL; } scb->raw_mbox[0] = MEGA_CLUSTER_CMD; scb->raw_mbox[2] = MEGA_RESERVATION_STATUS; scb->raw_mbox[3] = ldrv_num; scb->dma_direction = PCI_DMA_NONE; return scb; #else cmd->result = (DID_OK << 16); cmd->scsi_done(cmd); return NULL; #endif case MODE_SENSE: { char *buf; struct scatterlist *sg; sg = scsi_sglist(cmd); buf = kmap_atomic(sg_page(sg)) + sg->offset; memset(buf, 0, cmd->cmnd[4]); kunmap_atomic(buf - sg->offset); cmd->result = (DID_OK << 16); cmd->scsi_done(cmd); return NULL; } case READ_CAPACITY: case INQUIRY: if(!(adapter->flag & (1L << cmd->device->channel))) { dev_notice(&adapter->dev->dev, "scsi%d: scanning scsi channel %d " "for logical drives\n", adapter->host->host_no, cmd->device->channel); adapter->flag |= (1L << cmd->device->channel); } /* Allocate a SCB and initialize passthru */ if(!(scb = mega_allocate_scb(adapter, cmd))) { *busy = 1; return NULL; } pthru = scb->pthru; mbox = (mbox_t *)scb->raw_mbox; memset(mbox, 0, sizeof(scb->raw_mbox)); memset(pthru, 0, sizeof(mega_passthru)); pthru->timeout = 0; pthru->ars = 1; pthru->reqsenselen = 14; pthru->islogical = 1; pthru->logdrv = ldrv_num; pthru->cdblen = cmd->cmd_len; memcpy(pthru->cdb, cmd->cmnd, cmd->cmd_len); if( adapter->has_64bit_addr ) { mbox->m_out.cmd = MEGA_MBOXCMD_PASSTHRU64; } else { mbox->m_out.cmd = MEGA_MBOXCMD_PASSTHRU; } scb->dma_direction = PCI_DMA_FROMDEVICE; pthru->numsgelements = mega_build_sglist(adapter, scb, &pthru->dataxferaddr, &pthru->dataxferlen); mbox->m_out.xferaddr = scb->pthru_dma_addr; return scb; case READ_6: case WRITE_6: case READ_10: case WRITE_10: case READ_12: case WRITE_12: /* Allocate a SCB and initialize mailbox */ if(!(scb = mega_allocate_scb(adapter, cmd))) { *busy = 1; return NULL; } mbox = (mbox_t *)scb->raw_mbox; memset(mbox, 0, sizeof(scb->raw_mbox)); mbox->m_out.logdrv = ldrv_num; /* * A little hack: 2nd bit is zero for all scsi read * commands and is set for all scsi write commands */ if( adapter->has_64bit_addr ) { mbox->m_out.cmd = (*cmd->cmnd & 0x02) ? MEGA_MBOXCMD_LWRITE64: MEGA_MBOXCMD_LREAD64 ; } else { mbox->m_out.cmd = (*cmd->cmnd & 0x02) ? MEGA_MBOXCMD_LWRITE: MEGA_MBOXCMD_LREAD ; } /* * 6-byte READ(0x08) or WRITE(0x0A) cdb */ if( cmd->cmd_len == 6 ) { mbox->m_out.numsectors = (u32) cmd->cmnd[4]; mbox->m_out.lba = ((u32)cmd->cmnd[1] << 16) | ((u32)cmd->cmnd[2] << 8) | (u32)cmd->cmnd[3]; mbox->m_out.lba &= 0x1FFFFF; #if MEGA_HAVE_STATS /* * Take modulo 0x80, since the logical drive * number increases by 0x80 when a logical * drive was deleted */ if (*cmd->cmnd == READ_6) { adapter->nreads[ldrv_num%0x80]++; adapter->nreadblocks[ldrv_num%0x80] += mbox->m_out.numsectors; } else { adapter->nwrites[ldrv_num%0x80]++; adapter->nwriteblocks[ldrv_num%0x80] += mbox->m_out.numsectors; } #endif } /* * 10-byte READ(0x28) or WRITE(0x2A) cdb */ if( cmd->cmd_len == 10 ) { mbox->m_out.numsectors = (u32)cmd->cmnd[8] | ((u32)cmd->cmnd[7] << 8); mbox->m_out.lba = ((u32)cmd->cmnd[2] << 24) | ((u32)cmd->cmnd[3] << 16) | ((u32)cmd->cmnd[4] << 8) | (u32)cmd->cmnd[5]; #if MEGA_HAVE_STATS if (*cmd->cmnd == READ_10) { adapter->nreads[ldrv_num%0x80]++; adapter->nreadblocks[ldrv_num%0x80] += mbox->m_out.numsectors; } else { adapter->nwrites[ldrv_num%0x80]++; adapter->nwriteblocks[ldrv_num%0x80] += mbox->m_out.numsectors; } #endif } /* * 12-byte READ(0xA8) or WRITE(0xAA) cdb */ if( cmd->cmd_len == 12 ) { mbox->m_out.lba = ((u32)cmd->cmnd[2] << 24) | ((u32)cmd->cmnd[3] << 16) | ((u32)cmd->cmnd[4] << 8) | (u32)cmd->cmnd[5]; mbox->m_out.numsectors = ((u32)cmd->cmnd[6] << 24) | ((u32)cmd->cmnd[7] << 16) | ((u32)cmd->cmnd[8] << 8) | (u32)cmd->cmnd[9]; #if MEGA_HAVE_STATS if (*cmd->cmnd == READ_12) { adapter->nreads[ldrv_num%0x80]++; adapter->nreadblocks[ldrv_num%0x80] += mbox->m_out.numsectors; } else { adapter->nwrites[ldrv_num%0x80]++; adapter->nwriteblocks[ldrv_num%0x80] += mbox->m_out.numsectors; } #endif }