genode/repos/os/src/drivers/atapi/ata_device.cc

318 lines
7.4 KiB
C++

/*
* \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;
}