[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[cdi-devel] [PATCH v2 5/5] ahci: Add CD-ROM support
Signed-off-by: Kevin Wolf <kevin@xxxxxxxxxx>
---
ahci/ahci.h | 10 +++++
ahci/disk.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
ahci/main.c | 13 +++++++
3 files changed, 130 insertions(+), 3 deletions(-)
diff --git a/ahci/ahci.h b/ahci/ahci.h
index 46e8fd5..24a830b 100644
--- a/ahci/ahci.h
+++ b/ahci/ahci.h
@@ -29,6 +29,7 @@
#include <stdint.h>
#include "cdi/storage.h"
+#include "cdi/scsi.h"
#include "cdi/mem.h"
#define BIT(x) (1 << x)
@@ -45,6 +46,7 @@ enum {
ATA_CMD_READ_DMA_EXT = 0x25,
ATA_CMD_WRITE_DMA = 0xca,
ATA_CMD_WRITE_DMA_EXT = 0x35,
+ ATA_CMD_PACKET = 0xa0,
ATA_CMD_IDENTIFY_DEVICE = 0xec,
};
@@ -119,6 +121,7 @@ enum {
enum {
SATA_SIG_DISK = 0x00000101,
SATA_SIG_PACKET = 0xeb140101,
+ SATA_SIG_QEMU_CD = 0xeb140000, /* Broken value in qemu < 2.2 */
};
struct ahci_port {
@@ -162,6 +165,11 @@ struct ahci_disk {
bool lba48;
};
+struct ahci_atapi {
+ struct cdi_scsi_device scsi;
+ struct ahci_disk disk;
+};
+
enum {
FIS_TYPE_H2D = 0x27,
FIS_TYPE_D2H = 0x34,
@@ -276,6 +284,7 @@ struct ahci_prd {
enum {
CMD_HEADER_F_FIS_LENGTH_5_DW = (5 << 0),
CMD_HEADER_F_WRITE = (1 << 6),
+ CMD_HEADER_F_ATAPI = (1 << 5),
};
struct cmd_header {
@@ -331,6 +340,7 @@ void ahci_port_comreset(struct ahci_device* ahci, int port);
/* ahci/disk.c */
extern struct cdi_storage_driver ahci_disk_driver;
+extern struct cdi_scsi_driver ahci_atapi_driver;
#endif
diff --git a/ahci/disk.c b/ahci/disk.c
index 0879f20..aeeb65a 100644
--- a/ahci/disk.c
+++ b/ahci/disk.c
@@ -33,6 +33,7 @@
#include "ahci.h"
#define DISK_DRIVER_NAME "ahci-disk"
+#define ATAPI_DRIVER_NAME "ahci-cd"
/**
* This function is called for any requests (even succeeding ones). Its job is
@@ -72,7 +73,7 @@ static int ahci_request_handle_error(struct ahci_disk* disk, uint32_t is)
}
static int ahci_request(struct ahci_disk* disk, int cmd, uint64_t lba,
- uint64_t bytes, struct cdi_mem_area* buf)
+ uint64_t bytes, struct cdi_mem_area* buf, void* acmd)
{
struct ahci_port *port = &disk->ahci->port[disk->port];
uint32_t flags, device;
@@ -106,10 +107,17 @@ static int ahci_request(struct ahci_disk* disk, int cmd, uint64_t lba,
.dbc = bytes - 1,
};
+ if (acmd != NULL) {
+ memcpy(port->cmd_table->acmd, acmd, 16);
+ }
+
flags = CMD_HEADER_F_FIS_LENGTH_5_DW;
if (cmd == ATA_CMD_WRITE_DMA || cmd == ATA_CMD_WRITE_DMA_EXT) {
flags |= CMD_HEADER_F_WRITE;
}
+ if (cmd == ATA_CMD_PACKET) {
+ flags |= CMD_HEADER_F_ATAPI;
+ }
port->cmd_list[0] = (struct cmd_header) {
.flags = flags,
@@ -147,7 +155,7 @@ static int ahci_identify(struct ahci_disk* disk)
return -1;
}
- ret = ahci_request(disk, ATA_CMD_IDENTIFY_DEVICE, 0, 512, buf);
+ ret = ahci_request(disk, ATA_CMD_IDENTIFY_DEVICE, 0, 512, buf, NULL);
if (ret == 0) {
words = buf->vaddr;
disk->lba48 = !!(words[86] & (1 << 10));
@@ -186,7 +194,7 @@ static int ahci_rw_blocks(struct cdi_storage_device* device, uint64_t start,
memcpy(buf->vaddr, buffer, bs * count);
}
- ret = ahci_request(disk, cmd, start, bs * count, buf);
+ ret = ahci_request(disk, cmd, start, bs * count, buf, NULL);
if (ret == 0 && read) {
memcpy(buffer, buf->vaddr, bs * count);
@@ -348,6 +356,88 @@ static int ahci_disk_driver_destroy(void)
return 0;
}
+static struct cdi_device* atapi_init_device(struct cdi_bus_data* bus_data)
+{
+ struct ahci_bus_data* ahci_bus_data = (struct ahci_bus_data*) bus_data;
+ struct ahci_atapi* atapi;
+ struct ahci_disk* disk;
+
+ if (bus_data->bus_type != CDI_AHCI) {
+ return NULL;
+ }
+
+ atapi = calloc(1, sizeof(*atapi));
+ disk = &atapi->disk;
+ disk->ahci = ahci_bus_data->ahci;
+ disk->port = ahci_bus_data->port;
+ disk->storage.block_size = 2048;
+
+ if (ahci_disk_init(disk) < 0) {
+ goto fail;
+ }
+
+ atapi->scsi.type = CDI_STORAGE;
+ atapi->scsi.dev.driver = &ahci_atapi_driver.drv;
+ asprintf((char**) &atapi->scsi.dev.name, "atapi%d", disk->port);
+
+ cdi_scsi_device_init(&atapi->scsi);
+
+ return &atapi->scsi.dev;
+
+fail:
+ free(atapi);
+ return NULL;
+}
+
+static void atapi_remove_device(struct cdi_device* device)
+{
+ /* TODO */
+}
+
+/**
+ * Initialises the AHCI ATAPI driver
+ */
+static int ahci_atapi_driver_init(void)
+{
+ cdi_scsi_driver_init(&ahci_atapi_driver);
+ return 0;
+}
+
+/**
+ * Deinitialises the AHCI ATAPI driver
+ */
+static int ahci_atapi_driver_destroy(void)
+{
+ cdi_scsi_driver_destroy(&ahci_atapi_driver);
+
+ /* TODO Deinitialise all devices */
+
+ return 0;
+}
+
+int ahci_scsi_request(struct cdi_scsi_device* scsi,
+ struct cdi_scsi_packet* packet)
+{
+ struct ahci_atapi* atapi = (struct ahci_atapi*) scsi;
+ struct ahci_disk* disk = &atapi->disk;
+ struct cdi_mem_area* buf;
+ int ret;
+
+ buf = cdi_mem_alloc(packet->bufsize,
+ CDI_MEM_PHYS_CONTIGUOUS | CDI_MEM_DMA_4G | 1);
+ if (buf == NULL) {
+ return -1;
+ }
+
+ memcpy(buf->vaddr, packet->buffer, packet->bufsize);
+ ret = ahci_request(disk, ATA_CMD_PACKET, 0, packet->bufsize, buf,
+ packet->command);
+ memcpy(packet->buffer, buf->vaddr, packet->bufsize);
+
+ cdi_mem_free(buf);
+ return ret;
+}
+
struct cdi_storage_driver ahci_disk_driver = {
.drv = {
@@ -363,4 +453,18 @@ struct cdi_storage_driver ahci_disk_driver = {
.write_blocks = ahci_write_blocks,
};
+struct cdi_scsi_driver ahci_atapi_driver = {
+ .drv = {
+ .type = CDI_SCSI,
+ .bus = CDI_AHCI,
+ .name = ATAPI_DRIVER_NAME,
+ .init = ahci_atapi_driver_init,
+ .destroy = ahci_atapi_driver_destroy,
+ .init_device = atapi_init_device,
+ .remove_device = atapi_remove_device,
+ },
+ .request = ahci_scsi_request,
+};
+
CDI_DRIVER(DISK_DRIVER_NAME, ahci_disk_driver)
+CDI_DRIVER(ATAPI_DRIVER_NAME, ahci_atapi_driver)
diff --git a/ahci/main.c b/ahci/main.c
index 6a69775..1afc03c 100644
--- a/ahci/main.c
+++ b/ahci/main.c
@@ -120,6 +120,19 @@ static int ahci_init_hardware(struct ahci_device* ahci)
&ahci_disk_driver.drv);
break;
}
+ case SATA_SIG_QEMU_CD:
+ case SATA_SIG_PACKET: {
+ struct ahci_bus_data bus_data = {
+ .cdi = {
+ .bus_type = CDI_AHCI,
+ },
+ .ahci = ahci,
+ .port = port,
+ };
+ cdi_provide_device_internal_drv(&bus_data.cdi,
+ &ahci_atapi_driver.drv);
+ break;
+ }
default:
printf("ahci: Can't handle device with signature %x", sig);
break;
--
1.7.7