Thursday, 18 August 2016
|
Écrit par
Grégory Soutadé

Leo Moracchioli

Leo Moracchioli

Il existe des êtres doués d'une intelligence supra normale, des génies qui possèdent entre autres la faculté d'assumer n'importe quelle identité. En 1963, les chercheurs d'une entreprise appelée « Le Centre » ont mis en isolement un de ces êtres, un jeune garçon nommé Jarod et exploitèrent son génie pour des recherches secrètes. Mais un jour le « Caméléon » leur échappa...

Le Caméléon (The pretender)

C'est ainsi que je pourrais introduire cet article. Ce norvégien (dont le nom a une consonance très latine) est un véritable génie de la musique. En effet, être un bon chanteur de métal n'est pas si fréquent que ça. Être un chanteur guitariste l'est encore moins (hors pop-rock). Savoir jouer de la basse quand on maîtrise déjà la guitare, soit. Mais, en plus, maîtriser la batterie, c'est carrément exceptionnel.

Exceptionnel ? Plus encore, puisque Leo réalise lui-même ses enregistrements audio ET vidéo. Mais pas que, il est aussi capable de re créer une mélodie connue et de l'adapter en métal ! On pourrait aller jusqu'à dire qu'il a l'oreille absolue tant d'un point de vue théorique que pratique. Une sorte de Mozart des temps modernes.

Le créneau de M. Moracchioli est donc les reprises (covers) de morceaux connus en (nu-)métal (genre qui se prête parfaitement à l'exercice). Ce qu'il fait très bien d'une manière générale. Si certaines sont assez proches de l'original, d'autres donnent droit à une véritable interprétation. Il publie une reprise hebdomadaire sur sa chaîne Youtube Frog Leap Studios, qui en compte 128 à ce jour.

C'est justement grâce à Youtube qu'il a pu se faire connaître. Comme quoi, quand on utilise les réseaux interrnetsociaux de la bonne façon, on peut voir émerger des perles. La génération Youtube sera également rassasiée avec les vlogs hebdomadaires, bien qu'ils ne représentent pas, à mes yeux, un contenu très intéressant (il parle de sa vie/matériel musical).

Si on devait caractériser le style de le Leo, on pourrait le classifier très clairement dans le nu-métal. Ses reprises regorgent d'une énergie impressionnante qu'il met parfaitement en valeur dans ses clips. Il n'a pas une voix très puissante et ne cherche pas à aller au-delà de ses limites (comme dans Rolling In The Deep), ce qui montre une maîtrise technique avancée. Bref, c'est un artiste complet que chacun pourra adopter rapidement !

Mon top 5 :

Patreon

Vivre de sa passion, particulièrement quand on est artiste, est un privilège assez rare. L'économie numérique le permet dans certains cas. Ainsi, Leo propose via des plateformes audio numérique (Loudr, Amazon, Google et Apple) ses morceaux (actuellement les trois quarts sont disponibles).

Un autre concept à la kickstart est également disponible. Il s'agit de la plateforme Patreon, qui propose de devenir un mécène en finançant automatiquement un artiste lorsqu'il produit quelque chose (ici une reprise) ou mensuellement.

Il est fascinant de voir comment, grâce à l'Internet, les gens commencent à se ré approprier des biens et des services autrefois uniquement délivrés par des entreprises ou des services d'état. Néanmoins, ma référence à kickstarter n'est pas anodine. Les plateformes qui proposent ce genre de service touchent une commission. Si cela n'est pas aberrant puisqu'elles proposent un réel service, il ne faut pas l'oublier. C'est le nouveau filon qui fait rêver tout entrepreneur web. Même si le concept de financement participatif est largement détourné pour devenir une plateforme de pré vente.

D'un autre côté, les utilisateurs sont de plus en plus réticents à sortir le porte monnaie. L'exemple le plus flagrant étant la mise à disposition des contenus audio/vidéo sur les réseaux pair-à-pair. Pour couronner le tout, en plus de la gratuité du produit, l'utilisateur réclame constamment du contenu nouveau, ce qui peut être un frein à la réelle création ou implique de sortir quelque chose de mauvais pour tenir le rythme.

Et si le service semble gratuit, c'est souvent que la plateforme souhaite augmenter le plus possible sa base d'utilisateur afin :

  • D'être racheté
  • De vendre les données personnelles (préférences utilisateur)
  • De proposer de la publicité (ciblée ou non)

C'est donc un mélange assez subtil qui s'opère et qu'il n'est pas toujours évident de suivre.

En plus de son activité de Youtubeur, Leo propose également la prestation de ses talents d'ingénieur de son (enregistrement, mixage) via son studio Frog Leap Studios, ainsi que la réalisation de concerts privés.

Ten Masked Men

Petit aparté concernant Ten Masked Men. Les groupes de reprise ne sont pas une idée nouvelle. 20 ans plus tôt, les britanniques de Ten Masked Men sortaient un album éponyme. Contrairement à Leo, ils jouent du death métal, ce qui donne une certaine lourdeur aux morceaux, mais nécessite des prouesses vocales pour arriver à tenir le débit, comme on peut l'entendre sur Gangster's Paradise. Hélas pour leur notoriété, ils ne s'appuient pas sur la vidéo pour propager leur musique. Leur dernier album est paru en 2014.

Bonus

Un petit bonus, qui n'a rien à voir, pour les courageux qui sont arrivés jusqu'ici : Pretty Fly (for a White Guy) mis en vidéo à partir d'un mashup de 230 films. Le résultat est bluffant.

Monday, 08 August 2016
|
Écrit par
Grégory Soutadé

Après avoir ouvert le champomy pour les 6 ans du blog, je reviens rapidement sur IWLA car j'aime m'auto extasier sur ce petit outil. L'idée de base était de remplacer AWSTATS par quelque chose de plus facilement "hackable", ce qui est chose faite. Si on rajoute la concision du langage Python par dessus, on obtient un script du genre :

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import argparse
import gzip
import pickle
import re
import operator

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Statistics extraction')

    parser.add_argument('-f', '--file', dest='file',
                        help='Comma separated IWLA databases')

    args = parser.parse_args()

    blog_re = re.compile(r'^.*blog\.soutade\.fr\/post\/.*$')

    big_stats = {}

    for filename in args.file.split(','):
        with gzip.open(filename, 'r') as f:
            print 'open %s' % (filename)
            stats = pickle.load(f)
            print 'unzipped %s' % (filename)
            top_pages = stats['month_stats']['top_pages']
            for (uri, count) in top_pages.items():
                if not blog_re.match(uri):
                    continue
                big_stats[uri] = big_stats.get(uri,0) + count
            print 'analyzed %s' % (filename)

    print '\n\nResults\n\n'

    for (uri, count) in sorted(big_stats.items(), key=operator.itemgetter(1), reverse=True)[:10]:
        print '%s => %d' % (uri, count)

Que fait-il ? Il va tout simplement appliquer un filtre sur les pages du blog qui concernent les articles pour en extraire les 10 les plus consultées. Ce qui me fait gagner du temps pour mon bilan annuel !

Bien sûr, on peut créer des tas d'outil indépendants qui vont extraire et manipuler les données pour les mettre en forme, le tout avec une facilité déconcertante. Mieux encore, créer un plugin pour l'intégrer directement dans la sortie HTML quand ceci est nécessaire !

Thursday, 04 August 2016
|
Écrit par
Grégory Soutadé

Gâteau 6 ans

Le blog a six ans ! L'occasion de faire le petit bilan annuel. Les premières pensées qui me viennent à l'esprit vont vers les victimes d'une année noire (une de plus...). Une année où la France et l'Europe en général ont re découvert la barbarie aveugle. Les nouvelles d'un attentat au moyen orient étaient tellement (et tristement) courantes que l'on n'y faisait plus attention. Le fait que ces mêmes actes se passent sous notre nez a un impact totalement différent. Qui plus est quand on aurait pu se trouver soit même sur les lieux.

Paris, Bruxelles, Nice, Munich, Saint-Etienne-du-Rouvray. Autant de villes qui ont connu l'horreur. Difficile dans ce contexte de ne pas tomber dans une haine primaire. Difficile aussi de sortir sans avoir un sentiment de suspicion permanent.

Pour autant, il faut s'accrocher à ses idéaux, continuer à vivre.

Les statistiques de cette année seront déséquilibrées. En effet, j'ai décidé de ne plus compter les visites ne correspondant qu'à un seul élément (par exemple, quand on recherche une image et que l'on n'affiche que celle-ci sans visiter le site). Changement qui s'est opéré à partir de janvier 2015. Malgré cela, le nombre de visites totale est en légère hausse. La masse de données envoyée également. Cela provient des reportages photos qui sont très gourmands en bande passante.

On obtient en moyenne 65 visites par jour, là aussi, en hausse. Pourtant, depuis mai, il y a une baisse significative des visites qui s'établit à ~26/jour (non visible ici). Cette tendance est compensée par les bons chiffres de l'année passée, mais devrait se retrouver dans les statistiques de l'année prochaine.

Côté publication, encore une baisse avec seulement 31 articles, même si 3 ou 4 articles ont été avortés ou repoussés. Je dois dire que, si la matière première me manque, j'éprouve toujours autant de plaisir dans la rédaction. En substance, les articles sont un mélange de technique (dont 4 en anglais), découverte, politique. Mélange qui reste fidèle à l'objectif d'un blog pluridisciplinaire.

Côté technique, il y a la création de Dénote. Je n'aurais jamais cru que cette bête application de prise de notes puisse être aussi utile ! La fermeture de facebook2email, mais aussi et surtout, une mise à jour d'IWLA qui, conjointement avec iptogeo, devient de plus en plus puissant !

Quelques chiffres :

  • 31 articles publiés (34, 49, 50, 60, 60)
  • 23 800 visites (21 300, 25 000, 12 000, 18 000, 9 000)
  • 17,9 Go de données envoyées (9, 5.5, 2.7, 2.5)
  • 26 700 pages affichées

Le fameux top 10 (en gras, consultation principalement pour les images. Entre parenthèses, l'année de publication) :

C'est assez impressionnant de constater qu'il est quasi identique à celui de l'année dernière (modulo le classement), avec un seul article qui fait son entrée (position 8). Si on fait le cumul du top 10, on obtient 31,4 % des pages affichées. Bien sûr, tous ces chiffres ne tiennent pas compte de la page d'accueil.

Un autre point amusant : L'article sur la liseuse (GAME_OVER) est quasiment devenu (via les commentaires) un forum sur les firmwares Bookeen !

Toujours d'après mon analyseur de statistiques, il y a 3 personnes qui me suivent via les flux RSS et Atom.

On peut donc considérer que ce fut une bonne année en terme d'audience. Quels sont les projets pour l'année prochaine ? Tenir encore un an de plus ! Ce qui n'est pas du tout évident. De leurs côté, les outils arrivent à maturité, je ne fais presque plus que de la maintenance. Néanmoins, il faudrait retravailler le design du blog (notamment pour qu'il s'adapte aux petits écrans).

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_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 !

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

This was my problem for Dynastie (a static blog generator). I have a main super class Post and a derived class Draft that directly inherit from the first one.

class Post(models.Model):
    title = models.CharField(max_length=255)
    category = models.ForeignKey(Category, blank=True, null=True, on_delete=models.SET_NULL)
    creation_date = models.DateTimeField()
    modification_date = models.DateTimeField()
    author = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
    description = models.TextField(max_length=255, blank=True)
    ...

class Draft(Post):
    pass

A draft is a note that will not be published soon. When it's published, the creation date is reset. Using POO and inheritance, it's quick and easy to model this behavior. Nevertheless, there is one problem. When I do Post.objects.all() I get all Post objects + all Draft objects, which is not what I want !!

The trick to obtain only Post is a mix with Python and Django mechanisms called Managers. Managers are at the top level of QuerySet construction. To solve our problem, we'll override the models.Model attribute objects (which is a models.Manager).

Inside inheritance

To find a solution, we need to know what exactly happens when we do inheritance. The best thing to do, is to inspect the database.

CREATE TABLE "dynastie_post" (
    "id" integer NOT NULL PRIMARY KEY,
    "title" varchar(255) NOT NULL,
    "category_id" integer REFERENCES "dynastie_category" ("id"),
    "creation_date" datetime NOT NULL,
    "modification_date" datetime NOT NULL,
    "author_id" integer REFERENCES "auth_user" ("id"),
    "description" text NOT NULL);

CREATE TABLE "dynastie_draft" (
    "post_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "dynastie_post" ("id")
);

We can see that dynastie_draft has a reference to the dynastie_post table. So, doing Post.objects.all() is like writing "SELECT * from dynastie_post" that includes Post part of drafts.

Solution 1 : Without altering base class

The first solution is to create a Manager that will exclude draft id from the request. It has the advantage to keep base class as is, but it's not efficient (especially if there is a lot of child objects).

class PostOnlyManager(models.Manager):
    def get_queryset(self):
        query_set = super(PostOnlyManager, self).get_queryset()
        drafts = Draft.objects.all().only("id")
        return query_set.exclude(id__in=[draft.id for draft in drafts])

class Post(models.Model):
    objects = PostOnlyManager()

class Draft(Post):
    objects = models.Manager()

With this solution, we do two requests at each access. Plus, it's necessary to know every sub class we want to exclude. We have to keep the BaseManager for all subclasses. You can note the use of only method to limit the query and de serialization to minimum required.

Solution 2 : With altering base class

The solution here is to add a field called type that will be filtered in the query set. It's the recommended one in the Django documentation.

class PostOnlyManager(models.Manager):
    def get_query_set(self):
        return super(PostOnlyManager, self).get_queryset().filter(post_type='P')

class Post(models.Model):
    objects = PostOnlyManager()
    post_type = models.CharField(max_length=1, default='P')

class Draft(Post):
    objects = models.Manager()

@receiver(pre_save, sender=Draft)
def pre_save_draft_signal(sender, **kwargs):
    kwargs['instance'].post_type = 'D'

The problem here is the add of one field which increase database size, but filtering is easier and is done in the initial query. Plus, it's more flexible with many classes. I used a signal to setup post_type value, didn't found a better solution for now.

Conclusion

Depending on your constraints you can use the solution 1 or 2. These solutions can also be extended to a more complex filtering mechanism by dividing your class tree in different families.

Dernier gif les joies du code When I make a minor change in a legacy code