[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[tyndur-devel] [PATCH v2 13/24] cdi/scsi: Neu geschrieben
* Die týndur-Implementierung von cdi/scsi war im Prinzip nur zur
Benutzung mit dem CDI-ATAPI-Treiber geeignet. Dieser Patch macht die
Implementierung allgemeiner und auch für andere (zukünftige)
SCSI-CDI-Treiber einsatzfähig.
Signed-off-by: Max Reitz <max@xxxxxxxxxx>
---
src/modules/cdi/lib/cdi.c | 26 ++-
src/modules/cdi/lib/scsi/disk.c | 506 ++++++++++++++++++++++++++++++----------
2 files changed, 404 insertions(+), 128 deletions(-)
diff --git a/src/modules/cdi/lib/cdi.c b/src/modules/cdi/lib/cdi.c
index 5d519b4..11b3009 100644
--- a/src/modules/cdi/lib/cdi.c
+++ b/src/modules/cdi/lib/cdi.c
@@ -19,13 +19,17 @@
#include "cdi.h"
#include "cdi/audio.h"
#include "cdi/fs.h"
+#include "cdi/misc.h"
#include "cdi/pci.h"
+#include "cdi/scsi.h"
#include "cdi/storage.h"
extern void cdi_storage_driver_register(struct cdi_storage_driver* driver);
extern void cdi_audio_driver_register(struct cdi_audio_driver* driver);
extern void cdi_tyndur_net_device_init(struct cdi_device* device);
+void cdi_osdep_new_device(struct cdi_driver* drv, struct cdi_device* dev);
+
static list_t* drivers = NULL;
static void cdi_tyndur_run_drivers(void);
@@ -149,6 +153,7 @@ static void cdi_tyndur_init_pci_devices(void)
}
if (device != NULL) {
+ cdi_osdep_new_device(driver, device);
cdi_list_push(driver->devices, device);
printf("cdi: %x.%x.%x: Benutze Treiber %s\n",
pci->bus, pci->dev, pci->function, driver->name);
@@ -214,9 +219,8 @@ static void cdi_tyndur_run_drivers(void)
}
}
- /* Netzwerk ist schon initialisiert und SCSI hat kein RPC-Interface,
- * sondern muss mit scsidisk gelinkt werden. */
- if (driver->type != CDI_NETWORK && driver->type != CDI_SCSI) {
+ /* Netzwerk ist schon initialisiert */
+ if (driver->type != CDI_NETWORK) {
init_service_register((char*) driver->name);
}
}
@@ -277,6 +281,22 @@ list_t* cdi_tyndur_get_drivers(void)
return drivers;
}
+void cdi_osdep_new_device(struct cdi_driver* drv, struct cdi_device* dev)
+{
+ if (!dev) {
+ return;
+ }
+
+ dev->driver = drv;
+ cdi_list_push(drv->devices, dev);
+
+ switch ((int)drv->type) {
+ case CDI_SCSI:
+ cdi_scsi_device_init(CDI_UPCAST(dev, struct cdi_scsi_device, dev));
+ break;
+ }
+}
+
/**
* Wenn main nicht von einem Treiber ueberschrieben wird, ist hier der
* Einsprungspunkt. Die Standardfunktion ruft nur cdi_init() auf. Treiber, die
diff --git a/src/modules/cdi/lib/scsi/disk.c b/src/modules/cdi/lib/scsi/disk.c
index 49f408e..2f2b7bd 100644
--- a/src/modules/cdi/lib/scsi/disk.c
+++ b/src/modules/cdi/lib/scsi/disk.c
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2008 Mathias Gottschlag
* Copyright (C) 2009 Kevin Wolf
+ * Copyright (C) 2015 Max Reitz
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -26,14 +27,10 @@
#include <stdlib.h>
#include <cdi/lists.h>
+#include <cdi/misc.h>
#include <cdi/scsi.h>
#include <cdi/storage.h>
-#define big_endian_word(x) ((((x) & 0xFF) << 8) | (((x) & 0xFF00) >> 8))
-#define big_endian_dword(x) \
- ((big_endian_word((x) & 0xFFFF) << 16) | \
- (big_endian_word((x) >> 16)))
-
static int cdi_scsi_disk_read(struct cdi_storage_device* device,
uint64_t start, uint64_t count, void* buffer);
@@ -44,84 +41,272 @@ struct cdi_storage_driver cdi_scsi_disk_driver = {
.drv = {
.name = "scsidisk",
.type = CDI_STORAGE,
+ .bus = CDI_SCSI,
},
.read_blocks = cdi_scsi_disk_read,
.write_blocks = cdi_scsi_disk_write,
};
-// TODO: This is kind of incomplete... -.-
-// FIXME Das ist alles sehr CD-ROM-spezifisch hartkodiert...
-union cdrom_command
+typedef struct scsi_storage_device {
+ struct cdi_storage_device dev;
+ struct cdi_scsi_device* backend;
+ int lun;
+} scsi_storage_device_t;
+
+
+#define SCSI_REQUEST_SENSE 0x03
+#define SCSI_INQUIRY 0x12
+#define SCSI_START_STOP_UNIT 0x1b
+#define SCSI_READ_CAPACITY 0x25
+#define SCSI_READ10 0x28
+#define SCSI_WRITE10 0x2a
+#define SCSI_READ16 0x88
+#define SCSI_WRITE16 0x8a
+
+struct scsi_cmd {
+ uint8_t operation;
+} __attribute__((packed));
+
+struct scsi_cmd_request_sense {
+ struct scsi_cmd cmd;
+ uint8_t rsvd1;
+ uint16_t rsvd2;
+ uint8_t alloc_len;
+ uint8_t control;
+} __attribute__((packed));
+
+struct scsi_cmd_inquiry {
+ struct scsi_cmd cmd;
+ uint8_t rsvd;
+ uint8_t page;
+ uint16_t alloc_len;
+ uint8_t control;
+} __attribute__((packed));
+
+struct scsi_cmd_start_stop_unit {
+ struct scsi_cmd cmd;
+ uint8_t immed;
+ uint8_t rsvd;
+ uint8_t power_condition_modifier;
+ uint8_t flags_power_condition;
+ uint8_t control;
+} __attribute__((packed));
+
+struct scsi_cmd_read_capacity {
+ struct scsi_cmd cmd;
+ uint8_t rsvd1;
+ uint32_t lba;
+ uint32_t rsvd2;
+} __attribute__((packed));
+
+struct scsi_cmd_read10 {
+ struct scsi_cmd cmd;
+ uint8_t rsvd1;
+ uint32_t lba;
+ uint8_t rsvd2;
+ uint16_t length;
+ uint8_t control;
+} __attribute__((packed));
+
+struct scsi_cmd_write10 {
+ struct scsi_cmd cmd;
+ uint8_t rsvd1;
+ uint32_t lba;
+ uint8_t rsvd2;
+ uint16_t length;
+ uint8_t control;
+} __attribute__((packed));
+
+struct scsi_cmd_read16 {
+ struct scsi_cmd cmd;
+ uint8_t flags;
+ uint64_t lba;
+ uint32_t length;
+ uint8_t group;
+ uint8_t control;
+} __attribute__((packed));
+
+struct scsi_cmd_write16 {
+ struct scsi_cmd cmd;
+ uint8_t flags;
+ uint64_t lba;
+ uint32_t length;
+ uint8_t group;
+ uint8_t control;
+} __attribute__((packed));
+
+struct scsi_sense {
+ uint8_t resp_code;
+ uint8_t segment;
+ uint8_t flags;
+ uint32_t information;
+ uint8_t add_sense_length;
+ uint32_t cmd_spec_info;
+ uint8_t add_sense_code;
+ uint8_t add_sense_code_qual;
+ uint8_t field_replacable_unit_code;
+ uint8_t sense_key_spec1;
+ uint8_t sense_key_spec2;
+ uint8_t sense_key_spec3;
+} __attribute__((packed));
+
+struct scsi_inq_answer {
+ uint8_t dev_type;
+ uint8_t type_mod;
+ uint8_t version;
+ uint8_t resp_data_format;
+ uint8_t add_length;
+ uint8_t flags1, flags2, flags3;
+ char vendor[8];
+ char product[16];
+ uint32_t revision;
+ char serial[8];
+ uint8_t vendor_unique[12];
+ uint8_t clocking;
+ uint8_t rsvd1;
+ uint16_t version_desc[8];
+ uint8_t rsvd2[22];
+} __attribute__((packed));
+
+
+static int scsi_read10(scsi_storage_device_t* dev, uint32_t sector,
+ uint16_t nb_sectors, char* buffer)
{
- struct
- {
- uint8_t opcode;
- uint8_t reserved0;
- uint32_t address;
- uint8_t reserved1;
- uint16_t length;
- uint8_t reserved2;
- } __attribute__ ((packed)) dfl;
- struct
- {
- uint8_t opcode;
- uint8_t reserved0;
- uint32_t address;
- uint32_t length;
- uint16_t reserved1;
- } __attribute__ ((packed)) ext;
-};
-
-static int cdrom_read_partial(
- struct cdi_scsi_device *device, uint32_t sector, uint64_t nb_sectors,
- char *buffer)
-{
- struct cdi_scsi_packet packet;
- struct cdi_scsi_driver* drv = (struct cdi_scsi_driver*) device->dev.driver;
- union cdrom_command* cmd = (union cdrom_command*) &packet.command;
+ struct cdi_scsi_driver* drv = CDI_UPCAST(dev->backend->dev.driver,
+ struct cdi_scsi_driver, drv);
+ struct scsi_cmd_read10 r10 = {
+ .cmd = {
+ .operation = SCSI_READ10
+ },
+ .lba = cdi_cpu_to_be32(sector),
+ .length = cdi_cpu_to_be16(nb_sectors)
+ };
+ struct cdi_scsi_packet pkt = {
+ .lun = dev->lun,
+ .buffer = buffer,
+ .bufsize = (size_t)nb_sectors * dev->dev.block_size,
+ .direction = CDI_SCSI_READ,
+ .cmdsize = sizeof(r10)
+ };
+ memcpy(pkt.command, &r10, sizeof(r10));
+
+ if (drv->request(dev->backend, &pkt)) {
+ return -1;
+ }
- nb_sectors = (uint32_t) nb_sectors;
+ return 0;
+}
- memset(&packet, 0, sizeof(packet));
- packet.direction = CDI_SCSI_READ;
- packet.buffer = buffer;
- packet.bufsize = 2048 * nb_sectors;
- packet.cmdsize = 12;
+static int scsi_write10(scsi_storage_device_t* dev, uint32_t sector,
+ uint16_t nb_sectors, char* buffer)
+{
+ struct cdi_scsi_driver* drv = CDI_UPCAST(dev->backend->dev.driver,
+ struct cdi_scsi_driver, drv);
+ struct scsi_cmd_write10 w10 = {
+ .cmd = {
+ .operation = SCSI_WRITE10
+ },
+ .lba = cdi_cpu_to_be32(sector),
+ .length = cdi_cpu_to_be16(nb_sectors)
+ };
+ struct cdi_scsi_packet pkt = {
+ .lun = dev->lun,
+ .buffer = buffer,
+ .bufsize = (size_t)nb_sectors * dev->dev.block_size,
+ .direction = CDI_SCSI_WRITE,
+ .cmdsize = sizeof(w10)
+ };
+ memcpy(pkt.command, &w10, sizeof(w10));
+
+ if (drv->request(dev->backend, &pkt)) {
+ return -1;
+ }
- cmd->ext.opcode = 0xA8;
- cmd->ext.address = big_endian_dword(sector);
- cmd->ext.length = big_endian_dword(nb_sectors);
+ return 0;
+}
- if (drv->request(device, &packet)) {
+static int scsi_read16(scsi_storage_device_t* dev, uint64_t sector,
+ uint32_t nb_sectors, char* buffer)
+{
+ struct cdi_scsi_driver* drv = CDI_UPCAST(dev->backend->dev.driver,
+ struct cdi_scsi_driver, drv);
+ struct scsi_cmd_read16 r16 = {
+ .cmd = {
+ .operation = SCSI_READ16
+ },
+ .lba = cdi_cpu_to_be64(sector),
+ .length = cdi_cpu_to_be32(nb_sectors)
+ };
+ struct cdi_scsi_packet pkt = {
+ .lun = dev->lun,
+ .buffer = buffer,
+ .bufsize = (size_t)nb_sectors * dev->dev.block_size,
+ .direction = CDI_SCSI_READ,
+ .cmdsize = sizeof(r16)
+ };
+ memcpy(pkt.command, &r16, sizeof(r16));
+
+ if (drv->request(dev->backend, &pkt)) {
return -1;
}
- return nb_sectors;
+ return 0;
}
-static int cdrom_capacity(struct cdi_scsi_device *device,
- uint32_t* num_sectors, uint32_t* sector_size)
+static int scsi_write16(scsi_storage_device_t* dev, uint64_t sector,
+ uint32_t nb_sectors, char* buffer)
{
- uint32_t buffer[32];
- struct cdi_scsi_packet packet;
- struct cdi_scsi_driver* drv = (struct cdi_scsi_driver*) device->dev.driver;
- union cdrom_command* cmd = (union cdrom_command*) &packet.command;
-
- memset(&packet, 0, sizeof(packet));
- packet.direction = CDI_SCSI_READ;
- packet.buffer = buffer;
- packet.bufsize = sizeof(buffer);
- packet.cmdsize = 12;
+ struct cdi_scsi_driver* drv = CDI_UPCAST(dev->backend->dev.driver,
+ struct cdi_scsi_driver, drv);
+ struct scsi_cmd_write16 w16 = {
+ .cmd = {
+ .operation = SCSI_WRITE16
+ },
+ .lba = cdi_cpu_to_be64(sector),
+ .length = cdi_cpu_to_be32(nb_sectors)
+ };
+ struct cdi_scsi_packet pkt = {
+ .lun = dev->lun,
+ .buffer = buffer,
+ .bufsize = (size_t)nb_sectors * dev->dev.block_size,
+ .direction = CDI_SCSI_WRITE,
+ .cmdsize = sizeof(w16)
+ };
+ memcpy(pkt.command, &w16, sizeof(w16));
+
+ if (drv->request(dev->backend, &pkt)) {
+ return -1;
+ }
- cmd->ext.opcode = 0x25;
+ return 0;
+}
- if (drv->request(device, &packet)) {
+static int scsi_get_capacity(scsi_storage_device_t* dev)
+{
+ struct cdi_scsi_driver* drv = CDI_UPCAST(dev->backend->dev.driver,
+ struct cdi_scsi_driver, drv);
+ uint32_t capacity[2] = { 0 };
+ struct scsi_cmd_read_capacity read_cap = {
+ .cmd = {
+ .operation = SCSI_READ_CAPACITY
+ }
+ };
+ struct cdi_scsi_packet pkt = {
+ .lun = dev->lun,
+ .buffer = capacity,
+ .bufsize = sizeof(capacity),
+ .direction = CDI_SCSI_READ,
+ .cmdsize = sizeof(read_cap)
+ };
+ memcpy(pkt.command, &read_cap, sizeof(read_cap));
+
+ if (drv->request(dev->backend, &pkt)) {
return -1;
}
- *num_sectors = big_endian_dword(buffer[0]);
- *sector_size = big_endian_dword(buffer[1]);
+ dev->dev.block_count = (uint64_t)cdi_be32_to_cpu(capacity[0]) + 1;
+ dev->dev.block_size = cdi_be32_to_cpu(capacity[1]);
return 0;
}
@@ -160,27 +345,28 @@ static void cdrom_sense(FsSCSIDevice *device, uint32_t index)
}
#endif
-int cdrom_init(struct cdi_scsi_device* device)
+static int scsi_init(scsi_storage_device_t* dev)
{
- // Send inquiry command
- struct cdi_scsi_driver* drv = (struct cdi_scsi_driver*) device->dev.driver;
- struct cdi_scsi_packet packet;
- uint32_t status;
- unsigned char inqdata[96];
-
- memset(&packet, 0, sizeof(packet));
- memset(inqdata, 0xF0, sizeof(inqdata));
-
- packet.direction = CDI_SCSI_READ;
- packet.buffer = inqdata;
- packet.bufsize = 96;
- packet.cmdsize = 12;
-
- ((union cdrom_command*)packet.command)->dfl.opcode = 0x12;
- packet.command[4] = 96;
- status = drv->request(device, &packet);
- if (status)
- {
+ struct cdi_scsi_driver* drv = CDI_UPCAST(dev->backend->dev.driver,
+ struct cdi_scsi_driver, drv);
+ struct scsi_inq_answer inqdata;
+
+ struct scsi_cmd_inquiry inq = {
+ .cmd = {
+ .operation = SCSI_INQUIRY,
+ },
+ .alloc_len = cdi_cpu_to_be16(sizeof(inqdata))
+ };
+ struct cdi_scsi_packet inq_pkt = {
+ .lun = dev->lun,
+ .buffer = &inqdata,
+ .bufsize = sizeof(inqdata),
+ .direction = CDI_SCSI_READ,
+ .cmdsize = sizeof(inq)
+ };
+ memcpy(inq_pkt.command, &inq, sizeof(inq));
+
+ if (drv->request(dev->backend, &inq_pkt)) {
return -1;
}
@@ -189,56 +375,109 @@ int cdrom_init(struct cdi_scsi_device* device)
return KE_ERROR_UNKNOWN;
#endif
- // Start drive
- packet.direction = CDI_SCSI_NODATA;
- ((union cdrom_command*)packet.command)->dfl.opcode = 0x1B;
- packet.buffer = 0;
- packet.bufsize = 0;
- packet.command[4] = 0;
- packet.command[4] = 3;
- packet.cmdsize = 12;
-
- status = drv->request(device, &packet);
- if (status)
- {
- printf("cdrom %d: Start failed, %x\n", index, status);
- return -1;
+ struct scsi_cmd_start_stop_unit ssu = {
+ .cmd = {
+ .operation = SCSI_START_STOP_UNIT,
+ },
+ .flags_power_condition = 0x3 // LOEJ+START
+ };
+ struct cdi_scsi_packet ssu_pkt = {
+ .lun = dev->lun,
+ .direction = CDI_SCSI_NODATA,
+ .cmdsize = sizeof(ssu)
+ };
+ memcpy(ssu_pkt.command, &ssu, sizeof(ssu));
+
+ if ((inqdata.dev_type & 0x1f) == 0x05) { // CD/DVD
+ if (drv->request(dev->backend, &ssu_pkt)) {
+ printf("cdrom: Start failed\n");
+ return -1;
+ }
+ dev->dev.block_size = 2048;
+ } else {
+ dev->dev.block_size = 512;
}
return 0;
}
-static int cdi_scsi_disk_read(struct cdi_storage_device* device,
- uint64_t start, uint64_t count, void* buffer)
+static int cdi_scsi_disk_read(struct cdi_storage_device* dev, uint64_t start,
+ uint64_t count, void* buffer)
{
- struct cdi_scsi_device* dev = device->dev.backdev;
+ scsi_storage_device_t* scsi_dev = CDI_UPCAST(dev, scsi_storage_device_t,
+ dev);
+ bool read16 = (start >> 32) || ((start + count) >> 32);
while (count > 0) {
int ret;
+ uint32_t it_count;
+
+ if (read16) {
+ it_count = count > 0xffffffffu ? 0xffffffffu : count;
+ } else {
+ it_count = count > 0xffff ? 0xffff : count;
+ }
- ret = cdrom_read_partial(dev, start, count, buffer);
- if (ret <= 0) {
+ if (read16) {
+ ret = scsi_read16(scsi_dev, start, it_count, buffer);
+ } else {
+ ret = scsi_read10(scsi_dev, start, it_count, buffer);
+ }
+ if (ret < 0) {
return -1;
}
- start += ret;
- buffer += ret;
- count -= ret;
+
+ start += it_count;
+ count -= it_count;
+
+ buffer = (void*) ((uintptr_t)buffer + it_count * dev->block_size);
}
return 0;
}
-static int cdi_scsi_disk_write(struct cdi_storage_device* device,
- uint64_t start, uint64_t count, void* buffer)
+static int cdi_scsi_disk_write(struct cdi_storage_device* dev, uint64_t start,
+ uint64_t count, void* buffer)
{
- return -1;
+ scsi_storage_device_t* scsi_dev = CDI_UPCAST(dev, scsi_storage_device_t,
+ dev);
+ bool write16 = (start >> 32) || ((start + count) >> 32);
+
+ while (count > 0) {
+ int ret;
+ uint32_t it_count;
+
+ if (write16) {
+ it_count = count > 0xffffffffu ? 0xffffffffu : count;
+ } else {
+ it_count = count > 0xffff ? 0xffff : count;
+ }
+
+ if (write16) {
+ ret = scsi_write16(scsi_dev, start, it_count, buffer);
+ } else {
+ ret = scsi_write10(scsi_dev, start, it_count, buffer);
+ }
+ if (ret < 0) {
+ return -1;
+ }
+
+ start += it_count;
+ count -= it_count;
+
+ buffer = (void*) ((uintptr_t)buffer + it_count * dev->block_size);
+ }
+
+ return 0;
}
-static void driver_init(void)
+static void driver_init(struct cdi_driver* drv)
{
static int initialised = 0;
if (!initialised) {
+ cdi_scsi_disk_driver.drv.name = strdup(drv->name);
+
cdi_driver_register(&cdi_scsi_disk_driver.drv);
cdi_storage_driver_init(&cdi_scsi_disk_driver);
}
@@ -246,30 +485,47 @@ static void driver_init(void)
int cdi_scsi_disk_init(struct cdi_scsi_device* device)
{
- struct cdi_storage_device* frontdev;
- uint32_t num_sectors = 0, sector_size = 0;
+ int luns = device->lun_count ?: 1;
- driver_init();
+ driver_init(device->dev.driver);
- if (cdrom_init(device)) {
- return -1;
- }
+ for (int lun = 0; lun < luns; lun++) {
+ scsi_storage_device_t* dev = calloc(1, sizeof(*dev));
+ bool inquiry_failed;
+
+ dev->backend = device;
+ dev->lun = lun;
+
+ inquiry_failed = scsi_init(dev) < 0;
- frontdev = calloc(1, sizeof(*frontdev));
- frontdev->dev.driver = (struct cdi_driver*) &cdi_scsi_disk_driver;
- frontdev->dev.name = device->dev.name;
+ if (scsi_get_capacity(dev) < 0) {
+ if (inquiry_failed) {
+ /* We're fine as long as either INQUIRY or READ_CAPACITY work -
+ * but if both fail, there's not a lot we can do. */
+ fprintf(stderr,
+ "[scsi] Failed to initialize LUN %i of device %s\n",
+ lun, device->dev.name);
- // Groesse des Mediums bestimmen. Falls keins eingelegt ist, brauchen wir
- // trotzdem zumindest eine Sektorgroesse.
- cdrom_capacity(device, &num_sectors, §or_size);
- frontdev->block_size = sector_size ? sector_size : 2048;
- frontdev->block_count = num_sectors;
+ free(dev);
+ continue;
+ }
- frontdev->dev.backdev = device;
- device->frontdev = frontdev;
+ /* Apparently this is common for CD-ROM devices; at least the old
+ * SCSI implementation didn't care about it. A default block_size
+ * has been set by scsi_init(). */
+ dev->dev.block_count = 0xffffffffu;
+ }
+
+ dev->dev.dev.driver = (struct cdi_driver*) &cdi_scsi_disk_driver;
+ if (luns > 1) {
+ asprintf((char**) &dev->dev.dev.name, "%s_l%i",
+ device->dev.name, lun);
+ } else {
+ dev->dev.dev.name = strdup(device->dev.name);
+ }
- // LostIO-Verzeichnisknoten anlegen
- cdi_storage_device_init(frontdev);
+ cdi_storage_device_init(&dev->dev);
+ }
return 0;
}
--
2.6.3