InEnglish

Live Stock Monitor

Friday, 27 July 2018
|
Écrit par
Grégory Soutadé

Today, a small Python script to track live stock exchanges. It fetch data from boursorama website and format it for "Generic Monitor" XFCE applet which allows to display result of a command line script. Just setup the path of this script in genmon properties and set the delay to 60s (to avoid flooding website).

#!/usr/bin/python

#
# 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 <https://www.gnu.org/licenses/>.
#

import requests
import json

params_gettickseod = {"symbol":"%s","length":"1","period":"0","guid":""}
params_updatecharts = {"symbol":"%s","period":"-1"}

base_headers = {
    'Host': 'www.boursorama.com',
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'fr,en-US;q=0.7,en;q=0.3',
    'DNT': '1',
    'Upgrade-Insecure-Requests': '1',
    'Pragma': 'no-cache',
    'Cache-Control': 'no-cache',
}
base_address = 'https://www.boursorama.com/cours/'

headers = {
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0',
    'Accept': 'application/json, text/javascript, */*; q=0.01',
    'Accept-Language': 'fr,en-US;q=0.7,en;q=0.3',
    'Accept-Encoding': 'gzip, deflate, br',
    'Referer': 'https://www.boursorama.com/cours/%s/',
    'Content-Type': 'application/json; charset=utf-8',
    'X-Requested-With': 'XMLHttpRequest',
    'DNT': '1',
    'Connection': 'keep-alive',
}

xhr_address = 'https://www.boursorama.com/bourse/action/graph/ws/'
address_gettickseod = xhr_address + 'GetTicksEOD'
address_updatecharts = xhr_address + 'UpdateCharts'

cookies = None

def _do_request(address, params, headers):
    if cookies is None:
        req = requests.get(address, params=params, headers=headers)
    else:
        req = requests.get(address, params=params, headers=headers, cookies=cookies)

    if req.status_code == requests.codes.ok:
        j = req.json()
        if len(j) == 0:
            raise Exception('Not available')
        return j
    else:
        raise Exception("Request error!")

def getStock(stock, display_name=None):
    my_headers = headers.copy()
    my_headers['Referer'] = headers['Referer'] % (stock)

    close_value = 0
    res = ''

    my_params  = params_updatecharts.copy()
    my_params["symbol"] = stock
    try:
        j = _do_request(address_updatecharts, my_params, my_headers)
    except:
        req = requests.get(base_address + stock, headers=base_headers)
        # cookies = req.cookies
        j = _do_request(address_updatecharts, my_params, my_headers)

    current = float(j['d'][0]['c'])
    my_params  = params_gettickseod.copy()
    my_params["symbol"] = stock
    try:
        j = _do_request(address_gettickseod, my_params, my_headers)
        close_value = float(j['d']['qv']['c'])
    except Exception, e:
        if not len(j):
            raise e
        close_value = float(j['d'][0]['o']) # Open value

    if close_value != 0:
        var = ((current/close_value) - 1)*100
    else:
        var = 0
    if current < close_value:
        color = 'red'
        var = -var
    else:
        color = 'green'
    if not display_name is None:
        res += '%s ' % (display_name)
    res += '%.3f <span fgcolor="%s">%.2f</span>' % (current, color, var)

    return res

def getMail():
    res = ''
    nb_messages = ''
    pipew = open("/tmp/gmail-pipe-w", "wb+")
    piper = open("/tmp/gmail-pipe-r", "rb+")
    pipew.write("a\n")
    pipew.flush()
    while not len(nb_messages):
        nb_messages = piper.readline()
    if len(nb_messages):
        nb_messages = int(nb_messages)
        if nb_messages == 1:
            res = ', 1 msg'
        elif nb_messages > 1:
            res = ', %d msgs' % (nb_messages)
    pipew.close()
    piper.close()

    return res

def getStocks(stocks):
    res = ''
    for stock in stocks:
        if res != '': res += ', '
        try:
            res += getStock(*stock)
        except Exception, e:
            if len(stock) > 1:
                res += "%s %s" % (stock[1], str(e))
            else:
                res += str(e)
    res += getMail()
    print('<txt>%s</txt>' % (res))

getStocks([('1rPENX', 'Euronext'), ('1rPAIR',)])

Get stock code id from website URL (last part). A file version is available here.

I added another part to get email count from gmail. It relies on a bash script that fetches RSS feeds when data is wrote in the FIFO.

Body of the script :

#!/bin/bash

USER='soutade'

while [ 1 ] ; do
    echo -n "Please enter gmail account password : "
    read -s password
    echo ""
    echo -n "Confirm password : "
    read -s password2
    echo ""
    if [ "$password" != "$password2" ] ; then
        echo -e "Passwords doesn't match !!\n"
        continue
    fi
    break
done

pipew="/tmp/gmail-pipe-w"
piper="/tmp/gmail-pipe-r"

rm -f $pipew $piper
mkfifo $pipew $piper

while [ 1 ] ; do
    read line < $pipew
    feeds=`curl -u "$USER:$password" --silent "https://mail.google.com/mail/feed/atom"`
    echo $feeds | sed  s/.*\<fullcount\>//g | sed  s/\<\\/fullcount\>.*//g > $piper
done

You can hardcode password in the script, but I don't like having my password in clear on the harddrive. A file version is available here.

Tips : Linux kernel coding rules

Friday, 01 December 2017
|
Écrit par
Grégory Soutadé

Tux, the Linux mascot

For some projects I have to work inside Linux kernel code and, as a developer, I have my own preferences for coding rules (not stable for variable/function naming in facts...) . Especially, I prefer 4 spaces for indentation, curly brackets at a newline. But, the kernel is full of narcissists dictatorscoders and a strict set of coding rules has been determined some years ago. It can be found in Documentation/CodingStyle. Linux is a big project with thousands of people working on it, so I agree that it requires some code normalization for all contribution. Even if you don't plan to verse your patches into upstream, it's good to follow these rules. Here is some tips to comply with it.

First, as an emacs user I have my own rules in my ~/.emacs configuration file. But, when I work on Linux kernel, I want "linux" rules to be applied. A tip from emacswiki allows to automatically switch when a file with "linux" in its path name is found :

(defun maybe-linux-style ()
  (when (and buffer-file-name
         (string-match "linux" buffer-file-name))
    (c-set-style "Linux")
    (c-set-indentation-style "linux")
    (indent-tabs-mode t)
    ))
(add-hook 'c-mode-hook 'maybe-linux-style)
(add-hook 'before-save-hook 'delete-trailing-whitespace)

Another tip I use is a modified pre-commit hook that will checks my modifications before validate the commit. Edit you .git/hooks/pre-commit with the following lines :

#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments.  The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".

temp_file=`mktemp`
files=`git diff --name-only`
to_diff=""
for file in ${files} ; do
    # Filter .c and .h files
    echo ${file} | grep ".h$" > /dev/null 2>&1
    if [ $? -ne 0 ] ; then
    echo ${file} | grep ".c$" > /dev/null 2>&1
    if [ $? -ne 0 ] ; then
        continue
    fi
    fi
    # Add not deleted file
    if [ -f ${file} ] ; then
    to_diff="${to_diff} ${file}"
    fi
done
git diff --no-color -u --summary ${to_diff} > ${temp_file}
./scripts/checkpatch.pl --no-signoff --min-conf-desc-length=0 --no-summary --mailback ${temp_file}
ret=$?
rm -f ${temp_file}
exit $ret

Don't forget chmod +x .git/hooks/pre-commit

Unfortunately, this hook can't be stored into the central repository and have to be copied each time you clone it (or install a server hook). If you want to bypass it (for some reasons), just commit with "--no-verify" option.

Finally, when you already have a custom codebase ready and commited, you can use some scripts provided by the kernel team to check for coding rules. The first is scripts/Lindent that will indent your file following the kernel coding rules via indent util (needs to be installed). The second is scripts/cleanfile which remove unnecessary whitespaces. The third is scripts/checkpatch.pl -f that will checks the whole file (and not just a patch).

Division by 10

Wednesday, 27 September 2017
|
Écrit par
Grégory Soutadé

For a human, it's pretty simple to divide a number by ten because we used to calculate in ten base everyday. But ... a computer handle numbers in base 2. It doesn't means that it can't compute a division with a number that is not a power of 2, but operations are really faster in this base. Especially if you don't have floating point unit.

At work, I had to re implement the function "printf". To display decimal integers, I use an algorithm like :

while (value)
{
    *cur_ptr = '0' + (value%10);
    value = value/10;
    ...
}

This works fine, but two GCC builtin functions udivdi3 and umoddi3 are called which represent an amount of 3.5kB of code. So, I was looking for a code size optimized implementation on the Internet and didn't found my way.

Finally, I wrote my own. It's a basic one inspired from child learning method :

01. void div10(unsigned value, unsigned* _res, unsigned* _mod)
02. {
03.    unsigned res = value / 8;
04.    unsigned mod = value - (res*10);
05.
06.    while (mod > 10)
07.    {
08.        res -= 1;
09.        mod += 10;
10.    }
11.
12.    *_res = res;
13.    *_mod = mod;
14. }

This algorithm is a basic approach to division. It tries all numbers until it find the good one.

First thing : why I use variables instead of directly write values to pointers ? It's to indicate to GCC that they are temporary values which can be kept into registers and not written every loop into the memory (save instructions and memory accesses).

Line 3 is the begining. We will start at value / 8 which can be easily done by the computer because it is equivalent to a right shift of 3 bits (only one instruction). Note that 8 is the closest power of two to 10 and x/8 is greater than x/10 .

Line 4 is the computation of difference (distance) between my result multiplied by 10 and the current value. For the final result, this difference must be less than 10 (which correspond to the modulo).

Line 6 : while its not the case, we decrement result and increment modulo. Why incrementing modulo ? It's an optimization of the re computation of :

mod = value - (res*10);

If res is decremented, modulo is incremented as value is fixed. So, a simple addition is sufficient here.

There is another big trick in this code : the substract line 4 is done with UNSIGNED values and the result of line 4 is most of the time negative ! Which corresponds to big unsigned value (> 2147483648) that also implies > 10. We have to wait an integer overflow for mod to become positive and when it's done, we get the current modulo value (at least MAXLONGINT+10 = 9) !

If we does opposite operation ((res*10) - value), we have to decrement mod until it becomes less or equals to 0. But, in this case, all operations must be done in signed mode and the final modulo must be inverted at the end (more instructions) :

void div10(unsigned long value, unsigned long* _res, unsigned long* _mod)
{
    unsigned long res = value / 8;
    unsigned long mod = (res*10) - value;

    while (((signed long)mod) > 0)
    {
        res -= 1;
        mod -= 10;
    }

    *_res = res;
    *_mod = (unsigned long)-(signed long)mod;
}

Facts : the unsigned version of my algorithm is 15 instructions while udivdi3 + umoddi3 is 881 instructions. Wonderful !

Beware : this algorithm is slow. For small numbers it's not import because x/8 =~ x/10, but when x becomes bigger, the difference can be huge and requires one decrement, one increment and one test multiplied (x/8 - x/10)/10 times. For 32 bits numbers, it's 10 737 418 loops...

This algorithm can be extended to any divisor by replacing hardcoded divisor with a parameter and a function that finds the nearest and inferior power of 2.

void div(unsigned long value, unsigned long divisor, unsigned long* _res, unsigned long* _mod)
{
    unsigned long res, mod, tmp = divisor, power2 = 0;

    /* Nearest inferior power of 2 */
    while (tmp > 1)
    {
        tmp >>=1;
        power2++;
    }

    res = value >> power2;
    mod = value - (res*divisor);

    while (mod > divisor)
    {
        res -= 1;
        mod += divisor;
    }

    *_res = res;
    *_mod = mod;
}

iMac stuck at boot

Friday, 26 May 2017
|
Écrit par
Grégory Soutadé

Update : Finally it seems that the GPU is dead. I installed Debian testing with XFCE UI which is very smooth even n software rendering

The last week I was given an iMac 27" to repair, with Yosemite installed on it. It boots fine but was stuck at about 1/3 of the progress bar. Booting in safe mode works (left shift key pressed at startup), so it's not an hardware issue. After trying a lot of things (booting in verbose mode (command + V), reset SMC/PRAM, reinstalling system in recovery mode (command + R)), I found the solution thanks to this thread.

You have to boot in single user mode (command + S), then type :

# mount - uw /
# system_profiler SPExtensionsDataType > /tmp/a
# less a

Then search for "Not Signed" with "/" comamnd and goto next found iteration with "n" command.

We can see "EPSONUSBPrinter" and "hp_fax_io" are not signed, so delete these extensions and reboot like this :

# cd /System/Library/Extensions
# rm -rf name_of_the_kext.kext
# rm -rf /System/Library/Caches/*
# reboot

Be careful to not delete some other non signed core extensions (JMicronATA...). Select only external peripherals drivers. A blocking module was my first diagnostic, but impossible to know which is responsible until there is no really verbose boot mode (thanks Apple).

Update

There is one way to have access to logs during boot :

  • Go in recovery mode
  • Launch a terminal
  • Type "resetpassword"
  • Reset root password
  • Reboot in safe mode
  • Login as root (other)
  • Enable SSH access
  • Reboot
  • Connect to your mac in SSH (ssh root@XXX)
  • Read the log in /var/log/system.log

Create your own USB gadget with GadgetFS

Monday, 25 July 2016
|
Écrit par
Grégory Soutadé

Do It Yourself, make your own objects, this is in vogue. Since the first version RaspberryPI, we can see a lot of little boards with GPIO connections that handles basic sensors/connectors. Even if some prefer to use wireless communications to communicate with these little devices, USB is still there !

Today I'll show an example of a basic raw bulk USB communication. We could setup our device with the serial line gadget (/dev/ttyGS0), but if you need performance or you want to handle specific USB feature, it's interesting to use raw transfers. Plus, all of this is done in userspace thanks to GadgetFS.

I waste a lot of time due to a buggy USB driver (dwc2), but, finally, the right code is simple.

1. USB keywords

All technical details about USB can be found within usb.org or linux-usb.org, it's quite heavy. Basically, USB communication use a master/slave schema : host side (mainly a PC) sends requests to device side (device). Device never asks questions, it only replies.

Configuration of USB port can be static (host or device) or dynamic. This is the case for DRD (Dual Role Device) configurations, which was previously called OTG (On The Go : who is the first to talk ?).

On the device, we have endpoints grouped into interfaces. Each endpoint contains a hardware buffer (memory) to deal with requests. An endpoint is setup (hardcoded) to be in or out, meaning, it can do only "in" or "out" operation.

In addition to direction, the type of endpoint is important (it's assigned during endpoint configuration). It can be :

  • control for configuration/control requests
  • bulk for bulk transfers
  • isochronous for periodic transfers with a reserved bandwidth
  • int for transfers by interruption

A special endpoint called ep0 (the first one) is always present. It's an in and out endpoint with control attribute. It allows to read/write configuration of other endpoints.

All these information are described in descriptors. You can look at them with

lsusb -v

The low level command sent to controller is called an URB (USB Request Block).

A picture to sum up :

USB host/device schema

2. Enable and mount GadgetFS

First thing to do is to enable GadgetFS in the kernel you're running (if it's not already the case).

Run make menuconfig [ARCH=XXX] in the kernel build directory. Then, enable

Device Drivers -> USB support -> USB Gadget Support -> USB Gadget Drivers -> Gadget Filesystem

In the same section (or in the section above depending on your controller), select the right Peripheral Controller.

You can now rebuild your Linux kernel.

Once booted, mount GadgetFS (in /dev for example)

mkdir /dev/gadget
mount -t gadgetfs gadgetfs /dev/gadget

3. Device side

There is a reference code that can be found in linux-usb.org which manage everything and can use aio for asynchronous requests management. Mine is simpler, full code link can be found in Conclusion part. I'll explain each parts in details.

Let's start. Do includes. usbtring.c is copied from linux-usb.org (strings sent back to host must be encoded in UTF-16). Most of structures and defines are referenced in ch9.h (which corresponds to chapter 9 of the USB norm) and gadgetfs.h

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>

#include <linux/types.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadgetfs.h>

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <pthread.h>

#include <errno.h>

#include "usbstring.c"

Then, defines

#define FETCH(_var_)                            \
    memcpy(cp, &_var_, _var_.bLength);          \
    cp += _var_.bLength;

#define CONFIG_VALUE 2

// Specific to controller
#define USB_DEV "/dev/gadget/dwc2"
#define USB_EPIN "/dev/gadget/ep1in"
#define USB_EPOUT "/dev/gadget/ep2out"

enum {
    STRINGID_MANUFACTURER = 1,
    STRINGID_PRODUCT,
    STRINGID_SERIAL,
    STRINGID_CONFIG_HS,
    STRINGID_CONFIG_LS,
    STRINGID_INTERFACE,
    STRINGID_MAX
};

Config value is the number of endpoints. After that, we have paths relative to GadgetFS. When mounted, there is only USB_DEV, endpoints appears after the first configuration (ep0). Name of endpoints is dependent of the driver implementation.

Structures and static variables :

struct io_thread_args {
    unsigned stop;
    int fd_in, fd_out;
};

static struct io_thread_args thread_args;

static struct usb_string stringtab [] = {
    { STRINGID_MANUFACTURER, "MyOwnGadget", },
    { STRINGID_PRODUCT,      "Custom gadget", },
    { STRINGID_SERIAL,       "0001", },
    { STRINGID_CONFIG_HS,    "High speed configuration", },
    { STRINGID_CONFIG_LS,    "Low speed configuration", },
    { STRINGID_INTERFACE,    "Custom interface", },
    { STRINGID_MAX, NULL},
};

static struct usb_gadget_strings strings = {
    .language = 0x0409, /* en-us */
    .strings = stringtab,
};

static struct usb_endpoint_descriptor ep_descriptor_in;
static struct usb_endpoint_descriptor ep_descriptor_out;

The main thing here is the description of strings inside stringtag that will be parsed by usbstring functions.

int main()
{
    int fd=-1, ret, err=-1;
    uint32_t send_size;
    struct usb_config_descriptor config;
    struct usb_config_descriptor config_hs;
    struct usb_device_descriptor device_descriptor;
    struct usb_interface_descriptor if_descriptor;
    uint8_t init_config[2048];
    uint8_t* cp;

    fd = open(USB_DEV, O_RDWR|O_SYNC);

    if (fd <= 0)
    {
        printf("Unable to open %s (%m)\n", USB_DEV);
        return 1;
    }

    *(uint32_t*)init_config = 0;
    cp = &init_config[4];

    device_descriptor.bLength = USB_DT_DEVICE_SIZE;
    device_descriptor.bDescriptorType = USB_DT_DEVICE;
    device_descriptor.bDeviceClass = USB_CLASS_COMM;
    device_descriptor.bDeviceSubClass = 0;
    device_descriptor.bDeviceProtocol = 0;
    //device_descriptor.bMaxPacketSize0 = 255; Set by driver
    device_descriptor.idVendor = 0xAA; // My own id
    device_descriptor.idProduct = 0xBB; // My own id
    device_descriptor.bcdDevice = 0x0200; // Version
    // Strings
    device_descriptor.iManufacturer = STRINGID_MANUFACTURER;
    device_descriptor.iProduct = STRINGID_PRODUCT;
    device_descriptor.iSerialNumber = STRINGID_SERIAL;
    device_descriptor.bNumConfigurations = 1; // Only one configuration

    ep_descriptor_in.bLength = USB_DT_ENDPOINT_SIZE;
    ep_descriptor_in.bDescriptorType = USB_DT_ENDPOINT;
    ep_descriptor_in.bEndpointAddress = USB_DIR_IN | 1;
    ep_descriptor_in.bmAttributes = USB_ENDPOINT_XFER_BULK;
    ep_descriptor_in.wMaxPacketSize = 512; // HS size

    ep_descriptor_out.bLength = USB_DT_ENDPOINT_SIZE;
    ep_descriptor_out.bDescriptorType = USB_DT_ENDPOINT;
    ep_descriptor_out.bEndpointAddress = USB_DIR_OUT | 2;
    ep_descriptor_out.bmAttributes = USB_ENDPOINT_XFER_BULK;
    ep_descriptor_out.wMaxPacketSize = 512; // HS size

    if_descriptor.bLength = sizeof(if_descriptor);
    if_descriptor.bDescriptorType = USB_DT_INTERFACE;
    if_descriptor.bInterfaceNumber = 0;
    if_descriptor.bAlternateSetting = 0;
    if_descriptor.bNumEndpoints = 2;
    if_descriptor.bInterfaceClass = USB_CLASS_COMM;
    if_descriptor.bInterfaceSubClass = 0;
    if_descriptor.bInterfaceProtocol = 0;
    if_descriptor.iInterface = STRINGID_INTERFACE;

    config_hs.bLength = sizeof(config_hs);
    config_hs.bDescriptorType = USB_DT_CONFIG;
    config_hs.wTotalLength = config_hs.bLength +
        if_descriptor.bLength + ep_descriptor_in.bLength + ep_descriptor_out.bLength;
    config_hs.bNumInterfaces = 1;
    config_hs.bConfigurationValue = CONFIG_VALUE;
    config_hs.iConfiguration = STRINGID_CONFIG_HS;
    config_hs.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
    config_hs.bMaxPower = 1;

    config.bLength = sizeof(config);
    config.bDescriptorType = USB_DT_CONFIG;
    config.wTotalLength = config.bLength +
        if_descriptor.bLength + ep_descriptor_in.bLength + ep_descriptor_out.bLength;
    config.bNumInterfaces = 1;
    config.bConfigurationValue = CONFIG_VALUE;
    config.iConfiguration = STRINGID_CONFIG_LS;
    config.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
    config.bMaxPower = 1;

    FETCH(config);
    FETCH(if_descriptor);
    FETCH(ep_descriptor_in);
    FETCH(ep_descriptor_out);

    FETCH(config_hs);
    FETCH(if_descriptor);
    FETCH(ep_descriptor_in);
    FETCH(ep_descriptor_out);

    FETCH(device_descriptor);

    // Configure ep0
    send_size = (uint32_t)cp-(uint32_t)init_config;
    ret = write(fd, init_config, send_size);

    if (ret != send_size)
    {
        printf("Write error %d (%m)\n", ret);
        goto end;
    }

    printf("ep0 configured\n");

    handle_ep0(fd);

end:
    if (fd != -1) close(fd);

    return err;
}

The main function. We build the descriptors and send them to ep0. It's needed to send both low/full speed (USB 1) and high speed (USB 2) configurations. Here, they are quite the same. We have only one interface with two endpoints, one for in, and one for out. Descriptors are sent as a big char array that must starts by an uint32_t tag set to 0. All values are expressed in little endian.

ep0 function :

static void handle_ep0(int fd)
{
    int ret, nevents, i;
    fd_set read_set;
    struct usb_gadgetfs_event events[5];

    while (1)
    {
        FD_ZERO(&read_set);
        FD_SET(fd, &read_set);

        select(fd+1, &read_set, NULL, NULL, NULL);

        ret = read(fd, &events, sizeof(events));

        if (ret < 0)
        {
            printf("Read error %d (%m)\n", ret);
            goto end;        
        }

        nevents = ret / sizeof(events[0]);

        printf("%d event(s)\n", nevents);

        for (i=0; i<nevents; i++)
        {
            switch (events[i].type)
            {
            case GADGETFS_CONNECT:
                printf("EP0 CONNECT\n");
                break;
            case GADGETFS_DISCONNECT:
                printf("EP0 DISCONNECT\n");
                break;
            case GADGETFS_SETUP:
                printf("EP0 SETUP\n");
                handle_setup_request(fd, &events[i].u.setup);
                break;
            case GADGETFS_NOP:
            case GADGETFS_SUSPEND:
                break;
            }
        }
    }

end:
    return;
}

This one receives events and handle them. The most important are setup requests, which are requests that kernel cannot full handle by itself (or notice userspace).

static void handle_setup_request(int fd, struct usb_ctrlrequest* setup)
{
    int status;
    uint8_t buffer[512];
    pthread_t thread;

    printf("Setup request %d\n", setup->bRequest);

    switch (setup->bRequest)
    {
    case USB_REQ_GET_DESCRIPTOR:
        if (setup->bRequestType != USB_DIR_IN)
            goto stall;
        switch (setup->wValue >> 8)
        {
            case USB_DT_STRING:
                printf("Get string id #%d (max length %d)\n", setup->wValue & 0xff,
                    setup->wLength);
                status = usb_gadget_get_string (&strings, setup->wValue & 0xff, buffer);
                // Error 
                if (status < 0)
                {
                    printf("String not found !!\n");
                    break;
                }
                else
                {
                    printf("Found %d bytes\n", status);
                }
                write (fd, buffer, status);
                return;
        default:
            printf("Cannot return descriptor %d\n", (setup->wValue >> 8));
        }
        break;
    case USB_REQ_SET_CONFIGURATION:
        if (setup->bRequestType != USB_DIR_OUT)
        {
            printf("Bad dir\n");
            goto stall;
        }
        switch (setup->wValue) {
        case CONFIG_VALUE:
            printf("Set config value\n");
            if (!thread_args.stop)
            {
                thread_args.stop = 1;
                usleep(200000); // Wait for termination
            }
            if (thread_args.fd_in <= 0)
            {
                status = init_ep (&thread_args.fd_in, &thread_args.fd_out);
            }
            else
                status = 0;
            if (!status)
            {
                thread_args.stop = 0;
                pthread_create(&thread, NULL, io_thread, &thread_args);
            }
            break;
        case 0:
            printf("Disable threads\n");
            thread_args.stop = 1;
            break;
        default:
            printf("Unhandled configuration value %d\n", setup->wValue);
            break;
        }        
        // Just ACK
        status = read (fd, &status, 0);
        return;
    case USB_REQ_GET_INTERFACE:
        printf("GET_INTERFACE\n");
        buffer[0] = 0;
        write (fd, buffer, 1);
        return;
    case USB_REQ_SET_INTERFACE:
        printf("SET_INTERFACE\n");
        ioctl (thread_args.fd_in, GADGETFS_CLEAR_HALT);
        ioctl (thread_args.fd_out, GADGETFS_CLEAR_HALT);
        // ACK
        status = read (fd, &status, 0);
        return;
    }

stall:
    printf("Stalled\n");
    // Error
    if (setup->bRequestType & USB_DIR_IN)
        read (fd, &status, 0);
    else
        write (fd, &status, 0);
}

A bad response within this function can stall the endpoint. Two principle functions are to send back strings (not managed by driver) and starts/stop io_thread().

The init_ep() function is pretty simple. It justs sends endpoint descriptors (in low/full and high speed configuration). Like ep0, it must starts with an uint32_t tag of value 1 :

static int init_ep(int* fd_in, int* fd_out)
{
    uint8_t init_config[2048];
    uint8_t* cp;
    int ret = -1;
    uint32_t send_size;

    // Configure ep1 (low/full speed + high speed)
    *fd_in = open(USB_EPIN, O_RDWR);

    if (*fd_in <= 0)
    {
        printf("Unable to open %s (%m)\n", USB_EPIN);
        goto end;
    }

    *(uint32_t*)init_config = 1;
    cp = &init_config[4];

    FETCH(ep_descriptor_in);
    FETCH(ep_descriptor_in);

    send_size = (uint32_t)cp-(uint32_t)init_config;
    ret = write(*fd_in, init_config, send_size);

    if (ret != send_size)
    {
        printf("Write error %d (%m)\n", ret);
        goto end;
    }

    printf("ep1 configured\n");

    // Configure ep2 (low/full speed + high speed)
    *fd_out = open(USB_EPOUT, O_RDWR);

    if (*fd_out <= 0)
    {
        printf("Unable to open %s (%m)\n", USB_EPOUT);
        goto end;
    }

    *(uint32_t*)init_config = 1;
    cp = &init_config[4];

    FETCH(ep_descriptor_out);
    FETCH(ep_descriptor_out);

    send_size = (uint32_t)cp-(uint32_t)init_config;
    ret = write(*fd_out, init_config, send_size);

    if (ret != send_size)
    {
        printf("Write error %d (%m)\n", ret);
        goto end;
    }

    printf("ep2 configured\n");

    ret = 0;

end:
    return ret;
}

Finally, the io_thread() that responds to host requests. Here, I use select, but it seems not to be handled by driver, I/Os are just blocking, but it could be necessary if we want to stop thread.

/*
 * Respond to host requests
 */
static void* io_thread(void* arg)
{
    struct io_thread_args* thread_args = (struct io_thread_args*)arg;
    fd_set read_set, write_set;
    struct timeval timeout;
    int ret, max_read_fd, max_write_fd;
    char buffer[512];

    max_read_fd = max_write_fd = 0;

    if (thread_args->fd_in > max_write_fd) max_write_fd = thread_args->fd_in;
    if (thread_args->fd_out > max_read_fd) max_read_fd  = thread_args->fd_out;

    while (!thread_args->stop)
    {
        FD_ZERO(&read_set);
        FD_SET(thread_args->fd_out, &read_set);
        timeout.tv_sec = 0;
        timeout.tv_usec = 10000; // 10ms

        memset(buffer, 0, sizeof(buffer));
        ret = select(max_read_fd+1, &read_set, NULL, NULL, &timeout);

        // Timeout
        if (ret == 0)
            continue;

        // Error
        if (ret < 0)
            break;

        ret = read (thread_args->fd_out, buffer, sizeof(buffer));

        if (ret > 0)
            printf("Read %d bytes : %s\n", ret, buffer);
        else
            printf("Read error %d(%m)\n", ret);

        FD_ZERO(&write_set);
        FD_SET(thread_args->fd_in, &write_set);

        memset(buffer, 0, sizeof(buffer));
        ret = select(max_write_fd+1, NULL, &write_set, NULL, NULL);

        // Error
        if (ret < 0)
            break;

        strcpy(buffer, "My name is USBond !");

        ret = write (thread_args->fd_in, buffer, strlen(buffer)+1);

        printf("Write status %d (%m)\n", ret);
    }

    close (thread_args->fd_in);
    close (thread_args->fd_out);

    thread_args->fd_in = -1;
    thread_args->fd_out = -1;

    return NULL;
}

4. host side

Host part is very easy to implement. This part can be handled by libusb for a more complete and generic code.

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <linux/usbdevice_fs.h>
#include <linux/usb/ch9.h>

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>

#define USB_DEV "/proc/bus/usb/001/002"

int main()
{
    int fd, ret, err=-1;
    struct usbdevfs_connectinfo connectinfo;
    struct usbdevfs_bulktransfer transfert;
    uint32_t val;
    char buffer[512];

    printf("Build %s @ %s\n", __DATE__, __TIME__);

    fd = open(USB_DEV, O_RDWR);

    if (fd <= 0)
    {
        printf("Unable to open %s (%m)\n", USB_DEV);
        return 1;
    }

    printf("Device opened\n");

    // Optional get information
    ret = ioctl(fd, USBDEVFS_CONNECTINFO, &connectinfo);
    if (ret)
    {
        printf("USBDEVFS_CONNECTINFO error %d (%m)\n", ret);
        goto end;
    }

    printf("devnum %d, slow %d\n",
           connectinfo.devnum, connectinfo.slow);

    // Claim interface 0
    val = 0;
    ret = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &val);
    if (ret)
    {
        printf("USBDEVFS_CLAIMINTERFACE error %d (%m)\n", ret);
        goto end;
    }
    else
        printf("Interface claimed\n");

    // Send data on ep2out
    strcpy(buffer, "What is your name ?");

    transfert.ep = USB_DIR_OUT + 2;
    transfert.len = strlen(buffer)+1;
    transfert.timeout = 200;
    transfert.data = buffer;

    ret = ioctl(fd, USBDEVFS_BULK, &transfert);
    if (ret < 0)
    {
        printf("USBDEVFS_BULK 1 error %d (%m)\n", ret);
        goto end;
    }
    else
        printf("Transfert 1 OK %d\n", ret);

    // Receive data on ep1in
    transfert.ep = USB_DIR_IN + 1;
    transfert.len = sizeof(buffer);
    transfert.timeout = 200;
    transfert.data = buffer;

    ret = ioctl(fd, USBDEVFS_BULK, &transfert);
    if (ret < 0)
    {
        printf("USBDEVFS_BULK 2 error %d (%m)\n", ret);
        goto end;
    }
    else
        printf("Transfert 2 OK %d %s\n", ret, buffer);

    // Release interface 0
    val = 0;
    ret = ioctl(fd, USBDEVFS_RELEASEINTERFACE, &val);
    if (ret)
    {
        printf("USBDEVFS_RELEASEINTERFACE error %d (%m)\n", ret);
        goto end;
    }

    printf("Interface released\n");

    err = 0;

end:
    close(fd);

    return err;
}

To start, we claim an interface. This ioctl fully handled in host side driver (nothing is send to device). After that, a simple send/receive protocol. Finally we release interface. be carreful, USB_DEV path change when the device is disconnected.

5. Conclusion

The full code can be found on my server. This basic example can be extended a lot : isochronous, asynch requests, streams (USB 3). Enjoy !