| ==================== |
| How FunctionFS works |
| ==================== |
| |
| Overview |
| ======== |
| |
| From kernel point of view it is just a composite function with some |
| unique behaviour. It may be added to an USB configuration only after |
| the user space driver has registered by writing descriptors and |
| strings (the user space program has to provide the same information |
| that kernel level composite functions provide when they are added to |
| the configuration). |
| |
| This in particular means that the composite initialisation functions |
| may not be in init section (ie. may not use the __init tag). |
| |
| From user space point of view it is a file system which when |
| mounted provides an "ep0" file. User space driver need to |
| write descriptors and strings to that file. It does not need |
| to worry about endpoints, interfaces or strings numbers but |
| simply provide descriptors such as if the function was the |
| only one (endpoints and strings numbers starting from one and |
| interface numbers starting from zero). The FunctionFS changes |
| them as needed also handling situation when numbers differ in |
| different configurations. |
| |
| When descriptors and strings are written "ep#" files appear |
| (one for each declared endpoint) which handle communication on |
| a single endpoint. Again, FunctionFS takes care of the real |
| numbers and changing of the configuration (which means that |
| "ep1" file may be really mapped to (say) endpoint 3 (and when |
| configuration changes to (say) endpoint 2)). "ep0" is used |
| for receiving events and handling setup requests. |
| |
| When all files are closed the function disables itself. |
| |
| What I also want to mention is that the FunctionFS is designed in such |
| a way that it is possible to mount it several times so in the end |
| a gadget could use several FunctionFS functions. The idea is that |
| each FunctionFS instance is identified by the device name used |
| when mounting. |
| |
| One can imagine a gadget that has an Ethernet, MTP and HID interfaces |
| where the last two are implemented via FunctionFS. On user space |
| level it would look like this:: |
| |
| $ insmod g_ffs.ko idVendor=<ID> iSerialNumber=<string> functions=mtp,hid |
| $ mkdir /dev/ffs-mtp && mount -t functionfs mtp /dev/ffs-mtp |
| $ ( cd /dev/ffs-mtp && mtp-daemon ) & |
| $ mkdir /dev/ffs-hid && mount -t functionfs hid /dev/ffs-hid |
| $ ( cd /dev/ffs-hid && hid-daemon ) & |
| |
| On kernel level the gadget checks ffs_data->dev_name to identify |
| whether its FunctionFS is designed for MTP ("mtp") or HID ("hid"). |
| |
| If no "functions" module parameters is supplied, the driver accepts |
| just one function with any name. |
| |
| When "functions" module parameter is supplied, only functions |
| with listed names are accepted. In particular, if the "functions" |
| parameter's value is just a one-element list, then the behaviour |
| is similar to when there is no "functions" at all; however, |
| only a function with the specified name is accepted. |
| |
| The gadget is registered only after all the declared function |
| filesystems have been mounted and USB descriptors of all functions |
| have been written to their ep0's. |
| |
| Conversely, the gadget is unregistered after the first USB function |
| closes its endpoints. |
| |
| DMABUF interface |
| ================ |
| |
| FunctionFS additionally supports a DMABUF based interface, where the |
| userspace can attach DMABUF objects (externally created) to an endpoint, |
| and subsequently use them for data transfers. |
| |
| A userspace application can then use this interface to share DMABUF |
| objects between several interfaces, allowing it to transfer data in a |
| zero-copy fashion, for instance between IIO and the USB stack. |
| |
| As part of this interface, three new IOCTLs have been added. These three |
| IOCTLs have to be performed on a data endpoint (ie. not ep0). They are: |
| |
| ``FUNCTIONFS_DMABUF_ATTACH(int)`` |
| Attach the DMABUF object, identified by its file descriptor, to the |
| data endpoint. Returns zero on success, and a negative errno value |
| on error. |
| |
| ``FUNCTIONFS_DMABUF_DETACH(int)`` |
| Detach the given DMABUF object, identified by its file descriptor, |
| from the data endpoint. Returns zero on success, and a negative |
| errno value on error. Note that closing the endpoint's file |
| descriptor will automatically detach all attached DMABUFs. |
| |
| ``FUNCTIONFS_DMABUF_TRANSFER(struct usb_ffs_dmabuf_transfer_req *)`` |
| Enqueue the previously attached DMABUF to the transfer queue. |
| The argument is a structure that packs the DMABUF's file descriptor, |
| the size in bytes to transfer (which should generally correspond to |
| the size of the DMABUF), and a 'flags' field which is unused |
| for now. Returns zero on success, and a negative errno value on |
| error. |