mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-19 03:06:39 +00:00
parent
6789b86871
commit
4803937dd2
223
repos/os/include/genode_c_api/usb.h
Normal file
223
repos/os/include/genode_c_api/usb.h
Normal file
@ -0,0 +1,223 @@
|
||||
/*
|
||||
* \brief C-API Genode USB backend
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2021-09-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2021 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _GENODE_C_API__USB_H_
|
||||
#define _GENODE_C_API__USB_H_
|
||||
|
||||
#include <genode_c_api/base.h>
|
||||
|
||||
|
||||
struct genode_usb_session; /* definition is private to the implementation */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/********************
|
||||
** Initialization **
|
||||
********************/
|
||||
|
||||
/**
|
||||
* Callback called during peer session request to allocate dma-capable shared buffer
|
||||
*/
|
||||
typedef struct genode_attached_dataspace * (*genode_usb_alloc_peer_buffer_t)
|
||||
(unsigned long size);
|
||||
|
||||
/**
|
||||
* Callback called when closing peer session to free shared buffer
|
||||
*/
|
||||
typedef void (*genode_usb_free_peer_buffer_t)
|
||||
(struct genode_attached_dataspace * ds);
|
||||
|
||||
/**
|
||||
* Callback to copy over config descriptor for given device
|
||||
*/
|
||||
typedef unsigned (*genode_usb_rpc_config_desc_t)
|
||||
(unsigned long bus, unsigned long dev, void * dev_desc, void * conf_desc);
|
||||
|
||||
/**
|
||||
* Callback that returns number of alt-settings of an interface for given device
|
||||
*/
|
||||
typedef int (*genode_usb_rpc_alt_settings_t)
|
||||
(unsigned long bus, unsigned long dev, unsigned idx);
|
||||
|
||||
/**
|
||||
* Callback to copy over interface descriptor for given device/interface
|
||||
*/
|
||||
typedef int (*genode_usb_rpc_iface_desc_t)
|
||||
(unsigned long bus, unsigned long dev, unsigned idx, unsigned alt,
|
||||
void * buf, unsigned long buf_size, int * active);
|
||||
|
||||
/**
|
||||
* Callback to copy over additional vendor specific data of an interface
|
||||
*/
|
||||
typedef int (*genode_usb_rpc_iface_extra_t)
|
||||
(unsigned long bus, unsigned long dev, unsigned idx, unsigned alt,
|
||||
void * buf, unsigned long buf_size);
|
||||
|
||||
/**
|
||||
* Callback to copy over endpoint descriptor for given device/iface/endpoint
|
||||
*/
|
||||
typedef int (*genode_usb_rpc_endp_desc_t)
|
||||
(unsigned long bus, unsigned long dev, unsigned idx, unsigned alt,
|
||||
unsigned endp, void * buf, unsigned long buf_size);
|
||||
|
||||
struct genode_usb_rpc_callbacks {
|
||||
genode_usb_alloc_peer_buffer_t alloc_fn;
|
||||
genode_usb_free_peer_buffer_t free_fn;
|
||||
genode_usb_rpc_config_desc_t cfg_desc_fn;
|
||||
genode_usb_rpc_alt_settings_t alt_settings_fn;
|
||||
genode_usb_rpc_iface_desc_t iface_desc_fn;
|
||||
genode_usb_rpc_iface_extra_t iface_extra_fn;
|
||||
genode_usb_rpc_endp_desc_t endp_desc_fn;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize USB root component
|
||||
*
|
||||
* \param handler signal handler to be installed at each USB session
|
||||
*/
|
||||
void genode_usb_init(struct genode_env * env,
|
||||
struct genode_allocator * alloc,
|
||||
struct genode_signal_handler * handler,
|
||||
struct genode_usb_rpc_callbacks * callbacks);
|
||||
|
||||
|
||||
/************************************
|
||||
** USB device lifetime management **
|
||||
************************************/
|
||||
|
||||
void genode_usb_announce_device(unsigned long vendor,
|
||||
unsigned long product,
|
||||
unsigned long cla,
|
||||
unsigned long bus,
|
||||
unsigned long dev);
|
||||
|
||||
void genode_usb_discontinue_device(unsigned long bus, unsigned long dev);
|
||||
|
||||
|
||||
/**********************************
|
||||
** USB session request handling **
|
||||
**********************************/
|
||||
|
||||
typedef unsigned short genode_usb_session_handle_t;
|
||||
typedef unsigned short genode_usb_request_handle_t;
|
||||
|
||||
struct genode_usb_request_string
|
||||
{
|
||||
unsigned char index;
|
||||
unsigned length;
|
||||
};
|
||||
|
||||
struct genode_usb_request_control
|
||||
{
|
||||
unsigned char request;
|
||||
unsigned char request_type;
|
||||
unsigned short value;
|
||||
unsigned short index;
|
||||
int actual_size;
|
||||
int timeout;
|
||||
};
|
||||
|
||||
enum Iso { MAX_PACKETS = 32 };
|
||||
enum Transfer { BULK, IRQ, ISOC };
|
||||
typedef enum Transfer genode_usb_transfer_type_t;
|
||||
|
||||
struct genode_usb_request_transfer
|
||||
{
|
||||
unsigned char ep;
|
||||
int actual_size;
|
||||
int polling_interval;
|
||||
int number_of_packets;
|
||||
unsigned long packet_size[MAX_PACKETS];
|
||||
unsigned long actual_packet_size[MAX_PACKETS];
|
||||
};
|
||||
|
||||
enum Request_return_error {
|
||||
NO_ERROR,
|
||||
INTERFACE_OR_ENDPOINT_ERROR,
|
||||
MEMORY_ERROR,
|
||||
NO_DEVICE_ERROR,
|
||||
PACKET_INVALID_ERROR,
|
||||
PROTOCOL_ERROR,
|
||||
STALL_ERROR,
|
||||
TIMEOUT_ERROR,
|
||||
UNKNOWN_ERROR
|
||||
};
|
||||
typedef enum Request_return_error genode_usb_request_ret_t;
|
||||
|
||||
typedef genode_usb_request_ret_t (*genode_usb_req_ctrl_t)
|
||||
(struct genode_usb_request_control * req,
|
||||
void * payload,
|
||||
unsigned long payload_size,
|
||||
void * opaque_data);
|
||||
|
||||
typedef genode_usb_request_ret_t (*genode_usb_req_transfer_t)
|
||||
(struct genode_usb_request_transfer * req,
|
||||
genode_usb_transfer_type_t type,
|
||||
genode_usb_session_handle_t session_handle,
|
||||
genode_usb_request_handle_t request_handle,
|
||||
void * payload,
|
||||
unsigned long payload_size,
|
||||
void * opaque_data);
|
||||
|
||||
typedef genode_usb_request_ret_t (*genode_usb_req_string_t)
|
||||
(struct genode_usb_request_string * req,
|
||||
void * payload,
|
||||
unsigned long payload_size,
|
||||
void * opaque_data);
|
||||
|
||||
typedef genode_usb_request_ret_t (*genode_usb_req_altsetting_t)
|
||||
(unsigned iface, unsigned alt_setting, void * opaque_data);
|
||||
|
||||
typedef genode_usb_request_ret_t (*genode_usb_req_config_t)
|
||||
(unsigned config_idx, void * opaque_data);
|
||||
|
||||
typedef genode_usb_request_ret_t (*genode_usb_req_flush_t)
|
||||
(unsigned char ep, void * opaque_data);
|
||||
|
||||
typedef genode_usb_request_ret_t (*genode_usb_response_t)
|
||||
(struct genode_usb_request_transfer * req,
|
||||
void * opaque_data);
|
||||
|
||||
struct genode_usb_request_callbacks {
|
||||
genode_usb_req_ctrl_t control_fn;
|
||||
genode_usb_req_transfer_t transfer_fn;
|
||||
genode_usb_req_string_t string_fn;
|
||||
genode_usb_req_altsetting_t altsetting_fn;
|
||||
genode_usb_req_config_t config_fn;
|
||||
genode_usb_req_flush_t flush_fn;
|
||||
};
|
||||
|
||||
genode_usb_session_handle_t genode_usb_session_by_bus_dev(unsigned long bus,
|
||||
unsigned long dev);
|
||||
|
||||
int genode_usb_request_by_session(genode_usb_session_handle_t session_handle,
|
||||
struct genode_usb_request_callbacks * const c,
|
||||
void * callback_data);
|
||||
|
||||
void genode_usb_ack_request(genode_usb_session_handle_t session_handle,
|
||||
genode_usb_request_handle_t request_handle,
|
||||
genode_usb_response_t callback,
|
||||
void * callback_data);
|
||||
|
||||
void genode_usb_notify_peers(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _GENODE_C_API__USB_H_ */
|
||||
|
719
repos/os/src/lib/genode_c_api/usb.cc
Normal file
719
repos/os/src/lib/genode_c_api/usb.cc
Normal file
@ -0,0 +1,719 @@
|
||||
/*
|
||||
* \brief Genode USB service provider C-API
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2021-09-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2021 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <base/attached_dataspace.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/env.h>
|
||||
#include <root/component.h>
|
||||
#include <os/reporter.h>
|
||||
#include <os/session_policy.h>
|
||||
#include <usb_session/rpc_object.h>
|
||||
#include <util/bit_allocator.h>
|
||||
|
||||
#include <genode_c_api/usb.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
struct Device {
|
||||
unsigned long vendor;
|
||||
unsigned long product;
|
||||
unsigned long cla;
|
||||
unsigned long bus;
|
||||
unsigned long dev;
|
||||
genode_usb_session * usb_session { nullptr };
|
||||
|
||||
Device(unsigned long vendor,
|
||||
unsigned long product,
|
||||
unsigned long cla,
|
||||
unsigned long bus,
|
||||
unsigned long dev)
|
||||
: vendor(vendor), product(product),
|
||||
cla(cla), bus(bus), dev(dev) {}
|
||||
|
||||
using Label = String<64>;
|
||||
|
||||
Label label() { return Label("usb-", bus, "-", dev); }
|
||||
};
|
||||
|
||||
|
||||
class Root;
|
||||
|
||||
|
||||
class genode_usb_session : public Usb::Session_rpc_object
|
||||
{
|
||||
private:
|
||||
|
||||
friend class ::Root;
|
||||
|
||||
enum { MAX_PACKETS_IN_FLY = 32 };
|
||||
|
||||
struct Packet {
|
||||
enum State { FREE, USED } state;
|
||||
Usb::Packet_descriptor pd;
|
||||
} packets[MAX_PACKETS_IN_FLY] { Packet::FREE, {} };
|
||||
|
||||
::Root & _root;
|
||||
Attached_dataspace & _ds;
|
||||
Signal_context_capability _sigh_state_cap {};
|
||||
unsigned short _id;
|
||||
Session_label const _label;
|
||||
List_element<genode_usb_session> _le { this };
|
||||
|
||||
void _ack(int err, Usb::Packet_descriptor p);
|
||||
|
||||
genode_usb_session(const genode_usb_session&);
|
||||
|
||||
public:
|
||||
|
||||
genode_usb_session(::Root & root,
|
||||
Attached_dataspace & ds,
|
||||
Env & env,
|
||||
Signal_context_capability cap,
|
||||
unsigned short id,
|
||||
Session_label label);
|
||||
|
||||
virtual ~genode_usb_session() {}
|
||||
|
||||
/*
|
||||
* Handle one outstanding request from the packet-stream
|
||||
* of this session
|
||||
*
|
||||
* \param callbacks C-API callback struct to handle request
|
||||
* \param data opaque pointer for C-side to identify related data
|
||||
* \returns true if a request got handled, false if nothing was done
|
||||
*/
|
||||
bool request(genode_usb_request_callbacks & callbacks, void * data);
|
||||
|
||||
/*
|
||||
* Handle response for an asynchronous request
|
||||
*
|
||||
* \param handle handle that identifies the original request
|
||||
* \param callback C-API callback to handle the response
|
||||
* \param data opaque pointer for C-side to identify related data
|
||||
*/
|
||||
void handle_response(genode_usb_request_handle_t handle,
|
||||
genode_usb_response_t callback,
|
||||
void * data);
|
||||
|
||||
/*
|
||||
* Notify session about state changes
|
||||
*/
|
||||
void notify();
|
||||
|
||||
|
||||
/***************************
|
||||
** USB session interface **
|
||||
***************************/
|
||||
|
||||
void sigh_state_change(Signal_context_capability sigh) override;
|
||||
bool plugged() override;
|
||||
void config_descriptor(Usb::Device_descriptor *device_descr,
|
||||
Usb::Config_descriptor *config_descr) override;
|
||||
unsigned alt_settings(unsigned index) override;
|
||||
void interface_descriptor(unsigned index, unsigned alt_setting,
|
||||
Usb::Interface_descriptor *interface_descr) override;
|
||||
bool interface_extra(unsigned index, unsigned alt_setting,
|
||||
Usb::Interface_extra *interface_data) override;
|
||||
void endpoint_descriptor(unsigned interface_num,
|
||||
unsigned alt_setting,
|
||||
unsigned endpoint_num,
|
||||
Usb::Endpoint_descriptor *endpoint_descr) override;
|
||||
void claim_interface(unsigned interface_num) override;
|
||||
void release_interface(unsigned interface_num) override;
|
||||
};
|
||||
|
||||
|
||||
class Root : public Root_component<genode_usb_session>
|
||||
{
|
||||
private:
|
||||
|
||||
enum { MAX_DEVICES = 32 };
|
||||
|
||||
|
||||
Env & _env;
|
||||
Signal_context_capability _sigh_cap;
|
||||
Attached_rom_dataspace _config { _env, "config" };
|
||||
Reporter _reporter { _env, "devices" };
|
||||
Constructible<Device> _devices[MAX_DEVICES];
|
||||
List<List_element<genode_usb_session>> _sessions {};
|
||||
Bit_allocator<16> _id_alloc {};
|
||||
bool _announced { false };
|
||||
|
||||
Root(const Root&);
|
||||
Root & operator=(const Root&);
|
||||
|
||||
genode_usb_session * _create_session(const char * args,
|
||||
Affinity const &) override;
|
||||
|
||||
void _destroy_session(genode_usb_session * session) override;
|
||||
|
||||
template <typename FUNC>
|
||||
void _for_each_device(FUNC const & fn)
|
||||
{
|
||||
for (unsigned idx = 0; idx < MAX_DEVICES; idx++)
|
||||
if (_devices[idx].constructed())
|
||||
fn(*_devices[idx]);
|
||||
}
|
||||
|
||||
template <typename FUNC>
|
||||
void _for_each_session(FUNC const & fn)
|
||||
{
|
||||
for (List_element<genode_usb_session> * s = _sessions.first();
|
||||
s; s = s->next())
|
||||
fn(*s->object());
|
||||
}
|
||||
|
||||
/*
|
||||
* Update report about existing USB devices
|
||||
*/
|
||||
void _report();
|
||||
|
||||
/*
|
||||
* Returns true if given device matches the session's policy
|
||||
*/
|
||||
bool _matches(Device & d, genode_usb_session & s);
|
||||
|
||||
public:
|
||||
|
||||
Root(Env & env, Allocator & alloc, Signal_context_capability);
|
||||
|
||||
void announce_device(unsigned long vendor,
|
||||
unsigned long product,
|
||||
unsigned long cla,
|
||||
unsigned long bus,
|
||||
unsigned long dev);
|
||||
|
||||
void discontinue_device(unsigned long bus, unsigned long dev);
|
||||
|
||||
/*
|
||||
* Returns the session's handle for a given device,
|
||||
* or an invalid handle if there is no active session for the device
|
||||
*/
|
||||
genode_usb_session_handle_t session(unsigned long bus,
|
||||
unsigned long dev);
|
||||
|
||||
/*
|
||||
* Finds the bus/device numbers of the device associated for a
|
||||
* given session
|
||||
*
|
||||
* \returns true if a device is associated, otherwise false
|
||||
*/
|
||||
bool device_associated(genode_usb_session * session,
|
||||
unsigned long & bus,
|
||||
unsigned long & dev);
|
||||
|
||||
/*
|
||||
* Apply a functor to the session with the given handle
|
||||
*/
|
||||
template <typename FUNC>
|
||||
void session(genode_usb_session_handle_t id, FUNC const & fn);
|
||||
};
|
||||
|
||||
|
||||
static ::Root * _usb_root = nullptr;
|
||||
static genode_usb_rpc_callbacks * _callbacks = nullptr;
|
||||
|
||||
|
||||
void genode_usb_session::sigh_state_change(Signal_context_capability sigh)
|
||||
{
|
||||
_sigh_state_cap = sigh;
|
||||
}
|
||||
|
||||
|
||||
void genode_usb_session::notify()
|
||||
{
|
||||
Signal_transmitter state_changed { _sigh_state_cap };
|
||||
state_changed.submit();
|
||||
}
|
||||
|
||||
|
||||
bool genode_usb_session::plugged()
|
||||
{
|
||||
unsigned long bus, dev;
|
||||
return _root.device_associated(this, bus, dev);
|
||||
}
|
||||
|
||||
|
||||
void genode_usb_session::config_descriptor(Usb::Device_descriptor * device_descr,
|
||||
Usb::Config_descriptor * config_descr)
|
||||
{
|
||||
unsigned long bus, dev;
|
||||
if (!_root.device_associated(this, bus, dev))
|
||||
throw Device_not_found();
|
||||
|
||||
unsigned speed = _callbacks->cfg_desc_fn(bus, dev, (void*) device_descr,
|
||||
config_descr);
|
||||
device_descr->num = dev;
|
||||
device_descr->speed = speed;
|
||||
}
|
||||
|
||||
|
||||
unsigned genode_usb_session::alt_settings(unsigned index)
|
||||
{
|
||||
unsigned long bus, dev;
|
||||
if (!_root.device_associated(this, bus, dev))
|
||||
throw Device_not_found();
|
||||
|
||||
int err = _callbacks->alt_settings_fn(bus, dev, index);
|
||||
if (err < 0)
|
||||
throw Interface_not_found();
|
||||
return (unsigned) err;
|
||||
}
|
||||
|
||||
|
||||
void genode_usb_session::interface_descriptor(unsigned index,
|
||||
unsigned alt_setting,
|
||||
Usb::Interface_descriptor * desc)
|
||||
{
|
||||
unsigned long bus, dev;
|
||||
if (!_root.device_associated(this, bus, dev))
|
||||
throw Device_not_found();
|
||||
|
||||
int active;
|
||||
if (_callbacks->iface_desc_fn(bus, dev, index, alt_setting, (void*)desc,
|
||||
sizeof(Usb::Interface_descriptor), &active))
|
||||
throw Interface_not_found();
|
||||
|
||||
if (active)
|
||||
desc->active = true;
|
||||
}
|
||||
|
||||
|
||||
bool genode_usb_session::interface_extra(unsigned index,
|
||||
unsigned alt_setting,
|
||||
Usb::Interface_extra * interface_data)
|
||||
{
|
||||
unsigned long bus, dev;
|
||||
if (!_root.device_associated(this, bus, dev))
|
||||
throw Device_not_found();
|
||||
|
||||
int len = _callbacks->iface_extra_fn(bus, dev, index, alt_setting,
|
||||
(void*)interface_data->data,
|
||||
sizeof(interface_data->data));
|
||||
if (len < 0)
|
||||
throw Interface_not_found();
|
||||
|
||||
interface_data->length = len;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
void genode_usb_session::endpoint_descriptor(unsigned interface_num,
|
||||
unsigned alt_setting,
|
||||
unsigned endpoint_num,
|
||||
Usb::Endpoint_descriptor * endp)
|
||||
{
|
||||
unsigned long bus, dev;
|
||||
if (!_root.device_associated(this, bus, dev))
|
||||
throw Device_not_found();
|
||||
|
||||
if (_callbacks->endp_desc_fn(bus, dev, interface_num, alt_setting,
|
||||
endpoint_num, (void*)endp,
|
||||
sizeof(Usb::Endpoint_descriptor)))
|
||||
throw Interface_not_found();
|
||||
}
|
||||
|
||||
|
||||
void genode_usb_session::claim_interface(unsigned)
|
||||
{
|
||||
warning(__func__, " gets ignored!");
|
||||
}
|
||||
|
||||
|
||||
void genode_usb_session::release_interface(unsigned)
|
||||
{
|
||||
warning(__func__, " gets ignored!");
|
||||
}
|
||||
|
||||
|
||||
void genode_usb_session::_ack(int err, Usb::Packet_descriptor p)
|
||||
{
|
||||
if (err == NO_ERROR)
|
||||
p.succeded = true;
|
||||
else
|
||||
p.error = (Usb::Packet_descriptor::Error)err;
|
||||
sink()->acknowledge_packet(p);
|
||||
}
|
||||
|
||||
|
||||
bool genode_usb_session::request(genode_usb_request_callbacks & req, void * data)
|
||||
{
|
||||
using Packet_descriptor = Usb::Packet_descriptor;
|
||||
|
||||
genode_usb_request_handle_t idx;
|
||||
|
||||
/* find free packet slot */
|
||||
for (idx = 0; idx < MAX_PACKETS_IN_FLY; idx++) {
|
||||
if (packets[idx].state == Packet::FREE)
|
||||
break;
|
||||
}
|
||||
if (idx == MAX_PACKETS_IN_FLY)
|
||||
return false;
|
||||
|
||||
Packet_descriptor p;
|
||||
|
||||
/* get next packet from request stream */
|
||||
while (true) {
|
||||
if (!sink()->packet_avail() || !sink()->ack_slots_free())
|
||||
return false;
|
||||
|
||||
p = sink()->get_packet();
|
||||
if (sink()->packet_valid(p))
|
||||
break;
|
||||
|
||||
p.error = Packet_descriptor::PACKET_INVALID_ERROR;
|
||||
sink()->acknowledge_packet(p);
|
||||
}
|
||||
|
||||
addr_t offset = (addr_t)sink()->packet_content(p) -
|
||||
(addr_t)sink()->ds_local_base();
|
||||
void * addr = (void*)((addr_t)_ds.local_addr<void>() + offset);
|
||||
|
||||
switch (p.type) {
|
||||
case Packet_descriptor::STRING:
|
||||
_ack(req.string_fn((genode_usb_request_string*)&p.string,
|
||||
addr, p.size(), data), p);
|
||||
break;
|
||||
case Packet_descriptor::CTRL:
|
||||
_ack(req.control_fn((genode_usb_request_control*)&p.control,
|
||||
addr, p.size(), data), p);
|
||||
break;
|
||||
case Packet_descriptor::BULK:
|
||||
packets[idx].state = Packet::USED;
|
||||
packets[idx].pd = p;
|
||||
req.transfer_fn((genode_usb_request_transfer*)&p.transfer, BULK,
|
||||
_id, idx, addr, p.size(), data);
|
||||
break;
|
||||
case Packet_descriptor::IRQ:
|
||||
packets[idx].state = Packet::USED;
|
||||
packets[idx].pd = p;
|
||||
req.transfer_fn((genode_usb_request_transfer*)&p.transfer, IRQ,
|
||||
_id, idx, addr, p.size(), data);
|
||||
break;
|
||||
case Packet_descriptor::ISOC:
|
||||
packets[idx].state = Packet::USED;
|
||||
packets[idx].pd = p;
|
||||
req.transfer_fn((genode_usb_request_transfer*)&p.transfer, ISOC,
|
||||
_id, idx, addr, p.size(), data);
|
||||
break;
|
||||
case Packet_descriptor::ALT_SETTING:
|
||||
_ack(req.altsetting_fn(p.interface.number,
|
||||
p.interface.alt_setting, data), p);
|
||||
break;
|
||||
case Packet_descriptor::CONFIG:
|
||||
_ack(req.config_fn(p.number, data), p);
|
||||
break;
|
||||
case Packet_descriptor::RELEASE_IF:
|
||||
warning("Release interface gets ignored!");
|
||||
break;
|
||||
case Packet_descriptor::FLUSH_TRANSFERS:
|
||||
_ack(req.flush_fn(p.number, data), p);
|
||||
break;
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void genode_usb_session::handle_response(genode_usb_request_handle_t id,
|
||||
genode_usb_response_t callback,
|
||||
void * callback_data)
|
||||
{
|
||||
Usb::Packet_descriptor p = packets[id].pd;
|
||||
_ack(callback((genode_usb_request_transfer*)&p.transfer, callback_data), p);
|
||||
packets[id].state = Packet::FREE;
|
||||
}
|
||||
|
||||
|
||||
genode_usb_session::genode_usb_session(::Root & root,
|
||||
Attached_dataspace & ds,
|
||||
Env & env,
|
||||
Signal_context_capability cap,
|
||||
unsigned short id,
|
||||
Session_label const label)
|
||||
:
|
||||
Usb::Session_rpc_object(ds.cap(), env.ep().rpc_ep(), env.rm()),
|
||||
_root(root),
|
||||
_ds(ds),
|
||||
_id(id),
|
||||
_label(label)
|
||||
{
|
||||
_tx.sigh_packet_avail(cap);
|
||||
}
|
||||
|
||||
|
||||
bool ::Root::_matches(Device & d, genode_usb_session & s)
|
||||
{
|
||||
Session_policy const policy(s._label, _config.xml());
|
||||
|
||||
unsigned long vendor = policy.attribute_value<unsigned long>("vendor_id", 0);
|
||||
unsigned long product = policy.attribute_value<unsigned long>("product_id", 0);
|
||||
unsigned long bus = policy.attribute_value<unsigned long>("bus", 0);
|
||||
unsigned long dev = policy.attribute_value<unsigned long>("dev", 0);
|
||||
unsigned long cla = policy.attribute_value<unsigned long>("class", 0);
|
||||
|
||||
if (bus && dev)
|
||||
return (bus == d.bus) && (dev == d.dev);
|
||||
if (vendor && product)
|
||||
return (vendor == d.vendor) && (product == d.product);
|
||||
if (cla)
|
||||
return (cla == d.cla) && (d.label() == s._label.last_element());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
genode_usb_session * ::Root::_create_session(const char * args,
|
||||
Affinity const &)
|
||||
{
|
||||
Session_label const label = label_from_args(args);
|
||||
|
||||
size_t ram_quota = Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
|
||||
size_t tx_buf_size = Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
|
||||
|
||||
if (!tx_buf_size)
|
||||
throw Service_denied();
|
||||
|
||||
/* check session quota */
|
||||
size_t session_size = max<size_t>(4096, sizeof(genode_usb_session));
|
||||
if (ram_quota < session_size)
|
||||
throw Insufficient_ram_quota();
|
||||
|
||||
if (tx_buf_size > ram_quota - session_size) {
|
||||
error("Insufficient 'ram_quota',got ", ram_quota, " need ",
|
||||
tx_buf_size + session_size);
|
||||
throw Insufficient_ram_quota();
|
||||
}
|
||||
|
||||
Attached_dataspace & ds =
|
||||
*static_cast<Attached_dataspace*>(_callbacks->alloc_fn(tx_buf_size));
|
||||
genode_usb_session * ret = new (md_alloc())
|
||||
genode_usb_session(*this, ds, _env, _sigh_cap, _id_alloc.alloc(), label);
|
||||
_sessions.insert(&ret->_le);
|
||||
|
||||
if (!ret) throw Service_denied();
|
||||
|
||||
bool found_device = false;
|
||||
_for_each_device([&] (Device & d) {
|
||||
if (found_device || d.usb_session || !_matches(d, *ret))
|
||||
return;
|
||||
d.usb_session = ret;
|
||||
found_device = true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void ::Root::_destroy_session(genode_usb_session * session)
|
||||
{
|
||||
_for_each_device([&] (Device & d) {
|
||||
if (d.usb_session == session)
|
||||
d.usb_session = nullptr;
|
||||
});
|
||||
|
||||
unsigned short id = session->_id;
|
||||
Attached_dataspace & ds = session->_ds;
|
||||
_sessions.remove(&session->_le);
|
||||
Genode::destroy(md_alloc(), session);
|
||||
_id_alloc.free((addr_t)id);
|
||||
_callbacks->free_fn(genode_attached_dataspace_ptr(ds));
|
||||
}
|
||||
|
||||
|
||||
void ::Root::_report()
|
||||
{
|
||||
using Value = String<64>;
|
||||
|
||||
_reporter.enabled(true);
|
||||
Reporter::Xml_generator xml(_reporter, [&] () {
|
||||
_for_each_device([&] (Device & d) {
|
||||
xml.node("device", [&] {
|
||||
xml.attribute("label", d.label());
|
||||
xml.attribute("vendor_id", Value(Hex(d.vendor)));
|
||||
xml.attribute("product_id", Value(Hex(d.product)));
|
||||
xml.attribute("bus", Value(Hex(d.bus)));
|
||||
xml.attribute("dev", Value(Hex(d.dev)));
|
||||
xml.attribute("class", Value(Hex(d.cla)));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void ::Root::announce_device(unsigned long vendor,
|
||||
unsigned long product,
|
||||
unsigned long cla,
|
||||
unsigned long bus,
|
||||
unsigned long dev)
|
||||
{
|
||||
for (unsigned idx = 0; idx < MAX_DEVICES; idx++) {
|
||||
if (_devices[idx].constructed())
|
||||
continue;
|
||||
|
||||
_devices[idx].construct(vendor, product, cla, bus, dev);
|
||||
if (!_announced) {
|
||||
_env.parent().announce(_env.ep().manage(*this));
|
||||
_announced = true;
|
||||
}
|
||||
_report();
|
||||
|
||||
_for_each_session([&] (genode_usb_session & s) {
|
||||
if (_devices[idx]->usb_session || !_matches(*_devices[idx], s))
|
||||
return;
|
||||
_devices[idx]->usb_session = &s;
|
||||
s.notify();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
error("Could not announce driver for new USB device, no slot left!");
|
||||
}
|
||||
|
||||
|
||||
void ::Root::discontinue_device(unsigned long bus, unsigned long dev)
|
||||
{
|
||||
for (unsigned idx = 0; idx < MAX_DEVICES; idx++) {
|
||||
if (!_devices[idx].constructed() ||
|
||||
_devices[idx]->bus != bus ||
|
||||
_devices[idx]->dev != dev)
|
||||
continue;
|
||||
|
||||
if (_devices[idx]->usb_session)
|
||||
_devices[idx]->usb_session->notify();
|
||||
|
||||
_devices[idx].destruct();
|
||||
_report();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
genode_usb_session_handle_t ::Root::session(unsigned long bus, unsigned long dev)
|
||||
{
|
||||
genode_usb_session * session = nullptr;
|
||||
_for_each_device([&] (Device & d) {
|
||||
if (d.bus == bus && d.dev == dev)
|
||||
session = d.usb_session;
|
||||
});
|
||||
|
||||
return session ? session->_id : 0;
|
||||
}
|
||||
|
||||
|
||||
template <typename FUNC>
|
||||
void ::Root::session(genode_usb_session_handle_t id, FUNC const & fn)
|
||||
{
|
||||
_for_each_session([&] (genode_usb_session & s) {
|
||||
if (s._id == id) fn(s);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bool ::Root::device_associated(genode_usb_session * session,
|
||||
unsigned long & bus, unsigned long & dev)
|
||||
{
|
||||
bool ret = false;
|
||||
_for_each_device([&] (Device & d) {
|
||||
if (d.usb_session == session) {
|
||||
bus = d.bus;
|
||||
dev = d.dev;
|
||||
ret = true;
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
::Root::Root(Env & env, Allocator & alloc, Signal_context_capability cap)
|
||||
:
|
||||
Root_component<genode_usb_session>(env.ep(), alloc),
|
||||
_env(env), _sigh_cap(cap)
|
||||
{
|
||||
/* Reserve id zero which is invalid */
|
||||
_id_alloc.alloc();
|
||||
}
|
||||
|
||||
|
||||
extern "C" void genode_usb_init(genode_env * env_ptr,
|
||||
genode_allocator * alloc_ptr,
|
||||
genode_signal_handler * sigh_ptr,
|
||||
genode_usb_rpc_callbacks * callbacks)
|
||||
{
|
||||
static ::Root root(*static_cast<Env*>(env_ptr),
|
||||
*static_cast<Allocator*>(alloc_ptr),
|
||||
cap(sigh_ptr));
|
||||
_callbacks = callbacks;
|
||||
_usb_root = &root;
|
||||
}
|
||||
|
||||
|
||||
extern "C" void genode_usb_announce_device(unsigned long vendor,
|
||||
unsigned long product,
|
||||
unsigned long cla,
|
||||
unsigned long bus,
|
||||
unsigned long dev)
|
||||
{
|
||||
if (!_usb_root)
|
||||
return;
|
||||
|
||||
_usb_root->announce_device(vendor, product, cla, bus, dev);
|
||||
}
|
||||
|
||||
|
||||
extern "C" void genode_usb_discontinue_device(unsigned long bus,
|
||||
unsigned long dev)
|
||||
{
|
||||
if (_usb_root)
|
||||
_usb_root->discontinue_device(bus, dev);
|
||||
}
|
||||
|
||||
|
||||
extern "C" genode_usb_session_handle_t
|
||||
genode_usb_session_by_bus_dev(unsigned long bus, unsigned long dev)
|
||||
{
|
||||
genode_usb_session_handle_t ret = _usb_root ? _usb_root->session(bus, dev) : 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
extern "C" int
|
||||
genode_usb_request_by_session(genode_usb_session_handle_t id,
|
||||
struct genode_usb_request_callbacks * const req,
|
||||
void * data)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if (!_usb_root)
|
||||
return ret;
|
||||
|
||||
_usb_root->session(id, [&] (genode_usb_session & session) {
|
||||
ret = session.request(*req, data); });
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
extern "C" void genode_usb_ack_request(genode_usb_session_handle_t session_id,
|
||||
genode_usb_request_handle_t request_id,
|
||||
genode_usb_response_t callback,
|
||||
void * callback_data)
|
||||
{
|
||||
if (!_usb_root)
|
||||
return;
|
||||
|
||||
_usb_root->session(session_id, [&] (genode_usb_session & session) {
|
||||
session.handle_response(request_id, callback, callback_data); });
|
||||
}
|
||||
|
||||
|
||||
extern "C" void genode_usb_notify_peers() { }
|
Loading…
Reference in New Issue
Block a user