/* * \brief USB storage glue * \author Sebastian Sumpf * \date 2012-05-06 */ /* * Copyright (C) 2012-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. */ #include #include #include #include #include #include #include #include #include #include #include #include "signal.h" static Signal_helper *_signal = 0; class Storage_device : public Genode::List::Element, public Block::Driver { private: Genode::size_t _block_size; Block::sector_t _block_count; struct scsi_device *_sdev; static void _sync_done(struct scsi_cmnd *cmnd) { complete((struct completion *)cmnd->back); } static void _async_done(struct scsi_cmnd *cmnd); void _capacity() { struct completion comp; struct scsi_cmnd *cmnd = _scsi_alloc_command(); /* alloc data for command */ scsi_alloc_buffer(8, cmnd); cmnd->cmnd[0] = READ_CAPACITY; cmnd->cmd_len = 10; cmnd->device = _sdev; cmnd->sc_data_direction = DMA_FROM_DEVICE; init_completion(&comp); cmnd->back = ∁ cmnd->scsi_done = _sync_done; _sdev->host->hostt->queuecommand(_sdev->host, cmnd); wait_for_completion(&comp); Genode::uint32_t *data = (Genode::uint32_t *)scsi_buffer_data(cmnd); _block_count = host_to_big_endian(data[0]); _block_size = host_to_big_endian(data[1]); /* if device returns the highest block number */ if (!_sdev->fix_capacity) _block_count++; if (verbose) PDBG("block size: %zu block count: %llu", _block_size, _block_count); scsi_free_buffer(cmnd); _scsi_free_command(cmnd); } void _io(Block::sector_t block_nr, Genode::size_t block_count, Block::Packet_descriptor packet, Genode::addr_t phys, bool read) { if (block_nr > _block_count) throw Io_error(); if (verbose) PDBG("PACKET: phys: %lx block: %llu count: %zu %s", phys, block_nr, block_count, read ? "read" : "write"); /* check if we can call queuecommand */ struct us_data *us = (struct us_data *) _sdev->host->hostdata; if (us->srb != NULL) throw Request_congestion(); struct scsi_cmnd *cmnd = _scsi_alloc_command(); cmnd->cmnd[0] = read ? READ_10 : WRITE_10; cmnd->cmd_len = 10; cmnd->device = _sdev; cmnd->sc_data_direction = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; cmnd->scsi_done = _async_done; Block::Packet_descriptor *p = new (Genode::env()->heap()) Block::Packet_descriptor(); *p = packet; cmnd->packet = (void *)p; Genode::uint32_t be_block_nr = host_to_big_endian(block_nr); memcpy(&cmnd->cmnd[2], &be_block_nr, 4); /* transfer one block */ Genode::uint16_t be_block_count = host_to_big_endian(block_count); memcpy(&cmnd->cmnd[7], &be_block_count, 2); /* setup command */ scsi_setup_buffer(cmnd, block_count * _block_size, (void *)0, phys); /* * Required by 'last_sector_hacks' in 'drivers/usb/storage/transprot.c */ struct request req; req.rq_disk = 0; cmnd->request = &req; /* send command to host driver */ _sdev->host->hostt->queuecommand(_sdev->host, cmnd); } public: Storage_device(struct scsi_device *sdev) : Block::Driver(), _sdev(sdev) { /* read device capacity */ _capacity(); } Genode::size_t block_size() { return _block_size; } Block::sector_t block_count() { return _block_count; } Block::Session::Operations ops() { Block::Session::Operations o; o.set_operation(Block::Packet_descriptor::READ); o.set_operation(Block::Packet_descriptor::WRITE); return o; } void read_dma(Block::sector_t block_number, Genode::size_t block_count, Genode::addr_t phys, Block::Packet_descriptor &packet) { _io(block_number, block_count, packet, phys, true); } void write_dma(Block::sector_t block_number, Genode::size_t block_count, Genode::addr_t phys, Block::Packet_descriptor &packet) { _io(block_number, block_count, packet, phys, false); } bool dma_enabled() { return true; } Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size) { return Backend_memory::alloc(size, Genode::UNCACHED); } void free_dma_buffer(Genode::Ram_dataspace_capability cap) { return Backend_memory::free(cap); } }; void Storage::init(Server::Entrypoint &ep) { _signal = new (Genode::env()->heap()) Signal_helper(ep); } struct Factory : Block::Driver_factory { Storage_device device; Factory(struct scsi_device *sdev) : device(sdev) {} Block::Driver *create() { return &device; } void destroy(Block::Driver *driver) { } }; static Storage_device *device = nullptr; static work_struct delayed; extern "C" void ack_packet(work_struct *work) { Block::Packet_descriptor *packet = static_cast(work->data); if (verbose) PDBG("ACK packet for block: %llu", packet->block_number()); device->ack_packet(*packet); Genode::destroy(Genode::env()->heap(), packet); } void scsi_add_device(struct scsi_device *sdev) { using namespace Genode; static bool announce = false; static struct Factory factory(sdev); device = &factory.device; /* * XXX move to 'main' */ if (!announce) { PREPARE_WORK(&delayed, ack_packet); static Block::Root root(_signal->ep(), env()->heap(), factory); env()->parent()->announce(_signal->ep().rpc_ep().manage(&root)); announce = true; } } void Storage_device::_async_done(struct scsi_cmnd *cmnd) { /* * Schedule packet ack, because we are called here in USB storage thread * context, the from code that will clear the command queue later, so we * cannot send the next packet from here */ delayed.data = cmnd->packet; schedule_work(&delayed); scsi_free_buffer(cmnd); _scsi_free_command(cmnd); }