Giter Site home page Giter Site logo

linux-usb-gadgets / libusbgx Goto Github PK

View Code? Open in Web Editor NEW
203.0 25.0 68.0 741 KB

C library encapsulating the Linux kernel USB gadget configfs userspace API functionality

License: GNU General Public License v2.0

C 95.78% Shell 0.28% Makefile 0.46% M4 3.17% C++ 0.10% CMake 0.21%
hacktoberfest

libusbgx's Introduction

libusbg-neXt (libusbgx)
-------

libusbgx is a C library encapsulating the kernel USB gadget-configfs
userspace API functionality.

It provides routines for creating and parsing USB gadget devices using
the configfs API. Currently, all USB gadget configfs functions that can
be enabled in kernel release 3.11 (Linux for Workgroups!) are supported.

See the Doxygen docs and examples for complete details on the
programming API and INSTALL for installation of the library and
examples.

The library is licensed under the GNU LGPL-2.1 (or later) and
the examples are licensed under the GNU GPL-2.0 (or later).

To run the examples:

$ modprobe configfs
$ modprobe libcomposite
$ mount -t configfs none /sys/kernel/config
$ gadget-acm-ecm
$ show-gadgets
ID 1d6b:0104 'g1'
  UDC			3f120000.usbotg
  bDeviceClass		0x00
  bDeviceSubClass	0x00
  bDeviceProtocol	0x00
  bMaxPacketSize0	0x40
  bcdDevice		0x0311
  bcdUSB		0x0000
  idVendor		0x1d6b
  idProduct		0x0104
  Serial Number		0123456789
  Manufacturer		Foo Inc.
  Product		Bar Gadget
  Function 'acm.usb0'
    port_num		0
  Function 'acm.usb1'
    port_num		1
  Function 'ecm.usb0'
    dev_addr		32:1b:dc:a4:bc:a2
    host_addr		82:b7:58:62:f6:31
    ifname		usb0
    qmult		5
  Configuration 'c.1'
    MaxPower		2
    bmAttributes	0x80
    configuration	CDC 2xACM+ECM
    acm.GS0 -> acm.usb0
    acm.GS1 -> acm.usb0
    ecm.usb0 -> ecm.usb0

libusbgx's People

Contributors

a3f avatar agners avatar aristochen avatar dsacre avatar fginez avatar johnkeeping avatar koenkooi avatar kopasiak avatar manut avatar mgrzeschik avatar nekocwd avatar nl250060 avatar pabs3 avatar philippedeswert avatar pszewczyk avatar ric96 avatar sabine-schnabeltier avatar stachw avatar studiofuga avatar tmlind avatar trabucayre avatar tretter avatar ty317-kim avatar vianpl avatar vogtinator avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

libusbgx's Issues

libusbgx uses 0x409 for language instead of 0x0409

This caused me no end of grief today. I was kept getting USBG_ERROR_NOT_FOUND on my language directories. It turns out that libusbg regards 0x409 and 0x0409 as different.

It should probably deal with both, but I'm pretty sure that 0x0409 is canonical with the standard, so libusbgx probably needs to accept and emit that.

Thanks.

# git diff usbg.c 
diff --git a/src/usbg.c b/src/usbg.c
index d2bf381..86f9820 100644
--- a/src/usbg.c
+++ b/src/usbg.c
@@ -540,7 +540,7 @@ static int usbg_parse_config_strs(const char *path, const char *name,
        int nmb;
        char spath[USBG_MAX_PATH_LENGTH];
 
-       nmb = snprintf(spath, sizeof(spath), "%s/%s/%s/0x%x", path, name,
+       nmb = snprintf(spath, sizeof(spath), "%s/%s/%s/0x%04x", path, name,
                        STRINGS_DIR, lang);
        if (nmb >= sizeof(spath)) {
                ret = USBG_ERROR_PATH_TOO_LONG;
@@ -856,7 +856,7 @@ static int usbg_parse_gadget_strs(const char *path, const char *name, int lang,
        DIR *dir;
        char spath[USBG_MAX_PATH_LENGTH];
 
-       nmb = snprintf(spath, sizeof(spath), "%s/%s/%s/0x%x", path, name,
+       nmb = snprintf(spath, sizeof(spath), "%s/%s/%s/0x%04x", path, name,
                        STRINGS_DIR, lang);
        if (nmb >= sizeof(spath)) {
                ret = USBG_ERROR_PATH_TOO_LONG;

Permissions?

You have to run the creation examples as root, any chance of instructions on how to configure the system so that it can be run as a user?

UAC2 gadget not recognized on Windows 10.

Using gadget-import (initial config retrieved via gadget-uac2 + gadget-export) to initialize a UAC2 audio device, but it fails to load completely on Windows 10. This is the config file (modifications done to initial config market with !!!):

attrs :
{
    bcdUSB = 0x200;
    bDeviceClass = 0x1; !!!
    bDeviceSubClass = 0x0; !!!
    bDeviceProtocol = 0x20; !!!
    bMaxPacketSize0 = 0x40;
    idVendor = 0x1D6B;
    idProduct = 0x104;
    bcdDevice = 0x100; !!!
};
os_descs :
{
    use = 1; !!!
    qw_sign = "MSFT100"; !!!
    b_vendor_code = 0xcd; !!!
};
strings = (
    {
        lang = 0x0409;
        manufacturer = "Foo Inc.";
        product = "Bar Gadget";
        serialnumber = "0123456789";
    } );
functions :
{
    uac2_usb0 :
    {
        instance = "usb0";
        type = "uac2";
        attrs :
        {
            c_chmask = 3;
            c_srate = 48000; !!!
            c_ssize = 4;
            p_chmask = 3;
            p_srate = 48000; !!!
            p_ssize = 4;
        };
        os_descs = ( );
    };
};
configs = (
    {
        id = 1;
        name = "The only one";
        attrs :
        {
            bmAttributes = 0x80;
            bMaxPower = 0x2;
        };
        strings = (
            {
                lang = 0x0409;
                configuration = "1xUAC2";
            } );
        functions = (
            {
                name = "some_name";
                function = "uac2_usb0";
            } );
    } );

See descriptor dump in next comment.

Microsoft OS Descriptor is not available. Error code: 0x0000001F

but still osvc is set to 01cd in Windows Registry...

What is needed to get it recognized by Windows 10 ?

Error while running examples on beaglebone black latest image

I get the following error when i try to run the example:

debian@beaglebone:~/libusbg/examples$ gadget-acm-ecm
usbg_init_state() Success: unable to parse /sys/kernel/config/usb_gadget

usbg_init() Success: couldn't init gadget state

Error on USB gadget init
Error: USBG_ERROR_NOT_SUPPORTED : Function not supported

Additional Details:

debian@beaglebone:/libusbg/examples$ uname -a
Linux beaglebone 4.14.71-ti-r80 #1 SMP PREEMPT Fri Oct 5 23:50:11 UTC 2018 armv7l GNU/Linux
debian@beaglebone:
/libusbg/examples$ cat /etc/dogtag
BeagleBoard.org Debian Image 2018-10-07

However, when I perform the same procedure on older images I can run it without any issues? Can you please help me fix the issue

Better Schema Feedback

When writing a schema file, the feedback you get is pretty vague (from gt):

Error on import gadget
Error: USBG_ERROR_MISSING_TAG : One of mandatory tags is missing.

It gives no indication of what tag it's expecting or where it's looking for it, leaving me to kinda just guess and hope for the best.

Create a new GitHub release

Hello,

Would it be possible to create a new release of libusbgx? The last release (0.2.0) on GitHub is from March 2018 and is missing some changes including the gcc v8 fixes.
If these releases exist somewhere else that you could point me at that would also be great!

Thank you!

UDC convenience function

You can retrieve the name of the UDC using usbg_get_udc_name which in my case returns "UDC", the content of this file (which you check in some functions) is the real name of the UDC the gadget is attached to, something like 20980000.usb).

A convenience function to get the full "resolved" path to the UDC (i.e the /sys/class/udc/") would be super useful.

p = usbg_get_udc_path(g);

p = "/sys/class/udc/20980000.usb"

I can then use this easily to add a watch on the state file to monitor for the gadget being enumerated and disconnected from a host.

Can't compile out of tree

With the current 0.2.0 (999a6e5) the library can't be compiled out of the tree:

$ autoreconf -i
$ mkdir build
$ cd build
$ ../configure --without-libconfig
$ make -j8
...
libtool: compile:  gcc -DPACKAGE_NAME=\"libusbgx\" -DPACKAGE_TARNAME=\"libusbgx\" -DPACKAGE_VERSION=\"0.2.0\" "-DPACKAGE_STRING=\"libusbgx 0.2.0\"" -DPACKAGE_BUGREPORT=\"[email protected]\" -DPACKAGE_URL=\"\" -DPACKAGE=\"libusbgx\" -DVERSION=\"0.2.0\" "-D_GNU_SOURCE=/**/" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_DLFCN_H=1 -DLT_OBJDIR=\".libs/\" -I. -I../../src -I../../include/ -g -O2 -MT function/libusbgx_la-midi.lo -MD -MP -MF function/.deps/libusbgx_la-midi.Tpo -c ../../src/function/midi.c  -fPIC -DPIC -o function/.libs/libusbgx_la-midi.o
In file included from ../../src/usbg_common.c:13:0:
../../include/usbg/usbg.h:33:26: fatal error: usbg_version.h: File o directory non esistente
compilation terminated.
make[1]: *** [libusbgx_la-usbg_common.lo] Error 1
make[1]: *** Attesa per i processi non terminati....
In file included from ../../src/usbg_schemes_none.c:13:0:
../../include/usbg/usbg.h:33:26: fatal error: usbg_version.h: File o directory non esistente
compilation terminated.

It compiled correctly on 904b04c.

WORKAROUND:

Add the proper include path from the build directory when compiling:

$ ../configure --without-libconfig CFLAGS="-I${PWD}/include/usbg"

Running Examples

After passing the second step outlined in the README file:

gadget-acm-ecm

the following error is presented:

usbg_init()  No such file or directory: couldn't init gadget state

Error on USB gadget init
Error: USBG_ERROR_NOT_FOUND : Not found (file or directory removed)

How can this issue be resolved?

gadget-ffs example

Hi,

this is not an issue but general question. I want to check sending data over USB gadget so I used ffs gadget. This case input and output endpoints are created. I wrote small program using libusb1 to sent data to device. What bit puzzled me how to read data from endpoint. I see ffs/ep1 but when run cat ffs/ep1 got nothing. I tried to use simple aio-examples from kernel sources but result is same. Am I missing something? Thanks a lot.

Compilation fails on gcc v8

Compilation fails on gcc 8:

happycactus@linux-lngr:~/devel/libusbgx> gcc --version
gcc (SUSE Linux) 8.3.1 20190226 [gcc-8-branch revision 269204]

/usr/lib64/gcc/x86_64-suse-linux/8/../../../../x86_64-suse-linux/bin/ld: /home/happycactus/devel/libusbgx/src/.libs/libusbgx.so: undefined reference to `makedev'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:494: gadget-import] Error 1
libtool: link: gcc -g -O2 -o .libs/show-udcs show-udcs.o  -L../src/ /home/happycactus/devel/libusbgx/src/.libs/libusbgx.so
/usr/lib64/gcc/x86_64-suse-linux/8/../../../../x86_64-suse-linux/bin/ld: /home/happycactus/devel/libusbgx/src/.libs/libusbgx.so: undefined reference to `makedev'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:502: gadget-ms] Error 1
/usr/lib64/gcc/x86_64-suse-linux/8/../../../../x86_64-suse-linux/bin/ld: /home/happycactus/devel/libusbgx/src/.libs/libusbgx.so: undefined reference to `makedev'


gt enable <gadget> returns error string: Failed to enable gadget Invalid parameter

Just like in the subject the gt app returns error string Failed to enable gadget Invalid parameter. The error code USBG_ERROR_INVALID_PARAM is being returned from method usbg_enable_gadget in libusbgx/src/usbg.c. I have verified that it is being returned on no udc. I suspect that i am missing some kernel configuration for UDC, because even /sys/class/udc is empty. Correct me if i'm wrong, because i have no other clues.

License clarity?

What's the actual license of this library please? There's both GPL and LGPL copying files, but I can't find any explanation what is covered by each?

I'm guessing the intent is library under lgpl and examples and docs under gpl? but guessing is never a good idea on licensing :)

Printer header missing closing brace for extern "C" declaration

The header has an opening extern "C" declaration but is missing the closing brace.

From 9c3e09d254c475d1299bd3047dc672358627e3ce Mon Sep 17 00:00:00 2001
From: Niall Leonard <[email protected]>
Date: Fri, 28 Jul 2023 13:10:36 +0100
Subject: [PATCH] Added missing extern C closing brace

---
 include/usbg/function/printer.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/include/usbg/function/printer.h b/include/usbg/function/printer.h
index fecc17a..b556f39 100644
--- a/include/usbg/function/printer.h
+++ b/include/usbg/function/printer.h
@@ -48,4 +48,9 @@ union usbg_f_printer_attr_val {
 #define USBG_F_PRINTER_INT_TO_ATTR_VAL(WHAT) \
 	USBG_TO_UNION(usbg_f_printer_attr_val, qmult, WHAT)
 
+
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* USBG_FUNCTION_PRINTER__ */

What about inquiry_string?

Mass storage have one more option called "inquiry_string"

It made for setting custom scsi name for mass storage.

It shown as device name in my BIOS and in dmesg

There are example:
I runed

echo "MEOW" > inquiry_string

Before it:

[timesnap] scsi 6:0:0:0: Direct-Access     Linux    File-Stor Gadget 0607 PQ: 0 ANSI: 2
[timesnap] scsi 6:0:0:1: CD-ROM            Linux    File-Stor Gadget 0607 PQ: 0 ANSI: 2

After it:

[timesnap] scsi 6:0:0:0: Direct-Access     MEOW                           PQ: 0 ANSI: 2
[timesnap] scsi 6:0:0:1: CD-ROM            MEOW                           PQ: 0 ANSI: 2

stack-buffer-overflow (attr cast to union then pass by value): usbg_f_foo_set_attr_val(..., union usbg_f_foo_attr_val val)

Hi,
While working with HID gadget I discovered a bug which may be present in multiple places (attr set/get).
Easiest way is to run the code with address sanitizer:
CFLAGS="-O0 -g -fsanitize=address" ./configure; make; make install

HID bug:

==1476==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x007ffffff1d0 at pc 0x007ff74f61a4 bp 0x007fffffdca0 sp 0x007fffffdcb8
READ of size 16 at 0x007ffffff1d0 thread T0
...
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x0000007ff7376aa0 in __GI_abort () at abort.c:79
#2  0x0000007ff75e32a4 in __sanitizer::Abort () at ../../../../src/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cpp:155
#3  0x0000007ff75edce0 in __sanitizer::Die () at ../../../../src/libsanitizer/sanitizer_common/sanitizer_termination.cpp:58
#4  0x0000007ff75d0de8 in __asan::ScopedInErrorReport::~ScopedInErrorReport (this=0x7fffffd098, __in_chrg=<optimized out>) at ../../../../src/libsanitizer/asan/asan_report.cpp:186
#5  0x0000007ff75d06f0 in __asan::ReportGenericError (pc=549610021284, bp=bp@entry=549755804832, sp=sp@entry=549755804856, addr=549755810256, is_write=is_write@entry=false, 
    access_size=access_size@entry=16, exp=exp@entry=0, fatal=fatal@entry=true) at ../../../../src/libsanitizer/asan/asan_report.cpp:470
#6  0x0000007ff75d1460 in __asan::__asan_report_load16 (addr=<optimized out>) at ../../../../src/libsanitizer/asan/asan_rtl.cpp:121
#7  0x0000007ff74f61a4 in usbg_f_hid_set_attrs (hf=0x7ff3a01910, attrs=0x7ffffff1b0) at function/hid.c:329
#8  0x0000007ff74f596c in hid_set_attrs (f=0x7ff3a01910, f_attrs=0x7ffffff1b0) at function/hid.c:207
#9  0x0000007ff74eb0d4 in usbg_set_function_attrs (f=0x7ff3a01910, f_attrs=0x7ffffff1b0) at usbg.c:2621
#10 0x0000007ff74e78b8 in usbg_create_function (g=0x7ff2801ea0, type=USBG_F_HID, instance=0x5555553f60 "usb0", f_attrs=0x7ffffff1b0, f=0x7fffffef80) at usbg.c:2097
#11 0x00000055555531b4 in main () at gadget_multi.c:224

(gdb) f 7
#7  0x0000007ff74f61a4 in usbg_f_hid_set_attrs (hf=0x7ff3a01910, attrs=0x7ffffff1b0) at function/hid.c:329
329			ret = usbg_f_hid_set_attr_val(hf, i,
(gdb) list
324	
325		for (i = USBG_F_HID_ATTR_MIN; i < USBG_F_HID_ATTR_MAX; ++i) {
326			if (hid_attr[i].ro)
327				continue;
328	
329			ret = usbg_f_hid_set_attr_val(hf, i,
330						       *(union usbg_f_hid_attr_val *)
331						       ((char *)attrs
332							+ hid_attr[i].offset));
333			if (ret)
(gdb) p *attrs
$2 = {dev = 0, protocol = 1, report_desc = {desc = 0x5555565100 <report_desc> "\006", len = 28}, report_length = 8, subclass = 0}
(gdb) p sizeof(union usbg_f_hid_attr_val)
$3 = 16
(gdb) p i
$4 = 3
(gdb) p hid_attr[i].offset
$5 = 32
(gdb) f 11
#11 0x00000055555531b4 in main () at gadget_multi.c:224
224		usbg_ret = usbg_create_function(gadget, USBG_F_HID, "usb0", &hid_attrs, &f_hid);
(gdb) p sizeof(hid_attrs)
$6 = 40

As can be seen from GDB, union (union usbg_f_hid_attr_val) size is 16 byte (aligned to max member - struct in this case).
When user provided attribute address is cast to union: (union usbg_f_hid_attr_val *) ((char *)attrs + hid_attr[i].offset)
and then used to initialize new union object (call to usbg_f_hid_set_attr_val), to be passed by value to setter, we have problem as access is 16byte but only 8 byte belong to user provided attribute struct.

Improve net function to use Windows 10 native RNDIS driver

I am developing a multifunction gadget device with 2 different interfaces: HID and RNDIS.

The HID interface is working properly, but I coudn't make the RNDIS interface work with Windows 10 native RNDIS driver without manually writting some specific class/subclass/protocol values to the function attributes using the 'echo' command in the sysfs's exposed files. I coudn't manage to find another way to make it work when using a multifunction gadget.

By default, class, subclass and protocol attributes (inside rndis function folder) have the following values: 02 (CDC), 06 (NCM) and 00 (No specific protocol), and as far as I know it isn't possible to change these values using libusbgx.

I wrote a patch (attached) which allows the user to write any value to the above mentioned attributes, and specifically for RNDIS usage the values are: EF (Miscellaneous), 04 (RNDIS) and 01 (RNDIS over Ethernet).

I'd like to help another developers which may face a similar issue with the W10 driver. Do you think it worths to merge this feature to libusbgx?

net_attr.txt

WinUSB bulk in/out device

I created an small embedded WinUSB device which offers 2 bulk endpoints. This device can communicate with Linux and with Windows10 without installing driver, or a .inf file. And it's not recognized as serial com port.

Now I try to build an embedded Linux gadget with your libusbgx. It must respond to OS_DESC (0xee) as WinUSB device. And must not be recognized as serial com port. It shall behave like a g_serial with param "use_acm=no"

If I build it with filesystem commands. Then the os_desc setting in Window10 it's recognized in registry settings

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\usbflags<VIDPID>
osvc = 01 bc
SkipContainerIdQuery = 01 00

But not recognized as WinUSB device

It looks like, the os_desc feature does only fully work for RNDIS.

This is the respond to vendor request, which works when I send it from my embedded WinUSB to Windows:

const U8 u8ExtendedCompatIDOSFeatDesc[] =
{
0x28, 0x00, 0x00, 0x00, /* dwLength Length of this descriptor / //(40 bytes)
0x00, 0x01, /
bcdVersion = Version 1.0 /
0x04, 0x00, /
wIndex = 0x0004 /
0x01, /
bCount = 1 /
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /
Reserved /
0x00, /
Interface number = 0 /
0x01, /
Reserved /
0x57, 0x49, 0x4E, 0x55, 0x53, 0x42, 0x00, 0x00, //string = "WINUSB"
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /
subCompatibleID /
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /
Reserved */
};

I tried with a modified example of your code:
myWinusbGadget.zip
diff:
winusb-diff.zip

I get error when trying to usbg_set_interf_os_desc() :

Error setting function OS desc
Error: USBG_ERROR_NOT_FOUND : Not found (file or directory removed)

How can I make it working for gser ? Is it possible with changing your src? Or do I have to change the kernel gadget files?
Can you help me to add this feature to your lib?

FR: Available function list

I think it would be great if library could provide full list of functions, that can be used in gadget.
Maybe something like:
int *usbg_get_available_function_types()
Which will return null-terminated array of functions or error(f.e if modules directory doesn't contain modules.{order/builtin})

We can search in modules.order and modules.builtin for kernel/drivers/usb/gadget/function/usb_f_{function_name}.ko and then recognize it with usbg_lookup_function_type(function_name)
Are there better ways to do this?
If no, i can make a PR with it

Can't include usbg/function/ms.h from c++ source

Environment

Ubuntu Linux 16.04, up to date.

$ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.6) 5.4.0 20160609

Simply #including usbg/function/ms.h in a c++ file breaks the compilation, due to different handling of casting to unions in inlined functions.

#include <usbg/function/ms.h>
int main()
{
	return 0;
}

Expected result

Program compiles correctly like with gcc:

$ gcc -I ../include/ -o test test.c
$

Result

Program doesn't compile with g++:

In file included from test.cpp:1:0:
../include/usbg/function/ms.h: In function ‘int usbg_f_ms_set_lun_cdrom(usbg_f_ms*, int, bool)’:
../include/usbg/function/ms.h:179:38: error: no matching function for call to ‘usbg_f_ms_lun_attr_val::usbg_f_ms_lun_attr_val(bool&)’
        (union usbg_f_ms_lun_attr_val)cdrom);
                                      ^
../include/usbg/function/ms.h:51:7: note: candidate: usbg_f_ms_lun_attr_val::usbg_f_ms_lun_attr_val()
 union usbg_f_ms_lun_attr_val {
       ^
../include/usbg/function/ms.h:51:7: note:   candidate expects 0 arguments, 1 provided
../include/usbg/function/ms.h:51:7: note: candidate: usbg_f_ms_lun_attr_val::usbg_f_ms_lun_attr_val(const usbg_f_ms_lun_attr_val&)
../include/usbg/function/ms.h:51:7: note:   no known conversion for argument 1 from ‘bool’ to ‘const usbg_f_ms_lun_attr_val&’
../include/usbg/function/ms.h: In function ‘int usbg_f_ms_set_lun_ro(usbg_f_ms*, int, bool)’:
../include/usbg/function/ms.h:205:38: error: no matching function for call to ‘usbg_f_ms_lun_attr_val::usbg_f_ms_lun_attr_val(bool&)’
        (union usbg_f_ms_lun_attr_val)ro);
                                      ^
../include/usbg/function/ms.h:51:7: note: candidate: usbg_f_ms_lun_attr_val::usbg_f_ms_lun_attr_val()
 union usbg_f_ms_lun_attr_val {
       ^
../include/usbg/function/ms.h:51:7: note:   candidate expects 0 arguments, 1 provided
../include/usbg/function/ms.h:51:7: note: candidate: usbg_f_ms_lun_attr_val::usbg_f_ms_lun_attr_val(const usbg_f_ms_lun_attr_val&)
../include/usbg/function/ms.h:51:7: note:   no known conversion for argument 1 from ‘bool’ to ‘const usbg_f_ms_lun_attr_val&’
../include/usbg/function/ms.h: In function ‘int usbg_f_ms_set_lun_nofua(usbg_f_ms*, int, bool)’:
../include/usbg/function/ms.h:235:38: error: no matching function for call to ‘usbg_f_ms_lun_attr_val::usbg_f_ms_lun_attr_val(bool&)’
        (union usbg_f_ms_lun_attr_val)nofua);
                                      ^
../include/usbg/function/ms.h:51:7: note: candidate: usbg_f_ms_lun_attr_val::usbg_f_ms_lun_attr_val()
 union usbg_f_ms_lun_attr_val {
       ^
../include/usbg/function/ms.h:51:7: note:   candidate expects 0 arguments, 1 provided
../include/usbg/function/ms.h:51:7: note: candidate: usbg_f_ms_lun_attr_val::usbg_f_ms_lun_attr_val(const usbg_f_ms_lun_attr_val&)
../include/usbg/function/ms.h:51:7: note:   no known conversion for argument 1 from ‘bool’ to ‘const usbg_f_ms_lun_attr_val&’
../include/usbg/function/ms.h: In function ‘int usbg_f_ms_set_lun_removable(usbg_f_ms*, int, bool)’:
../include/usbg/function/ms.h:263:38: error: no matching function for call to ‘usbg_f_ms_lun_attr_val::usbg_f_ms_lun_attr_val(bool&)’
        (union usbg_f_ms_lun_attr_val)removable);
                                      ^
../include/usbg/function/ms.h:51:7: note: candidate: usbg_f_ms_lun_attr_val::usbg_f_ms_lun_attr_val()
 union usbg_f_ms_lun_attr_val {
       ^
../include/usbg/function/ms.h:51:7: note:   candidate expects 0 arguments, 1 provided
../include/usbg/function/ms.h:51:7: note: candidate: usbg_f_ms_lun_attr_val::usbg_f_ms_lun_attr_val(const usbg_f_ms_lun_attr_val&)
../include/usbg/function/ms.h:51:7: note:   no known conversion for argument 1 from ‘bool’ to ‘const usbg_f_ms_lun_attr_val&’
../include/usbg/function/ms.h: In function ‘int usbg_f_ms_set_lun_file(usbg_f_ms*, int, const char*)’:
../include/usbg/function/ms.h:309:38: error: no matching function for call to ‘usbg_f_ms_lun_attr_val::usbg_f_ms_lun_attr_val(const char*&)’
        (union usbg_f_ms_lun_attr_val)file);
                                      ^
../include/usbg/function/ms.h:51:7: note: candidate: usbg_f_ms_lun_attr_val::usbg_f_ms_lun_attr_val()
 union usbg_f_ms_lun_attr_val {
       ^
../include/usbg/function/ms.h:51:7: note:   candidate expects 0 arguments, 1 provided
../include/usbg/function/ms.h:51:7: note: candidate: usbg_f_ms_lun_attr_val::usbg_f_ms_lun_attr_val(const usbg_f_ms_lun_attr_val&)
../include/usbg/function/ms.h:51:7: note:   no known conversion for argument 1 from ‘const char*’ to ‘const usbg_f_ms_lun_attr_val&’
$

Version affected

commit 9e71df5

Workaround

Add a proper #if instruction to remove the offending functions when compiling in c++

Failing to build documentation

In a fresh clone, I've run:

autoreconf -i
./configure
make
make doxygen-doc

The error log is at the end of this post since it's quite long. I'm not too familiar with autotools or Doxygen, so let me know if there's any extra info that would be useful for diagnosing this.

Tools/packages in use:

$ autoreconf --version
autoreconf (GNU Autoconf) 2.69
...

$ make --version
GNU Make 4.2.1
...

$ doxygen --version
1.8.13

$ pdflatex --version
pdfTeX 3.14159265-2.6-1.40.18 (TeX Live 2017/Arch Linux)
kpathsea version 6.2.3

$ pacman -Qo `which pdflatex`
/usr/bin/pdflatex is owned by texlive-bin 2017.44590-9

$ pacman -Qi texlive-bin
Name            : texlive-bin
Version         : 2017.44590-9

Output log:

This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Arch Linux) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode
(./refman.tex
LaTeX2e <2017-04-15>
Babel <3.15> and hyphenation patterns for 84 language(s) loaded.
(/usr/share/texmf-dist/tex/latex/base/book.cls
Document Class: book 2014/09/29 v1.4h Standard LaTeX document class
(/usr/share/texmf-dist/tex/latex/base/bk10.clo))
(/usr/share/texmf-dist/tex/latex/base/fixltx2e.sty

Package fixltx2e Warning: fixltx2e is not required with releases after 2015
(fixltx2e)                All fixes are now in the LaTeX kernel.
(fixltx2e)                See the latexrelease package for details.

) (/usr/share/texmf-dist/tex/latex/tools/calc.sty) (./doxygen.sty
(/usr/share/texmf-dist/tex/latex/base/alltt.sty)
(/usr/share/texmf-dist/tex/latex/tools/array.sty)
(/usr/share/texmf-dist/tex/latex/float/float.sty)
(/usr/share/texmf-dist/tex/latex/base/ifthen.sty)
(/usr/share/texmf-dist/tex/latex/tools/verbatim.sty)
(/usr/share/texmf-dist/tex/latex/xcolor/xcolor.sty
(/usr/share/texmf-dist/tex/latex/graphics-cfg/color.cfg)
(/usr/share/texmf-dist/tex/latex/graphics-def/pdftex.def)
(/usr/share/texmf-dist/tex/latex/colortbl/colortbl.sty))
(/usr/share/texmf-dist/tex/latex/tools/longtable.sty)
(/usr/share/texmf-dist/tex/latex/tabu/tabu.sty
(/usr/share/texmf-dist/tex/latex/varwidth/varwidth.sty))
(/usr/share/texmf-dist/tex/latex/tools/tabularx.sty)
(/usr/share/texmf-dist/tex/latex/multirow/multirow.sty))
(/usr/share/texmf-dist/tex/latex/adjustbox/adjustbox.sty
(/usr/share/texmf-dist/tex/latex/xkeyval/xkeyval.sty
(/usr/share/texmf-dist/tex/generic/xkeyval/xkeyval.tex
(/usr/share/texmf-dist/tex/generic/xkeyval/xkvutils.tex
(/usr/share/texmf-dist/tex/generic/xkeyval/keyval.tex))))
(/usr/share/texmf-dist/tex/latex/adjustbox/adjcalc.sty)
(/usr/share/texmf-dist/tex/latex/adjustbox/trimclip.sty
(/usr/share/texmf-dist/tex/latex/graphics/graphicx.sty
(/usr/share/texmf-dist/tex/latex/graphics/graphics.sty
(/usr/share/texmf-dist/tex/latex/graphics/trig.sty)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/graphics.cfg)))
(/usr/share/texmf-dist/tex/latex/collectbox/collectbox.sty)
(/usr/share/texmf-dist/tex/latex/adjustbox/tc-pdftex.def))
(/usr/share/texmf-dist/tex/latex/ifoddpage/ifoddpage.sty))
(/usr/share/texmf-dist/tex/latex/base/inputenc.sty
(/usr/share/texmf-dist/tex/latex/base/utf8.def
(/usr/share/texmf-dist/tex/latex/base/t1enc.dfu)
(/usr/share/texmf-dist/tex/latex/base/ot1enc.dfu)
(/usr/share/texmf-dist/tex/latex/base/omsenc.dfu)))
(/usr/share/texmf-dist/tex/latex/base/makeidx.sty)
(/usr/share/texmf-dist/tex/latex/tools/multicol.sty)
(/usr/share/texmf-dist/tex/latex/base/textcomp.sty
(/usr/share/texmf-dist/tex/latex/base/ts1enc.def
(/usr/share/texmf-dist/tex/latex/base/ts1enc.dfu)))
(/usr/share/texmf-dist/tex/latex/wasysym/wasysym.sty)
(/usr/share/texmf-dist/tex/latex/base/fontenc.sty
(/usr/share/texmf-dist/tex/latex/base/t1enc.def))
(/usr/share/texmf-dist/tex/latex/psnfss/helvet.sty)
(/usr/share/texmf-dist/tex/latex/psnfss/courier.sty)
(/usr/share/texmf-dist/tex/latex/amsfonts/amssymb.sty
(/usr/share/texmf-dist/tex/latex/amsfonts/amsfonts.sty))
(/usr/share/texmf-dist/tex/latex/sectsty/sectsty.sty)
(/usr/share/texmf-dist/tex/latex/geometry/geometry.sty
(/usr/share/texmf-dist/tex/generic/oberdiek/ifpdf.sty)
(/usr/share/texmf-dist/tex/generic/oberdiek/ifvtex.sty)
(/usr/share/texmf-dist/tex/generic/ifxetex/ifxetex.sty))
(/usr/share/texmf-dist/tex/latex/fancyhdr/fancyhdr.sty)
(/usr/share/texmf-dist/tex/latex/natbib/natbib.sty)
(/usr/share/texmf-dist/tex/latex/tocloft/tocloft.sty)
Writing index file refman.idx
(/usr/share/texmf-dist/tex/latex/hyperref/hyperref.sty
(/usr/share/texmf-dist/tex/generic/oberdiek/hobsub-hyperref.sty
(/usr/share/texmf-dist/tex/generic/oberdiek/hobsub-generic.sty))
(/usr/share/texmf-dist/tex/latex/oberdiek/auxhook.sty)
(/usr/share/texmf-dist/tex/latex/oberdiek/kvoptions.sty)
(/usr/share/texmf-dist/tex/latex/hyperref/pd1enc.def)
(/usr/share/texmf-dist/tex/latex/latexconfig/hyperref.cfg)
(/usr/share/texmf-dist/tex/latex/hyperref/backref.sty
(/usr/share/texmf-dist/tex/latex/oberdiek/rerunfilecheck.sty))
(/usr/share/texmf-dist/tex/latex/url/url.sty))

Package hyperref Message: Driver: hpdftex.

(/usr/share/texmf-dist/tex/latex/hyperref/hpdftex.def)
(/usr/share/texmf-dist/tex/latex/hyperref/puenc.def)
(/usr/share/texmf-dist/tex/latex/caption/caption.sty
(/usr/share/texmf-dist/tex/latex/caption/caption3.sty)
(/usr/share/texmf-dist/tex/latex/caption/ltcaption.sty)) (./refman.aux

LaTeX Warning: Label `group__libusbgx_gadfb1a7e1855f9d8072f57d6d51ce0402' multi
ply defined.


LaTeX Warning: Label `group__libusbgx_ga49d7ddf93131e92ac5d8f0a24ad96f56' multi
ply defined.


LaTeX Warning: Label `group__libusbgx_ga61197026a63c0f9bc95f2c1d39308c59' multi
ply defined.


LaTeX Warning: Label `group__libusbgx_ga0cd65b6b5cfa50387cc9b0ea8bbcc832' multi
ply defined.


LaTeX Warning: Label `group__libusbgx_ga53ff55df4793b365cb6bf2723053d018' multi
ply defined.


! LaTeX Error: Missing \begin{document}.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...

l.884 e
       r}{Index}{107}{section*.46}}
?
! Emergency stop.
 ...

l.884 e
       r}{Index}{107}{section*.46}}
!  ==> Fatal error occurred, no output PDF file produced!
Transcript written on refman.log.
mv: cannot stat 'refman.pdf': No such file or directory
make: *** [Makefile:1018: doxygen-doc/libusbgx.pdf] Error 1
make: *** Waiting for unfinished jobs....

doesn't work with kernel 6.1.34+

gadget-uvc fails with:

symlink("/sys/kernel/config/usb_gadget/g1/functions/uvc.uvc", "/sys/kernel/config/usb_gadget/g1/configs/The only one.1/uvc.cam") = 0
openat(AT_FDCWD, "/sys/kernel/config/usb_gadget/g1/UDC", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
statx(3, "", AT_STATX_SYNC_AS_STAT|AT_NO_AUTOMOUNT|AT_EMPTY_PATH, STATX_BASIC_STATS, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0644, stx_size=0, ...}) = 0
write(3, "20980000.usb", 12)            = -1 EBUSY (Device or resource busy)
close(3)                                = 0
write(2, "Error enabling gadget\n", 22Error enabling gadget
) = 22
write(2, "Error: USBG_ERROR_BUSY : Busy (g"..., 47Error: USBG_ERROR_BUSY : Busy (gadget enabled)
) = 47
exit_group(-22)                         = ?

and in dmesg it says:

Sun Jun 25 20:54:25 2023] UDC core: g1: couldn't find an available UDC or it's busy

Invalid attribute is set in multiple places because of incorrect MACRO

Because macros are used in multiple places to initialize unnamed unions for example:

#define USBG_F_NET_ETHER_ADDR_TO_ATTR_VAL(WHAT)		\
	USBG_TO_UNION(usbg_f_net_attr_val, dev_addr, WHAT)

....

static inline int usbg_f_net_set_host_addr(usbg_f_net *nf,
					   const struct ether_addr *addr)
{
	return usbg_f_net_set_attr_val(nf, USBG_F_NET_HOST_ADDR,
				       USBG_F_NET_ETHER_ADDR_TO_ATTR_VAL(*addr));
}

this causes invalid attributes to be set, in this case dev_addr field is set to addr which, from function name, should set host_addr.
This issue is present in multiple places.

This commit in 8221d20
removes problematic macros.

Empty instance name does not allow re-initialization

Creating a mass storage function using an empty instance name e.g. by calling usbg_create_function(g_ms, USBG_F_MASS_STORAGE, "", &f_ms_attrs, &f_ms) seems to work fine and allow to use the gadget device.

However, trying then to reinitialize the configuration using usbg_ret = usbg_init("/sys/kernel/config", &s); fails with -3 USBG_ERROR_INVALID_PARAM.

One obvious work-around is to use a non-empty instance name.

usbg_disable_gadget() doesn't work

I get USBG_ERROR_BUSY when using usbg_disable_gadget().

The problem is an off-by-one error in the kernel:

diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 502a096fc380..fbc3a9444805 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -260,8 +260,8 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item,
        name = kstrdup(page, GFP_KERNEL);
        if (!name)
                return -ENOMEM;
-       if (name[len - 1] == '\n')
-               name[len - 1] = '\0';
+       if (name[len - 2] == '\n')
+               name[len - 2] = '\0';

        mutex_lock(&gi->lock);

This also works:

diff --git a/src/usbg.c b/src/usbg.c
index 3a2c674..fc74167 100644
--- a/src/usbg.c
+++ b/src/usbg.c
@@ -2232,7 +2232,7 @@ int usbg_disable_gadget(usbg_gadget *g)
        if (!g)
                return ret;

-       ret = usbg_write_string(g->path, g->name, "UDC", "\n");
+       ret = usbg_write_string(g->path, g->name, "UDC", "");
        if (ret != USBG_SUCCESS)
                goto out;

A compiling warning "-Waddress-of-packed-member"

I encountered a comping warning as follows:

In file included from git/usbgadgethelper.c:11:
/usr/include/usbgx/usbg/function/net.h: In function 'usbg_f_net_get_dev_addr':
/usr/include/usbgx/usbg/function/net.h:131:19: warning: converting a packed 'struct ether_addr' pointer (alignment 1) to a 'union usbg_f_net_attr_val' pointer (alignment 4) may result in an unaligned pointer value [-Waddress-of-packed-member]
  131 |            (union usbg_f_net_attr_val *)addr);
      |                   ^~~~~~~~~~~~~~~~~~~
In file included from /usr/include/netinet/if_ether.h:60,
                 from /usr/include/netinet/ether.h:25,
                 from /usr/include/usbgx/usbg/usbg.h:26,
                 from git/usbgadgethelper.c:9:
/usr/include/net/ethernet.h:33:8: note: defined here
   33 | struct ether_addr
      |        ^~~~~~~~~~
In file included from git/usbgadgethelper.c:11:
/usr/include/usbgx/usbg/function/net.h:43:7: note: defined here
   43 | union usbg_f_net_attr_val {
      |       ^~~~~~~~~~~~~~~~~~~
/usr/include/usbgx/usbg/function/net.h: In function 'usbg_f_net_get_host_addr':
/usr/include/usbgx/usbg/function/net.h:157:19: warning: converting a packed 'struct ether_addr' pointer (alignment 1) to a 'union usbg_f_net_attr_val' pointer (alignment 4) may result in an unaligned pointer value [-Waddress-of-packed-member]
  157 |            (union usbg_f_net_attr_val *)addr);
      |                   ^~~~~~~~~~~~~~~~~~~
In file included from /usr/include/netinet/if_ether.h:60,
                 from /usr/include/netinet/ether.h:25,
                 from /usr/include/usbgx/usbg/usbg.h:26,
                 from git/usbgadgethelper.c:9:
/usr/include/net/ethernet.h:33:8: note: defined here
   33 | struct ether_addr
      |        ^~~~~~~~~~
In file included from git/usbgadgethelper.c:11:
/usr/include/usbgx/usbg/function/net.h:43:7: note: defined here
   43 | union usbg_f_net_attr_val {
      |       ^~~~~~~~~~~~~~~~~~~

when comping with gcc9/kernel 5.4, any idea how to fix this, it looks like a potential issue.

Gadget Printer Support?

The current function enumeration within libusbgx doesn't support a printer function. Is this on purpose, or for some reason, or because it wouldn't work for some reason?

Isochronous endpoint support?

Is is possible to use this library and/or configfs for creation of an USB device with an isochronous endpoint? Or can I only create devices with functions that are pre-defined in the usbg_function_type enum?

usbg_write_buf() can fail without reporting it

I had a problem where usbg_enable_gadget() reported success, but it should have failed:

[869503.937381] configfs-gadget 20980000.usb: failed to start g1: -19

I haven't got time to make a PR, but the following worked for me, now returning USBG_ERROR_NO_DEV.

diff --git a/src/usbg_common.c b/src/usbg_common.c
index de9e40d..6321993 100644
--- a/src/usbg_common.c
+++ b/src/usbg_common.c
@@ -172,7 +172,9 @@ int usbg_write_buf(const char *path, const char *name,
                        ret = USBG_ERROR_IO;
        }

-       fclose(fp);
+       if (fclose(fp) == -1 && ret > 0) /* don't mask previous error */
+               ret = usbg_translate_error(errno);
+
 out:
        return ret;
 }
@@ -192,7 +194,7 @@ int usbg_write_int(const char *path, const char *name, const char *file,
        if (ret > 0)
                ret = 0;

-       return 0;
+       return ret;
 }

 int usbg_write_string(const char *path, const char *name,
@@ -204,7 +206,7 @@ int usbg_write_string(const char *path, const char *name,
        if (ret > 0)
                ret = 0;

-       return 0;
+       return ret;
 }

 int ubsg_rm_file(const char *path, const char *name)

usbg_set_function_attrs returns NO_ACCESS

When I followed the example gadget_uac2.c to create UAC2 gadget, usbg_create_function returns NO_ACCESS error. And I found that &f_uac2_attrs causes this error in usbg_set_function_attrs method.

Do you have any idea what causes it?

how to find the gadget device file?

Hi,

When I create an UVC device, a /dev/video1 appears. How do I find that device file programmatically? I know that udev can signal this, but how do I then know that the newly created device is the one I just initialized and not some other by an other application?

Max power is limited to uint8_t

In this struct:

struct usbg_config_attrs
{
	uint8_t bmAttributes;
	uint8_t bMaxPower;
};

the max power is limited to the uint8_t type, but the system expects the power to be set in mA, not the 2mA units that the USB spec describes, so amounts like 500 get capped to 244. Would it be appropriate to change the type in this struct, or bit shift the value provided here before passing it along to gadget? Composite specifies MaxPower as u16.

full path vs basename oddness with "gt load"

if I attempt to use "gt load /mnt/some/full/path/my.schema.file" I get an error, but if I cd to that path and just "gt load my.schema.file" it works happily.

example log output:

root@eg-13E128:~# gt load /mnt/kroot/owrt_private_feeds/remake_master/files/etc/remake.d/eth2-acm.gt 
Error on import gadget     <<<<< note this
Error: USBG_ERROR_NOT_FOUND : Not found (file or directory removed)
root@eg-13E128:~# ls -l /^C
root@eg-13E128:~# cd /mnt/kroot/owrt_private_feeds/remake_master/files/etc/remake.d/
root@eg-13E128:/mnt/kroot/owrt_private_feeds/remake_master/files/etc/remake.d# gt load eth2-acm.gt 
[ 9562.708101] using random self ethernet address
[ 9562.712594] using random host ethernet address
[ 9562.718874] usb0: HOST MAC 7e:ed:2b:ac:61:64
[ 9562.722543] etb: port 1(usb0) entered blocking state
[ 9562.723248] usb0: MAC 0a:d4:88:aa:3c:a1
[ 9562.728256] etb: port 1(usb0) entered disabled state
root@eg-13E128:/mnt/kroot/owrt_private_feeds/remake_master/files[ 9562.737566] device usb0 entered promiscuous mode
/etc/remake.d#

I've tried looking at the code for this, and the "Error on import gadget" is after it's loaded the file, so I'm not sure what's happening here?

Header usbg/function/hid.h not installed

The header file usbg/function/hid.h is missing from installation.

When HID functionality was added the source file was added to src/Makefile.am but the header file was forgotten in Makefile.am

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.