/* * Copyright (C) 2014 Samsung Electronics * * Krzysztof Opasiak * * 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 2 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. */ /** * @file gadget-ffs.c * @example gadget-ffs.c * This is an example of how to create gadget with FunctionFS based functions * in two ways. After executing this program gadget will not be enabled * because ffs instances should be mounted and both descriptors and strings * should be written to ep0. * For more details about FunctionFS please refer to FunctionFS documentation * in linux kernel repository. */ #include #include #include #define VENDOR 0x1d6b #define PRODUCT 0x0104 int main(void) { usbg_state *s; usbg_gadget *g; usbg_config *c; usbg_function *f_ffs1, *f_ffs2; int ret = -EINVAL; int usbg_ret; usbg_function_attrs f_attrs = { .ffs = { .dev_name = "my_awesome_dev_name", }, }; usbg_gadget_attrs g_attrs = { 0x0200, /* bcdUSB */ 0x00, /* Defined at interface level */ 0x00, /* subclass */ 0x00, /* device protocol */ 0x0040, /* Max allowed packet size */ VENDOR, PRODUCT, 0x0001, /* Verson of device */ }; usbg_gadget_strs g_strs = { "0123456789", /* Serial number */ "Foo Inc.", /* Manufacturer */ "Bar Gadget" /* Product string */ }; usbg_config_strs c_strs = { "2xFFS" }; usbg_ret = usbg_init("/sys/kernel/config", &s); if (usbg_ret != USBG_SUCCESS) { fprintf(stderr, "Error on USB gadget init\n"); fprintf(stderr, "Error: %s : %s\n", usbg_error_name(usbg_ret), usbg_strerror(usbg_ret)); goto out1; } usbg_ret = usbg_create_gadget(s, "g1", &g_attrs, &g_strs, &g); if (usbg_ret != USBG_SUCCESS) { fprintf(stderr, "Error on create gadget\n"); fprintf(stderr, "Error: %s : %s\n", usbg_error_name(usbg_ret), usbg_strerror(usbg_ret)); goto out2; } usbg_ret = usbg_create_function(g, F_FFS, "my_dev_name", NULL, &f_ffs1); if (usbg_ret != USBG_SUCCESS) { fprintf(stderr, "Error creating ffs1 function\n"); fprintf(stderr, "Error: %s : %s\n", usbg_error_name(usbg_ret), usbg_strerror(usbg_ret)); goto out2; } /* When NULL is passed as instance name, dev_name take from f_attrs is used as instance name for this function */ usbg_ret = usbg_create_function(g, F_FFS, NULL, &f_attrs, &f_ffs2); if (usbg_ret != USBG_SUCCESS) { fprintf(stderr, "Error creating ffs2 function\n"); fprintf(stderr, "Error: %s : %s\n", usbg_error_name(usbg_ret), usbg_strerror(usbg_ret)); goto out2; } /* NULL can be passed to use kernel defaults */ usbg_ret = usbg_create_config(g, 1, "The only one", NULL, &c_strs, &c); if (usbg_ret != USBG_SUCCESS) { fprintf(stderr, "Error creating config\n"); fprintf(stderr, "Error: %s : %s\n", usbg_error_name(usbg_ret), usbg_strerror(usbg_ret)); goto out2; } usbg_ret = usbg_add_config_function(c, "some_name_here", f_ffs1); if (usbg_ret != USBG_SUCCESS) { fprintf(stderr, "Error adding ffs1\n"); fprintf(stderr, "Error: %s : %s\n", usbg_error_name(usbg_ret), usbg_strerror(usbg_ret)); goto out2; } usbg_ret = usbg_add_config_function(c, "some_name_here_too", f_ffs2); if (usbg_ret != USBG_SUCCESS) { fprintf(stderr, "Error adding ffs2\n"); fprintf(stderr, "Error: %s : %s\n", usbg_error_name(usbg_ret), usbg_strerror(usbg_ret)); goto out2; } fprintf(stdout, "2xFFS gadget has been created.\n" "Enable it after preparing your functions.\n"); /* * Here we end up with two created ffs instances but they are not * fully operational. Now we have to do step by step: * 1) Mount both instances: * $ mount my_dev_name -t functionfs /path/to/mount/dir1 * $ mount my_awesome_dev_name -t functionfs /path/to/mount/dir2 * * 2) Run ffs daemons for both instances: * $ my-ffs-daemon /path/to/mount/dir1 * $ my-ffs-daemon /path/to/mount/dir2 * * 3) Enable your gadget: * $ echo "my_udc_name" > /sys/kernel/config/usb_gadget/g1/UDC */ ret = 0; out2: usbg_cleanup(s); out1: return ret; }