From 8843564850e2ebc2c60ba1064571569f02327e25 Mon Sep 17 00:00:00 2001 From: Sebastian Sumpf Date: Wed, 17 Dec 2014 16:16:36 +0100 Subject: [PATCH] usb_drv: USB terminal driver Issue #1329 --- repos/dde_linux/run/usb_terminal.run | 130 +++++++ .../dde_linux/src/server/usb_terminal/main.cc | 327 ++++++++++++++++++ .../src/server/usb_terminal/target.mk | 3 + 3 files changed, 460 insertions(+) create mode 100644 repos/dde_linux/run/usb_terminal.run create mode 100644 repos/dde_linux/src/server/usb_terminal/main.cc create mode 100644 repos/dde_linux/src/server/usb_terminal/target.mk diff --git a/repos/dde_linux/run/usb_terminal.run b/repos/dde_linux/run/usb_terminal.run new file mode 100644 index 000000000..b200c40ef --- /dev/null +++ b/repos/dde_linux/run/usb_terminal.run @@ -0,0 +1,130 @@ +# +# Check if USB_RAW_DEVICE is set for Qemu +# +if {![info exists ::env(USB_RAW_DEVICE)] && [is_qemu_available]} { + puts "\nPlease define USB_RAW_DEVICE environment variable and set it to your USB device \n" + exit 0 +} + +# +# Build +# +set build_components { + core init + drivers/timer + drivers/usb + server/usb_terminal + test/terminal_echo +} + +lappend_if [have_spec acpi] build_components drivers/acpi +lappend_if [have_spec pci] build_components drivers/pci +lappend_if [have_spec pci] build_components drivers/pci/device_pd +lappend_if [have_spec platform_arndale] build_components drivers/platform +lappend_if [have_spec gpio] build_components drivers/gpio + +build $build_components + +create_boot_directory + +# +# Generate config +# + +append config { + + + + + + + + + + + + + + + + + } + +append_if [have_spec platform_arndale] config { + + + + } + +append_if [have_spec gpio] config { + + + + + } + +append_if [have_spec acpi] config { + + + + + + + + + + + + } + +append_if [expr ![have_spec acpi] && [have_spec pci]] config { + + + + } + +append config { + + + + + + + + + + + + + + + + + + +} + +install_config $config + +# +# Boot modules +# + +# generic modules +set boot_modules { + core init timer usb_drv test-terminal_echo + usb_terminal +} + +lappend_if [have_spec acpi] boot_modules acpi_drv +lappend_if [have_spec pci] boot_modules pci_drv +lappend_if [have_spec nova] boot_modules pci_device_pd +lappend_if [have_spec platform_arndale] boot_modules platform_drv +lappend_if [have_spec gpio] boot_modules gpio_drv + +build_boot_image $boot_modules + +append qemu_args " -m 256 -nographic -usb -usbdevice host:$::env(USB_RAW_DEVICE) -nographic" + + +run_genode_until forever diff --git a/repos/dde_linux/src/server/usb_terminal/main.cc b/repos/dde_linux/src/server/usb_terminal/main.cc new file mode 100644 index 000000000..e0e0d7994 --- /dev/null +++ b/repos/dde_linux/src/server/usb_terminal/main.cc @@ -0,0 +1,327 @@ +/** + * \brief PL2303-USB-UART driver that exposes a terminal session + * \author Sebastian Sumpf + * \date 2014-12-17 + */ + +/* + * Copyright (C) 2014 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 + +constexpr bool verbose = false; + +namespace Terminal { + class Main; + class Root_component; + class Session_component; + using namespace Genode; +} + + +namespace Usb { + struct Pl2303_driver; + using namespace Genode; +} + + +struct pl2303_config +{ + Genode::uint32_t baud = 115200; + Genode::uint8_t stop_bits = 0; + Genode::uint8_t parity = 0; + Genode::uint8_t data_bits = 8; +} __attribute__((packed)); + + +struct Usb::Pl2303_driver : Completion, + Ring_buffer +{ + enum { VENDOR = 0x67b, PRODUCT = 0x2303 /* Prolific 2303 seriral port */ }; + + enum { + PACKET_BUFFER = 2, /* number of read packets */ + RING_SIZE = 4096, /* size of ring buffer */ + MAX_PACKET_SIZE = 256 /* max packet size for bulk request */ + }; + + /* Pl2303 endpoints */ + enum Endpoints { STATUS = 0, OUT = 1, IN = 2 }; + + Server::Entrypoint &ep; + Server::Signal_rpc_member dispatcher{ ep, *this, &Pl2303_driver::state_change }; + Genode::Allocator_avl alloc; + Usb::Connection connection{ &alloc, VENDOR, PRODUCT, 512 * 1024, dispatcher }; + Usb::Device device; + Signal_context_capability connected_sigh; + Signal_context_capability read_sigh; + + + Pl2303_driver(Server::Entrypoint &ep) + : ep(ep), alloc(Genode::env()->heap()), + device(Genode::env()->heap(), connection, ep) + { } + + + /* send control message for to read/write from/to PL2303 endpoint */ + void pl2303_magic(Usb::Interface &iface, uint16_t value, uint16_t index, bool read) + { + Usb::Packet_descriptor p = iface.alloc(0); + + uint8_t request_type = read ? Usb::ENDPOINT_IN : Usb::ENDPOINT_OUT + | Usb::TYPE_VENDOR + | Usb::RECIPIENT_DEVICE; + + iface.control_transfer(p, request_type, 1, value, index,100); + iface.release(p); + } + + void pl2303_magic_read(Usb::Interface &iface, uint16_t value, uint16_t index) { + pl2303_magic(iface, value, index, true); } + + void pl2303_magic_write(Usb::Interface &iface, uint16_t value, uint16_t index) { + pl2303_magic(iface, value, index, false); } + + void bulk_packet(Packet_descriptor &p) + { + Interface iface = device.interface(0); + + /* error or write packet */ + if (!p.succeded || !p.is_read_transfer()) { + iface.release(p); + return; + } + + /* buffer data */ + bool send_sigh = empty() && p.transfer.actual_size; + char *data = (char *)iface.content(p); + + try { + for (int i = 0; i < p.transfer.actual_size; i++) + add(data[i]); + } catch (Ring_buffer::Overflow) { + PWRN("Pl2303 buffer overflow"); + } + + /* submit back to device (async) */ + iface.submit(p); + + /* notify client */ + if (send_sigh && read_sigh.valid()) + Signal_transmitter(read_sigh).submit(); + } + + void complete(Packet_descriptor &p) + { + switch (p.type) { + case Packet_descriptor::BULK: bulk_packet(p); break; + default: break; + } + } + + size_t write(void *dst, size_t num_bytes) + { + num_bytes = min(num_bytes, MAX_PACKET_SIZE); + + Interface &iface = device.interface(0); + Endpoint &ep = iface.endpoint(OUT); + Packet_descriptor p = iface.alloc(num_bytes); + + memcpy(iface.content(p), dst, num_bytes); + iface.bulk_transfer(p, ep, 0, false, this); + + return num_bytes; + } + + /** + * Init 2303 controller + */ + void init() + { + /* read device configuration */ + device.update_config(); + + enum { BUF = 128 }; + char buffer[BUF]; + + PINF("PL2303 controller: ready"); + PINF("Manufacturer : %s", device.manufactorer_string.to_char(buffer, BUF)); + PINF("Product : %s", device.product_string.to_char(buffer, BUF)); + + Interface &iface = device.interface(0); + iface.claim(); + + /* undocumented magic, taken from Linux and GRUB */ + pl2303_magic_read (iface, 0x8484, 0x0000); + pl2303_magic_write(iface, 0x0404, 0x0000); + pl2303_magic_read (iface, 0x8484, 0x0000); + pl2303_magic_read (iface, 0x8383, 0x0000); + pl2303_magic_read (iface, 0x8484, 0x0000); + pl2303_magic_write(iface, 0x0404, 0x0001); + pl2303_magic_read (iface, 0x8484, 0x0000); + pl2303_magic_read (iface, 0x8383, 0x0000); + pl2303_magic_write(iface, 0x0000, 0x0001); + pl2303_magic_write(iface, 0x0001, 0x0000); + pl2303_magic_write(iface, 0x0002, 0x0044); + pl2303_magic_write(iface, 0x0008, 0x0000); + pl2303_magic_write(iface, 0x0009, 0x0000); + + Usb::Packet_descriptor p = iface.alloc(sizeof(pl2303_config)); + iface.control_transfer(p, 0xa1, 0x21, 0, 0, 100); + + /* set baud rate to 115200 */ + pl2303_config *cfg = (pl2303_config *)iface.content(p); + + if (verbose) + PDBG("GET_LINE %u %u %u %u\n", + cfg->baud, cfg->stop_bits, cfg->parity, cfg->data_bits); + + pl2303_config cfg_new; + *cfg = cfg_new; + + if (verbose) + PDBG("SET_LINE %u %u %u %u\n", + cfg_new.baud, cfg_new.stop_bits, cfg_new.parity, cfg_new.data_bits); + + iface.control_transfer(p, 0x21, 0x20, 0, 0, 100); + iface.release(p); + + /* finish it */ + pl2303_magic_write(iface, 0x0, 0x0); + + /* submit read transfers (async) */ + Usb::Endpoint &ep = iface.endpoint(IN); + for (int i = 0; i < PACKET_BUFFER; i++) { + p = iface.alloc(ep.max_packet_size); + iface.bulk_transfer(p, ep, 0, false, this); + } + + /* send signal to terminal client */ + if (connected_sigh.valid()) + Signal_transmitter(connected_sigh).submit(); + } + + /** + * Called from USB driver when the desired device is active + */ + void state_change(unsigned) + { + if (connection.plugged()) { + if (verbose) + PDBG("Device: Plugged"); + init(); + } + else if (verbose) + PDBG("Device: Unplugged"); + } +}; + + +class Terminal::Session_component : public Rpc_object +{ + private: + + Attached_ram_dataspace _io_buffer; + Usb::Pl2303_driver &_driver; + + public: + + Session_component(size_t io_buffer_size, Usb::Pl2303_driver &driver) + : _io_buffer(env()->ram_session(), io_buffer_size), _driver(driver) + { } + + void read_avail_sigh(Signal_context_capability sigh) + { + _driver.read_sigh = sigh; + } + + void connected_sigh(Signal_context_capability sigh) + { + _driver.connected_sigh = sigh; + } + + Size size() { return Size(0, 0); } + bool avail() { return !_driver.empty(); } + + size_t _read(size_t dst_len) + { + size_t num_bytes = 0; + char *data = _io_buffer.local_addr(); + + for (;num_bytes < dst_len && !_driver.empty(); num_bytes++) + data[num_bytes] = _driver.get(); + + return num_bytes; + } + + void _write(size_t num_bytes) + { + char *dst = _io_buffer.local_addr(); + while (num_bytes) { + size_t size = _driver.write(dst, num_bytes); + num_bytes -= size; + dst += size; + } + } + + Dataspace_capability _dataspace() { return _io_buffer.cap(); } + + size_t read(void *buf, size_t) { return 0; } + size_t write(void const *buf, size_t) { return 0; } +}; + + +class Terminal::Root_component : public Genode::Root_component +{ + private: + + Usb::Pl2303_driver _driver; + + protected: + + Session_component *_create_session(char const *args) + { + size_t const io_buffer_size = 4096; + return new (md_alloc()) Session_component(io_buffer_size, _driver); + } + + public: + + Root_component(Server::Entrypoint &ep, Genode::Allocator &md_alloc) + : Genode::Root_component(&ep.rpc_ep(), &md_alloc), + _driver(ep) + { } +}; + + +struct Terminal::Main +{ + Server::Entrypoint &ep; + Sliced_heap heap = { env()->ram_session(), env()->rm_session() }; + Root_component terminal_root = { ep, heap }; + + Main(Server::Entrypoint &ep) : ep(ep) + { + env()->parent()->announce(ep.manage(terminal_root)); + } +}; + + +namespace Server { + char const *name() { return "usb_terminal_ep"; }; + size_t stack_size() { return 2*1024*sizeof(long); } + + void construct(Entrypoint &ep) + { + static Terminal::Main server(ep); + } +} diff --git a/repos/dde_linux/src/server/usb_terminal/target.mk b/repos/dde_linux/src/server/usb_terminal/target.mk new file mode 100644 index 000000000..f308ff5e5 --- /dev/null +++ b/repos/dde_linux/src/server/usb_terminal/target.mk @@ -0,0 +1,3 @@ +TARGET = usb_terminal +SRC_CC = main.cc +LIBS = base server