The driver will be superseeded soon by a new AHCI driver that supports ATAPI devices. There is no IDE support in Gende anymore, however. Issue #1456.devel
parent
97dc9664fe
commit
2002e1ccba
@ -1,24 +0,0 @@
|
||||
This directory contains a port of the MINDRVR PATA/SATA driver (See:
|
||||
[http://ata-atapi.com].
|
||||
|
||||
The driver will probe the system's IDE bus and will use the first ATAPI device
|
||||
present.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Simply start the 'atapi_drv' in your Genode environment. The front-end
|
||||
implemented by the driver is Genode's block interface (see:
|
||||
'os/include/block_session').
|
||||
|
||||
Configuration example:
|
||||
|
||||
! <start name="atapi_drv">
|
||||
! <resource name="RAM" quantum="1M" />
|
||||
! <provides><service name="Block" /></provides>
|
||||
! </start>
|
||||
|
||||
To use the driver as IDE block driver supporting both read and write
|
||||
transactions, supply the XML attribute 'ata="yes"' to the config node of
|
||||
'atapi_drv'.
|
@ -1,140 +0,0 @@
|
||||
/*
|
||||
* \brief I/O interface to the IDE bus master
|
||||
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
|
||||
* \date 2010-07-15
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <io_port_session/connection.h>
|
||||
#include <pci_session/connection.h>
|
||||
#include <pci_device/client.h>
|
||||
|
||||
/* local includes */
|
||||
#include "ata_bus_master.h"
|
||||
#include "mindrvr.h"
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
Ata::Bus_master::Bus_master(bool secondary) : _bmiba(0), _secondary(secondary)
|
||||
{}
|
||||
|
||||
|
||||
Ata::Bus_master::~Bus_master()
|
||||
{
|
||||
destroy(env()->heap(), _pio);
|
||||
}
|
||||
|
||||
|
||||
bool Ata::Bus_master::scan_pci()
|
||||
{
|
||||
Pci::Connection pci;
|
||||
Pci::Device_capability prev_device_cap,
|
||||
device_cap = pci.first_device();
|
||||
|
||||
/* test if standard legacy ports are used, if not skip */
|
||||
uint32_t legacy = _secondary ? PI_CH2_LEGACY : PI_CH1_LEGACY;
|
||||
|
||||
while (device_cap.valid()) {
|
||||
pci.release_device(prev_device_cap);
|
||||
prev_device_cap = device_cap;
|
||||
|
||||
::Pci::Device_client device(device_cap);
|
||||
|
||||
if ((device.class_code() & CLASS_MASK)
|
||||
== (CLASS_MASS_STORAGE | SUBCLASS_IDE) &&
|
||||
!(device.class_code() & legacy)) {
|
||||
Genode::printf("Found IDE Bus Master (Vendor ID: %04x Device ID: %04x Class: %08x)\n",
|
||||
device.vendor_id(), device.device_id(), device.class_code());
|
||||
|
||||
/* read BMIBA */
|
||||
_bmiba = device.config_read(PCI_CFG_BMIBA_OFF,
|
||||
::Pci::Device::ACCESS_32BIT);
|
||||
|
||||
if (_bmiba == 0xffff)
|
||||
_bmiba = 0;
|
||||
|
||||
/* port I/O or memory mapped ? */
|
||||
_port_io = _bmiba & 0x1 ? true : false;
|
||||
|
||||
/* XXX cbass: This may only be true for Intel IDE controllers */
|
||||
if (_bmiba && _port_io) {
|
||||
_bmiba &= 0xfff0;
|
||||
_pio = new(env()->heap()) Genode::Io_port_connection(_bmiba, 0x10);
|
||||
|
||||
if (_secondary)
|
||||
_bmiba += 0x8;
|
||||
}
|
||||
|
||||
Genode::printf("\tBus master interface base addr: %08x (%s) secondary (%s) (%s)\n",
|
||||
_bmiba, _port_io ? "I/O" : "MEM",
|
||||
_secondary ? "yes" : "no",
|
||||
_bmiba ? "supported" : "invalid");
|
||||
|
||||
pci.release_device(prev_device_cap);
|
||||
return _bmiba;
|
||||
}
|
||||
|
||||
device_cap = pci.next_device(prev_device_cap);
|
||||
}
|
||||
|
||||
/* release last device */
|
||||
pci.release_device(prev_device_cap);
|
||||
|
||||
return _bmiba;
|
||||
}
|
||||
|
||||
|
||||
unsigned char Ata::Bus_master::read_cmd()
|
||||
{
|
||||
if (!_bmiba || !_port_io)
|
||||
return 0;
|
||||
|
||||
return _pio->inb(_bmiba + BM_COMMAND_REG);
|
||||
}
|
||||
|
||||
|
||||
unsigned char Ata::Bus_master::read_status()
|
||||
{
|
||||
if (!_bmiba || !_port_io)
|
||||
return 0;
|
||||
|
||||
return _pio->inb(_bmiba + BM_STATUS_REG);
|
||||
}
|
||||
|
||||
|
||||
void Ata::Bus_master::write_cmd(unsigned char val)
|
||||
{
|
||||
if (!_bmiba || !_port_io)
|
||||
return;
|
||||
|
||||
_pio->outb(_bmiba + BM_COMMAND_REG, val);
|
||||
}
|
||||
|
||||
|
||||
void Ata::Bus_master::write_status(unsigned char val)
|
||||
{
|
||||
if (!_bmiba || !_port_io)
|
||||
return;
|
||||
|
||||
_pio->outb(_bmiba + BM_STATUS_REG, val);
|
||||
}
|
||||
|
||||
|
||||
void Ata::Bus_master::write_prd(unsigned long val)
|
||||
{
|
||||
if (!_bmiba || !_port_io)
|
||||
return;
|
||||
|
||||
if (val == _prd_virt) {
|
||||
_pio->outl(_bmiba + BM_PRD_ADDR_LOW, _prd_phys);
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
/*
|
||||
* \brief I/O interface to the IDE bus master
|
||||
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
|
||||
* \date 2010-07-15
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _ATA_PCI_H_
|
||||
#define _ATA_PCI_H_
|
||||
|
||||
namespace Genode {
|
||||
class Io_port_connection;
|
||||
}
|
||||
|
||||
namespace Ata {
|
||||
|
||||
class Bus_master {
|
||||
|
||||
enum {
|
||||
PCI_CFG_BMIBA_OFF = 0x20, /* offset in PCI config space */
|
||||
CLASS_MASS_STORAGE = 0x10000,
|
||||
SUBCLASS_IDE = 0x0100,
|
||||
CLASS_MASK = 0xffff00,
|
||||
|
||||
/* These bits of the programming interface part within the class code
|
||||
* define if the corresponding channel operates on standard legacy ports
|
||||
* (bits are cleared) or if the corresponding ports in base register 0-1
|
||||
* and 2-3 for channel 2 must be used as I/O bases (bits are set) */
|
||||
PI_CH1_LEGACY = 0x1,
|
||||
PI_CH2_LEGACY = 0x4,
|
||||
};
|
||||
|
||||
/**
|
||||
* Bus master interface base address
|
||||
*/
|
||||
unsigned _bmiba;
|
||||
|
||||
bool _port_io;
|
||||
bool _secondary;
|
||||
unsigned long _prd_virt;
|
||||
unsigned long _prd_phys;
|
||||
Genode::Io_port_connection *_pio;
|
||||
|
||||
public:
|
||||
|
||||
Bus_master(bool secondary);
|
||||
~Bus_master();
|
||||
|
||||
unsigned char read_cmd();
|
||||
unsigned char read_status();
|
||||
|
||||
void write_cmd(unsigned char val);
|
||||
void write_status(unsigned char val);
|
||||
void write_prd(unsigned long val);
|
||||
|
||||
void set_prd(unsigned long virt, unsigned long phys)
|
||||
{
|
||||
_prd_virt = virt;
|
||||
_prd_phys = phys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan PCI Bus for IDE devices and set BMBIA
|
||||
*/
|
||||
bool scan_pci();
|
||||
|
||||
};
|
||||
}
|
||||
#endif /* _ATA_PCI_H_ */
|
@ -1,317 +0,0 @@
|
||||
/*
|
||||
* \brief ATA device class
|
||||
* \author Sebastian.Sump <Sebastian.Sumpf@genode-labs.com>
|
||||
* \date 2010-07-15
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/lock.h>
|
||||
#include <io_port_session/io_port_session.h>
|
||||
#include <irq_session/connection.h>
|
||||
|
||||
/* local includes */
|
||||
#include "ata_device.h"
|
||||
#include "ata_bus_master.h"
|
||||
#include "io.h"
|
||||
#include "mindrvr.h"
|
||||
|
||||
static const int verbose = 0;
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
Ata::Device::Device(unsigned base_cmd, unsigned base_ctrl)
|
||||
: _lba48(false), _host_protected_area(false)
|
||||
{
|
||||
_pio = new(env()->heap()) Ata::Io_port(base_cmd, base_ctrl);
|
||||
}
|
||||
|
||||
|
||||
Ata::Device::~Device()
|
||||
{
|
||||
destroy(env()->heap(), _pio);
|
||||
|
||||
if (_irq)
|
||||
destroy(env()->heap(), _irq);
|
||||
|
||||
if (_bus_master)
|
||||
destroy(env()->heap(), _bus_master);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Ata::Device::set_bus_master(bool secondary)
|
||||
{
|
||||
_bus_master = new(env()->heap()) Ata::Bus_master(secondary);
|
||||
}
|
||||
|
||||
|
||||
void Ata::Device::set_irq(unsigned irq)
|
||||
{
|
||||
_irq = new(env()->heap()) Irq_connection(irq);
|
||||
}
|
||||
|
||||
|
||||
void Ata::Device::probe_dma()
|
||||
{
|
||||
/* find bus master interface base address */
|
||||
if (!_bus_master->scan_pci())
|
||||
return;
|
||||
|
||||
/* read device information */
|
||||
reg_reset(dev_num());
|
||||
|
||||
unsigned char *buffer;
|
||||
if (!(env()->heap()->alloc(4096, &buffer))) return;
|
||||
|
||||
unsigned char cmd = dynamic_cast<Atapi_device *>(this) ? CMD_IDENTIFY_DEVICE_PACKET : CMD_IDENTIFY_DEVICE;
|
||||
if (!reg_pio_data_in_lba28(
|
||||
dev_num(),
|
||||
cmd,
|
||||
0, 1,
|
||||
0L,
|
||||
buffer,
|
||||
1L, 0 )) {
|
||||
|
||||
/* check command set's LBA48 bit */
|
||||
_lba48 = !!(buffer[167] & 0x4);
|
||||
/* check for host protected area feature */
|
||||
_host_protected_area = !!(buffer[165] & 0x4);
|
||||
|
||||
Genode::printf( "Adress mode is LBA%d\n", _lba48 ? 48 : 28 );
|
||||
Genode::printf("UDMA Modes supported:\n");
|
||||
|
||||
for (int i = 0; i <= 5; i++) {
|
||||
Genode::printf("\t%d and below: %s enabled: %s\n",
|
||||
i ,(buffer[176] & (1 << i)) ? "yes" : "no",
|
||||
(buffer[177] & (1 << i)) ? "yes" : "no");
|
||||
|
||||
if (buffer[177] & (1 << i)) {
|
||||
|
||||
/* initialize PRD's */
|
||||
dma_pci_config();
|
||||
_dma = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
env()->heap()->free(buffer, 4096);
|
||||
}
|
||||
|
||||
|
||||
void Ata::Device::read_capacity()
|
||||
{
|
||||
_block_start = 0;
|
||||
_block_size = 512;
|
||||
|
||||
enum {
|
||||
CMD_NATIVE_MAX_ADDRESS = 0xf8, /* LBA28 */
|
||||
CMD_NATIVE_MAX_ADDRESS_EXT = 0x27, /* LBA48 */
|
||||
};
|
||||
|
||||
/*
|
||||
* If both LBA48 is enabled and host protected area feature is set, then NATIVE MAX
|
||||
* ADDRESS EXT becomes mandatory, use LBA28 otherwise
|
||||
*/
|
||||
if (_lba48 && _host_protected_area) {
|
||||
if (!reg_non_data_lba48(dev_num(), CMD_NATIVE_MAX_ADDRESS_EXT, 0, 1, 0UL, 0UL)) {
|
||||
|
||||
_block_end = _pio->inb(CB_SN); /* 0 - 7 */
|
||||
_block_end |= _pio->inb(CB_CL) << 8; /* 8 - 15 */
|
||||
_block_end |= _pio->inb(CB_CH) << 16; /* 16 - 23 */
|
||||
|
||||
/* read higher order LBA registers */
|
||||
_pio->outb(CB_DC, CB_DC_HOB);
|
||||
_block_end |= _pio->inb(CB_SN) << 24; /* 24 - 31 */
|
||||
/* FIXME: read bits 32 - 47 */
|
||||
|
||||
}
|
||||
} else {
|
||||
if (!reg_non_data_lba28(dev_num(), CMD_NATIVE_MAX_ADDRESS, 0, 1, 0UL)) {
|
||||
|
||||
_block_end = _pio->inb(CB_SN); /* 0 - 7 */
|
||||
_block_end |= _pio->inb(CB_CL) << 8; /* 8 - 15 */
|
||||
_block_end |= _pio->inb(CB_CH) << 16; /* 16 - 23 */
|
||||
_block_end |= (_pio->inb(CB_DH) & 0xf) << 24; /* 24 - 27 */
|
||||
}
|
||||
}
|
||||
|
||||
PINF("First block: %u last block %u, block size %u",
|
||||
_block_start, _block_end, _block_size);
|
||||
}
|
||||
|
||||
|
||||
void Ata::Device::_read(Block::sector_t block_nr,
|
||||
Genode::size_t count,
|
||||
char *buffer,
|
||||
bool dma)
|
||||
{
|
||||
Genode::size_t offset = 0;
|
||||
|
||||
while (count > 0) {
|
||||
Genode::size_t c = count > 255 ? 255 : count;
|
||||
|
||||
if (dma) {
|
||||
|
||||
if (verbose)
|
||||
PDBG("DMA read: block %llu, c %zu, buffer: %p",
|
||||
block_nr, c, (void*)(buffer + offset));
|
||||
|
||||
if (!_lba48)
|
||||
{
|
||||
if (dma_pci_lba28(dev_num(), CMD_READ_DMA, 0, c, block_nr,
|
||||
(unsigned char*)(buffer + offset), c))
|
||||
throw Io_error();
|
||||
} else {
|
||||
if (dma_pci_lba48(dev_num(), CMD_READ_DMA_EXT, 0, c, 0, block_nr,
|
||||
(unsigned char*)(buffer + offset), c))
|
||||
throw Io_error();
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!_lba48)
|
||||
{
|
||||
if (reg_pio_data_in_lba28(dev_num(), CMD_READ_SECTORS, 0, c, block_nr,
|
||||
(unsigned char*)(buffer + offset), c, 0))
|
||||
throw Io_error();
|
||||
} else {
|
||||
if (reg_pio_data_in_lba48(dev_num(), CMD_READ_SECTORS_EXT, 0, c, 0, block_nr,
|
||||
(unsigned char*)(buffer + offset), c, 0))
|
||||
throw Io_error();
|
||||
}
|
||||
}
|
||||
|
||||
count -= c;
|
||||
block_nr += c;
|
||||
offset += c * block_size();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Ata::Device::_write(Block::sector_t block_nr,
|
||||
Genode::size_t count,
|
||||
char const *buffer,
|
||||
bool dma)
|
||||
{
|
||||
Genode::size_t offset = 0;
|
||||
|
||||
while (count > 0) {
|
||||
Genode::size_t c = count > 255 ? 255 : count;
|
||||
|
||||
if (dma) {
|
||||
|
||||
if (verbose)
|
||||
PDBG("DMA read: block %llu, c %zu, buffer: %p",
|
||||
block_nr, c, (void*)(buffer + offset));
|
||||
|
||||
if (!_lba48)
|
||||
{
|
||||
if (dma_pci_lba28(dev_num(), CMD_WRITE_DMA, 0, c, block_nr,
|
||||
(unsigned char*)(buffer + offset), c))
|
||||
throw Io_error();
|
||||
}
|
||||
else {
|
||||
if (dma_pci_lba48(dev_num(), CMD_WRITE_DMA_EXT, 0, c, 0, block_nr,
|
||||
(unsigned char*)(buffer + offset), c))
|
||||
throw Io_error();
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!_lba48)
|
||||
{
|
||||
if (reg_pio_data_out_lba28(dev_num(), CMD_WRITE_SECTORS, 0, c, block_nr,
|
||||
(unsigned char*)(buffer + offset), c, 0))
|
||||
throw Io_error();
|
||||
} else {
|
||||
if (reg_pio_data_out_lba48(dev_num(), CMD_WRITE_SECTORS_EXT, 0, c, 0, block_nr,
|
||||
(unsigned char*)(buffer + offset), c, 0))
|
||||
throw Io_error();
|
||||
}
|
||||
}
|
||||
|
||||
count -= c;
|
||||
block_nr += c;
|
||||
offset += c * block_size();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ata::Device * Ata::Device::probe_legacy(int search_type)
|
||||
{
|
||||
unsigned base[] = { 0x1f0, 0x170 };
|
||||
unsigned dev_irq[] = { 14, 15 };
|
||||
|
||||
const char *type = "";
|
||||
for (int i = 0; i < 2; i++) {
|
||||
|
||||
Device *dev = new (env()->heap() )Device(base[i], base[i] + 0x200);
|
||||
Device::current(dev);
|
||||
|
||||
/* Scan for devices */
|
||||
reg_config();
|
||||
|
||||
for (unsigned char j = 0; j < 2; j++) {
|
||||
|
||||
switch (reg_config_info[j]) {
|
||||
|
||||
case REG_CONFIG_TYPE_NONE:
|
||||
type = "none"; break;
|
||||
|
||||
case REG_CONFIG_TYPE_UNKN:
|
||||
type = "unknown"; break;
|
||||
|
||||
case REG_CONFIG_TYPE_ATA:
|
||||
type = "ATA"; break;
|
||||
|
||||
case REG_CONFIG_TYPE_ATAPI:
|
||||
type = "ATAPI";
|
||||
destroy (env()->heap(), dev);
|
||||
dev = new (env()->heap() )Atapi_device(base[i], base[i] + 0x200);
|
||||
break;
|
||||
|
||||
default:
|
||||
type = "";
|
||||
}
|
||||
|
||||
Genode::printf("IDE %d Device %d: %s IRQ: %u\n", i, j, type, dev_irq[i]);
|
||||
|
||||
/* prepare device */
|
||||
if (reg_config_info[j] == search_type) {
|
||||
|
||||
dev->set_bus_master(i);
|
||||
dev->set_dev_num(j);
|
||||
dev->set_irq(dev_irq[i]);
|
||||
|
||||
dev->probe_dma();
|
||||
Genode::printf("Device initialized! Enabling interrupts ...\n");
|
||||
int_use_intr_flag = 1;
|
||||
reg_reset(dev->dev_num());
|
||||
|
||||
return dev;
|
||||
}
|
||||
}
|
||||
|
||||
type = "";
|
||||
destroy(env()->heap(), dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Block::Session::Operations Ata::Device::ops()
|
||||
{
|
||||
Block::Session::Operations o;
|
||||
o.set_operation(Block::Packet_descriptor::READ);
|
||||
o.set_operation(Block::Packet_descriptor::WRITE);
|
||||
return o;
|
||||
}
|
@ -1,185 +0,0 @@
|
||||
/*
|
||||
* \brief ATA device class
|
||||
* \author Sebastian.Sump <Sebastian.Sumpf@genode-labs.com>
|
||||
* \date 2010-07-15
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _ATA_DEVICE_H_
|
||||
#define _ATA_DEVICE_H_
|
||||
|
||||
#include <base/exception.h>
|
||||
#include <base/stdint.h>
|
||||
#include <block/component.h>
|
||||
|
||||
namespace Genode {
|
||||
class Io_port_session;
|
||||
class Irq_connection;
|
||||
}
|
||||
|
||||
namespace Ata {
|
||||
|
||||
class Bus_master;
|
||||
|
||||
class Device : public Block::Driver
|
||||
{
|
||||
|
||||
/* I/O back end may access private data (see io.cc) */
|
||||
friend Genode::Io_port_session *get_io(Device *dev);
|
||||
friend Genode::Irq_connection *get_irq(Device *dev);
|
||||
friend Bus_master *get_bus_master(Device *dev);
|
||||
|
||||
protected:
|
||||
|
||||
unsigned char _dev_num;
|
||||
Genode::Io_port_session *_pio;
|
||||
Genode::Irq_connection *_irq;
|
||||
Bus_master *_bus_master;
|
||||
bool _dma;
|
||||
unsigned _block_start;
|
||||
unsigned _block_end;
|
||||
unsigned _block_size;
|
||||
bool _lba48;
|
||||
bool _host_protected_area;
|
||||
|
||||
protected:
|
||||
|
||||
void probe_dma();
|
||||
|
||||
void set_bus_master(bool secondary);
|
||||
void set_irq(unsigned irq);
|
||||
void set_dev_num(unsigned char dev_num) { _dev_num = dev_num; }
|
||||
|
||||
unsigned char dev_num() { return _dev_num; }
|
||||
Bus_master *bus_master(){ return _bus_master; }
|
||||
Genode::Irq_connection *irq() { return _irq; }
|
||||
Genode::Io_port_session *io() { return _pio; }
|
||||
|
||||
virtual void _read(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char *out_buffer,
|
||||
bool dma);
|
||||
virtual void _write(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char const *buffer,
|
||||
bool dma);
|
||||
|
||||
public:
|
||||
|
||||
class Exception : public ::Genode::Exception { };
|
||||
class Io_error : public Exception { };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param base_cmd I/O port base of command registers
|
||||
* \param base_ctrl I/O prot base of control registers
|
||||
*/
|
||||
Device(unsigned base_cmd, unsigned base_ctrl);
|
||||
~Device();
|
||||
|
||||
/**
|
||||
* Return and set current device
|
||||
*/
|
||||
static Device * current(Device *dev = 0)
|
||||
{
|
||||
static Device *_current = 0;
|
||||
if (dev) _current = dev;
|
||||
return _current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Probe legacy bus for device class
|
||||
*
|
||||
* \param search_type REG_CONFIG_TYPE_ATA or REG_CONFIG_TYPE_ATAPI
|
||||
*/
|
||||
static Device * probe_legacy(int search_type);
|
||||
|
||||
/**
|
||||
* Read block size and block count (from device),
|
||||
* updates block_count and block_size
|
||||
*/
|
||||
virtual void read_capacity();
|
||||
|
||||
|
||||
/*******************************
|
||||
** Block::Driver interface **
|
||||
*******************************/
|
||||
|
||||
Block::sector_t block_count() {
|
||||
return _block_end - _block_start + 1; }
|
||||
Genode::size_t block_size() { return _block_size; }
|
||||
|
||||
virtual Block::Session::Operations ops();
|
||||
|
||||
void read(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char *buffer,
|
||||
Block::Packet_descriptor &packet)
|
||||
{
|
||||
_read(block_number, block_count, buffer, false);
|
||||
ack_packet(packet);
|
||||
}
|
||||
|
||||
void write(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char const *buffer,
|
||||
Block::Packet_descriptor &packet)
|
||||
{
|
||||
_write(block_number, block_count, buffer, false);
|
||||
ack_packet(packet);
|
||||
}
|
||||
|
||||
void read_dma(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
Genode::addr_t phys,
|
||||
Block::Packet_descriptor &packet)
|
||||
{
|
||||
_read(block_number, block_count, (char*)phys, true);
|
||||
ack_packet(packet);
|
||||
}
|
||||
|
||||
void write_dma(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
Genode::addr_t phys,
|
||||
Block::Packet_descriptor &packet)
|
||||
{
|
||||
_write(block_number, block_count, (char*)phys, true);
|
||||
ack_packet(packet);
|
||||
}
|
||||
|
||||
bool dma_enabled() { return _dma; }
|
||||
};
|
||||
|
||||
|
||||
class Atapi_device : public Device
|
||||
{
|
||||
private:
|
||||
|
||||
int read_sense(unsigned char *sense, int length);
|
||||
void read_capacity();
|
||||
|
||||
void _read(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char *out_buffer,
|
||||
bool dma);
|
||||
void _write(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char const *buffer,
|
||||
bool dma);
|
||||
|
||||
public:
|
||||
|
||||
Atapi_device(unsigned base_cmd, unsigned base_ctrl);
|
||||
|
||||
bool test_unit_ready(int level = 0);
|
||||
Block::Session::Operations ops();
|
||||
};
|
||||
}
|
||||
#endif
|
@ -1,155 +0,0 @@
|
||||
/*
|
||||
* \brief ATAPI specific implementation of an Ata::Device
|
||||
* \author Sebastian.Sumpf <sebastian.sumpf@genode-labs.com>
|
||||
* \date 2010-07-19
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/env.h>
|
||||
#include <base/printf.h>
|
||||
#include <util/string.h>
|
||||
|
||||
/* local includes */
|
||||
#include "ata_device.h"
|
||||
#include "ata_bus_master.h"
|
||||
#include "endian.h"
|
||||
#include "mindrvr.h"
|
||||
|
||||
static const int verbose = 0;
|
||||
|
||||
using namespace Ata;
|
||||
using namespace Genode;
|
||||
|
||||
enum Commands {
|
||||
CMD_READ_10 = 0x28,
|
||||
CMD_READ_CAPACITY = 0x25,
|
||||
CMD_REQUEST_SENSE = 0x03,
|
||||
CMD_TEST_UNIT_READY = 0x00,
|
||||
};
|
||||
|
||||
|
||||
Atapi_device::Atapi_device(unsigned base_cmd, unsigned base_ctrl)
|
||||
: Device(base_cmd, base_ctrl) { }
|
||||
|
||||
|
||||
int Atapi_device::read_sense(unsigned char *sense, int length)
|
||||
{
|
||||
unsigned char cmd_sense[12];
|
||||
|
||||
memset(cmd_sense, 0, 12);
|
||||
memset(sense, 0, length);
|
||||
|
||||
cmd_sense[0] = CMD_REQUEST_SENSE;
|
||||
cmd_sense[4] = length; /* allocation length */
|
||||
return reg_packet(dev_num(), 12, cmd_sense, 0, length, sense);
|
||||
}
|
||||
|
||||
|
||||
bool Atapi_device::test_unit_ready(int level)
|
||||
{
|
||||
unsigned char cmd[12];
|
||||
|
||||
memset(cmd, 0, 12);
|
||||
cmd[0] = CMD_TEST_UNIT_READY;
|
||||
|
||||
if (reg_packet(dev_num(), 12, cmd, 0, 0, 0)) {
|
||||
unsigned char sense[32];
|
||||
read_sense(sense, 32);
|
||||
|
||||
PERR("Sense: key %x sub-key %x", sense[2], sense[12]);
|
||||
if (level < 3)
|
||||
return test_unit_ready(level + 1);
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Atapi_device::read_capacity()
|
||||
{
|
||||
unsigned char cmd[12];
|
||||
unsigned buffer[2];
|
||||
memset(cmd, 0, 12);
|
||||
memset(buffer, 0, 8);
|
||||
|
||||
cmd[0] = CMD_READ_CAPACITY;
|
||||
|
||||
if (!test_unit_ready())
|
||||
throw Io_error();
|
||||
|
||||
if (reg_packet(dev_num(), 12, cmd, 0, 8, (unsigned char *)buffer))
|
||||
throw Io_error();
|
||||
|
||||
_block_start = 0;
|
||||
_block_end = bswap32(buffer[0]);
|
||||
_block_size = bswap32(buffer[1]);
|
||||
|
||||
if (verbose)
|
||||
PDBG("First block: %u last block %u, block size %u",
|
||||
_block_start, _block_end, _block_size);
|
||||
}
|
||||
|
||||
|
||||
void Atapi_device::_read(Block::sector_t block_nr,
|
||||
Genode::size_t count,
|
||||
char *buffer,
|
||||
bool dma)
|
||||
{
|
||||
unsigned char cmd[12];
|
||||
memset(cmd, 0, 12);
|
||||
|
||||
cmd[0] = CMD_READ_10;
|
||||
|
||||
block_nr += _block_start;
|
||||
|
||||
if (block_nr < _block_start || block_nr + count > _block_end + 1)
|
||||
throw Io_error();
|
||||
|
||||
/* block address */
|
||||
cmd[2] = (block_nr >> 24) & 0xff;
|
||||
cmd[3] = (block_nr >> 16) & 0xff;
|
||||
cmd[4] = (block_nr >> 8) & 0xff;
|
||||
cmd[5] = block_nr & 0xff;
|
||||
|
||||
/* transfer length */
|
||||
cmd[7] = (count >> 8) & 0xff;
|
||||
cmd[8] = count & 0xff;
|
||||
|
||||
if (dma) {
|
||||
if (verbose)
|
||||
PDBG("DMA read: block %llu, count %zu, buffer: %p",
|
||||
block_nr, count, (void*)buffer);
|
||||
|
||||
if (dma_pci_packet(dev_num(), 12, cmd, 0, count * _block_size,
|
||||
(unsigned char*)buffer))
|
||||
throw Io_error();
|
||||
} else {
|
||||
if (reg_packet(dev_num(), 12, cmd, 0, count * _block_size,
|
||||
(unsigned char*)buffer))
|
||||
throw Io_error();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Atapi_device::_write(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char const *buffer,
|
||||
bool dma) { throw Io_error(); }
|
||||
|
||||
|
||||
Block::Session::Operations Atapi_device::ops()
|
||||
{
|
||||
Block::Session::Operations o;
|
||||
o.set_operation(Block::Packet_descriptor::READ);
|
||||
return o;
|
||||
}
|
@ -1,387 +0,0 @@
|
||||
|
||||
|
||||
MINDRVR
|
||||
Simple ATA/ATAPI Low Level Driver for Embedded Systems
|
||||
User's Guide
|
||||
|
||||
by Hale Landis
|
||||
|
||||
Version 0G and higher
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
MINDRVR is a simple version of ATADRVR. ATADRVR is Hale Landis'
|
||||
C code that shows the low level programming required to configure
|
||||
and execute commands on ATA and ATAPI devices. MINDRVR is
|
||||
suitable for embedded systems that have simple ATA interface
|
||||
hardware that is memory mapped. MINDRVR does not have some
|
||||
features of ATADRVR, such as, the command and low level trace
|
||||
facility, no support for ATA CHS addressing, no and checking of
|
||||
ATAPI command protocol errors.
|
||||
|
||||
MINDRVR is fully contained in two files: MINDRVR.H and
|
||||
MINDRVR.C.
|
||||
|
||||
MINDRVR, like ATADRVR, supports all of the ATA and ATA/ATAPI
|
||||
standards.
|
||||
|
||||
The MINDRVR C code has been placed into the public domain by Hale
|
||||
Landis. This C code has no copyright or other restrictions on
|
||||
how it is used.
|
||||
|
||||
|
||||
USING MINDRVR
|
||||
-------------
|
||||
|
||||
Normally a "file system" driver would call MINDRVR to perform actual
|
||||
phyical I/O operations on ATA or ATAPI devices using PIO or DMA data
|
||||
transfer modes. Any program or driver that calls MINDRVR must include
|
||||
the MINDRVR.H header file. MINDRVR.H defines all the MINDRVR public
|
||||
functions and public data.
|
||||
|
||||
The basics of using MINDRVR are:
|
||||
|
||||
1) Review the MINDRVR.H and MINDRVR.C files. In these files look
|
||||
for comment lines that have the string '!!!'. These comments
|
||||
explain the information you must provide at compile time, such
|
||||
as, the memory addresses for the ATA registers in your
|
||||
system's memory.
|
||||
|
||||
2) #include "mindrvr.h" in your program or driver that will call
|
||||
MINDRVR.
|
||||
|
||||
3) Call reg_config() so that MINDRVR can determine what devices
|
||||
are attached to the ATA host adapter.
|
||||
|
||||
4) Call reg_reset() or any of the other "reg_" functions to issue
|
||||
ATA or ATAPI commands in PIO data transfer mode. If your
|
||||
system can support ATA/ATAPI DMA then call the dma_pci_"
|
||||
functions to issue ATA or ATAPI commands in DMA data transfer
|
||||
mode.
|
||||
|
||||
Note that MINDRVR is designed for systems with a single ATA
|
||||
controller (single ATA inteface). If you system has multiple ATA
|
||||
controllers/interfaces then you will need to either: a) make a
|
||||
separate MINDRVR for each controller, or b) switch between
|
||||
controllers by swapping the MINDRVR configuration information
|
||||
between calls to MINDRVR.
|
||||
|
||||
|
||||
MINDRVR REFERENCE
|
||||
-----------------
|
||||
|
||||
Each of the public functions and symbols of MINDRVR are described
|
||||
below in alphabetical order.
|
||||
|
||||
Note that there is no "interrupt handler" defined by MINDRVR.
|
||||
You must supply the appropriate interrupt handler as described in
|
||||
MINDRVR.H and MINDRVR.C.
|
||||
|
||||
|
||||
Public Data
|
||||
-----------
|
||||
|
||||
unsigned char int_ata_status;
|
||||
|
||||
When using interrupts the interrupt handler must return this
|
||||
data. This is the interrupting device's ATA status as read by
|
||||
interrupt handler.
|
||||
|
||||
unsigned char int_bmide_status;
|
||||
|
||||
When using interrupts the interrupt handler must return this
|
||||
data. This is the interrupting controller's BMIDE status read
|
||||
by interrupt handler.
|
||||
|
||||
unsigned char int_use_intr_flag;
|
||||
|
||||
MINDRVR can be switched between polling mode and interrupt
|
||||
mode. Note that interrupt mode is required for DMA data
|
||||
transfers.
|
||||
|
||||
Make this value not zero to use interrupts.
|
||||
|
||||
unsigned char pio_xfer_width;
|
||||
|
||||
This variable controls the width of PIO data transfers.
|
||||
This variable can have the following values:
|
||||
|
||||
8 = 8-bits. PIO transfers will use 8-bit memory write/read
|
||||
when accessing the ATA Data register.
|
||||
|
||||
16 = 16-bits. PIO transfers will use 16-bit memory write/read
|
||||
when accessing the ATA Data register.
|
||||
|
||||
32 = 32-bits. PIO transfers will 32-bit memory write/read
|
||||
when accessing the ATA Data register.
|
||||
|
||||
Any other value is treated the same as 16.
|
||||
|
||||
struct REG_CMD_INFO
|
||||
{
|
||||
// command code
|
||||
unsigned char cmd; // command code
|
||||
// command parameters
|
||||
unsigned int fr; // feature (8 or 16 bits)
|
||||
unsigned int sc; // sec cnt (8 or 16 bits)
|
||||
unsigned int sn; // sec num (8 or 16 bits)
|
||||
unsigned int cl; // cyl low (8 or 16 bits)
|
||||
unsigned int ch; // cyl high (8 or 16 bits)
|
||||
unsigned char dh; // device head
|
||||
unsigned char dc; // device control
|
||||
long ns; // actual sector count
|
||||
int mc; // current multiple block setting
|
||||
unsigned char lbaSize; // size of LBA used
|
||||
#define LBACHS 0 // last command used ATA CHS (not supported by MINDRVR)
|
||||
// -or- last command was ATAPI PACKET command
|
||||
#define LBA28 28 // last command used ATA 28-bit LBA
|
||||
#define LBA48 48 // last command used ATA 48-bit LBA
|
||||
unsigned long lbaLow; // lower 32-bits of ATA LBA
|
||||
unsigned long lbaHigh; // upper 32-bits of ATA LBA
|
||||
// status and error regs
|
||||
unsigned char st; // status reg
|
||||
unsigned char as; // alt status reg
|
||||
unsigned char er ; // error reg
|
||||
// driver error codes
|
||||
unsigned char ec; // detailed error code
|
||||
unsigned char to; // not zero if time out error
|
||||
// additional result info
|
||||
long totalBytesXfer; // total bytes transfered
|
||||
long drqPackets; // number of PIO DRQ packets
|
||||
} ;
|
||||
struct REG_CMD_INFO reg_cmd_info;
|
||||
|
||||
This data structure contains information about the last reset or command
|
||||
that was executed.
|
||||
|
||||
int reg_config( void );
|
||||
|
||||
This function shoul be called so that MINDRVR can correctly
|
||||
configure itself. reg_config() sets the values into
|
||||
reg_config_info[2].
|
||||
|
||||
Note that the program calling MINDRVR may bypass calling this
|
||||
function as long as reg_config_info[] is set appropriately
|
||||
before attempting to execute any resets or commands.
|
||||
|
||||
int reg_config_info[2];
|
||||
|
||||
This array is set by calling reg_config(). reg_config_info[0]
|
||||
describes device 0 and reg_config_info[1] describes device 1.
|
||||
The possible values in these words are (see MINDRVR.H):
|
||||
REG_CONFIG_TYPE_NONE, REG_CONFIG_TYPE_UNKN,
|
||||
REG_CONFIG_TYPE_ATA, REG_CONFIG_TYPE_ATAPI.
|
||||
|
||||
Note that MINDRVR is not able to determine the type of some
|
||||
devices. However, after issuing some commands the calling
|
||||
program may be able to determine the exact type of a device.
|
||||
The calling program may change the values in this array but
|
||||
this must be done very carefully:
|
||||
|
||||
a) DO NOT CHANGE the value REG_CONFIG_TYPE_NONE.
|
||||
b) DO NOT CHANGE a value to REG_CONFIG_TYPE_NONE.
|
||||
c) The value REG_CONFIG_TYPE_UNKN can be changed to either
|
||||
REG_CONFIG_TYPE_ATA or REG_CONFIG_TYPE_ATAPI.
|
||||
|
||||
int reg_non_data_lba28( unsigned char dev, // device (0 or 1)
|
||||
unsigned char cmd, // command register
|
||||
int fr, // feature register
|
||||
int sc, // sector count
|
||||
long lba ); // LBA
|
||||
|
||||
Execute an ATA Non-Data command using LBA sector addressing.
|
||||
|
||||
Returns 0 if no error or 1 if there is an error. See the
|
||||
contents of reg_cmd_info.
|
||||
|
||||
int reg_non_data_lba48( unsigned char dev, // device (0 or 1)
|
||||
unsigned char cmd, // command register
|
||||
int fr, // feature register
|
||||
int sc, // sector count
|
||||
long lbahi, // LBA upper 16-bits
|
||||
long lbalo ); // LBA lower 32 bits
|
||||
|
||||
Execute an ATA Non-Data command using LBA sector addressing.
|
||||
|
||||
Returns 0 if no error or 1 if there is an error. See the
|
||||
contents of reg_cmd_info.
|
||||
|
||||
int reg_packet( unsigned char dev, // device (0 or 1)
|
||||
unsigned int cpbc, // command packet size
|
||||
unsigned char * cdbBufAddr, // CDB buffer
|
||||
int dir, // 0 for no data or read, 1 for write
|
||||
unsigned int dpbc, // max data packet size
|
||||
unsigned char * dataBufAddr ); // data packet buffer
|
||||
|
||||
Execute an ATAPI Packet (A0H) command in PIO mode. Note that
|
||||
the first byte of the Commmand Packet buffer is the command
|
||||
code of the "SCSI CDB" that is to be executed by the device.
|
||||
|
||||
Returns 0 if no error or 1 if there is an error. See the
|
||||
contents of reg_cmd_info.
|
||||
|
||||
int reg_pio_data_in_lba28( unsigned char dev, // device (0 or 1)
|
||||
unsigned char cmd, // command register
|
||||
int fr, // feature register
|
||||
int sc, // sector count
|
||||
long lba, // LBA
|
||||
unsigned char * bufAddr, // buffer address
|
||||
int numSect, // number of sectors to transfer
|
||||
int multiCnt ); // current multiple count
|
||||
|
||||
Execute an ATA PIO Data In command in LBA sector addressing
|
||||
mode.
|
||||
|
||||
numSect is the actual number of sectors to be transferred.
|
||||
This value may be different than the sc value.
|
||||
|
||||
If cmd is C4H (Read Multiple) then multiCnt MUST be set to the
|
||||
current sectors per block.
|
||||
|
||||
Returns 0 if no error or 1 if there is an error. See the
|
||||
contents of reg_cmd_info.
|
||||
|
||||
int reg_pio_data_in_lba48( unsigned char dev, // device (0 or 1)
|
||||
unsigned char cmd, // command register
|
||||
int fr, // feature register
|
||||
int sc, // sector count
|
||||
long lbahi, // LBA upper 16-bits
|
||||
long lbalo, // LBA lower 32 bits
|
||||
unsigned char * bufAddr, // buffer address
|
||||
int numSect, // number of sectors to transfer
|
||||
int multiCnt ); // current multiple count
|
||||
|
||||
Execute an ATA PIO Data In command in LBA sector addressing
|
||||
mode.
|
||||
|
||||
numSect is the actual number of sectors to be transferred.
|
||||
This value may be different than the sc value.
|
||||
|
||||
If cmd is C4H (Read Multiple) then multiCnt MUST be set to the
|
||||
current sectors per block.
|
||||
|
||||
Returns 0 if no error or 1 if there is an error. See the
|
||||
contents of reg_cmd_info.
|
||||
|
||||
int reg_pio_data_out_lba28( unsigned char dev, // device (0 or 1)
|
||||
unsigned char cmd, // command register
|
||||
int fr, // feature register
|
||||
int sc, // sector count
|
||||
long lba, // LBA
|
||||
unsigned char * bufAddr, // buffer address
|
||||
int numSect, // number of sectors to transfer
|
||||
int multiCnt ); // current multiple count
|
||||
|
||||
Execute an ATA PIO Data Out command in LBA sector addressing
|
||||
mode.
|
||||
|
||||
numSect is the actual number of sectors to be transferred.
|
||||
This value may be different than the sc value.
|
||||
|
||||
If cmd is C5H (Write Multiple) then multiCnt MUST be set to
|
||||
the current sectors per block.
|
||||
|
||||
Returns 0 if no error or 1 if there is an error. See the
|
||||
contents of reg_cmd_info.
|
||||
|
||||
int reg_pio_data_out_lba48( unsigned char dev, // device (0 or 1)
|
||||
unsigned char cmd, // command register
|
||||
int fr, // feature register
|
||||
int sc, // sector count
|
||||
long lbahi, // LBA upper 16-bits
|
||||
long lbalo, // LBA lower 32 bits
|
||||
unsigned char * bufAddr, // buffer address
|
||||
int numSect, // number of sectors to transfer
|
||||
int multiCnt ); // current multiple count
|
||||
|
||||
Execute an ATA PIO Data Out command in LBA sector addressing
|
||||
mode.
|
||||
|
||||
numSect is the actual number of sectors to be transferred.
|
||||
This value may be different than the sc value.
|
||||
|
||||
If cmd is C5H (Write Multiple) then multiCnt MUST be set to
|
||||
the current sectors per block.
|
||||
|
||||
Returns 0 if no error or 1 if there is an error. See the
|
||||
contents of reg_cmd_info.
|
||||
|
||||
int reg_reset( unsigned char devRtrn ); // device's data returned
|
||||
|
||||
Execute an ATA Soft Reset. Set devRtrn to 0 or 1 to determine
|
||||
which device's register contents are returned in reg_cmd_info.
|
||||
|
||||
|
||||
DMA Data Transfer Functions And Data
|
||||
------------------------------------
|
||||
|
||||
These functions setup PCI bus DMA (ATA Multiword or Ultra DMA)
|
||||
and perform ATA and ATAPI commands using DMA. The function
|
||||
dma_pci_config() MUST be called with no error before any of the
|
||||
other functions will attempt to execute a command.
|
||||
|
||||
int dma_pci_config( void );
|
||||
|
||||
Configure MINDRVR to use PCI bus DMA (ATA Multiword or Ultra
|
||||
DMA) on a PCI Bus Mastering ATA controller.
|
||||
|
||||
This function may not be needed in your system - see the MINDRVR.H
|
||||
and MINDRVR.C files.
|
||||
|
||||
int dma_pci_lba28( unsigned char dev, // device (0 or 1)
|
||||
unsigned char cmd, // command register
|
||||
int fr, // feature register
|
||||
int sc, // sector count
|
||||
long lba, // LBA
|
||||
unsigned char * bufAddr ); // buffer address
|
||||
|
||||
Execute an ATA Read DMA (C8H) or ATA Write DMA (CAH) command
|
||||
using LBA sector addressing.
|
||||
|
||||
Returns 0 if no error or 1 if there is an error. See the
|
||||
contents of reg_cmd_info.
|
||||
|
||||
int dma_pci_lba48( unsigned char dev, // device (0 or 1)
|
||||
unsigned char cmd, // command register
|
||||
int fr, // feature register
|
||||
int sc, // sector count
|
||||
long lbahi, // LBA upper 16-bits
|
||||
long lbalo, // LBA lower 32 bits
|
||||
unsigned char * bufAddr ); // buffer address
|
||||
|
||||
Execute an ATA Read DMA (C8H) or ATA Write DMA (CAH) command
|
||||
using LBA sector addressing.
|
||||
|
||||
Returns 0 if no error or 1 if there is an error. See the
|
||||
contents of reg_cmd_info.
|
||||
|
||||
int dma_pci_packet( unsigned char dev, // device (0 or 1)
|
||||
unsigned int cpbc, // command packet size
|
||||
unsigned char * cdbBufAddr, // CDB buffer
|
||||
int dir, // 0 for no data or read, 1 for write
|
||||
unsigned int dpbc, // max data packet size
|
||||
unsigned char * dataBufAddr ); // data packet buffer
|
||||
|
||||
Execute an ATAPI Packet (A0H) command in DMA mode. Note that
|
||||
the first byte of the Commmand Packet buffer is the command
|
||||
code of the "SCSI CDB" that is to be executed by the device.
|
||||
|
||||
Returns 0 if no error or 1 if there is an error. See the
|
||||
contents of reg_cmd_info.
|
||||
|
||||
|
||||
QUESTIONS OR PROBLEMS?
|
||||
----------------------
|
||||
|
||||