This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
laforge gerrit-no-reply at lists.osmocom.orglaforge has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ccid-firmware/+/15499 Change subject: add small program to play with how Linux reacts to hubs with many ports ...................................................................... add small program to play with how Linux reacts to hubs with many ports Change-Id: Ic3d2bec3f55f4d9e13183795c5825723f9427476 --- M ccid/Makefile A ccid/hub_create_gadget.sh A ccid/hub_main_functionfs.c A ccid/hub_remove_gadget.sh 4 files changed, 589 insertions(+), 0 deletions(-) git pull ssh://gerrit.osmocom.org:29418/osmo-ccid-firmware refs/changes/99/15499/1 diff --git a/ccid/Makefile b/ccid/Makefile index 3258967..0fadd22 100644 --- a/ccid/Makefile +++ b/ccid/Makefile @@ -3,6 +3,9 @@ ccid_functionfs: ccid_main_functionfs.o ccid_proto.o ccid_device.o ccid_slot_sim.o $(CC) $(CFLAGS) -o $@ $^ -lasan -losmocore -ltalloc -laio +hub_functionfs: hub_main_functionfs.o + $(CC) $(CFLAGS) -o $@ $^ -lasan -losmocore -ltalloc -laio + %.o: %.c $(CC) $(CFLAGS) -o $@ -c $^ diff --git a/ccid/hub_create_gadget.sh b/ccid/hub_create_gadget.sh new file mode 100755 index 0000000..3689c9f --- /dev/null +++ b/ccid/hub_create_gadget.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +GADGET_NAME=osmo-hub + + + +GADGET_CONFIGFS=/sys/kernel/config/usb_gadget + +die() { + echo ERROR: $1 + exit 2 +} + +modprobe configfs +modprobe usb_f_fs +modprobe dummy_hcd is_high_speed=0 is_super_speed=0 + +[ -d $GADGET_CONFIGFS ] || die "usb_gadget configfs not mounted" + +gadgetdir="$GADGET_CONFIGFS/$GADGET_NAME" + +# create gadget +[ -d $gadgetdir ] || mkdir $gadgetdir || die "Cannot create $gadgetdir. Permission problem?" +set -e -x +cd $gadgetdir +echo 0x2342 > idVendor +echo 0x4200 > idProduct +echo 0x09 > bDeviceClass +echo 1 > bDeviceProtocol +[ -d strings/0x409 ] || mkdir strings/0x409 +echo 2342 > strings/0x409/serialnumber +echo "sysmocom GmbH" > strings/0x409/manufacturer +echo "sysmoHUB" > strings/0x409/product + +# create config +[ -d configs/c.1 ] || mkdir configs/c.1 +[ -d configs/c.1/strings/0x409 ] || mkdir configs/c.1/strings/0x409 +echo "sysmoHUB config" > configs/c.1/strings/0x409/configuration + +[ -d functions/ffs.usb0 ] || mkdir functions/ffs.usb0 +[ -e configs/c.1/ffs.usb0 ] || ln -s functions/ffs.usb0 configs/c.1 + +[ -d /dev/ffs-hub ] || mkdir /dev/ffs-hub +[ -e /dev/ffs-hub/ep0 ] || mount -t functionfs usb0 /dev/ffs-hub/ + +# enable device, only works after program has opened EP FDs +#echo dummy_udc.0 > UDC + diff --git a/ccid/hub_main_functionfs.c b/ccid/hub_main_functionfs.c new file mode 100644 index 0000000..c019cde --- /dev/null +++ b/ccid/hub_main_functionfs.c @@ -0,0 +1,505 @@ + +#include <errno.h> +#include <stdint.h> +#include <endian.h> +#include <signal.h> +#include <sys/types.h> +#include <linux/usb/functionfs.h> +#include <linux/usb/ch11.h> + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_le16(x) (x) +#define cpu_to_le32(x) (x) +#else +#define cpu_to_le16(x) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8)) +#define cpu_to_le32(x) \ + ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \ + (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24)) +#endif + +#define le32_to_cpu(x) le32toh(x) +#define le16_to_cpu(x) le16toh(x) + +enum { + DUSB, +}; + +/*********************************************************************** + * Actual USB Descriptors + ***********************************************************************/ + +static const struct { + struct usb_functionfs_descs_head_v2 header; + __le32 fs_count; + struct { + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor_no_audio ep_int; + } __attribute__ ((packed)) fs_descs; +} __attribute__ ((packed)) descriptors = { + .header = { + .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), + .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_ALL_CTRL_RECIP), + .length = cpu_to_le32(sizeof(descriptors)), + }, + .fs_count = cpu_to_le32(2), + .fs_descs = { + .intf = { + .bLength = sizeof(descriptors.fs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HUB, + .iInterface = 1, + }, + .ep_int = { + .bLength = sizeof(descriptors.fs_descs.ep_int), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 64, + .bInterval = 12, + }, + }, +}; + +#define STR_INTERFACE_ "Osmocom USB Hub" + +static const struct { + struct usb_functionfs_strings_head header; + struct { + __le16 code; + const char str1[sizeof(STR_INTERFACE_)]; + } __attribute__((packed)) lang0; +} __attribute__((packed)) strings = { + .header = { + .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), + .length = cpu_to_le32(sizeof(strings)), + .str_count = cpu_to_le32(1), + .lang_count = cpu_to_le32(1), + }, + .lang0 = { + cpu_to_le16(0x0409), /* en-us */ + STR_INTERFACE_, + }, +}; + + +struct usb2_hub_desc_header { + __u8 bDescLength; + __u8 bDescriptorType; + __u8 bNbrPorts; + __le16 wHubCharacteristics; + __u8 bPwrOn2PwrGood; + __u8 bHubContrCurrent; + __u8 data[0]; +}; + +#define NUM_PORTS 31 +#define HDESC_ARR_BYTES ((NUM_PORTS + 1 + 7) / 8) +static const struct { + struct usb2_hub_desc_header hdr; + uint8_t DeviceRemovable[HDESC_ARR_BYTES]; + uint8_t PortPwrCtrlMask[HDESC_ARR_BYTES]; +} __attribute__ ((packed)) hub_desc = { + .hdr = { + .bDescLength = sizeof(hub_desc), + .bDescriptorType = USB_DT_HUB, + .bNbrPorts = NUM_PORTS, + .wHubCharacteristics = cpu_to_le16(0x0001), + .bPwrOn2PwrGood = 100, + .bHubContrCurrent = 0, + }, +}; + + +/*********************************************************************** + * USB FunctionFS interface + ***********************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <osmocom/core/select.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/application.h> +#include <osmocom/core/logging.h> + +#ifndef FUNCTIONFS_SUPPORTS_POLL +#include <libaio.h> +struct aio_help { + struct msgb *msg; + struct iocb *iocb; +}; +#endif + +/* usb function handle */ +struct ufunc_handle { + struct osmo_fd ep0; + struct osmo_fd ep_int; + struct llist_head ep_int_queue; +#ifndef FUNCTIONFS_SUPPORTS_POLL + struct osmo_fd aio_evfd; + io_context_t aio_ctx; + struct aio_help aio_int; +#endif +}; + +static int ep_int_cb(struct osmo_fd *ofd, unsigned int what) +{ + LOGP(DUSB, LOGL_DEBUG, "%s\n", __func__); + return 0; +} + +const struct value_string ffs_evt_type_names[] = { + { FUNCTIONFS_BIND, "BIND" }, + { FUNCTIONFS_UNBIND, "UNBIND" }, + { FUNCTIONFS_ENABLE, "ENABLE" }, + { FUNCTIONFS_DISABLE, "DISABLE" }, + { FUNCTIONFS_SETUP, "SETUP" }, + { FUNCTIONFS_SUSPEND, "SUSPEND" }, + { FUNCTIONFS_RESUME, "RESUME" }, + { 0, NULL } +}; + +/* local, stand-alone definition of a USB control request */ +struct _usb_ctrl_req { + uint8_t bRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} __attribute__ ((packed));; + +/* class requests from the USB 2.0 hub spec, table 11-15 */ +#define HUB_CLASS_REQ(dir, type, request) ((((dir) | (type)) << 8) | (request)) +/* GetBusState and SetHubDescriptor are optional, omitted */ +#define ClearHubFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_HUB, USB_REQ_CLEAR_FEATURE) +#define ClearPortFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, USB_REQ_CLEAR_FEATURE) +#define GetHubDescriptor HUB_CLASS_REQ(USB_DIR_IN, USB_RT_HUB, USB_REQ_GET_DESCRIPTOR) +#define GetHubStatus HUB_CLASS_REQ(USB_DIR_IN, USB_RT_HUB, USB_REQ_GET_STATUS) +#define GetPortStatus HUB_CLASS_REQ(USB_DIR_IN, USB_RT_PORT, USB_REQ_GET_STATUS) +#define SetHubFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_HUB, USB_REQ_SET_FEATURE) +#define SetPortFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, USB_REQ_SET_FEATURE) + +static const struct value_string hub_class_spec_req_vals[] = { + OSMO_VALUE_STRING(USB_REQ_CLEAR_FEATURE), + OSMO_VALUE_STRING(USB_REQ_GET_DESCRIPTOR), + OSMO_VALUE_STRING(USB_REQ_GET_STATUS), + OSMO_VALUE_STRING(USB_REQ_SET_FEATURE), + { 0, NULL } +}; + +#define CCID_CTRL_RET_INVALID -1 +#define CCID_CTRL_RET_UNKNOWN -2 +#define CCID_CTRL_RET_OK 0 + +/*! Handle [class specific] CTRL request. We assume the caller has already verified that the + * request was made to the correct interface as well as it is a class-specific request. + * \param[in] ci CCID Instance for which CTRL request was received + * \param[in] ctrl_req buffer holding the 8 bytes CTRL transfer header + * \param[out] data_in data to be returned to the host in the IN transaction (if any) + * \returns CCID_CTRL_RET_OK, CCID_CTRL_RET_INVALID or CCID_CTRL_RET_UNKNOWN + */ +int hub_handle_ctrl(void *ci, const uint8_t *ctrl_req, const uint8_t **data_in) +{ + const struct _usb_ctrl_req *req = (const struct _usb_ctrl_req *) ctrl_req; + static uint16_t status[2]; + int rc; + + LOGP(DUSB, LOGL_NOTICE, "CTRL bmReqT=0x%02X bRequest=%s, wValue=0x%04X, wIndex=0x%04X, wLength=%d\n", + req->bRequestType, get_value_string(hub_class_spec_req_vals, req->bRequest), + req->wValue, req->wIndex, req->wLength); + + switch (req->bRequestType & 0x7f) { + case USB_RT_HUB: + switch (req->bRequest) { + case USB_REQ_GET_DESCRIPTOR: + if (req->wIndex != 0) { + LOGP(DUSB, LOGL_ERROR, "GET_DESC wIndex invalid\n"); + return CCID_CTRL_RET_INVALID; + } + if (0) // ctrl_req->wValue != FIXME + return CCID_CTRL_RET_INVALID; + *data_in = (const uint8_t *) &hub_desc; + return sizeof(hub_desc); + case USB_REQ_CLEAR_FEATURE: + switch (req->wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + return CCID_CTRL_RET_OK; + } + break; + case USB_REQ_GET_STATUS: + status[0] = cpu_to_le16(HUB_STATUS_LOCAL_POWER); + status[1] = cpu_to_le16(0); + *data_in = (const uint8_t *) status; + return sizeof(status); + case USB_REQ_SET_FEATURE: + if (req->wValue > 1) + return CCID_CTRL_RET_INVALID; + return CCID_CTRL_RET_OK; + } + break; + case USB_RT_PORT: + switch (req->bRequest) { + case USB_REQ_CLEAR_FEATURE: + switch (req->wValue) { + case USB_PORT_FEAT_CONNECTION: + case USB_PORT_FEAT_ENABLE: + case USB_PORT_FEAT_SUSPEND: + case USB_PORT_FEAT_OVER_CURRENT: + case USB_PORT_FEAT_RESET: + case USB_PORT_FEAT_L1: + case USB_PORT_FEAT_POWER: + case USB_PORT_FEAT_LOWSPEED: + case USB_PORT_FEAT_C_CONNECTION: + case USB_PORT_FEAT_C_ENABLE: + case USB_PORT_FEAT_C_SUSPEND: + case USB_PORT_FEAT_C_OVER_CURRENT: + case USB_PORT_FEAT_C_RESET: + case USB_PORT_FEAT_TEST: + case USB_PORT_FEAT_C_PORT_L1: + return CCID_CTRL_RET_OK; + } + break; + case USB_REQ_GET_STATUS: + status[0] = cpu_to_le16(0); + status[1] = cpu_to_le16(0); + *data_in = (const uint8_t *) status; + return sizeof(status); + case USB_REQ_SET_FEATURE: + //selector = wIndex >> 8 + //port = wIndex & 0xff + return CCID_CTRL_RET_OK; + } + } + return CCID_CTRL_RET_UNKNOWN; +} + +static void handle_setup(int fd, const struct usb_ctrlrequest *setup) +{ + const uint8_t *data_in = NULL; + int rc; + + LOGP(DUSB, LOGL_NOTICE, "EP0 SETUP bRequestType=0x%02x, bRequest=0x%02x wValue=0x%04x, " + "wIndex=0x%04x, wLength=%u\n", setup->bRequestType, setup->bRequest, + le16_to_cpu(setup->wValue), le16_to_cpu(setup->wIndex), le16_to_cpu(setup->wLength)); + + rc = hub_handle_ctrl(NULL, (const uint8_t *) setup, &data_in); + switch (rc) { + case CCID_CTRL_RET_INVALID: + if (setup->bRequestType & USB_DIR_IN) + read(fd, NULL, 0); /* cause stall */ + else + write(fd, NULL, 0); /* cause stall */ + break; + case CCID_CTRL_RET_UNKNOWN: + /* FIXME: is this correct behavior? */ + if (setup->bRequestType & USB_DIR_IN) + write(fd, NULL, 0); /* send ZLP */ + else + read(fd, NULL, 0); + break; + default: + if (setup->bRequestType & USB_DIR_IN) { + uint16_t len = rc; + if (setup->wLength < len) + len = setup->wLength; + LOGP(DUSB, LOGL_NOTICE, "Writing %u: %s\n", len, osmo_hexdump_nospc(data_in, len)); + write(fd, data_in, le16_to_cpu(len)); + } else + read(fd, NULL, 0); /* FIXME: control OUT? */ + break; + } +} + +static int ep_0_cb(struct osmo_fd *ofd, unsigned int what) +{ + struct ufunc_handle *uh = (struct ufunc_handle *) ofd->data; + int rc; + + if (what & BSC_FD_READ) { + struct usb_functionfs_event evt; + rc = read(ofd->fd, (uint8_t *)&evt, sizeof(evt)); + if (rc < sizeof(evt)) + return -23; + LOGP(DUSB, LOGL_NOTICE, "EP0 %s\n", get_value_string(ffs_evt_type_names, evt.type)); + switch (evt.type) { + case FUNCTIONFS_ENABLE: + //aio_refill_out(uh); + break; + case FUNCTIONFS_SETUP: + handle_setup(ofd->fd, &evt.u.setup); + break; + } + + } + return 0; +} + +#ifndef FUNCTIONFS_SUPPORTS_POLL + +/* dequeue the next msgb from ep_int_queue and set up AIO for it */ +static void dequeue_aio_write_int(struct ufunc_handle *uh) +{ + struct aio_help *ah = &uh->aio_int; + struct msgb *d; + int rc; + + if (ah->msg) + return; + + d = msgb_dequeue(&uh->ep_int_queue); + if (!d) + return; + + OSMO_ASSERT(ah->iocb); + ah->msg = d; + io_prep_pwrite(ah->iocb, uh->ep_int.fd, msgb_data(d), msgb_length(d), 0); + io_set_eventfd(ah->iocb, uh->aio_evfd.fd); + rc = io_submit(uh->aio_ctx, 1, &ah->iocb); + OSMO_ASSERT(rc >= 0); +} + +static int evfd_cb(struct osmo_fd *ofd, unsigned int what) +{ + struct ufunc_handle *uh = (struct ufunc_handle *) ofd->data; + struct io_event evt[1]; + struct msgb *msg; + uint64_t ev_cnt; + int i, rc; + + rc = read(ofd->fd, &ev_cnt, sizeof(ev_cnt)); + assert(rc == sizeof(ev_cnt)); + + rc = io_getevents(uh->aio_ctx, 1, 1, evt, NULL); + if (rc <= 0) { + LOGP(DUSB, LOGL_ERROR, "error in io_getevents(): %d\n", rc); + return rc; + } + + for (i = 0; i < rc; i++) { + int fd = evt[i].obj->aio_fildes; + if (fd == uh->ep_int.fd) { + /* interrupt endpoint AIO has completed. This means the IRQ transfer + * which we generated has reached the host */ + LOGP(DUSB, LOGL_DEBUG, "IRQ AIO completed, free()ing msgb\n"); + msgb_free(uh->aio_int.msg); + uh->aio_int.msg = NULL; + dequeue_aio_write_int(uh); + } + } + return 0; +} +#endif + + +static int ep0_init(struct ufunc_handle *uh) +{ + int rc; + + /* open control endpoint and write descriptors to it */ + rc = open("ep0", O_RDWR); + assert(rc >= 0); + osmo_fd_setup(&uh->ep0, rc, BSC_FD_READ, &ep_0_cb, uh, 0); + osmo_fd_register(&uh->ep0); + rc = write(uh->ep0.fd, &descriptors, sizeof(descriptors)); + if (rc != sizeof(descriptors)) { + LOGP(DUSB, LOGL_ERROR, "Cannot write descriptors: %s\n", strerror(errno)); + return -1; + } + rc = write(uh->ep0.fd, &strings, sizeof(strings)); + if (rc != sizeof(strings)) { + LOGP(DUSB, LOGL_ERROR, "Cannot write strings: %s\n", strerror(errno)); + return -1; + } + + /* open other endpoint file descriptors */ + INIT_LLIST_HEAD(&uh->ep_int_queue); + rc = open("ep1", O_RDWR); + assert(rc >= 0); + osmo_fd_setup(&uh->ep_int, rc, 0, &ep_int_cb, uh, 1); +#ifdef FUNCTIONFS_SUPPORTS_POLL + osmo_fd_register(&uh->ep_int); +#endif + +#ifndef FUNCTIONFS_SUPPORTS_POLL +#include <sys/eventfd.h> + /* for some absolutely weird reason, gadgetfs+functionfs don't support + * the standard methods of non-blocking I/o (select/poll). We need to + * work around using Linux AIO, which is not to be confused with POSIX AIO! */ + + memset(&uh->aio_ctx, 0, sizeof(uh->aio_ctx)); + rc = io_setup(1, &uh->aio_ctx); + OSMO_ASSERT(rc >= 0); + + /* create an eventfd, which will be marked readable once some AIO completes */ + rc = eventfd(0, 0); + OSMO_ASSERT(rc >= 0); + osmo_fd_setup(&uh->aio_evfd, rc, BSC_FD_READ, &evfd_cb, uh, 0); + osmo_fd_register(&uh->aio_evfd); + + uh->aio_int.iocb = malloc(sizeof(struct iocb)); +#endif + + return 0; +} + +static const struct log_info_cat log_info_cat[] = { + [DUSB] = { + .name = "USB", + .description = "USB Transport", + .enabled = 1, + .loglevel = LOGL_NOTICE, + }, +}; + +static const struct log_info log_info = { + .cat = log_info_cat, + .num_cat = ARRAY_SIZE(log_info_cat), +}; + +static void *tall_main_ctx; + +static void signal_handler(int signal) +{ + switch (signal) { + case SIGUSR1: + talloc_report_full(tall_main_ctx, stderr); + break; + } +} + + +int main(int argc, char **argv) +{ + struct ufunc_handle ufh = (struct ufunc_handle) { 0, }; + int rc; + + tall_main_ctx = talloc_named_const(NULL, 0, "hub_main_functionfs"); + msgb_talloc_ctx_init(tall_main_ctx, 0); + osmo_init_logging2(tall_main_ctx, &log_info); + + signal(SIGUSR1, &signal_handler); + + if (argc < 2) { + fprintf(stderr, "You have to specify the mount-path of the functionfs\n"); + exit(2); + } + + chdir(argv[1]); + rc = ep0_init(&ufh); + if (rc < 0) { + fprintf(stderr, "Error %d\n", rc); + exit(1); + } + + while (1) { + osmo_select_main(0); + } +} diff --git a/ccid/hub_remove_gadget.sh b/ccid/hub_remove_gadget.sh new file mode 100755 index 0000000..e206d6f --- /dev/null +++ b/ccid/hub_remove_gadget.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +GADGET_NAME=osmo-hub + +GADGET_CONFIGFS=/sys/kernel/config/usb_gadget + +set -e +set -x + +gadgetdir="$GADGET_CONFIGFS/$GADGET_NAME" + +# unmount the endpoints from the filesystem +umount /dev/ffs-hub + +# detach from USB gadget/bus +echo "" > "$gadgetdir/UDC" || true + +# remove function from config +rm "$gadgetdir/configs/c.1/ffs.usb0" + +# remove strings in config +rmdir "$gadgetdir/configs/c.1/strings/0x409" + +# remove config +rmdir "$gadgetdir/configs/c.1" + +# remove function +rmdir "$gadgetdir/functions/ffs.usb0" + +# remove strings in gadget +rmdir "$gadgetdir/strings/0x409" + +rmdir $gadgetdir -- To view, visit https://gerrit.osmocom.org/c/osmo-ccid-firmware/+/15499 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmo-ccid-firmware Gerrit-Branch: master Gerrit-Change-Id: Ic3d2bec3f55f4d9e13183795c5825723f9427476 Gerrit-Change-Number: 15499 Gerrit-PatchSet: 1 Gerrit-Owner: laforge <laforge at gnumonks.org> Gerrit-MessageType: newchange -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20190912/9021469a/attachment.htm>