1
0
mirror of git://projects.qi-hardware.com/iris.git synced 2024-06-28 18:59:51 +03:00

use usb server for communication

This commit is contained in:
Bas Wijnen 2009-12-18 22:27:26 +01:00
parent 85948735ab
commit ad2f531ab0
5 changed files with 321 additions and 44 deletions

View File

@ -83,6 +83,24 @@ class Udc:
u16 max_packet_size;
u8 interval;
} __attribute__ ((packed))
/**/struct Device_Qualifier {
static u8 const Type = 6;
u8 length;
u8 type;
u16 version;
u8 dev_class;
u8 subclass;
u8 protocol;
u8 max_packet_size0;
u8 num_configurations;
u8 reserved;
} __attribute__ ((packed))
/**/struct Langs {
static u8 const Type = 3;
u8 length;
u8 type;
u8 lang;
} __attribute__ ((packed))
template <unsigned size> struct String {
static u8 const Type = 3;
u8 length;
@ -118,10 +136,12 @@ class Udc:
Interface interface;
Endpoint endpoint[2];
} __attribute__ ((packed))
static Device const device_descriptor
static my_config const config_descriptor
static String <7> const s_manufacturer
static String <22> const s_product
static Device device_descriptor
//static Device_Qualifier device_qualifier_descriptor
static my_config config_descriptor; //, other_config_descriptor
static String <1> s_langs
static String <6> s_manufacturer
static String <16> s_product
enum State:
IDLE
TX
@ -139,17 +159,43 @@ class Udc:
void log (unsigned c)
void interrupt ()
Udc::Device const Udc::device_descriptor = { sizeof (Device), Device::Type, 0x200, 0, 0, 0, max_packet_size0, 0x601a, 0x4740, 0x100, 1, 2, 0, 1 }
Udc::my_config const Udc::config_descriptor = {
Udc::Device Udc::device_descriptor = { sizeof (Device), Device::Type, 0x200, 0, 0, 0, max_packet_size0, 0xfffe, 0x0002, 0x100, 1, 2, 0, 1 }
Udc::my_config Udc::config_descriptor = {
(Configuration){ sizeof (Configuration), Configuration::Type, sizeof (my_config), 1, 1, 0, 0xc0, 1 },
(Interface){ sizeof (Interface), Interface::Type, 0, 0, 2, 0xff, 0, 0x80, 0 }, {
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 1, BULK, max_packet_size_bulk, 0 },
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 0x81, BULK, max_packet_size_bulk, 0 }
}
}
Udc::String <7> const Udc::s_manufacturer = { sizeof (String <7>), String <7>::Type, { 'I', 'n', 'g', 'e', 'n', 'i', 'c' } }
Udc::String <22> const Udc::s_product = { sizeof (String <22>), String <22>::Type, { 'J', 'Z', '4', '7', '4', '0', ' ', 'U', 'S', 'B', ' ', 'B', 'o', 'o', 't', ' ', 'D', 'e', 'v', 'i', 'c', 'e'} }
Udc::String <1> Udc::s_langs = { sizeof (String <1>), String <1>::Type, { 0x0409 } }
Udc::String <6> Udc::s_manufacturer = { sizeof (String <6>), String <6>::Type, { 's', 'h', 'e', 'v', 'e', 'k' } }
Udc::String <16> Udc::s_product = { sizeof (String <16>), String <16>::Type, { 'I', 'r', 'i', 's', ' ', 'o', 'n', ' ', 'N', 'a', 'n', 'o', 'N', 'o', 't', 'e' } }
//Udc::Device_Qualifier const Udc::device_qualifier_descriptor = { sizeof (Device_Qualifier), Device_Qualifier::Type, 0x200, 0, 0, 0, max_packet_size0, 1, 0 }
//Udc::my_config const Udc::other_config_descriptor = {
// (Configuration){ sizeof (Configuration), 7, sizeof (my_config), 1, 1, 0, 0xc0, 1 },
// (Interface){ sizeof (Interface), Interface::Type, 0, 0, 2, 0xff, 0, 0x80, 0 }, {
// (Endpoint){ sizeof (Endpoint), Endpoint::Type, 1, BULK, max_packet_size_bulk, 0 },
// (Endpoint){ sizeof (Endpoint), Endpoint::Type, 0x81, BULK, max_packet_size_bulk, 0 }
// }
// }
void Udc::init ():
// Initialize the globals. My method of compiling doesn't do that properly.
device_descriptor = (Device){ sizeof (Device), Device::Type, 0x200, 0, 0, 0, max_packet_size0, 0xfffe, 0x0002, 0x100, 1, 2, 0, 1 }
config_descriptor = (my_config){
(Configuration){ sizeof (Configuration), Configuration::Type, sizeof (my_config), 1, 1, 0, 0xc0, 1 },
(Interface){ sizeof (Interface), Interface::Type, 0, 0, 2, 0xff, 0, 0x80, 0 }, {
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 1, BULK, max_packet_size_bulk, 0 },
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 0x81, BULK, max_packet_size_bulk, 0 }
}
}
s_langs = (String <1>){ sizeof (String <1>), String <1>::Type, { 0x0409 } }
s_manufacturer = (String <6>){ sizeof (String <6>), String <6>::Type, { 's', 'h', 'e', 'v', 'e', 'k' } }
s_product = (String <16>){ sizeof (String <16>), String <16>::Type, { 'I', 'r', 'i', 's', ' ', 'o', 'n', ' ', 'N', 'a', 'n', 'o', 'N', 'o', 't', 'e' } }
// Disconnect from the bus and don't try to get high-speed.
UDC_POWER &= ~(UDC_POWER_SOFTCONN | UDC_POWER_HSENAB)
state = IDLE
configuration = 0
size = 0
@ -164,12 +210,15 @@ void Udc::init ():
// enable interrupts on endpoint 0.
UDC_INTRINE |= 1 << 0
UDC_INDEX = 0
// Wait a bit and connect to the host.
Kernel::my_receiver.sleep (10)
UDC_POWER |= UDC_POWER_SOFTCONN
bool Udc::vendor (Setup *s):
if s->request_type & 0x80:
static char const *name = "abcdefgh"
static char const *name = "Reboot"
ptr = name
size = 8
size = s->length < 6 ? s->length : 6
state = TX
rebooting = true
return true
@ -178,20 +227,34 @@ bool Udc::vendor (Setup *s):
bool Udc::get_descriptor (unsigned type, unsigned idx, unsigned len):
switch type:
case Configuration::Type:
if idx != 1:
//kdebug ("config\n")
if idx != 0:
return false
ptr = reinterpret_cast <char const *> (&config_descriptor)
size = (len < sizeof (config_descriptor) ? len : sizeof (config_descriptor))
break
case Device::Type:
//kdebug ("device\n")
if idx != 0:
return false
ptr = reinterpret_cast <char const *> (&device_descriptor)
size = (len < sizeof (device_descriptor) ? len : sizeof (device_descriptor))
break
// The 7 is an arbitrary number.
case String <7>::Type:
case Device_Qualifier::Type:
//if idx != 0:
// return false
//ptr = reinterpret_cast <char const *> (&device_qualifier_descriptor)
//size = (len < sizeof (device_qualifier_descriptor) ? len : sizeof (device_qualifier_descriptor))
//break
return false
// The 6 is an arbitrary number, except that String <6> in instantiated already.
case String <6>::Type:
//kdebug ("string\n")
switch idx:
case 0:
ptr = reinterpret_cast <char const *> (&s_langs)
size = (len < sizeof (s_langs) ? len : sizeof (s_langs))
break
case 1:
ptr = reinterpret_cast <char const *> (&s_manufacturer)
size = (len < sizeof (s_manufacturer) ? len : sizeof (s_manufacturer))
@ -205,19 +268,21 @@ bool Udc::get_descriptor (unsigned type, unsigned idx, unsigned len):
break
default:
return false
state = TX
return true
state = TX
return true
bool Udc::handle_setup (Setup *s):
kdebug ("udc: setup: type=")
kdebug_num (s->request_type)
kdebug ("; request=")
kdebug_num (s->request)
kdebug ("; value=")
kdebug_num (s->value)
kdebug ("; index=")
kdebug_num (s->index)
kdebug ("\n")
//kdebug ("udc: setup: type=")
//kdebug_num (s->request_type, 2)
//kdebug ("; request=")
//kdebug_num (s->request, 2)
//kdebug ("; value=")
//kdebug_num (s->value, 4)
//kdebug ("; index=")
//kdebug_num (s->index, 4)
//kdebug ("; length=")
//kdebug_num (s->length, 4)
//kdebug ("\n")
switch s->request_type:
case STANDARD_TO_DEVICE:
switch s->request:
@ -268,7 +333,7 @@ bool Udc::handle_setup (Setup *s):
void Udc::interrupt ():
unsigned i = UDC_INTRUSB
if i & UDC_INTR_RESET:
kdebug ("udc: reset\n")
//kdebug ("udc: reset\n")
state = IDLE
return
i = UDC_INTRIN
@ -276,7 +341,7 @@ void Udc::interrupt ():
// Interrupt on endpoint 0.
unsigned csr = UDC_CSR0
if csr & UDC_CSR0_SENTSTALL:
csr &= ~UDC_CSR0_SENTSTALL
csr &= ~(UDC_CSR0_SENTSTALL | UDC_CSR0_SENDSTALL)
state = IDLE
if csr & UDC_CSR0_SETUPEND:
csr |= UDC_CSR0_SVDSETUPEND
@ -292,23 +357,24 @@ void Udc::interrupt ():
packet.d[1] = UDC_FIFO (0)
UDC_CSR0 = csr | UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY
if !handle_setup (&packet.s):
//kdebug ("stall 1\n")
csr |= UDC_CSR0_SENDSTALL
break
if size == 0:
return
// Fall through.
case TX:
kdebug ("udc: send next:")
//kdebug ("udc: send next:")
unsigned i
for i = 0; (size & ~3) > 0 && i < max_packet_size0; i += 4, size -= 4:
for unsigned i = 0; i < 4; ++i:
kdebug_num (ptr[i], 2)
//for unsigned i = 0; i < 4; ++i:
//kdebug_num (ptr[i], 2)
UDC_FIFO (0) = *(unsigned *)ptr
ptr += 4
for ; size > 0 && i < max_packet_size0; ++i, --size:
kdebug_num (*ptr, 2)
//kdebug_num (*ptr, 2)
UDC_FIFO8 (0) = *ptr++
kdebug ("\n")
//kdebug ("\n")
if i == max_packet_size0:
csr |= UDC_CSR0_INPKTRDY
else:
@ -317,6 +383,7 @@ void Udc::interrupt ():
break
case RX:
// Not supported.
//kdebug ("stall 2\n")
csr |= UDC_CSR0_SVDOUTPKTRDY | UDC_CSR0_SENDSTALL
state = IDLE
break
@ -340,20 +407,9 @@ Kernel::Num start ():
Kernel::Cap logcap = Kernel::my_receiver.create_capability (LOG)
__asm__ volatile ("li $a0, 1\nlw $a1, %0\nbreak" :: "m"(logcap.code): "a0", "a1", "memory")
Kernel::register_interrupt (IRQ_UDC)
//Kernel::my_receiver.set_alarm (HZ)
while true:
Kernel::wait ()
switch Kernel::recv.protected_data.l:
case ~0:
// alarm.
Kernel::my_receiver.set_alarm (HZ)
kdebug ("alarm; d =")
unsigned mask[4] = { 0x00000000, 0xfe000000, 0x09c00000, 0x3dfc8055 }
for unsigned i = 0; i < 4; ++i:
kdebug (" ")
kdebug_num (gpio_get_port (i) & mask[i])
kdebug ("\n")
break
case IRQ_UDC:
udc.interrupt ()
Kernel::register_interrupt (IRQ_UDC)

View File

@ -30,10 +30,13 @@ boot_sources = mips/init.cc mips/nanonote/board.cc
arch_headers = mips/arch.hh mips/nanonote/jz4740.hh mips/nanonote/board.hh
boot_threads = init udc nanonote-gpio buzzer metronome lcd
test: iris.raw nanonote-boot
./nanonote-boot iris.raw 0xa$(shell /bin/sh -c '$(OBJDUMP) -t iris.elf | grep __start$$ | cut -b2-8')
test: iris.raw mips/nanonote/server/usb-server
echo "reboot 0xa$(shell /bin/sh -c '$(OBJDUMP) -t iris.elf | grep __start$$ | cut -b2-8')" | nc localhost 5050
.PHONY: test
mips/nanonote/server/usb-server: mips/nanonote/server/usb-server.ccp mips/nanonote/server/Makefile.am mips/nanonote/server/configure.ac
$(MAKE) -C mips/nanonote/server
%.elf: %.o
$(LD) $(LDFLAGS) -o $@ $<

View File

@ -0,0 +1,30 @@
# Iris: micro-kernel for a capability-based operating system.
# mips/nanonote/server/Makefile.am: build rules
# Copyright 2009 Bas Wijnen <wijnen@debian.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
AUTOMAKE_OPTIONS = foreign
bin_PROGRAMS = usb-server
usb_server_SOURCES = usb-server.cc
usb_server_CPPFLAGS = $(SHEVEK_CFLAGS) -DSTAGE1_FILE=\"mips/nanonote/sdram-setup.raw\" -DSTAGE2_FILE=\"iris.raw\"
usb_server_LDFLAGS = $(SHEVEK_LIBS) -lusb
PYPP = /usr/bin/pypp
%.cc: %.ccp
$(PYPP) --name $< < $< > $@
BUILT_SOURCES = usb-server.cc

View File

@ -0,0 +1,9 @@
AC_INIT(usb-server, 1.0, wijnen@debian.org)
AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
AC_PROG_CXX
PKG_CHECK_MODULES(SHEVEK, shevek,, AC_MSG_ERROR(You need libshevek to compile AC_PACKAGE_NAME))
AC_CONFIG_FILES(Makefile)
AC_OUTPUT

View File

@ -0,0 +1,179 @@
#pypp 0
// Iris: micro-kernel for a capability-based operating system.
// mips/nanonote/server/usb-server.ccp: Host-side helper for USB.
// Copyright 2009 Bas Wijnen <wijnen@debian.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <unistd.h>
#include <usb.h>
#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <shevek/mainloop.hh>
#include <shevek/server.hh>
#include <shevek/args.hh>
struct client
struct data:
Glib::RefPtr <shevek::server <client, data *> > server
usb_dev_handle *handle
static int const boot_vendor = 0x601a
static int const boot_product = 0x4740
static int const run_vendor = 0xfffe
static int const run_product = 0x0002
static unsigned const timeout = 1000
void boot (unsigned entry)
data (std::string const &port):
handle = NULL
server = shevek::server <client, data *>::create ()
server->data () = this
server->open (port)
private:
static unsigned const STAGE1_LOAD = 0x80002000
static unsigned const STAGE2_LOAD = 0x80000000
static unsigned const STAGE1_ENTRY = STAGE1_LOAD
enum requests:
VR_GET_CPU_INFO = 0
VR_SET_DATA_ADDRESS = 1
VR_SET_DATA_LENGTH = 2
VR_FLUSH_CACHES = 3
VR_PROGRAM_START1 = 4
VR_PROGRAM_START2 = 5
void request (requests num, unsigned data = 0)
void send_file (unsigned address, unsigned size, char const *data)
void reboot ():
// This always fails, because the device doesn't answer. However, that means there's nothing wrong, so don't complain.
usb_control_msg (handle, USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, VR_GET_CPU_INFO, 0, 0, NULL, 8, timeout)
usb_release_interface (handle, 0)
usb_close (handle)
handle = NULL
void get_device (unsigned vendor, unsigned product, unsigned tries)
struct client : public shevek::server <client, data *>::connection:
static Glib::RefPtr <client> create ():
return Glib::RefPtr <client> (new client ())
bool keep
void pickup (bool is_stdio):
keep = is_stdio
void read (std::string const &line):
shevek::istring l (line)
unsigned entry
if l ("reboot %x%", entry):
get_server ()->data ()->boot (entry)
else:
out->write ("invalid command\n")
if !keep:
out->write_block ()
disconnect ()
void data::request (requests r, unsigned data):
if usb_control_msg (handle, USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, r, (data >> 16) & 0xffff, data & 0xffff, NULL, 0, timeout) < 0:
std::cerr << "unable to send control message to NanoNote: " << usb_strerror () << ".\n"
usb_release_interface (handle, 0)
usb_close (handle)
handle = NULL
void data::send_file (unsigned address, unsigned size, char const *data):
request (VR_SET_DATA_ADDRESS, address)
char const *ptr = data
while ptr - data < size:
int ret = usb_bulk_write (handle, 1, ptr, size - (ptr - data), timeout)
if ret <= 0:
std::cerr << "failed to write to NanoNote.\n"
usb_release_interface (handle, 0)
usb_close (handle)
handle = NULL
return
ptr += ret
void data::get_device (unsigned vendor, unsigned product, unsigned tries):
for unsigned i = 0; i < tries; ++i:
usb_find_busses ()
usb_find_devices ()
for struct usb_bus *bus = usb_busses; bus; bus = bus->next:
for struct usb_device *dev = bus->devices; dev; dev = dev->next:
if dev->descriptor.idProduct != product || dev->descriptor.idVendor != vendor:
//std::cerr << shevek::ostring ("Not using %04x:%04x when looking for %04x:%04x\n", dev->descriptor.idVendor, dev->descriptor.idProduct, vendor, product)
continue
handle = usb_open (dev)
if usb_claim_interface (handle, 0) < 0:
std::cerr << "unable to claim interface\n"
usb_close (handle)
handle = NULL
continue
return
if i + 1 < tries:
//std::cerr << "failed to find device, still trying...\n"
sleep (1)
std::cerr << shevek::ostring ("giving up finding device %04x:%04x\n", vendor, product)
void data::boot (unsigned entry):
std::cerr << "booting " << shevek::ostring ("%x", entry) << "\n"
if handle:
usb_release_interface (handle, 0)
usb_close (handle)
handle = NULL
get_device (boot_vendor, boot_product, 1)
if !handle:
get_device (run_vendor, run_product, 1)
if !handle:
std::cerr << "unable to find device\n"
return
reboot ()
get_device (boot_vendor, boot_product, 5)
if !handle:
std::cerr << "unable to reboot device\n"
return
std::cerr << "sending stage 1\n"
std::ifstream file (STAGE1_FILE)
std::ostringstream stage1
stage1 << file.rdbuf ()
send_file (STAGE1_LOAD, stage1.str ().size (), stage1.str ().data ())
std::cerr << "running stage 1\n"
request (VR_PROGRAM_START1, STAGE1_ENTRY)
usleep (100)
std::ostringstream stage2
usb_release_interface (handle, 0)
file.close ()
file.open (STAGE2_FILE)
stage2 << file.rdbuf ()
std::cerr << "sending Iris\n"
send_file (STAGE2_LOAD, stage2.str ().size (), stage2.str ().data ())
std::cerr << "flushing caches\n"
request (VR_FLUSH_CACHES)
std::cerr << "running Iris\n"
request (VR_PROGRAM_START2, entry)
usb_release_interface (handle, 0)
usb_close (handle)
handle = NULL
get_device (run_vendor, run_product, 5)
if !handle:
std::cerr << "unable to open booted device\n"
return
std::cerr << "(re)booted NanoNote\n"
int main (int argc, char **argv):
usb_init ()
std::string port ("5050")
shevek::args::option opts[] = {
shevek::args::option ('p', "port", "port to listen for commands", true, port)
}
shevek::args args (argc, argv, opts, 0, 0, "device server for testing Iris on NanoNote", "2009")
data d (port)
shevek::loop ()
return 0