⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 writingscsi driver.htm

📁 What is this ``device driver stuff anyway? Here s a very short introduction to the concept.
💻 HTM
📖 第 1 页 / 共 4 页
字号:
  <tt>linux/kernel/irq.c</tt>) for the <tt>irqaction()</tt> function is
</p><pre>    int irqaction( unsigned int irq, struct sigaction *new )
</pre>
where the first parameter, <tt>irq</tt>, is the number of the IRQ
that is being requested, and the second parameter, <tt>new</tt>, is a
structure with the definition (Linux 0.99.5 kernel source code,
<tt>linux/include/linux/signal.h</tt>) shown here:
<pre>    struct sigaction
    {
      __sighandler_t sa_handler;
      sigset_t       sa_mask;
      int            sa_flags;
      void           (*sa_restorer)(void);
    };
</pre>

<p>In this structure, <tt>sa_handler</tt> should point to your
interrupt handler routine, which should have a definition
similar to the following:
</p><pre>    void fdomain_16x0_intr( int irq )
</pre>
where <tt>irq</tt> will be the number of the IRQ which caused the
interrupt handler routine to be invoked.

<p>The <tt>sa_mask</tt> variable is used as an internal flag by
the <tt>irqaction()</tt> routine.  Traditionally, this variable
is set to zero prior to calling <tt>irqaction()</tt>.

</p><p>The <tt>sa_flags</tt> variable can be set to zero or to
<tt>SA_INTERRUPT</tt>.  If zero is selected, the interrupt
handler will run with other interrupts enabled, and will return
via the signal-handling return functions.  This option is
recommended for relatively slow IRQ's, such as those associated
with the keyboard and timer interrupts.  If
<tt>SA_INTERRUPT</tt> is selected, the handler will be called
with interrupts disabled and return will avoid the
signal-handling return functions. <tt>SA_INTERRUPT</tt> selects
``fast'' IRQ handler invocation routines, and is recommended
for interrupt driven hard disk routines.  The interrupt handler
should turn interrupts on as soon as possible, however, so that
other interrupts can be processed.

</p><p>The <tt>sa_restorer</tt> variable is not currently used, and
is traditionally set to <tt>NULL</tt>.

</p><p>The <tt>request_irq()</tt> and <tt>irqaction()</tt>
functions will return zero if the IRQ was successfully assigned
to the specified interrupt handler routine.  Non-zero result
codes may be interpreted as follows:
</p><dl>
<dt><tt>-EINVAL</tt>
</dt><dd>Either the IRQ requested was larger than 15, or a
  <tt>NULL</tt> pointer was passed instead of a valid pointer to the
  interrupt handler routine.
</dd><dt><tt>-EBUSY</tt>
</dt><dd>The IRQ requested has already been allocated to another
  interrupt handler.  This situation should never occur, and is reasonable
  cause for a call to <tt>panic()</tt>.
</dd></dl>

<p>The kernel uses an Intel ``interrupt gate'' to set up IRQ
handler routines requested via the <tt>irqaction()</tt>
function.  The Intel i486 manual [Int90, p. 9-11] explains the
interrupt gate as follows:
</p><blockquote>
    Interrupts using... interrupt gates... cause the TF flag [trap
    flag] to be cleared after its current value is saved on the stack as
    part of the saved contents of the EFLAGS register.  In so doing, the
    processor prevents instruction tracing from affecting interrupt
    response.  A subsequent IRET [interrupt return] instruction restores
    the TF flag to the value in the saved contents of the EFLAGS register
    on the stack.

    <p>... An interrupt which uses an interrupt gate clears the IF flag
    [interrupt-enable flag], which prevents other interrupts from
    interfering with the current interrupt handler.  A subsequent IRET
    instruction restores the IF flag to the value in the saved contents of
    the EFLAGS register on the stack.
</p></blockquote>

<h5>Requesting the DMA channel</h5>

<p>Some SCSI host adapters use DMA to access large blocks of
data in memory. Since the CPU does not have to deal with the
individual DMA requests, data transfers are faster than
CPU-mediated transfers and allow the CPU to do other useful
work during a block transfer (assuming interrupts are enabled).

</p><p>The host adapter will use a specific DMA channel.  This DMA
channel will be determined by the <tt>detect()</tt> function
and requested from the kernel with the <tt>request_dma()</tt>
function.  This function takes the DMA channel number as its
only parameter and returns zero if the DMA channel was
successfully allocated.  Non-zero results may be interpreted as
follows:
</p><dl>
<dt><tt>-EINVAL</tt>
</dt><dd>The DMA channel number requested was larger than 7.
</dd><dt><tt>-EBUSY</tt>
</dt><dd>The requested DMA channel has already been allocated.
  This is a very serious situation, and will probably cause any SCSI
  requests to fail.  It is worthy of a call to <tt>panic()</tt>.
</dd></dl>


<h5><tt>info()</tt></h5>

<p>The <tt>info()</tt> function merely returns a pointer to a
static area containing a brief description of the low-level
driver.  This description, which is similar to that pointed to
by the <tt>name</tt> variable, will be printed at boot time.


</p><h5><tt>queuecommand()</tt></h5>
<a name="sec:queuecommand"></a>

<p>The <tt>queuecommand()</tt> function sets up the host
adapter for processing a SCSI command and then returns.  When
the command is finished, the <tt>done()</tt> function is called
with the <tt>Scsi_Cmnd</tt> structure pointer as a parameter.
This allows the SCSI command to be executed in an
interrupt-driven fashion.  Before returning, the
<tt>queuecommand()</tt> function must do several things:
</p><ol>
<li>Save the pointer to the <tt>Scsi_Cmnd</tt> structure.
</li><li>Save the pointer to the <tt>done()</tt> function in the
  <tt>scsi_done()</tt> function pointer in the <tt>Scsi_Cmnd</tt> structure.
  See section <a href="#sec:done"><tt>done()</tt></a> for more information.
</li><li>Set up the special <tt>Scsi_Cmnd</tt> variables required by the driver.
  See section <a href="#sec:scsi.cmnd">The <tt>Scsi_Cmnd</tt> Structure</a> for detailed information on the
  <tt>Scsi_Cmnd</tt> structure.
</li><li>Start the SCSI command.  For an advanced host adapter, this may be as
  simple as sending the command to a host adapter ``mailbox.''  For less
  advanced host adapters, the ARBITRATION phase is manually started.
</li></ol>

<p>The <tt>queuecommand()</tt> function is called <i>only</i>
if the <tt>can_queue</tt> variable (see section <a href="#sec:can.queue"><tt>can_queue</tt></a>) is non-zero.
Otherwise the <tt>command()</tt> function is used for all SCSI
requests.  The <tt>queuecommand()</tt> function should return
zero on success (the current high-level SCSI code presently
ignores the return value).


</p><h5><tt>done()</tt></h5>
<a name="sec:done"></a>

<p>The <tt>done()</tt> function is called after the SCSI
command completes.  The single parameter that this command
requires is a pointer to the same <tt>Scsi_Cmnd</tt> structure
that was previously passed to the <tt>queuecommand()</tt>
function.  Before the <tt>done()</tt> function is called, the
<tt>result</tt> variable must be set correctly.  The
<tt>result</tt> variable is a 32 bit integer, each byte of
which has specific meaning:
</p><dl>
<dt>Byte 0 (LSB)
</dt><dd>This byte contains the SCSI STATUS code for the
  command, as described in section <a href="#sec:status">SCSI phases</a>.
</dd><dt>Byte 1
</dt><dd>This byte contains the SCSI MESSAGE, as described in
  section <a href="#sec:message">SCSI phases</a>.
</dd><dt>Byte 2
</dt><dd>This byte holds the host adapter's return code.  The valid
  codes for this byte are given in <tt>scsi.h</tt> and are described below:
  <dl>
  <dt><tt>DID_OK</tt>
  </dt><dd>No error.
  </dd><dt><tt>DID_NO_CONNECT</tt>
  </dt><dd>SCSI SELECTION failed because there was no
    device at the address specified.
  </dd><dt><tt>DID_BUS_BUSY</tt>
  </dt><dd>SCSI ARBITRATION failed.
  </dd><dt><tt>DID_TIME_OUT</tt>
  </dt><dd>A time-out occurred for some unknown reason,
    probably during SELECTION or while waiting for RESELECTION.
  </dd><dt><tt>DID_BAD_TARGET</tt>
  </dt><dd>The SCSI ID of the target was the same as
    the SCSI ID of the host adapter.
  </dd><dt><tt>DID_ABORT</tt>
  </dt><dd>The high-level code called the low-level
    <tt>abort()</tt> function (see section <a href="#sec:abort"><tt>abort()</tt></a>).
  </dd><dt><tt>DID_PARITY</tt>
  </dt><dd>A SCSI PARITY error was detected.
  </dd><dt><tt>DID_ERROR</tt>
  </dt><dd>An error occurred which lacks a more appropriate
    error code (for example, an internal host adapter error).
  </dd><dt><tt>DID_RESET</tt>
  </dt><dd>The high-level code called the low-level
    <tt>reset()</tt> function (see section <a href="#sec:reset"><tt>reset()</tt></a>).
  </dd><dt><tt>DID_BAD_INTR</tt>
  </dt><dd>An unexpected interrupt occurred <i>and</i>
    there is no appropriate way to handle this interrupt.
  </dd></dl>

  Note that returning <tt>DID_BUS_BUSY</tt> will force the command to be
  retried, whereas returning <tt>DID_NO_CONNECT</tt> will abort the command.
</dd><dt>Byte 3 (MSB)
</dt><dd>This byte is for a high-level return code, and should
  be left as zero by the low-level code.
</dd></dl>

<p>Current low-level drivers do not uniformly (or correctly)
implement error reporting, so it may be better to consult
scsi.c to determine exactly how errors should be reported,
rather than exploring existing drivers.


</p><h5><tt>command()</tt></h5>

<p>The <tt>command()</tt> function processes a SCSI command and
returns when the command is finished.  When the original SCSI
code was written, interrupt-driven drivers were not supported.
The old drivers are much less efficient (in terms of response
time and latency) than the current interrupt-driven drivers,
but are also much easier to write.  For new drivers, this
command can be replaced with a call to the
<tt>queuecommand()</tt> function, as demonstrated here. (Linux
0.99.5 kernel, linux/kernel/blk_drv/scsi/aha1542.c, written by
Tommy Thorn.)

</p><pre>    static volatile int internal_done_flag    = 0;
    static volatile int internal_done_errcode = 0;
    static void         internal_done( Scsi_Cmnd *SCpnt )
    {
      internal_done_errcode = SCpnt-&gt;result;
      ++internal_done_flag;
    }

    int aha1542_command( Scsi_Cmnd *SCpnt )
    {
      aha1542_queuecommand( SCpnt, internal_done );

      while (!internal_done_flag);
      internal_done_flag = 0;
      return internal_done_errcode;
    }
</pre>

<p>The return value is the same as the <tt>result</tt> variable
in the <tt>Scsi_Cmnd</tt> structure.  Please see sections
<a href="#sec:done"><tt>done()</tt></a> and
<a href="#sec:scsi.cmnd">The <tt>Scsi_Cmnd</tt> Structure</a> for
more details.


</p><h5><tt>abort()</tt></h5>
<a name="sec:abort"></a>

<p>The high-level SCSI code handles all timeouts.  This frees
the low-level driver from having to do timing, and permits
different timeout periods to be used for different devices
(e.g., the timeout for a SCSI tape drive is nearly infinite,
whereas the timeout for a SCSI disk drive is relatively short).

</p><p>The <tt>abort()</tt> function is used to request that the
currently outstanding SCSI command, indicated by the
<tt>Scsi_Cmnd</tt> pointer, be aborted.  After setting the
<tt>result</tt> variable in the <tt>Scsi_Cmnd</tt> structure,
the <tt>abort()</tt> function returns zero.  If <tt>code</tt>,
the second parameter to the <tt>abort()</tt> function, is zero,
then <tt>result</tt> should be set to <tt>DID_ABORT</tt>.
Otherwise, <tt>result</tt> shoudl be set equal to
<tt>code</tt>.  If <tt>code</tt> is not zero, it is usually
<tt>DID_TIME_OUT</tt> or <tt>DID_RESET</tt>.

</p><p>Currently, none of the low-level drivers is able to
correctly abort a SCSI command.  The initiator should request
(by asserting the <tt>-ATN</tt> line) that the target enter a
MESSAGE OUT phase.  Then, the initiator should send an ABORT
message to the target.


</p><h5><tt>reset()</tt></h5>
<a name="sec:reset"></a>

<p>The <tt>reset()</tt> function is used to reset the SCSI bus.
After a SCSI bus reset, any executing command should fail with
a <tt>DID_RESET</tt> result code (see section <a href="#sec:done"><tt>done()</tt></a>).

</p><p>Currently, none of the low-level drivers handles resets
correctly.  To correctly reset a SCSI command, the initiator
should request (by asserting the <tt>-ATN</tt> line) that the
target enter a MESSAGE OUT phase.  Then, the initiator should
send a BUS DEVICE RESET message to the target.  It may also be
necessary to initiate a SCSI RESET by asserting the
<tt>-RST</tt> line, which will cause all target devices to be
reset.  After a reset, it may be necessary to renegotiate a
synchronous communications protocol with the targets.


</p><h5><tt>slave_attach()</tt></h5>

<p>The <tt>slave_attach()</tt> function is <i>not</i> currently
implemented. This function would be used to negotiate

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -