├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── doc ├── Makefile ├── asynchapi.adoc ├── colony.css ├── context.adoc ├── datahandling.adoc ├── device.adoc ├── emulator.adoc ├── enums.adoc ├── hostmem.adoc ├── hotplug.adoc ├── index.adoc ├── index.html ├── introduction.adoc ├── links.adoc ├── miscellanea.adoc ├── polling.adoc ├── powered-by-lua.gif ├── preface.adoc ├── structs.adoc └── synchapi.adoc ├── examples ├── describe.lua ├── gamepad-emulator.lua ├── gamepads.lua ├── hello.lua ├── hotplug.lua ├── malloc.lua ├── open_short.lua ├── setup.lua └── version.lua ├── moonusb ├── bosdescriptors.lua ├── emulator.lua └── utils.lua ├── src ├── Makefile ├── _make ├── compat-5.3.c ├── compat-5.3.h ├── context.c ├── datahandling.c ├── datastructs.c ├── devhandle.c ├── device.c ├── enums.c ├── enums.h ├── hostmem.c ├── hotplug.c ├── interface.c ├── internal.h ├── main.c ├── moonusb.h ├── objects.c ├── objects.h ├── polling.c ├── synch.c ├── tracing.c ├── transfer.c ├── tree.h ├── udata.c ├── udata.h └── utils.c └── thirdparty ├── asciidoctor-styles-license ├── lua-compat-5.3-license ├── openbsd-tree-license └── powered-by-lua-license /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.dylib 3 | *.log 4 | *.so 5 | *.dll 6 | *.o 7 | *.swp 8 | *.symbols 9 | core.* 10 | local/ 11 | src/TODO 12 | src/DIFF 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Stefano Trettel 4 | 5 | Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | 26 | (See also the THIRD-PARTY LICENSES contained in the thirdparty/ directory 27 | of the Software repository.) 28 | 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | default: build 3 | 4 | build install uninstall where: 5 | @cd src; $(MAKE) $@ 6 | 7 | clean : 8 | @cd src; $(MAKE) $@ 9 | @cd doc; $(MAKE) $@ 10 | 11 | docs: 12 | @cd doc; $(MAKE) 13 | 14 | cleanall: clean 15 | 16 | backup: clean 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## MoonUSB: Lua bindings for libusb 2 | 3 | MoonUSB is a Lua binding library for [libusb](https://libusb.info/), 4 | allowing applications to access and use USB devices. 5 | 6 | MoonUSB also provides a submodule for emulating USB devices via [USB/IP](http://usbip.sourceforge.net/). 7 | 8 | It runs on GNU/Linux and requires 9 | [Lua](http://www.lua.org/) (>=5.3) and [libusb](https://github.com/libusb/libusb/releases) (>= 1.0.24). 10 | 11 | _Author:_ _[Stefano Trettel](https://www.linkedin.com/in/stetre)_ 12 | 13 | [![Lua logo](./doc/powered-by-lua.gif)](http://www.lua.org/) 14 | 15 | #### License 16 | 17 | MIT/X11 license (same as Lua). See [LICENSE](./LICENSE). 18 | 19 | #### Documentation 20 | 21 | See the [Reference Manual](https://stetre.github.io/moonusb/doc/index.html). 22 | 23 | #### Getting and installing 24 | 25 | Setup the build environment as described [here](https://github.com/stetre/moonlibs), then: 26 | 27 | ```sh 28 | $ git clone https://github.com/stetre/moonusb/ 29 | $ cd moonusb 30 | moonusb$ make 31 | moonusb$ sudo make install 32 | ``` 33 | 34 | #### Example 35 | 36 | The example below lists the devices currently attached to the system. 37 | 38 | Other examples can be found in the **examples/** directory. 39 | 40 | ```lua 41 | -- MoonUSB example: hello.lua 42 | local usb = require("moonusb") 43 | 44 | local ctx = usb.init() 45 | 46 | local devices = ctx:get_device_list() 47 | 48 | for i, dev in ipairs(devices) do 49 | local descr = dev:get_device_descriptor() 50 | local devhandle = dev:open() 51 | print(string.format("USB %s - bus:%d port:%d %.4x:%.4x %s %s (%s)", 52 | descr.usb_version, dev:get_bus_number(), dev:get_port_number(), 53 | descr.vendor_id, descr.product_id, 54 | devhandle:get_string_descriptor(descr.manufacturer_index) or "???", 55 | devhandle:get_string_descriptor(descr.product_index) or "???", 56 | descr.class)) 57 | devhandle:close() 58 | dev:free() 59 | end 60 | 61 | ``` 62 | 63 | The script can be executed at the shell prompt with the standard Lua interpreter: 64 | 65 | ```shell 66 | $ lua hello.lua 67 | ``` 68 | 69 | #### See also 70 | 71 | * [MoonLibs - Graphics and Audio Lua Libraries](https://github.com/stetre/moonlibs). 72 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | TgtAdoc := index 2 | 3 | Stylesdir = ./ 4 | Stylesheet = colony.css 5 | Style = -a stylesheet=$(Stylesheet) -a stylesdir=$(Stylesdir) 6 | Style = 7 | 8 | Src = $(TgtAdoc).adoc 9 | 10 | default: html 11 | 12 | check: 13 | 14 | clean: 15 | @-rm -f *~ 16 | @-rm -f *.html 17 | 18 | install: 19 | 20 | html: clean 21 | @asciidoctor $(Style) $(Src) 22 | 23 | docs: html 24 | -------------------------------------------------------------------------------- /doc/asynchapi.adoc: -------------------------------------------------------------------------------- 1 | 2 | [[asynchapi]] 3 | == Asynchronous I/O 4 | 5 | [small]#Rfr: link:++http://libusb.sourceforge.net/api-1.0/group__libusb__asyncio.html++[Asynchronous device I//O].# 6 | 7 | This API needs an event loop. See the <> section. 8 | 9 | [[transfer_callback]] 10 | *Transfer submit methods* 11 | 12 | Asynchronous transfers are submitted using the _devhandle_++:++*submit_xxx*( ) methods, that 13 | create and return a new _transfer_ object. 14 | Each of these methods requires a function _func_ as one of its arguments. 15 | This is the callback that will be executed, as *boolean=func(transfer, status)*, when the 16 | transfer either completes, fails, or is canceled. 17 | As the callback returns, if its return value is _nil_ or _false_, then the _transfer_ object is 18 | automatically deleted, otherwise it is automatically resubmitted. 19 | 20 | [small]#Rfr: _libusb_alloc_transfer( )_, _libusb_fill_xxx_transfer( )_, _libusb_submit_transfer( )_.# 21 | 22 | 23 | * _transfer_ = <>++:++*submit_control_transfer*(_ptr_, _size_, _timeout_, _func_) + 24 | [small]#Submit a USB control transfer. + 25 | _ptr_: lightuserdata containing a pointer to at least _size_ bytes of contiguous memory, + 26 | _timeout_: timeout in milliseconds (=0 for unlimited timeout). + 27 | _func_: the <> function. + 28 | The first 8 bytes pointed to by _ptr_ must contain an encoded control setup packet, and the _size_ 29 | of the memory area must be at least _wLength_pass:[+]8. + 30 | The setup packet must be followed by the _wLength_ bytes of data to be transferred to the device ('out' transfers), or of space for the data to be received from the device ('in' transfers). + 31 | For host to device transfers ('_out_'), the setup packet must be followed by the _wLength_ bytes of data to be transferred. + 32 | For device to host transfers ('_in_'), up the setup packet must be followed by at least _wLength_ bytes of space where to receive data (the received data thus will start at _ptr_pass:[+]8). + 33 | Rfr: _libusb_fill_control_transfer( )_.# 34 | 35 | * _transfer_ = <>++:++*submit_interrupt_transfer*(_endpoint_, _ptr_, _length_, _timeout_, _func_) + 36 | _transfer_ = <>++:++*submit_bulk_transfer*(_endpoint_, _ptr_, _length_, _timeout_, _func_) + 37 | _transfer_ = <>++:++*submit_bulk_stream_transfer*(_endpoint_, _stream_id_, _ptr_, _length_, _timeout_, _func_) + 38 | [small]#Submit a USB interrupt or bulk transfer. + 39 | _endpoint_: endpoint address, + 40 | _stream_id_: stream identifier, see _devhandle:<>( )_, + 41 | _ptr_: lightuserdata containing a pointer to at least _length_ bytes of contiguous memory, + 42 | _timeout_: timeout in milliseconds (=0 for unlimited timeout). + 43 | _func_: the <> function. + 44 | For host to device transfers ('_out_'), the memory pointed to by _ptr_ must contain the _length_ bytes of data to be transferred. + 45 | For device to host transfers ('_in_'), up to _length_ bytes of data will be received and store there, provided the transfer succeeds. + 46 | Rfr: _libusb_fill_interrupt_transfer( )_, _libusb_fill_bulk_transfer( )_, _libusb_fill_bulk_stream_transfer( )_.# 47 | 48 | * _transfer_ = <>++:++*submit_iso_transfer*(_endpoint_, _ptr_, _length_, _num_iso_packets_, _iso_packet_length_, _timeout_, _func_) + 49 | [small]#Submit a USB isochronous transfer. + 50 | _endpoint_: endpoint address, + 51 | _ptr_: lightuserdata containing a pointer to at least _length_ bytes of contiguous memory, + 52 | _num_iso_packets_: the number of isochronous packets to be transferred. + 53 | _iso_packet_length_: the length of each isochronous packet. + 54 | _timeout_: timeout in milliseconds (=0 for unlimited timeout). + 55 | _func_: the <> function. + 56 | For host to device transfers ('_out_'), the memory pointed to by _ptr_ must contain the _length_ bytes of data to be transferred, consisting of _num_iso_packets_ concatenated packets of _iso_packet_length_ bytes each. + 57 | For device to host transfers ('_in_'), up to _length_ bytes of data will be received and store there, provided the transfer succeeds. To locate the 58 | actually received packets within the memory, use the _transfer:<>( )_ method. + 59 | Rfr: _libusb_fill_iso_transfer( )_.# 60 | 61 | * _transfer_++:++*cancel*( ) + 62 | [small]#Cancels the transfer and deletes the _transfer_ object. + 63 | Note that _transfer_ objects are automatically deleted at the end of the execution of their callback, 64 | so calling this method is usually not needed. + 65 | Rfr: _libusb_cancel_transfer( )_.# 66 | 67 | * <> = _transfer_++:++*get_status*( ) + 68 | [small]#Returns the current status of the transfer.# 69 | 70 | * _endpoint_ = _transfer_++:++*get_endpoint*( ) + 71 | [small]#Returns the endpoint address for the endpoint involved in this transfer.# 72 | 73 | * _length_ = _transfer_++:++*get_actual_length*( ) + 74 | [small]#Returns the no. of bytes of data actually transferred. + 75 | This method is meant to be called only within a transfer callback.# 76 | 77 | * _id_ = _transfer_++:++*get_stream_id*( ) + 78 | [small]#Returns the stream id used in the transfer. + 79 | This method is meant to be called only within a bulk stream transfer callback.# 80 | 81 | [[get_iso_packet_descriptors]] 82 | * _descr_ = _transfer_++:++*get_iso_packet_descriptors*( ) + 83 | [small]#Returns a list of descriptors for the packets of a completed isochronous transfer. + 84 | This method is meant to be called only within an isochronous transfer callback. + 85 | Returns _nil_ if the transfer's status is not '_completed_', otherwise returns a table 86 | containing _num_endpoints_ tables, each describing a packet as follows: + 87 | pass:[-] _descr[i].status_: <>, status of the i-th packet, + 88 | pass:[-] _descr[i].offset_: integer, offset of the i-th packet in the buffer (pointed to by _ptr_), + 89 | pass:[-] _descr[i].length_: integer, actual length of the i-th packet.# 90 | 91 | -------------------------------------------------------------------------------- /doc/context.adoc: -------------------------------------------------------------------------------- 1 | 2 | [[context]] 3 | == Contexts 4 | 5 | [small]#Rfr: link:++http://libusb.sourceforge.net/api-1.0/group__libusb__lib.html++[Library initialization/deinitialization].# 6 | 7 | A context represents a libusb session. 8 | There is no default context in MoonUSB, so an application must begin by creating at least 9 | one _context_ object using the <>( ) function. 10 | It will typically create just one context, but multiple concurrent contexts are allowed too. 11 | 12 | When a context exits, all the corresponding objects are deleted too, with graceful release 13 | of underlying resources whenever possible. Contexts exit automatically at program termination. 14 | 15 | [[init]] 16 | * _context_ = *init*( ) + 17 | _context_++:++*exit*( ) + 18 | [small]#Initialize/deinitialize a libusb session, creating/deleting a context. + 19 | Rfr: _libusb_init( )_, _libusb_exit( )_.# 20 | 21 | [[set_option]] 22 | * *set_option*([_context_], <>, [_..._]) + 23 | _context_++:++*set_option*(<>, [_..._]) + 24 | [small]#Set an option in the library. The currently available options are: + 25 | pass:[-] set_option(_context_, '_log level_', <>) + 26 | pass:[-] set_option(_context_, '_use usbdk_') + 27 | pass:[-] set_option(_nil_, '_weak authority_') + 28 | Rfr: _libusb_set_option( )_.# 29 | 30 | [[set_log_cb]] 31 | * *set_log_cb*([_context_], _func_) + 32 | _context_++:++*set_log_cb*(_func_) + 33 | [small]#Set a log callback for context-related log messages (if _context_ is given) or for 34 | global log messages (if _context_ is not given). + 35 | The _func_ callback, a function, is executed as *func(context, <>, message)*, where 36 | _message_ is a string, and _context_ is _nil_ if the callback is global. + 37 | Rfr: _libusb_set_log_cb( )_.# 38 | 39 | 40 | -------------------------------------------------------------------------------- /doc/datahandling.adoc: -------------------------------------------------------------------------------- 1 | 2 | [[datahandling]] 3 | == Data handling 4 | 5 | This section describes additional utilities that can be used to encode data from Lua 6 | variables to binary strings and viceversa. 7 | 8 | [[datahandling_flatten]] 9 | * _val~1~_, _..._, _val~N~_ = *flatten*(_table_) + 10 | [small]#Flattens out the given _table_ and returns the terminal elements in the order they are found. + 11 | Similar to Lua's 12 | http://www.lua.org/manual/5.3/manual.html#pdf-table.unpack[table.unpack]( ), but it also unpacks 13 | any nested table. Only the array part of the table and of nested tables is considered.# 14 | 15 | [[datahandling_flatten_table]] 16 | * {_val~1~_, _..._, _val~N~_} = *flatten_table*(_table_) + 17 | [small]#Same as <>( ), but returns the values in a flat table. 18 | Unlike <>( ), this function can be used also with very large tables.# 19 | 20 | [[datahandling_sizeof]] 21 | * _size_ = *sizeof*(<>) + 22 | [small]#Returns the size in bytes of the given _type_.# 23 | 24 | [[datahandling_pack]] 25 | * _data_ = *pack*(<>, _val~1~_, _..._, _val~N~_) + 26 | _data_ = *pack*(<>, _table_) + 27 | [small]#Packs the numbers _val~1~_, _..._, _val~N~_, encoding them according to the given _type_, and returns the resulting binary string. + 28 | The values may also be passed in a (possibly nested) table. Only the array part of the table (and of nested tables) is considered.# 29 | 30 | [[datahandling_unpack]] 31 | * {_val~1~_, _..._, _val~N~_} = *unpack*(<>, _data_) + 32 | [small]#Unpacks the binary string _data_, interpreting it as a sequence of values of the given _type_, 33 | and returns the extracted values in a flat table. + 34 | The length of _data_ must be a multiple of <>(_type_).# 35 | 36 | [[encode_control_setup]] 37 | * *encode_control_setup*(_ptr_, <>) + 38 | _bstring_ = *encode_control_setup*(_nil_, <>) + 39 | <> = *decode_control_setup*(_ptr_) + 40 | <> = *decode_control_setup*(_bstring_) + 41 | [small]#Encode/decode a Setup packet for control transfers. + 42 | The packet is encoded to / decoded from the first 8 bytes pointed by _ptr_, or of the binary string _bstring_. + 43 | _ptr_: a lightuserdata containing a pointer to at least 8 bytes of memory, + 44 | _bstring_: a binary string with length greater or equal to 8.# 45 | 46 | -------------------------------------------------------------------------------- /doc/device.adoc: -------------------------------------------------------------------------------- 1 | 2 | [[device]] 3 | == Devices and device handles 4 | 5 | [small]#Rfr: link:++http://libusb.sourceforge.net/api-1.0/group__libusb__dev.html++[Device handling and enumeration].# 6 | 7 | Devices are represented with two types of objects: a *device* object that represents 8 | a device that has been detected, and a *devhandle* object that represents a device 9 | that has been opened by the application. 10 | The former is sufficient to inspect the USB descriptors of a device, but to perform I/O and 11 | control operations on it, the latter is needed. 12 | 13 | To obtain _device_ objects one can either use the _context_:<>( ) 14 | method, or the <>. 15 | Any device of interest can then be opened with _device_:<>( ), that creates the corresponding _devhandle_ object. 16 | Depending on the system, this operation may require access permissions. 17 | 18 | [[get_device_list]] 19 | * _{device}_ = <>++:++*get_device_list*( ) + 20 | _device_++:++*free*( ) + 21 | [small]#Returns a list of USB devices currently attached to the system. + 22 | An application should delete any _device_ objects it obtains but it is not interested in, using the 23 | _device:free()_ method. + 24 | Rfr: _libusb_get_device_list( )_, _libusb_unref_device( )_.# 25 | 26 | [[open]] 27 | * _devhandle_ = _device_++:++*open*( ) + 28 | _devhandle_++:++*close*( ) + 29 | [small]#Open/close a device, creating/deleting a _devhandle_ object. + 30 | A _devhandle_ object is automatically closed when its device is deleted. + 31 | Rfr: _libusb_open( )_, _libusb_close( )_.# 32 | 33 | [[open_device]] 34 | * _device_, _devhandle_ = <>++:++*open_device*(_vendor_id_, _product_id_) + 35 | [small]#Shortcut to locate and open a device with a particular _vendor_id_ + _product_id_ combination. + 36 | Creates and returns both the _device_ and the _devhandle_ objects, or raises an error if no such device is found. + 37 | Rfr: _libusb_open_device_with_vid_pid( )_.# 38 | 39 | [[lock_on_close]] 40 | * *lock_on_close*(_boolean_) + 41 | [small]#If _true_, lock events when closing a device (defaults to not locking). See issues #1 and #2.# 42 | 43 | === Device properties 44 | 45 | The methods described here can be used to query information about devices without the need 46 | to open them. The relevant information, if available, has already been retrieved during 47 | enumeration and cached by the lower levels (i.e. by libusb and the OS). 48 | 49 | [[get_port_number]] 50 | * _bus_number_ = _device_++:++*get_bus_number*( ) + 51 | _port_number_ = _device_++:++*get_port_number*( ) + 52 | _{port_number}_ = _device_++:++*get_port_numbers*( ) + 53 | [small]#Get the number of the bus the device is connected to, the number of the port, and 54 | the list of all port numbers from root for the device. + 55 | Rfr: _libusb_get_bus_number( )_, _libusb_get_port_number( )_, _libusb_get_port_numbers( )_.# 56 | 57 | [[get_parent]] 58 | * _parent_device_ = _device_++:++*get_parent*( ) + 59 | [small]#Get the parent device of the specified device. + 60 | Rfr: _libusb_get_parent( )_.# 61 | 62 | [[get_address]] 63 | * _address_ = _device_++:++*get_address*( ) + 64 | [small]#Get the address of the device on the bus it is connected to. + 65 | Rfr: _libusb_get_device_address( )_.# 66 | 67 | [[get_speed]] 68 | * <> = _device_++:++*get_speed*( ) + 69 | [small]#Get the negotiated connection speed for the device. + 70 | Rfr: _libusb_get_device_speed( )_.# 71 | 72 | [[get_max_packet_size]] 73 | * _size_ = _device_++:++*get_max_packet_size*(_endpoint_) + 74 | _size_ = _device_++:++*get_max_iso_packet_size*(_endpoint_) + 75 | [small]#Get/calculate the wMaxPacketSize for the given endpoint in the active device configuration. + 76 | _endpoint_: endpoint address. + 77 | Rfr: _libusb_get_max_packet_size( )_, _libusb_get_max_iso_packet_size( )_.# 78 | 79 | [[descriptors]] 80 | === USB descriptors 81 | 82 | [small]#Rfr: link:++http://libusb.sourceforge.net/api-1.0/group__libusb__desc.html++[USB descriptors].# 83 | 84 | [[get_device_descriptor]] 85 | * <> = _device_++:++*get_device_descriptor*( ) + 86 | <> = _device_++:++*get_config_descriptor*(_index_) + 87 | <> = _device_++:++*get_config_descriptor_by_value*(_value_) + 88 | <> = _device_++:++*get_active_config_descriptor*( ) + 89 | [small]#Get a descriptor for _device_. + 90 | These methods return immediately since the descriptors are cached during enumeration. + 91 | _index_: configuration index (0, .., _bNumConfigurations_-1), + 92 | _value_: configuration value (_bConfigurationValue_). + 93 | Rfr: _libusb_get_device_descriptor( )_, _libusb_get_config_descriptor( )_, _libusb_get_config_descriptor_by_value( )_, _libusb_get_active_config_descriptor( )_.# 94 | 95 | [[get_descriptor]] 96 | * _string_ = _devhandle_++:++*get_string_descriptor*(_index_) + 97 | _length_ = _devhandle_++:++*get_descriptor*(_descriptor_type_, _descriptor_index_, _ptr_, _length_) + 98 | <> = _devhandle_++:++*get_bos_descriptor*( ) + 99 | [small]#Retrieve a descriptor. + 100 | These methods are blocking since they involve transfers. + 101 | Rfr: _libusb_get_string_descriptor( )_, _libusb_get_descriptor( )_, _libusb_get_bos_descriptor( )_.# 102 | 103 | 104 | === Devhandle operations 105 | 106 | Beware that the methods described hereafter perform operations that may involve control transfers, 107 | causing the calls to block until the transfers either complete or fail. 108 | For more details, refer to the libusb manual. 109 | 110 | [[get_configuration]] 111 | * _value_ = _devhandle_++:++*get_configuration*( ) + 112 | _devhandle_++:++*set_configuration*(_value_) + 113 | [small]#Determine / set the bConfigurationValue for the currently active configuration. May block. + 114 | Rfr: _libusb_get_configuration( )_, _libusb_set_configuration( )_.# 115 | 116 | [[clear_halt]] 117 | * _devhandle_++:++*clear_halt*(_endpoint_) + 118 | [small]#Clear the halt/stall condition for the given endpoint. Blocking. + 119 | Rfr: _libusb_clear_halt( )_.# 120 | 121 | [[reset_device]] 122 | * _devhandle_++:++*reset_device*( ) + 123 | [small]#Perform a USB reset to reinitialize the device. Blocking. + 124 | Rfr: _libusb_clear_halt( )_.# 125 | 126 | [[alloc_streams]] 127 | * _n_ = _devhandle_++:++*alloc_streams*(_num_streams_, _{endpoint}_) + 128 | _devhandle_++:++*free_streams*(_{endpoint}_) + 129 | [small]#Allocate up to _num_streams_ USB bulk streams on the specified endpoints. + 130 | _{endpoints}_: list of endpoint addresses (must belong to the same interface). + 131 | Returns the number _n_ of actually allocated streams, with stream identifiers 1 to _n_. + 132 | The streams are automatically released with the interface. + 133 | Rfr: _libusb_alloc_streams( )_, _libusb_free_streams( )_.# 134 | 135 | === Claiming/releasing interfaces 136 | 137 | [[interface]] 138 | * _interface_ = <>++:++*claim_interface*(_interface_number_) + 139 | [small]#Claim an interface and create the corresponding _interface_ object. + 140 | This also detaches the interface's kernel driver (where applicable). + 141 | Rfr: _libusb_claim_interface( )_.# 142 | 143 | [[release_interface]] 144 | * _interface_++:++*release*( ) + 145 | [small]#Release the interface and delete the corresponding object. + 146 | This also reattaches the interface's kernel driver (where applicable). + 147 | The interface is automatically released when its _devhandle_ gets closed. + 148 | Rfr: _libusb_release_interface( )_.# 149 | 150 | [[set_interface_alt_setting]] 151 | * _interface_++:++*set_alt_setting*(_alternate_setting_) + 152 | [small]#Activate an alternate setting for the interface. + 153 | Rfr: _libusb_set_interface_alt_setting( )_.# 154 | 155 | -------------------------------------------------------------------------------- /doc/emulator.adoc: -------------------------------------------------------------------------------- 1 | 2 | [[emulator]] 3 | == Emulating USB devices 4 | 5 | This section describes the optional *moonusb.emulator* module, that can be loaded with: 6 | 7 | [source,lua,indent=1] 8 | ---- 9 | local emulator = require("moonusb.emulator") 10 | ---- 11 | 12 | The module uses Diego Nehab's 13 | http://w3.impa.br/~diego/software/luasocket/[LuaSocket] (required) and the 14 | https://github.com/stetre/moontimers[MoonTimers] module from the MoonLibs collection (optional). 15 | 16 | === Overview 17 | 18 | The purpose of libusb, and hence of MoonUSB, is to implement USB user applications. 19 | That is, applications that use USB devices. MoonUSB however provides an additonal - and 20 | experimental - submodule named *moonusb.emulator* to emulate USB devices. The emulation 21 | leverages the http://usbip.sourceforge.net/[USB/IP] framework, which should be 22 | available by default on most GNU/Linux systems. 23 | 24 | The *original goal of USB/IP* is to allow applications to access USB devices that are plugged 25 | into a remote host, as if they were plugged into the local host. This is achieved as follows: 26 | 27 | * on the remote host, a TCP server implements the 28 | https://www.kernel.org/doc/Documentation/usb/usbip_protocol.txt[USB/IP protocol] (server-side) to 29 | make USB devices plugged into that host available for export to clients that request them; 30 | 31 | * on the local host, a USB/IP client - usually the https://www.mankier.com/8/usbip[usbip] tool - 32 | can then connect to the remote server, list the available devices, possibly import one 33 | or more of them and send them USB requests over the connection. The server relays the client's 34 | requests to the remote devices and their responses back to the client over the TCP connection. 35 | If the _usbip_ tool is used, once it has imported a device it handles the connection to the 36 | https://sourceforge.net/p/usb-vhci/wiki/Home/[VHCI] driver, which in turns makes the device 37 | available to user applications in the same way that real USB devices plugged in locally are 38 | made available by their drivers. 39 | 40 | Even if it is not its original goal, USB/IP can be used also to *emulate a USB device*. This is 41 | what the _moonusb.emulator_ module does: it implements a USB/IP server that runs locally and 42 | exports a fake USB device, as opposed to running remotely to export real devices (the emulation 43 | server could also run remotely, but I'm not sure USB/IP is safe enough to advise it). 44 | 45 | Since there are gazillions of USB device types out there, in order to fake a particular device 46 | the emulator module has to be customized by a *user script*. 47 | The emulator module implements the core, device-independent parts of the server: it manages 48 | the TCP connection, the reception and delivery of data over it, and encodes/decodes USB/IP 49 | protocol messages. The user script, on the other hand, is responsible for configuring 50 | the module and for implementing the device-specific parts of the emulation. 51 | Loosely speaking, the emulator module implements the USB/IP protocol (server-side), 52 | while the *user script* implements the USB protocol (device-side) on top of 53 | the <> described in a later section. 54 | 55 | The code snippet below shows the skeleton of a user script. More complete 56 | (and working) examples can be found in the *examples/* directory. 57 | 58 | [source,lua,indent=1] 59 | ---- 60 | -- Script: myfakedevice.lua 61 | local emulator = require("moonusb.emulator") 62 | -- local timers = require("moontimers") -- optional 63 | 64 | local function attached() 65 | -- callback executed when the device has been attached 66 | end 67 | 68 | local function detached() 69 | -- callback executed when the device has been detached 70 | end 71 | 72 | local function receive_submit(submit) 73 | -- callback executed when a 'submit' command has been received 74 | -- ... process the command and prepare the response ... 75 | emulator.send_submit_response(submit, status, error_count, data) 76 | end 77 | 78 | local function receive_unlink(unlink) 79 | -- callback executed when a 'unlink' command has been received 80 | -- ... process the command and prepare the response ... 81 | emulator.send_unlink_response(unlink, status) 82 | end 83 | 84 | -- Configure the emulator and start the emulation 85 | emulator.start({ 86 | -- ... -- 87 | busnum = 4, 88 | devnum = 5, 89 | vendor_id = 0x1234, 90 | product_id = 0x5678, 91 | -- ... -- 92 | attached = attached, 93 | detached = detached, 94 | receive_submit = receive_submit, 95 | receive_unlink = receive_unlink, 96 | }) 97 | ---- 98 | 99 | [[emulator_api]] 100 | === Emulator API 101 | 102 | This section describes the API exposed by the *moonusb.emulator* module to the 103 | user scripts that implement emulators for particular devices. 104 | 105 | * *emulator.start*(_cfg_) + 106 | [small]#Configure the emulator and start the emulation. + 107 | _cfg_: <>.# 108 | 109 | * *attached*( ) _callback_ + 110 | *detached*( ) _callback_ + 111 | [small]#Signature for the _cfg.attached_ and _cfg.detached_ callbacks. + 112 | These callbacks are executed respectively when the fake device is attached (that 113 | is, imported by a client), and when it is detached (that is, the connection is 114 | closed either intentionally or due to an error).# 115 | 116 | * *receive_submit*(<>) _callback_ + 117 | *receive_unlink*(<>) _callback_ + 118 | [small]#Signatures for the _cfg.receive_submit_ and _cfg.receive_unlink_ callbacks. + 119 | These callbacks are executed respectively when a USBIP_CMD_SUBMIT or a USBIP_CMD_UNLINK 120 | message is received on the connection in attached state. The user is expected to process 121 | the message, and possibly send a response using the _send_xxx_response( )_ functions 122 | described below. + 123 | Rfr: link:++https://www.kernel.org/doc/Documentation/usb/usbip_protocol.txt++[USBIP_CMD_SUBMIT, USBIP_CMD_UNLINK].# 124 | 125 | * *emulator.send_submit_response*(<>, _status_, _error_count_, [_data_]) + 126 | *emulator.send_unlink_response*(<>, _status_) + 127 | [small]#Respectively send a USBIP_RET_SUBMIT or a USBIP_RET_UNLINK response. + 128 | The first argument is the table received in the corresponding _receive_xxx( )_ callback, unchanged. + 129 | _status_: signed integer, 0 for success, otherwise an error code. + 130 | _error_count_: integer. + 131 | _data_: binary string containing the data to be transferred, or _nil_ if none. + 132 | (I presume that the error codes are from , but I am not sure. Neither am I sure 133 | about the meaning of the error count. The USB/IP specification is vague, to say the least.) + 134 | Rfr: link:++https://www.kernel.org/doc/Documentation/usb/usbip_protocol.txt++[USBIP_RET_SUBMIT, USBIP_RET_UNLINK].# 135 | 136 | 137 | ''' 138 | *Structs* 139 | 140 | * [[emulatorconfig]] 141 | [small]#*emulatorconfig* = { + 142 | _usbip_ver_: string (opt. USB/P bcd version, defaults to '_01.11_'), + 143 | _ip_: string (opt. IP address, defaults to '_localhost_'), + 144 | _port_: integer (opt. TCP port, defaults to 3240), + 145 | _busnum_: integer (opt., defaults to 1), + 146 | _devnum_: integer (opt., defaults to 1), + 147 | _path_: string (opt., defaults to '_moonusb emulated device_'), + 148 | _vendor_id_: integer (opt., defaults to 0x0000), + 149 | _product_id_: integer (opt., defaults to 0x0000), + 150 | _release_number_: string (opt. bcd device version, defaults to '_00.00_'), + 151 | _speed_: <> (opt., defaults to '_high_'), + 152 | _device_class_: <> (opt., defaults to '_per interface_'), + 153 | _device_subclass_: integer (opt., defaults to 0), + 154 | _device_protocol_: integer (opt., defaults to 0), + 155 | _configuration_value_: integer (opt., defaults to 1), + 156 | _num_configurations_: integer (opt., defaults to 1), + 157 | _interfaces_: {{ _class_=<>, _subclass_=integer, _protocol_=integer }}, + 158 | _attached_: function (opt. callback, see signature above), + 159 | _detached_: function (opt. callback, see signature above), + 160 | _receive_submit_: function (callback, see signature above), + 161 | _receive_unlink_: function (callback, see signature above), + 162 | } (rfr: link:++https://www.kernel.org/doc/Documentation/usb/usbip_protocol.txt++[OP_REQ_DEVLIST])# 163 | 164 | * [[submit]] 165 | [small]#*submit* = { + 166 | _seqnum_: integer, + 167 | _devid_: integer, + 168 | _direction_: <>, + 169 | _ep_: integer, + 170 | _transfer_flags_: integer, + 171 | _transfer_buffer_length_: integer, + 172 | _start_frame_: integer, + 173 | _number_of_packets_: integer, + 174 | _interval_: integer, + 175 | _setup_: binary strings (8 bytes long), + 176 | _data_: binary string or _nil_, + 177 | } (rfr: link:++https://www.kernel.org/doc/Documentation/usb/usbip_protocol.txt++[USBIP_CMD_SUBMIT])# 178 | 179 | * [[unlink]] 180 | [small]#*unlink* = { + 181 | _seqnum_: integer, + 182 | _devid_: integer, + 183 | _direction_: <>, + 184 | _ep_: integer, + 185 | _victim_seqnum_: integer, + 186 | } (rfr: link:++https://www.kernel.org/doc/Documentation/usb/usbip_protocol.txt++[USBIP_CMD_UNLINK])# 187 | 188 | === Emulator usage 189 | 190 | This section shows how to run a device emulation. It is assumed that the emulator 191 | is implemented in a user script named *myfakedevice.lua*, and a client application 192 | (using that particular USB device) is implemented in a script named *myclient.lua*. 193 | 194 | First of all, ensure that the needed kernel modules are loaded: 195 | 196 | [source,bash,indent=1] 197 | ---- 198 | $ sudo modprobe usbip-core 199 | $ sudo modprobe vhci-hcd 200 | ---- 201 | 202 | Then, launch the emulator: 203 | 204 | [source,bash,indent=1] 205 | ---- 206 | $ lua myfakedevice.lua 207 | ---- 208 | 209 | The emulator will open a TCP socket on the specified port and wait for clients to connect. 210 | 211 | From another shell, list the devices exported by the script, using the 212 | https://www.mankier.com/8/usbip[usbip] tool: 213 | 214 | [source,bash,indent=1] 215 | ---- 216 | $ usbip list -r 127.0.0.1 217 | ---- 218 | 219 | This command should list a single device, and indicate "4-5" as its _busid_ (assuming 220 | _busnum_=4 and _devnum_=5). Import the device, again with the usbip tool: 221 | 222 | [source,bash,indent=1] 223 | ---- 224 | $ sudo usbip attach -r 127.0.0.1 -b 4-5 225 | ---- 226 | 227 | This should start the configuration of the device, with the vhci driver issuing commands 228 | (get descriptor, etc) and the fake device responding. At the end of this phase, the fake 229 | device should appear in the list of USB devices available on the system, which you can see 230 | using the 231 | https://www.mankier.com/8/lsusb[lsusb] tool: 232 | 233 | [source,bash,indent=1] 234 | ---- 235 | $ lsusb 236 | ---- 237 | 238 | Finally, launch the client application from yet another shell: 239 | [source,bash,indent=1] 240 | ---- 241 | $ lua myclient.lua 242 | ---- 243 | 244 | If everything goes as expected, the client should now be able to detect and use the 245 | fake device as if it were a real one. 246 | 247 | To detach the fake device, first see its 'port' number (likely 00), then issue the 248 | detach command: 249 | 250 | [source,bash,indent=1] 251 | ---- 252 | $ sudo usbip port # list the imported devices 253 | $ sudo usbip detach -p 00 # detach Port 00 254 | ---- 255 | 256 | As a final note, the traffic between the emulator and the vhci driver can be captured 257 | and analized using 258 | https://www.wireshark.org/[Wireshark], 259 | which has a built-in dissector for the USB/IP protocol. 260 | (The traffic travels on the loopback interface, assuming the server uses the 127.0.0.1 ip address.) 261 | 262 | -------------------------------------------------------------------------------- /doc/enums.adoc: -------------------------------------------------------------------------------- 1 | 2 | [[enums]] 3 | == Enums 4 | 5 | Libusb enums are mapped in MoonUSB to sets of string literals (as is customary in Lua). 6 | 7 | If needed, the following function can be used to obtain the list of literals admitted by 8 | a particular enum type. 9 | 10 | [[usb.enum]] 11 | * {_literal_} = *usb.enum*(_enumtype_) + 12 | [small]#Returns a table listing the literals admitted by _enumtype_ (given as a string, e.g. 13 | '_blendop_', '_format_', etc).# 14 | 15 | Below is the list of the enum types and the literals they admit. 16 | 17 | [[capability]] 18 | [small]#*capability*: _libusb_capability_ + 19 | Values: '_hotplug_', '_hid access_', '_detach kernel driver_'.# 20 | 21 | [[class]] 22 | [small]#*class*: USB class codes (see: https://www.usb.org/defined-class-codes). + 23 | Values: '_per interface_', '_audio_', '_cdc_', '_hid_', '_physical_', '_image_', '_printer_', '_mass storage_', '_hub_', '_cdc data_', '_smart card_', '_content security_', '_video_', '_personal healthcare_', '_audio video_', '_billboard_', '_type c bridge_', '_diagnostic_', '_wireless_', '_miscellaneous_', '_application specific_', '_vendor specific_'.# 24 | 25 | [[direction]] 26 | [small]#*direction*: _libusb_endpoint_direction_ + 27 | Values: '_out_', '_in_'.# 28 | 29 | [[errcode]] 30 | [small]#*errcode*: _libusb_error_, _libusb_transfer_status_ + 31 | Values: '_success_', '_io error_', '_invalid param_', '_access_', '_no device_', '_not found_', '_busy_', '_timeout_', '_overflow_', '_pipe_', '_interrupted_', '_no mem_', '_not supported_', '_other_', '_success_', '_error_', '_timeout_', '_cancelled_', '_stall_', '_no device_', '_overflow_'.# 32 | 33 | [[hotplugevent]] 34 | [small]#*hotplugevent*: _libusb_hotplug_event + libusb_hotplug_flags_ + 35 | Values: '_attached_', '_detached_'.# 36 | 37 | [[isosynctype]] 38 | [small]#*isosynctype*: _libusb_iso_sync_type_ + 39 | Values: '_none_', '_async_', '_adaptive_', '_sync_'.# 40 | 41 | [[isousagetype]] 42 | [small]#*isousagetype*: _libusb_iso_usage_type_ + 43 | Values: '_data_', '_feedback_', '_implicit_'.# 44 | 45 | [[loglevel]] 46 | [small]#*loglevel*: _libusb_log_level_ + 47 | Values: '_none_', '_error_', '_warning_', '_info_', '_debug_'.# 48 | 49 | [[option]] 50 | [small]#*option*: _libusb_option_ + 51 | Values: '_log level_', '_use usbdk_', '_weak authority_'.# 52 | 53 | [[requestrecipient]] 54 | [small]#*requestrecipient*: _libusb_request_recipient_ + 55 | Values: '_device_', '_interface_', '_endpoint_', '_other_'.# 56 | 57 | [[requesttype]] 58 | [small]#*requesttype*: _libusb_request_type_ + 59 | Values: '_standard_', '_class_', '_vendor_', '_reserved_'.# 60 | 61 | [[speed]] 62 | [small]#*speed*: _libusb_speed_ + 63 | Values: '_unknown_', '_low_', '_full_', '_high_', '_super_', '_super plus_'.# 64 | 65 | [[standardrequest]] 66 | [small]#*standardrequest*: _libusb_standard_request_ + 67 | Values: '_get status_', '_clear feature_', '_set feature_', '_set address_', '_get descriptor_', '_set descriptor_', '_get configuration_', '_set configuration_', '_get interface_', '_set interface_', '_synch frame_', '_set sel_', '_set isoch delay_'.# 68 | 69 | [[transfertype]] 70 | [small]#*transfertype*: _libusb_endpoint_transfer_type_ + 71 | Values: '_control_', '_isochronous_', '_bulk_', '_interrupt_'.# 72 | 73 | [[transferstatus]] 74 | [small]#*transferstatus*: _libusb_transfer_status_ + 75 | Values: '_completed_', '_error_', '_timeout_', '_cancelled_', '_stall_', '_no device_', '_overflow_'.# 76 | 77 | [[type]] 78 | [small]#*type*: primitive type (char=8 bit, short=16 bit, int=32 bit, long=64 bit, float=32 bit, double=64 bit) + 79 | Values: '_char_', '_uchar_', '_short_', '_ushort_', '_int_', '_uint_', '_long_', '_ulong_', '_float_', '_double_'.# 80 | 81 | -------------------------------------------------------------------------------- /doc/hostmem.adoc: -------------------------------------------------------------------------------- 1 | 2 | [[hostmem]] 3 | == Host memory 4 | 5 | An hostmem object encapsulates (a pointer to) host accessible memory, with methods 6 | to access it from Lua and to retrieve pointers to any of its locations in form of 7 | http://www.lua.org/manual/5.3/manual.html#lua_pushlightuserdata[lightuserdata]. 8 | 9 | (Note that hostmem objets are specific to MoonUSB, i.e. they do not 10 | correspond to libusb objects). 11 | 12 | The memory encapsulated by an hostmem object may be either host memory allocated via 13 | the <>( ) or the <>( ) 14 | functions, or memory obtained by other means and passed to the <>( ) constructor. 15 | 16 | Hostmem objects are automatically deleted at exit, but they may also be deleted manually 17 | via the <>( ) function or the corresponding method. Hostmem 18 | objects that are tied to a <> object are automatically deleted when 19 | the _devhandle_ is closed. 20 | 21 | Hostmem objects come in handy, for example, when issuing transfer commands either via the <> or vie the <>. 22 | 23 | [[hostmem_malloc]] 24 | * _hostmem_ = *malloc*([<>], _size_) + 25 | _hostmem_ = *malloc*([<>], _data_) + 26 | _hostmem_ = *malloc*([<>], <>, {_value~1~_, _..._, _value~N~_}) + 27 | _hostmem_ = *malloc*([<>], <>, _value~1~_, _..._, _value~N~_) + 28 | [small]#Allocates host memory and creates an _hostmem_ object to encapsulate it. + 29 | *malloc*(_devhandle_, _size_), where _size_ is an integer, allocates _size_ bytes of contiguous memory 30 | and initializes them to 0; + 31 | *malloc*(_devhandle_, _data_), where _data_ is a binary string, allocates _#data_ bytes of contiguous 32 | memory and initializes them with the contents of _data_; + 33 | *malloc*(_devhandle_, _type_, _..._) is functionally equivalent to _malloc(usb.pack(type, ...))_. + 34 | If the _devhandle_ argument is not _nil_, an attempt is made to allocate DMA memory for the given device (rfr: _libusb_dev_mem_alloc( )_). 35 | If the attempt fails, normal heap memory is allocated instead. In both cases the _hostmem_ is automatically deleted (and its memory released) when the _devhandle_ is closed.# 36 | 37 | [[hostmem_aligned_alloc]] 38 | * _hostmem_ = *aligned_alloc*(_alignment_, _size_) + 39 | _hostmem_ = *aligned_alloc*(_alignment_, _data_) + 40 | _hostmem_ = *aligned_alloc*(_alignment_, <>, {_value~1~_, _..._, _value~N~_}) + 41 | _hostmem_ = *aligned_alloc*(_alignment_, <>, _value~1~_, _..._, _value~N~_) + 42 | [small]#Same as <>( ), with the additional _alignment_ parameter to control 43 | memory address alignment (rfr. _aligned_alloc(3)_).# 44 | 45 | [[hostmem_hostmem]] 46 | * _hostmem_ = *hostmem*(_size_, _ptr_) + 47 | _hostmem_ = *hostmem*(_data_) + 48 | [small]#Creates a _hostmem_ object encapsulating user provided memory. + 49 | Such memory may be provided in form of a lightuserdata (_ptr_) containing a valid pointer to _size_ bytes of contiguous memory, or as a binary string (_data_). + 50 | In both cases, care must be taken that the memory area remains valid until the _hostmem_ object is 51 | deleted, or at least until it is accessed via its methods. 52 | In the _data_ case, this means ensuring that _data_ is not garbage collected during the hostmem object lifetime. + 53 | (Note that _malloc(data)_ and _hostmem(data)_ differ in that the former allocates memory and copies 54 | _data_ in it, while the latter just stores a pointer to _data_).# 55 | 56 | [[hostmem_free]] 57 | * *free*(_hostmem_) + 58 | hostmem++:++*free*( ) + 59 | [small]#Deletes the _hostmem_ object. If _hostmem_ was created with 60 | <>( ) or <>( ), this function also releases the encapsulated memory.# 61 | 62 | [[hostmem_ptr]] 63 | * _ptr_ = hostmem++:++*ptr*([_offset_=0], [_nbytes_=0]) + 64 | [small]#Returns a pointer (lightuserdata) to the location at _offset_ bytes from the beginning of the encapsulated memory. + 65 | Raises an error if the requested location is beyond the boundaries of the memory area, or if there are not at least _nbytes_ of memory after it.# 66 | 67 | [[hostmem_size]] 68 | * _nbytes_ = hostmem++:++*size*([_offset_=0]) + 69 | _nbytes_ = hostmem++:++*size*(_ptr_) + 70 | [small]#Returns the number of bytes of memory available after _offset_ bytes from the beginning 71 | of the encapsulated memory area, or after _ptr_ (a lightuserdata obtained with hostmem:<>( )).# 72 | 73 | [[hostmem_read]] 74 | * _data_ = hostmem++:++*read*([_offset_], [_nbytes_]) + 75 | {_val~1~_, _..._, _val~N~_} = hostmem++:++*read*([_offset_], [_nbytes_], <>) + 76 | [small]#Reads _nbytes_ of data starting from _offset_, and returns it as a binary string or as 77 | a table of primitive values. + 78 | The _offset_ parameter defaults to 0, and _nbytes_ defaults to the memory size minus _offset_. + 79 | _hostmem:read(offset, nbytes, type)_ is functionally equivalent to 80 | _cl.unpack(type, hostmem:read(offset, nbytes))_.# 81 | 82 | [[hostmem_write]] 83 | * hostmem++:++*write*(_offset_, _nil_, _data_) + 84 | hostmem++:++*write*(_offset_, <>, _val~1~_, _..._, _val~N~_) + 85 | hostmem++:++*write*(_offset_, <>, {_value~1~_, _..._, _value~N~_}) + 86 | [small]#Writes to the encapsulated memory area, starting from the byte at _offset_. + 87 | *write*(_offset_, _nil_, _data_) writes the contents of _data_ (a binary string); + 88 | *write*(_offset_, _type_, _..._) is equivalent to _write(offset, nil, usb.pack(type, ...))_.# 89 | 90 | [[hostmem_copy]] 91 | * hostmem++:++*copy*(_offset_, _size_, _srcptr_) + 92 | hostmem++:++*copy*(_offset_, _size_, _srchostmem_, _srcoffset_) + 93 | [small]#Copies _size_ bytes to the encapsulated memory area, starting from the byte at _offset_. + 94 | *copy*(_offset_, _size_, _srcptr_), copies the _size_ bytes pointed to by _srcptr_ (a lightuserdata). + 95 | *copy*(_offset_, _size_, _srchostmem_, _srcoffset_), copies _size_ bytes from the memory encapsulated 96 | by _srchostmem_ (a hostmem object), starting from the location at _srcoffset_.# 97 | 98 | [[hostmem_clear]] 99 | * hostmem++:++*clear*(_offset_, _nbytes_, [_val_=0]) + 100 | [small]#Clears _nbytes_ of memory starting from _offset_. If the _val_ parameter is given, 101 | the bytes are set to its value instead of 0 (_val_ may be an integer or a character, i.e. 102 | a string of length 1).# 103 | 104 | 105 | -------------------------------------------------------------------------------- /doc/hotplug.adoc: -------------------------------------------------------------------------------- 1 | 2 | [[hotplug]] 3 | == Hotplug 4 | 5 | [small]#Rfr: link:++http://libusb.sourceforge.net/api-1.0/group__libusb__hotplug.html++[Device hotplug event notification].# 6 | 7 | This API needs an event loop. See the <> section. 8 | 9 | * _hotplug_ = <>++:++*hotplug_register*(_event_, _func_, [_enumerate_], [_vendor_id_], [_product_id_], [_device_class_] ) + 10 | [small]#Registers a hotplug callback function and returns the corresponding _hotplug_ object. + 11 | _event_: <>, the event of interest, + 12 | _func_: the callback function, + 13 | _enumerate_: boolean, if _true_, the callback is executed also for already discovered devices, + 14 | _vendor_id_: integer (_nil_ means '_any_'), + 15 | _product_id_: integer (_nil_ means '_any_'), + 16 | _device_class_: integer (_nil_ means '_any_'), + 17 | The _func_ callback is executed as *boolean = func(context, device, event)*. By returning _true_ it causes the hotplug to be deregistered. + 18 | The callback should free any device it is not interested in, by calling _device:free( )_. + 19 | Rfr: _libusb_hotplug_register_callback( )_.# 20 | 21 | * _hotplug_++:++*deregister*( ) + 22 | [small]#Deregisters the callback and deletes the _hotplug_ object. + 23 | Rfr: _libusb_hotplug_deregister_callback( )_.# 24 | 25 | -------------------------------------------------------------------------------- /doc/index.adoc: -------------------------------------------------------------------------------- 1 | = MoonUSB Reference Manual 2 | Stefano Trettel 3 | v0.1, 2021-01-11 4 | :toc: left 5 | :toclevels: 3 6 | :stylesdir: ./ 7 | :stylesheet: colony.css 8 | :source-highlighter: pygments 9 | :pygments-style: autumn 10 | :source-language: lua 11 | :exampledir: ../examples 12 | 13 | image::powered-by-lua.gif[Lua logo, link=http://www.lua.org] 14 | 15 | // Macros for trees: {tS} = " ", {tI} = "│  ", {tH} = "├─ ", {tL} = "└─ " 16 | :tS:      17 | :tI: │    18 | :tH: ├─  19 | :tL: └─  20 | 21 | include::preface.adoc[] 22 | include::introduction.adoc[] 23 | 24 | include::context.adoc[] 25 | include::device.adoc[] 26 | include::hotplug.adoc[] 27 | include::asynchapi.adoc[] 28 | include::polling.adoc[] 29 | include::synchapi.adoc[] 30 | include::hostmem.adoc[] 31 | include::datahandling.adoc[] 32 | include::miscellanea.adoc[] 33 | include::structs.adoc[] 34 | include::enums.adoc[] 35 | include::emulator.adoc[] 36 | include::links.adoc[] 37 | 38 | -------------------------------------------------------------------------------- /doc/introduction.adoc: -------------------------------------------------------------------------------- 1 | 2 | == Introduction 3 | 4 | MoonUSB is an almost one-to-one Lua binding library to https://libusb.info/[libusb]. 5 | This means that by and large, it is intended to be used as described in the 6 | http://libusb.sourceforge.net/api-1.0/[libusb documentation]. 7 | 8 | MoonUSB binds libusb structs (contexts, devices, device handles, etc) to Lua userdata, 9 | exposing an *object oriented* interface to the Lua programmer. Most libusb functions are 10 | exposed by MoonUSB as methods of the relevant objects they act on. 11 | For example, the _libusb_open( )_ and _libusb_close( )_ functions are exposed 12 | as the _device:open( )_ and _devhandle:close( )_ methods, respectively. 13 | 14 | Lua object types in MoonUSB are listed in the tree below, together with the corresponding 15 | structs in libusb, if any: 16 | 17 | [small]#<> _(libusb_context)_ + 18 | {tH}<> _(libusb_hotplug_callback_handle)_ + 19 | {tH}<> _(libusb_device)_ + 20 | {tL}{tL}<> _(libusb_device_handle)_ + 21 | {tS}{tS}{tH}<> _(libusb_device_handle + interface number)_ + 22 | {tS}{tS}{tH}<> _(libusb_transfer)_ + 23 | {tS}{tS}{tL}<> _(none)_ + 24 | <> _(none)_# 25 | 26 | The <> object is a MoonUSB-specific object that encapsulates a memory 27 | area, to be (optionally) used as memory buffer in transfer functions. It is listed twice 28 | because it may be either standalone, or tied to a <> (possibly 29 | encapsulating DMA memory for that specific device). 30 | 31 | Objects are *garbage collected at exit* (which includes on error), and automatically 32 | deleted at the libusb level with graceful release whenever possible, so there is no need to 33 | explicitly invoke their deletion methods at exit for cleanup. 34 | 35 | Apart from at exit, however, objects are not automatically garbage collected 36 | footnote:[Objects are anchored to the Lua registry at their creation, so even if the script does not 37 | have references to an object, a reference always exists on the registry and this prevents the 38 | GC to collect it.] 39 | and one must release them explicitly when needed, e.g. to release resources when the 40 | application is not exiting and some objects are no longer needed. 41 | 42 | Releasing an object causes the automatic (pre) destruction of all its children 43 | objects (as per the above tree), and the invalidation of any reference to the object and to 44 | its children.footnote:[It is good practice to not leave invalid references to objects around, because 45 | they prevent the GC to collect the memory associated with the userdata.] 46 | 47 | Unless otherwise stated, *on error* all MoonUSB functions and methods raise a 48 | http://www.lua.org/manual/5.3/manual.html#lua_error[Lua error]. 49 | If needed, this behaviour can be overridden by wrapping function calls in the standard Lua 50 | http://www.lua.org/manual/5.3/manual.html#pdf-pcall[pcall]( ). 51 | 52 | MoonUSB supports multiple concurrent libusb <> (that is, sessions), but 53 | it does not support multithreading. 54 | 55 | -------------------------------------------------------------------------------- /doc/links.adoc: -------------------------------------------------------------------------------- 1 | 2 | [[links]] 3 | == Useful links 4 | 5 | * Lua Manuals: 6 | link:++http://www.lua.org/manual/5.3/++[5.3], 7 | link:++http://www.lua.org/manual/5.4/++[5.4]. 8 | 9 | * link:++http://libusb.sourceforge.net/api-1.0/++[Libusb-1.0 API documentation]. 10 | 11 | * https://github.com/stetre/moonlibs[MoonLibs] collection. 12 | 13 | * https://www.usb.org/documents[USB-IF Document Library] (USB specifications). 14 | 15 | * http://www.linux-usb.org/usb-ids.html[The USB ID Repository] (vendor ids, product ids, etc). 16 | 17 | * https://gitlab.com/wireshark/wireshark/-/wikis/CaptureSetup/USB[Wireshark USB CaptureSetup] 18 | (how to capture USB packets with Wireshark). 19 | 20 | * Frank Zhao's 21 | http://eleccelerator.com/usbdescreqparser/[USB parser] and 22 | https://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/[HID report tutorial]. 23 | 24 | 25 | * USB/IP http://usbip.sourceforge.net/[project homepage] and 26 | https://www.kernel.org/doc/Documentation/usb/usbip_protocol.txt[protocol specification]. 27 | 28 | * https://www.mankier.com/8/usbip[usbip(8)], https://www.mankier.com/8/lsusb[lsusb(8)] (manpages). 29 | 30 | -------------------------------------------------------------------------------- /doc/miscellanea.adoc: -------------------------------------------------------------------------------- 1 | 2 | [[miscellanea]] 3 | == Miscellanea 4 | 5 | [small]#Rfr: link:++http://libusb.sourceforge.net/api-1.0/group__libusb__misc.html++[Miscellaneous].# 6 | 7 | [[get_version]] 8 | * _major_, _minor_, _micro_, _nano_, _rc_ = *get_version*( ) + 9 | [small]#Returns libusb version information. + 10 | Also available are the _usb._VERSION_ and _usb._LIBUSB_VERSION_ variables, that contain 11 | the string versions for MoonUSB and libusb, respectively. + 12 | Rfr: _libusb_get_version( )_.# 13 | 14 | [[set_locale]] 15 | * *set_locale*(_locale_) + 16 | [small]#_locale_: string. + 17 | Rfr: _libusb_set_locale( )_.# 18 | 19 | [[has_capability]] 20 | * *has_capability*(<>) + 21 | [small]#Rfr: _libusb_has_capability( )_.# 22 | 23 | 24 | [[trace_objects]] 25 | * *trace_objects*(_boolean_) + 26 | [small]#Enable/disable tracing of objects creation and destruction (which by default 27 | is disabled). + 28 | If enabled, a printf is generated whenever an object is created or deleted, 29 | indicating the object type and the value of its raw handle.# 30 | 31 | [[now]] 32 | * _t_ = *now*( ) + 33 | [small]#Returns the current time in seconds (a Lua number). + 34 | This is implemented with monotonic _clock_gettime(3)_, if available, or 35 | with _gettimeofday(3)_ otherwise.# 36 | 37 | [[since]] 38 | * _dt_ = *since*(_t_) + 39 | [small]#Returns the time in seconds (a Lua number) elapsed since the time _t_, 40 | previously obtained with the <>( ) function.# 41 | 42 | -------------------------------------------------------------------------------- /doc/polling.adoc: -------------------------------------------------------------------------------- 1 | 2 | [[polling]] 3 | == Polling 4 | 5 | [small]#Rfr: link:++http://libusb.sourceforge.net/api-1.0/group__libusb__poll.html++[Polling and timing].# 6 | 7 | The functions described in this section are to be used in the main event loop of the application. 8 | 9 | An event loop is required when using the <> API for transfers, and/or 10 | the <> API for detecting devices, since callbacks registered via those APIs are 11 | executed within calls of the _context_:<>( ) method described below. 12 | 13 | Note that MoonUSB does not support multithreading, so the related libusb functions are not exposed. 14 | 15 | [[handle_events]] 16 | * <>++:++*handle_events*([_timeout_]) + 17 | [small]#Handle any pending events. + 18 | _timeout_: number of seconds to block waiting for events to handle. + 19 | Passing _timeout_=0 makes the function handle any pending event and return immediately. + 20 | Passing _timeout_=_nil_ (or not passing it at all) makes it block indefinitely. + 21 | Rfr: _libusb_handle_events( )_, _libusb_handle_events_timeout( )_.# 22 | 23 | * _next_timeout_ = <>++:++*get_next_timeout*( ) + 24 | [small]#Returns the next internal timeout (in number of seconds) that libusb needs to handle, or _nil_ if none. + 25 | Rfr: _libusb_get_next_timeout( )_.# 26 | 27 | -------------------------------------------------------------------------------- /doc/powered-by-lua.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stetre/moonusb/da4331b897a7e0edc7e6634cc17489716579f6a2/doc/powered-by-lua.gif -------------------------------------------------------------------------------- /doc/preface.adoc: -------------------------------------------------------------------------------- 1 | 2 | == Preface 3 | 4 | This is the reference manual of *MoonUSB*, which is a 5 | https://www.lua.org[*Lua*] binding library for https://libusb.info/[*libusb*]. 6 | footnote:[ 7 | This manual is written in 8 | http://www.methods.co.nz/asciidoc/[AsciiDoc], rendered with 9 | http://asciidoctor.org/[AsciiDoctor] and a CSS from the 10 | https://github.com/asciidoctor/asciidoctor-stylesheet-factory[AsciiDoctor Stylesheet Factory].] 11 | 12 | It is assumed that the reader is familiar with both libusb and the Lua programming language. 13 | 14 | For convenience of reference, this document contains external (deep) links to the 15 | https://www.lua.org/manual/5.3/manual.html[Lua Reference Manual] and the 16 | http://libusb.sourceforge.net/api-1.0/[libusb-1.0 API Reference]. 17 | 18 | === Getting and installing 19 | 20 | For installation intructions, refer to the README file in the 21 | https://github.com/stetre/moonusb[*MoonUSB official repository*] 22 | on GitHub. 23 | 24 | === Module organization 25 | 26 | The MoonUSB module is loaded using Lua's 27 | http://www.lua.org/manual/5.3/manual.html#pdf-require[require]() and 28 | returns a table containing the functions it provides 29 | (as usual with Lua modules). This manual assumes that such 30 | table is named *usb*, i.e. that it is loaded with: 31 | 32 | [source,lua,indent=1] 33 | ---- 34 | usb = require("moonusb") 35 | ---- 36 | 37 | but nothing forbids the use of a different name. 38 | 39 | === Examples 40 | 41 | Complete examples can be found in the *examples/* directory of the release package. 42 | 43 | === License 44 | 45 | MoonUSB is released under the *MIT/X11 license* (same as 46 | http://www.lua.org/license.html[Lua], and with the same only requirement to give proper 47 | credits to the original author). 48 | The copyright notice is in the LICENSE file in the base directory 49 | of the https://github.com/stetre/moonusb[official repository] on GitHub. 50 | 51 | [[see-also]] 52 | === See also 53 | 54 | MoonUSB is part of https://github.com/stetre/moonlibs[MoonLibs], a collection of 55 | Lua libraries for graphics and audio programming. 56 | 57 | -------------------------------------------------------------------------------- /doc/structs.adoc: -------------------------------------------------------------------------------- 1 | 2 | [[structs]] 3 | == Structs 4 | 5 | * [[devicedescriptor]] 6 | [small]#*devicedescriptor* = { + 7 | _usb_version_: string (e.g. '_02.00_', _bcdUSB_), + 8 | _class_: <> (_bDeviceClass_), + 9 | _subclass_: integer (_bDeviceSubClass_), + 10 | _protocol_: integer (_bDeviceProtocol_), + 11 | _max_packet_size_0_: integer (_bMaxPacketSize0_), + 12 | _vendor_id_: integer (_idVendor_), + 13 | _product_id_: integer (_idProduct_), + 14 | _release_number_: string (e.g. '_01.02_', _bcdDevice_), + 15 | _manufacturer_index_: integer (_iManufacturer_), + 16 | _product_index_: integer (_iProduct_), + 17 | _serial_number_index_: integer (_iSerialNumber_), + 18 | _num_configurations_: integer (_bNumConfigurations_), + 19 | _configuration_: {<>}, + 20 | } (rfr: link:++http://libusb.sourceforge.net/api-1.0/structlibusb__device__descriptor.html++[libusb_device_descriptor])# 21 | 22 | * [[configdescriptor]] 23 | [small]#*configdescriptor* = { + 24 | _value_: integer (_bConfigurationValue_), + 25 | _index_: integer (_iConfiguration_), + 26 | _self_powered_: boolean (_bmAttributes_), + 27 | _remote_wakeup_: boolean (_bmAttributes_), + 28 | _max_power_: integer (_MaxPower_), + 29 | _num_interfaces_: integer (_bNumInterfaces_), + 30 | _interface_: {{<>}}, + 31 | _extra_: binary string, + 32 | } (rfr: link:++http://libusb.sourceforge.net/api-1.0/structlibusb__config__descriptor.html++[libusb_config_descriptor])# 33 | 34 | * [[interfacedescriptor]] 35 | [small]#*interfacedescriptor* = { + 36 | _number_: integer (_bInterfaceNumber_), + 37 | _alt_setting_: integer (_bAlternateSetting_), + 38 | _class_: <> (_bInterfaceClass_), + 39 | _subclass_: integer (_bInterfaceSubClass_), + 40 | _protocol_: integer (_bInterfaceProtocol_), + 41 | _index_: integer (_iInterface_), + 42 | _num_endpoints_: integer (_bNumEndpoints_), + 43 | _endpoint_: {<>}, + 44 | _extra_: binary string, + 45 | } (rfr: link:++http://libusb.sourceforge.net/api-1.0/structlibusb__interface__descriptor.html++[libusb_interface_descriptor])# 46 | 47 | * [[endpointdescriptor]] 48 | [small]#*endpointdescriptor* = { + 49 | _address_: integer (_bEndpointAddress_), + 50 | _number_: integer (_bEndpointAddress_), + 51 | _direction_: <> (_bEndpointAddress_), + 52 | _transfer_type_: <> (_bmAttributes_), + 53 | _iso_sync_type_: <> (_bmAttributes_, isochronous transfers only), + 54 | _iso_usage_type_: <> (_bmAttributes_, isochronous transfers only), + 55 | _max_packet_size_: integer (_wMaxPacketSize_), + 56 | _interval_: integer (_bInterval_), + 57 | _refresh_: integer (_bRefresh_), + 58 | _synch_address_: integer (_bSynchAddress_), + 59 | _ss_endpoint_companion_descriptor_: <> or _nil_, + 60 | _extra_: binary string, + 61 | } (rfr: link:++http://libusb.sourceforge.net/api-1.0/structlibusb__endpoint__descriptor.html++[libusb_endpoint_descriptor])# 62 | 63 | * [[ssendpointcompaniondescriptor]] 64 | [small]#*ssendpointcompaniondescriptor* = { + 65 | _max_burst_: integer (_bMaxBurst_), + 66 | _attributes_: integer (_bmAttributes_), + 67 | _bytes_per_interval_: integer (_wBytesPerInterval_), + 68 | } (rfr: link:++http://libusb.sourceforge.net/api-1.0/structlibusb__ss__endpoint__companion__descriptor.html++[libusb_ss_endpoint_companion_descriptor])# 69 | 70 | '''' 71 | 72 | * [[bosdescriptor]] 73 | [small]#*bosdescriptor* = { + 74 | _num_capabilities_: integer (_bNumDeviceCaps_), + 75 | _capability_: {<>}, + 76 | } (rfr: link:++http://libusb.sourceforge.net/api-1.0/structlibusb__bos__descriptor.html++[libusb_bos_descriptor])# 77 | 78 | * [[bosdevcapabilitydescriptor]] 79 | [small]#*bosdevcapabilitydescriptor* = { + 80 | _type_ = integer (_bDevCapabilityType_), + 81 | _data_ = binary string (_capability dependent data, see USB 3.2 Specification Table 9-13_), + 82 | } 83 | (rfr: link:++http://libusb.sourceforge.net/api-1.0/structlibusb__bos__dev__capability__descriptor.html++[libusb_bos_dev_capability_descriptor])# 84 | 85 | ''' 86 | 87 | * [[setup]] 88 | [small]#*setup* = { + 89 | _request_type_ = <>, + 90 | _request_recipient_ = <>, + 91 | _direction_ = <>, + 92 | _request_ = <> or integer (if _request_type_ is not '_standard_'), + 93 | _value_ = integer, + 94 | _index_ = integer, + 95 | _length_ = integer. + 96 | } (rfr: link:++http://libusb.sourceforge.net/api-1.0/structlibusb__control__setup.html++[libusb_control_setup struct])# 97 | 98 | -------------------------------------------------------------------------------- /doc/synchapi.adoc: -------------------------------------------------------------------------------- 1 | 2 | [[synchapi]] 3 | == Synchronous I/O 4 | 5 | [small]#Rfr: link:++http://libusb.sourceforge.net/api-1.0/group__libusb__synchio.html++[Synchronous device I//O].# 6 | 7 | The functions listed in this section are blocking and return only when the requested transfer either completes or fails. 8 | This means that they do not require an event loop (see <>), but also that 9 | they may block the application for long times, which may or may not be acceptable. 10 | If it is not, then the <> API should be used instead. 11 | 12 | * _transferred_ = <>++:++*control_transfer*(_ptr_, _length_, _timeout_) + 13 | [small]#Perform a USB control transfer and returns the number of bytes of data actually transferred. + 14 | _ptr_: lightuserdata containing a pointer to at least _length_ bytes of contiguous memory, + 15 | _timeout_: timeout in milliseconds (=0 for unlimited timeout). + 16 | The first 8 bytes pointed to by _ptr_ must contain an encoded control setup packet, and the _length_ 17 | of the memory area must be at least 8 + _wLength_. + 18 | The setup packet must be followed by the _wLength_ bytes of data to be transferred to the device, or 19 | of space for the data to be received from the device. + 20 | Rfr: _libusb_control_transfer( )_.# 21 | 22 | * _transferred_ = <>++:++*bulk_transfer*(_endpoint_, _ptr_, _length_, _timeout_) + 23 | _transferred_ = <>++:++*interrupt_transfer*(_endpoint_, _ptr_, _length_, _timeout_) + 24 | [small]#Perform a USB bulk or interrupt transfer and returns the number of bytes of data actually transferred. + 25 | _endpoint_: endpoint address, + 26 | _ptr_: lightuserdata containing a pointer to at least _length_ bytes of contiguous memory, + 27 | _timeout_: timeout in milliseconds (=0 for unlimited timeout). + 28 | Rfr: _libusb_bulk_transfer( )_, _libusb_interrupt_transfer( )_.# 29 | 30 | -------------------------------------------------------------------------------- /examples/describe.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- MoonUSB example: describe.lua 3 | -- Describes a device, given its vendor id and product id. 4 | 5 | local usb = require("moonusb") 6 | 7 | local function fmt(...) return string.format(...) end 8 | 9 | local vendor_id = tonumber(arg[1]) 10 | local product_id = tonumber(arg[2]) 11 | if not vendor_id or not product_id or 12 | vendor_id & 0x0000ffff ~= vendor_id or product_id & 0x0000ffff ~= product_id then 13 | print("Usage: "..arg[0].." vendor_id product_id") 14 | print("Example: "..arg[0].." 0x0012 0x0034 \n") 15 | os.exit(true) 16 | end 17 | 18 | local function hex(bytes) 19 | -- Convert a binary string to a readable string of hexadecimal bytes 20 | -- (eg. "00 21 f3 54") 21 | local fmt = string.rep("B", #bytes) 22 | local t = { string.unpack(fmt, bytes) } 23 | t[#t] = nil -- this entry is the index of the next unread byte, we don't want it! 24 | for i, x in ipairs(t) do t[i] = string.format("%.2x", x) end 25 | return table.concat(t, " ") 26 | end 27 | 28 | local function bcd2string(x) 29 | return string.format("%d%d.%d%d", (x>>12)&0x0f, (x>>8)&0x0f, (x>>4)&0xf, x&0xf) 30 | end 31 | 32 | local function decode_hid_descriptor(bytes) 33 | -- Decode a HID descriptor as per section 6.2.1 of the specification 34 | -- "Device Class Definition for Human Interface Devices (HID)" release 1.11 35 | local len, dt, rel, cc, n, ofs = string.unpack("I1I1I2I1I1", bytes) 36 | -- Check that it is indeed a HID descriptor of the correct length 37 | assert(dt == 0x21 and len == (6 + 3*n)) 38 | local desc = {} 39 | desc.release = bcd2string(rel) 40 | desc.country_code = cc 41 | desc.num_descriptors = n 42 | desc.descriptor = {} 43 | for i=1, n do 44 | local t, l, ofs = string.unpack("I1I2", bytes, ofs) 45 | if t == 0x22 then t = "report" 46 | elseif t == 0x23 then t = "physical" 47 | else error("unsexpected hid descriptor type "..t) 48 | end 49 | desc.descriptor[i] = { type=t, length=l } 50 | end 51 | return desc 52 | end 53 | 54 | -- Create a context and try to open the device: 55 | local ctx = usb.init() 56 | local device, devhandle = ctx:open_device(vendor_id, product_id) 57 | 58 | -- Get and print the basic properties of the device (these are cached): 59 | local bus = device:get_bus_number() 60 | local port = device:get_port_number() 61 | local ports = device:get_port_numbers() 62 | local address = device:get_address() 63 | local speed = device:get_speed() 64 | 65 | print("Device properties") 66 | print(" bus number: "..bus) 67 | print(" port number: "..port) 68 | print(" port path: ".. (#ports>0 and table.concat(ports, ', ') or "n.a.")) 69 | print(" address: "..address) 70 | print(" speed: "..speed) 71 | 72 | -- Get the device descriptor (also cached) and print its fields 73 | local desc = device:get_device_descriptor() 74 | print("Device descriptor") 75 | print(" usb_version: "..desc.usb_version) 76 | print(" class: "..desc.class) 77 | print(" subclass: "..desc.subclass) 78 | print(" protocol: "..desc.protocol) 79 | print(" vendor_id: "..fmt("0x%.4x", desc.vendor_id)) 80 | print(" product_id: "..fmt("0x%.4x", desc.product_id)) 81 | print(" release_number: "..desc.release_number) 82 | print(" num_configurations: "..desc.num_configurations) 83 | 84 | -- The descriptor contains indices of string descriptors. 85 | -- For any non-zero index, we should be able to retrieve the corresponding string. 86 | local manufacturer, product, serial_number = "???", "???", "???" 87 | if desc.manufacturer_index ~= 0 then 88 | manufacturer = devhandle:get_string_descriptor(desc.manufacturer_index) 89 | end 90 | print(" manufacturer: "..manufacturer) 91 | if desc.product_index ~= 0 then 92 | product = devhandle:get_string_descriptor(desc.product_index) 93 | end 94 | print(" product: "..product) 95 | if desc.serial_number_index ~= 0 then 96 | serial_number = devhandle:get_string_descriptor(desc.serial_number_index) 97 | end 98 | print(" serial number: "..serial_number) 99 | 100 | -- Get and print the configuration(s) 101 | for _, conf in ipairs(desc.configuration) do 102 | print("Configuration") 103 | print(" value: "..conf.value) 104 | print(" index: "..conf.index) 105 | print(" self_powered: "..tostring(conf.self_powered)) -- boolean are not coerced... 106 | print(" remote_wakeup: "..tostring(conf.remote_wakeup)) 107 | print(" max_power: "..conf.max_power) 108 | print(" num_interfaces: "..conf.num_interfaces) 109 | print(" extra bytes: ".. (conf.extra and hex(conf.extra) or "-")) 110 | -- Print the interface(s) for this configuration 111 | for _, alt in ipairs(conf.interface) do 112 | -- Each entry in conf.interface is a list of 1+ interfacdescriptor's, 113 | -- which are the 'alternate settings' for the interface. 114 | for _, itf in ipairs(alt) do 115 | print(" Interface") 116 | print(" number: "..itf.number) 117 | print(" alt_setting: "..itf.alt_setting) 118 | print(" class: "..itf.class) 119 | print(" subclass: "..itf.subclass) 120 | print(" protocol: "..itf.protocol) 121 | print(" index: "..itf.index) 122 | print(" num_endpoints: "..itf.num_endpoints) 123 | if itf.class == 'hid' then 124 | -- The extra bytes should contain a HID descriptor, so let's decode it 125 | local hid = decode_hid_descriptor(itf.extra) 126 | print(" HID descriptor (extra bytes)") 127 | print(" release: "..hid.release) 128 | print(" country_code: "..hid.country_code) 129 | print(" num_descriptors: "..hid.num_descriptors) 130 | for _, d in ipairs(hid.descriptor) do 131 | print(" type: "..d.type) 132 | print(" length: "..d.length) 133 | end 134 | else 135 | print(" extra bytes: ".. (itf.extra and hex(itf.extra) or "-")) 136 | end 137 | -- Print the endpoints for this interface 138 | for _, ep in ipairs(itf.endpoint) do 139 | print(" Endpoint") 140 | print(" address: "..fmt("0x%.2x", ep.address)) 141 | print(" number: "..ep.number) 142 | print(" direction: "..ep.direction) 143 | print(" transfer_type: "..ep.transfer_type) 144 | print(" iso_sync_type: "..(ep.iso_sync_type or "n.a.")) 145 | print(" iso_usage_type: "..(ep.iso_usage_type or "n.a.")) 146 | print(" max_packet_size: "..ep.max_packet_size) 147 | print(" interval: "..ep.interval) 148 | print(" refresh: "..ep.refresh) 149 | print(" synch_address: "..fmt("0x%.2x", ep.synch_address)) 150 | print(" extra bytes: ".. (ep.extra and hex(ep.extra) or "-")) 151 | local comp = ep.ss_endpoint_companion_descriptor 152 | if comp then 153 | print(" Endpoint companion") 154 | print(" max_burst: ", comp.max_burst) 155 | print(" attributes: ", comp.attributes) 156 | print(" bytes_per_interval: ", comp.bytes_per_interval) 157 | end 158 | end 159 | end 160 | end 161 | end 162 | 163 | -------------------------------------------------------------------------------- /examples/gamepad-emulator.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- MoonUSB example: gamepad-emulator.lua 3 | -- 4 | -- Gamepad Device Emulator 5 | -- ---------------------------------------------------------------------------- 6 | -- This example uses the moonusb.emulator module to emulate a gamepad device. 7 | -- The emulated gamepad is of the same type as expected by the gamepads.lua 8 | -- example, that can therefore be used as client to test this fake gamepad. 9 | -- 10 | -- Usage 11 | -- ---------- 12 | -- 13 | -- First of all, ensure that the needed kernel modules are loaded: 14 | -- $ sudo modprobe usbip-core 15 | -- $ sudo modprobe vhci-hcd 16 | -- 17 | -- Launch the emulator. It will open a tcp port and listen for client connects. 18 | -- $[examples]./gamepad-emulator.lua 19 | -- 20 | -- From another shell, list the devices exported by the script, using the 21 | -- usbip tool: 22 | -- $ usbip list -r 127.0.0.1 23 | -- 24 | -- This command should list a single device, and indicate "4-5" as its busid 25 | -- (the busid is composed as busnum-devnum). 26 | -- 27 | -- Import (or attach) the device, again with the usbip tool: 28 | -- $ sudo usbip attach -r 127.0.0.1 -b 4-5 29 | -- 30 | -- This should start the configuration of the device, with the vhci driver 31 | -- issuing commands (get descriptor, etc) and the fake device responding. 32 | -- At the end of this phase, the fake device should appear in the list of 33 | -- USB devices available on the system. To see this list, use the lsusb tool: 34 | -- $ lsusb 35 | -- 36 | -- Now, from yet another shell, launch the client application: 37 | -- $[examples]./gamepads.lua 38 | -- 39 | -- If everything goes as expected, the client should end up receiving the fake 40 | -- HID reports from the fake driver (in this case, a report is 8 bytes long, 41 | -- with values from 0x01 to 0x08). 42 | -- 43 | -- To detach the fake device, first see its 'port' number (likely 00), then 44 | -- then issue the detach command: 45 | -- $ sudo usbip port # list the imported devices 46 | -- $ sudo usbip detach -p 00 # detach Port 00 47 | -- 48 | -- (Or simply send a SIGINT (ctl-C) to the gamepad-emulator.lua script, which 49 | -- should be equivalent to unplugging the device). 50 | -- 51 | local usb = require("moonusb") 52 | local emulator = require("moonusb.emulator") 53 | -- local timers = require("moontimers") 54 | 55 | -- Utilities 56 | local fmt = string.format 57 | local function printf(...) io.write(fmt(...)) end 58 | local rep, pack, unpack = string.rep, string.pack, string.unpack 59 | local doubleface = usb.doubleface 60 | local hex, bcd2str, str2bcd = usb.hex, usb.bcd2str, usb.str2bcd 61 | local zeropad, packbytes, unpackbytes = usb.zeropad, usb.packbytes, usb.unpackbytes 62 | local send_submit_response = emulator.send_submit_response 63 | local send_unlink_response = emulator.send_unlink_response 64 | 65 | local cfg = { 66 | -- usbip_ver = "01.11", 67 | busnum = 4, 68 | devnum = 5, 69 | vendor_id = 0x0079, -- DragonRise Inc. 70 | product_id = 0x0006, -- PC TWIN SHOCK Gamepad 71 | release_number = '00.00', 72 | device_class = 'per interface', 73 | device_subclass = 0, 74 | device_protocol = 0, 75 | configuration_value = 1, 76 | num_configurations = 1, 77 | interfaces = {{class='hid', subclass=0, protocol=0}} 78 | } 79 | 80 | ------------------------------------------------------------------------------- 81 | -- Descriptors 82 | ------------------------------------------------------------------------------- 83 | local devicedescriptor = packbytes{ 84 | 0x12, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x79, 0x00, 85 | 0x06, 0x00, 0x07, 0x01, 0x01, 0x02, 0x00, 0x01 86 | } 87 | 88 | local interfacedescriptor = packbytes{0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00} 89 | local hiddescriptor = packbytes{0x09, 0x21, 0x10, 0x01, 0x21, 0x01, 0x22, 0x65, 0x00} 90 | local epin1descriptor = packbytes{0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x0a} 91 | local epout1descriptor = packbytes{0x07, 0x05, 0x01, 0x03, 0x08, 0x00, 0x0a} 92 | 93 | local configdescriptor = table.concat{ 94 | packbytes{ 0x09, 0x02, 0x29, 0x00, 0x01, 0x01, 0x00, 0x80, 0xfa }, 95 | interfacedescriptor, 96 | hiddescriptor, 97 | epin1descriptor, 98 | epout1descriptor 99 | } 100 | 101 | local stringdescriptor = { 102 | [0] = packbytes{ -- language 103 | 0x04, 0x03, 0x09, 0x04 }, 104 | [1] = packbytes{ -- manufacturer 105 | 0x24, 0x03, 0x44, 0x00, 0x72, 0x00, 0x61, 0x00, 0x67, 0x00, 106 | 0x6f, 0x00, 0x6e, 0x00, 0x52, 0x00, 0x69, 0x00, 0x73, 0x00, 107 | 0x65, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00, 108 | 0x2e, 0x00, 0x20, 0x00, 0x20, 0x00 }, 109 | [2] = packbytes{ -- product 110 | 0x34, 0x03, 0x47, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x65, 0x00, 111 | 0x72, 0x00, 0x69, 0x00, 0x63, 0x00, 0x20, 0x00, 0x20, 0x00, 112 | 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 113 | 0x20, 0x00, 0x4a, 0x00, 0x6f, 0x00, 0x79, 0x00, 0x73, 0x00, 114 | 0x74, 0x00, 0x69, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x20, 0x00, 115 | 0x20, 0x00 }, 116 | } 117 | 118 | local hidreportdescriptor = packbytes{ 119 | 0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0xa1, 0x02, 0x75, 0x08, 120 | 0x95, 0x05, 0x15, 0x00, 0x26, 0xff, 0x00, 0x35, 0x00, 0x46, 121 | 0xff, 0x00, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32, 0x09, 0x32, 122 | 0x09, 0x35, 0x81, 0x02, 0x75, 0x04, 0x95, 0x01, 0x25, 0x07, 123 | 0x46, 0x3b, 0x01, 0x65, 0x14, 0x09, 0x39, 0x81, 0x42, 0x65, 124 | 0x00, 0x75, 0x01, 0x95, 0x0c, 0x25, 0x01, 0x45, 0x01, 0x05, 125 | 0x09, 0x19, 0x01, 0x29, 0x0c, 0x81, 0x02, 0x06, 0x00, 0xff, 126 | 0x75, 0x01, 0x95, 0x08, 0x25, 0x01, 0x45, 0x01, 0x09, 0x01, 127 | 0x81, 0x02, 0xc0, 0xa1, 0x02, 0x75, 0x08, 0x95, 0x07, 0x46, 128 | 0xff, 0x00, 0x26, 0xff, 0x00, 0x09, 0x02, 0x91, 0x02, 0xc0, 129 | 0xc0, 130 | } 131 | 132 | ------------------------------------------------------------------------------- 133 | -- USB protocol 134 | ------------------------------------------------------------------------------- 135 | -- References 136 | -- [USB2] USB 2.0 Specification 137 | -- [HID] Device Class Specification for Human Interface Devices (HID) ver 1.11 138 | 139 | -- Request codes (0xTTRR where TT=bmRequestType, RR=bmRequest) 140 | local REQ = doubleface{ 141 | -- Standard requests ([USB2]/9.4) 142 | ['CLEAR_FEATURE_DEV'] = 0x8001, 143 | ['CLEAR_FEATURE_ITF'] = 0x8101, 144 | ['CLEAR_FEATURE_EP'] = 0x8201, 145 | ['GET_CONFIGURATION'] = 0x8008, 146 | ['GET_DESCRIPTOR'] = 0x8006, 147 | ['GET_DESCRIPTOR_ITF'] = 0x8106, -- see [HID]/7.1.1 148 | ['GET_INTERFACE'] = 0x810a, 149 | ['GET_STATUS_DEV'] = 0x8000, 150 | ['GET_STATUS_ITF'] = 0x8100, 151 | ['GET_STATUS_EP'] = 0x8200, 152 | ['SET_ADDRESS'] = 0x0005, 153 | ['SET_CONFIGURATION'] = 0x0009, 154 | ['SET_DESCRIPTOR'] = 0x0007, 155 | ['SET_DESCRIPTOR_ITF'] = 0x0107, -- see [HID]/7.1.2 156 | ['SET_FEATURE_DEV'] = 0x0003, 157 | ['SET_FEATURE_ITF'] = 0x0103, 158 | ['SET_FEATURE_EP'] = 0x0203, 159 | ['SET_INTERFACE'] = 0x010b, 160 | ['SYNCH_FRAME'] = 0x020c, 161 | -- HID-class requests ([HID]/7) 162 | ['GET_REPORT'] = 0xa101, 163 | ['SET_REPORT'] = 0x2109, 164 | ['GET_IDLE'] = 0xa102, 165 | ['SET_IDLE'] = 0x210a, 166 | ['GET_PROTOCOL'] = 0xa103, 167 | ['SET_PROTOCOL'] = 0x210b, 168 | } 169 | 170 | local DTYPE = doubleface{ -- descriptor types 171 | -- Standard descriptors ([USB2]/table 9.5) 172 | ['device'] = 1, 173 | ['configuration'] = 2, 174 | ['string'] = 3, 175 | ['interface'] = 4, 176 | ['endpoint'] = 5, 177 | ['device qualifier'] = 6, 178 | ['other speed configuration'] = 7, 179 | ['interface power'] = 8, 180 | -- HID class descriptors ([HID]/7.1 181 | ['hid'] = 0x21, 182 | ['report'] = 0x22, 183 | ['physical'] = 0x23, 184 | } 185 | 186 | local handle_submit = {} -- table of functions indexed by request name 187 | 188 | handle_submit['GET_DESCRIPTOR'] = function(submit, req_code, req_name) -- [USB2]/9.4.3 189 | local dindex, dtype = unpack('I1I1', submit.setup, 3) 190 | local what = DTYPE[dtype] 191 | local data 192 | if what == 'device' then data = devicedescriptor 193 | elseif what == 'configuration' then data = configdescriptor 194 | elseif what == 'string' then data = stringdescriptor[dindex] 195 | elseif what == 'hid' then data = hiddescriptor 196 | elseif what == 'report' then data = hidreportdescriptor 197 | end 198 | if data then 199 | send_submit_response(submit, 0, 0, data) 200 | else 201 | printf("unknown or unsupported descriptor type = %d\n", dtype) 202 | end 203 | end 204 | 205 | handle_submit['GET_DESCRIPTOR_ITF'] = handle_submit['GET_DESCRIPTOR'] -- [HID]/7.1.1 206 | 207 | handle_submit['SET_CONFIGURATION'] = function(submit, req_code, req_name) -- [USB2]/9.4.7 208 | local value = unpack('I1', submit.setup, 3) 209 | if value == cfg.configuration_value then 210 | send_submit_response(submit, 0, 0, nil) 211 | else 212 | printf("invalid configuration value %d\n", value) 213 | end 214 | end 215 | 216 | handle_submit['SET_IDLE'] = function(submit, req_code, req_name) -- [HID]/7.2.4 217 | send_submit_response(submit, 0, 0, nil) 218 | end 219 | 220 | local function receive_control(submit) 221 | local req_code = unpack('>I2', submit.setup) 222 | local req_name = REQ[req_code] 223 | if not req_name then 224 | printf("received unknown request (setup: %s)\n", hex(submit.setup)) 225 | return 226 | end 227 | local f = handle_submit[req_name] 228 | if not f then 229 | printf("received unsupported %s (setup: %s)\n", req_name, hex(submit.setup)) 230 | return 231 | end 232 | printf("received %s (setup: %s)\n", req_name or "???", hex(submit.setup)) 233 | return f(submit) 234 | end 235 | 236 | local fakereport = packbytes{ 1, 2, 3, 4, 5, 6, 7, 8 } 237 | local function receive_endpoint1(submit) 238 | if submit.direction == 'in' then 239 | send_submit_response(submit, 0, 0, fakereport) 240 | elseif submit.direction == 'out' then 241 | printf("received report\n") -- @@ ?? 242 | send_submit_response(submit, 0, 0, nil) 243 | end 244 | end 245 | 246 | local function receive_submit(submit) 247 | if submit.ep == 0 then return receive_control(submit) end 248 | if submit.ep == 0x01 then return receive_endpoint1(submit) end 249 | printf("received submit on unknown endpoint 0x%.4x\n", submit.ep) 250 | end 251 | 252 | local function receive_unlink(unlink) 253 | printf("received unlink for seqnum %d\n", unlink.victim_seqnum) 254 | send_unlink_response(unlink, 0) -- status? 255 | end 256 | 257 | local function attached() 258 | print("starting configuration") 259 | end 260 | 261 | cfg.attached = attached 262 | cfg.receive_submit = receive_submit 263 | cfg.receive_unlink = receive_unlink 264 | emulator.start(cfg) 265 | 266 | -------------------------------------------------------------------------------- /examples/gamepads.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- MoonUSB example: gamepads.lua 3 | -- 4 | -- This example uses the hotplug and asynchronous IO APIs to handle concurrently one or 5 | -- more gamepads of a known type (vendor id + product id). 6 | -- It detects when a new device of the given type is plugged in, and then continuously 7 | -- retrieves reports from it and prints them, until the device is unplugged. 8 | -- Note that here we use hardcoded information, such as the address of the endpoint 9 | -- that sends the reports, or the report length. We know this information in advance 10 | -- because we limit ourself to a known product, otherwise we would have to learn it 11 | -- from the device descriptors. This would involve: 12 | -- 1) Getting the device and configuration descriptors (these have already been retrieved 13 | -- and cached by libusb during enumeration, so no transfer is needed). 14 | -- 2) Learning from them the needed information, including the length of the HID report 15 | -- descriptor. 16 | -- 3) Submitting a standard 'get descriptor' control transfer to retrieve the HID report 17 | -- descriptor. 18 | -- 4) Parsing the HID report descriptor to learn the length and meaning of the reports 19 | -- delivered (and/or expected) by the device. 20 | -- 21 | -- I wrote this example for a type of gamepad of which I happen to have a couple around 22 | -- (I'm not even a gamer, but fortunately there are some in my family :-). Hopefully, it 23 | -- should work also for similar gamepads from different vendors, by appropriately setting 24 | -- the upper-case variables that follow: 25 | local VENDOR_ID = 0x0079 -- DragonRise Inc. 26 | local PRODUCT_ID = 0x0006 -- PC TWIN SHOCK Gamepad 27 | local ITF_NO = 0 -- interface number 28 | local EP_INT_IN = 0x81 -- address of the 'interrupt in' endpoint that sends the reports 29 | local REPORT_LEN = 8 -- report length for this device 30 | 31 | local usb = require("moonusb") 32 | usb.lock_on_close(true) -- see issues #1 and #2 33 | 34 | -- usb.trace_objects(true) 35 | local ctx = usb.init() 36 | 37 | -- Table of known gamepad devices, indexed by the device object. 38 | -- The corresponding value is an integer id that we use here to discriminate between the 39 | -- gamepads currently attached. Whenever a device is plugged in, we assign it a new id. 40 | local known_devices = {} 41 | local id = 1 -- next id to assign 42 | 43 | local function P(dev, ...) 44 | -- Utility to print a formatted string, prepending the device's id. 45 | print("[" .. (known_devices[dev] or '?').."] ".. string.format(...)) 46 | end 47 | 48 | local function hex(bytes) 49 | -- Convert a binary string to a readable string of hexadecimal bytes (eg. "00 21 f3 54") 50 | local fmt = string.rep("B", #bytes) 51 | local t = { string.unpack(fmt, bytes) } 52 | t[#t] = nil -- this entry is the index of the next unread byte, we don't want it! 53 | for i, x in ipairs(t) do t[i] = string.format("%.2x", x) end 54 | return table.concat(t, " ") 55 | end 56 | 57 | local function start_retrieving_reports(dev, devhandle) 58 | -- Allocates memory for a report, and submits an interrupt transfer. 59 | -- By returning true at the end of the callback, the interrupt transfer will 60 | -- be resubmitted again and again. 61 | local mem = usb.malloc(devhandle, REPORT_LEN) 62 | devhandle:submit_interrupt_transfer(EP_INT_IN, mem:ptr(), REPORT_LEN, 2000, 63 | function(transfer, status) -- the callback 64 | if status ~= 'completed' then 65 | P(dev, "transfer status: "..status) 66 | mem:free() 67 | return false -- delete this transfer 68 | end 69 | -- Get and print the received report. A real application would instead 70 | -- interpret it (as per the HID report descriptor) and process the 71 | -- information it contains (axes movements, button presses, etc). 72 | local bytes = mem:read(0, REPORT_LEN) 73 | P(dev, "report %s", hex(bytes)) 74 | return true -- submit again 75 | end) 76 | end 77 | 78 | -- Hotplug callbacks 79 | 80 | local function attached(ctx, dev, event) 81 | local descr = dev:get_device_descriptor() 82 | if descr.vendor_id == VENDOR_ID and descr.product_id == PRODUCT_ID then 83 | known_devices[dev] = id 84 | id = id + 1 85 | P(dev, "Attached") 86 | local devhandle = dev:open() 87 | local itf = devhandle:claim_interface(ITF_NO) 88 | start_retrieving_reports(dev, devhandle) 89 | else -- we are not interested in this device, so we free the object 90 | dev:free() 91 | end 92 | end 93 | 94 | local function detached(ctx, dev, event) 95 | local descr = dev:get_device_descriptor() 96 | if known_devices[dev] then 97 | P(dev, "Detached") 98 | -- Delete the device object. This also gracefully deletes any related object 99 | -- that is still around (interface, devhandle, hostmem, transfers, and so on...) 100 | dev:free() 101 | known_devices[dev] = nil 102 | else -- we are not interested in this device, so we free the object 103 | dev:free() 104 | end 105 | end 106 | 107 | ctx:hotplug_register("attached", attached, true) 108 | ctx:hotplug_register("detached", detached) 109 | 110 | -- The event loop 111 | while true do 112 | ctx:handle_events(0) 113 | -- ... 114 | -- draw frames etc 115 | -- ... 116 | end 117 | 118 | -------------------------------------------------------------------------------- /examples/hello.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- MoonUSB example: hello.lua 3 | local usb = require("moonusb") 4 | 5 | local ctx = usb.init() 6 | 7 | local devices = ctx:get_device_list() 8 | 9 | for i, dev in ipairs(devices) do 10 | local descr = dev:get_device_descriptor() 11 | local devhandle = dev:open() 12 | print(string.format("USB %s - bus:%d port:%d %.4x:%.4x %s %s (%s)", 13 | descr.usb_version, dev:get_bus_number(), dev:get_port_number(), 14 | descr.vendor_id, descr.product_id, 15 | devhandle:get_string_descriptor(descr.manufacturer_index) or "???", 16 | devhandle:get_string_descriptor(descr.product_index) or "???", 17 | descr.class)) 18 | devhandle:close() 19 | dev:free() 20 | end 21 | 22 | -------------------------------------------------------------------------------- /examples/hotplug.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- MoonUSB example: hotplug.lua 3 | local usb = require("moonusb") 4 | 5 | --usb.trace_objects(true) 6 | local ctx = usb.init() 7 | 8 | local devices = ctx:get_device_list() 9 | 10 | local function cb_plugged(ctx, device, event) 11 | local descr = device:get_device_descriptor() 12 | print("device", device, event) 13 | print(string.format("USB %s - bus:%d port:%d %.4x:%.4x (%s)", 14 | descr.usb_version, device:get_bus_number(), device:get_port_number(), 15 | descr.vendor_id, descr.product_id, descr.class)) 16 | device:free() 17 | end 18 | 19 | local function cb_unplugged(ctx, device, event) 20 | print("device", device, event) 21 | device:free() -- free any device you are not interested in keeping around! 22 | end 23 | 24 | local hp = ctx:hotplug_register("attached", cb_plugged, true) 25 | local hp = ctx:hotplug_register("detached", cb_unplugged) 26 | 27 | while true do 28 | ctx:handle_events(0) 29 | end 30 | 31 | -------------------------------------------------------------------------------- /examples/malloc.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- MoonUSB example: malloc.lua 3 | 4 | local usb = require("moonusb") 5 | 6 | usb.trace_objects(true) -- trace creation/deletion of objects 7 | local ctx = usb.init() 8 | 9 | -- We need a device to try out dma memory allocation, so replace these values 10 | -- with those from one of your devices: 11 | local vendor_id, product_id = 0x046d, 0xc534 12 | 13 | local device, devhandle = ctx:open_device(vendor_id, product_id) 14 | local mem1 = usb.malloc(nil, 256) -- this allocates normal host memory 15 | local mem2 = usb.malloc(devhandle, 256) -- this attempts to allocate dma memory 16 | print("1 - closing the device") 17 | devhandle:close() -- mem2 is released with the devhandle closure 18 | print("2 - exiting") 19 | -- mem1 is automatically released at exit, unless one calls mem1:free() on it 20 | 21 | -------------------------------------------------------------------------------- /examples/open_short.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- MoonUSB example: open_short.lua 3 | 4 | local usb = require("moonusb") 5 | 6 | usb.trace_objects(true) 7 | local ctx = usb.init() 8 | 9 | -- replace this with values from one of your devices: 10 | local vendor_id, product_id = 0x1d6b, 0x0002 11 | 12 | local device, devhandle = ctx:open_device(vendor_id, product_id) 13 | 14 | -------------------------------------------------------------------------------- /examples/setup.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- MoonUSB example: setup.lua 3 | -- Encoding/decoding control setup structures 4 | 5 | local usb = require("moonusb") 6 | local ctx = usb.init() 7 | 8 | -- Allocate memory where to write/read the setup struct. 9 | -- This struct is 8 bytes long, so we need at least that space, 10 | -- plus the space for data to be transferred. 11 | local mem = usb.malloc(nil, 256) 12 | 13 | local function test(setup) 14 | usb.encode_control_setup(mem:ptr(), setup) 15 | local setup1 = usb.decode_control_setup(mem:ptr()) 16 | print("--------------") 17 | for k, v in pairs(setup1) do 18 | print(k, v) 19 | -- check that the value is the same as the original 20 | assert(v == setup[k]) 21 | end 22 | local bytes = mem:read(0, 8, 'uchar') 23 | for i, b in ipairs(bytes) do bytes[i] = string.format("%.2x", b) end 24 | print("hex", table.concat(bytes, ' ')) 25 | end 26 | 27 | local standard_setup = { 28 | request_recipient = 'interface', 29 | request_type = 'standard', 30 | direction = 'in', 31 | request = 'get descriptor', -- we must use string literals here, since it's a standard request 32 | value = 123, 33 | index = 4, 34 | length = 128, 35 | } 36 | 37 | local vendor_setup = { 38 | request_recipient = 'other', 39 | request_type = 'vendor', 40 | direction = 'out', 41 | request = 0xf1, -- since this is not a standard request, this field is numeric (a byte) 42 | value = 123, 43 | index = 4, 44 | length = 128, 45 | } 46 | 47 | test(standard_setup) 48 | test(vendor_setup) 49 | 50 | mem:free() 51 | 52 | print("--------------") 53 | -- Encoding/decoding to/from binary string 54 | local b = string.pack('I1I1I1I1I1I1I1I1', 0x81, 0x06, 0x00, 0x22, 0x00, 0x00, 0x34, 0x00) 55 | local s = usb.decode_control_setup(b) 56 | for k,v in pairs(s) do print(k, v) end 57 | local b1 = usb.encode_control_setup(nil, s) 58 | assert(b1 == b) 59 | -------------------------------------------------------------------------------- /examples/version.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- MoonUSB example: version.lua 3 | 4 | local usb = require("moonusb") 5 | 6 | print(_VERSION) 7 | print(usb._VERSION) 8 | print(usb._LIBUSB_VERSION) 9 | print(usb.get_version()) 10 | 11 | usb.setlocale("en") 12 | 13 | print("Supported capabilities:") 14 | for _, cap in ipairs(usb.enum("capability")) do 15 | local has = usb.has_capability(cap) 16 | print(cap, has and "yes" or "no") 17 | end 18 | 19 | local ctx = usb.init() 20 | usb.set_option(ctx, "log level", "warning") 21 | ctx:exit() 22 | 23 | -------------------------------------------------------------------------------- /moonusb/bosdescriptors.lua: -------------------------------------------------------------------------------- 1 | -- The MIT License (MIT) 2 | -- 3 | -- Copyright (c) 2021 Stefano Trettel 4 | -- 5 | -- Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | -- 7 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 8 | -- of this software and associated documentation files (the "Software"), to deal 9 | -- in the Software without restriction, including without limitation the rights 10 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | -- copies of the Software, and to permit persons to whom the Software is 12 | -- furnished to do so, subject to the following conditions: 13 | -- 14 | -- The above copyright notice and this permission notice shall be included in all 15 | -- copies or substantial portions of the Software. 16 | -- 17 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | -- SOFTWARE. 24 | -- 25 | 26 | -- ********************************************************************* 27 | -- DO NOT require() THIS MODULE (it is loaded automatically by MoonUSB) 28 | -- ********************************************************************* 29 | do 30 | local usb = moonusb -- require("moonusb") 31 | 32 | --[[@@ TODO? 33 | usb.decode_bos_device_capability_descriptor = function(dev_cap_type, data) 34 | -- Decodes a BOS device capability descriptor 35 | -- See USB 3.2 Specification, table 9.13: 36 | -- dev_cap_type = bDevCapabilityType 37 | -- data = Capability-Dependent data 38 | local descr = {} 39 | 40 | return descr 41 | end 42 | --]] 43 | 44 | end 45 | -------------------------------------------------------------------------------- /moonusb/emulator.lua: -------------------------------------------------------------------------------- 1 | -- The MIT License (MIT) 2 | -- 3 | -- Copyright (c) 2021 Stefano Trettel 4 | -- 5 | -- Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | -- 7 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 8 | -- of this software and associated documentation files (the "Software"), to deal 9 | -- in the Software without restriction, including without limitation the rights 10 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | -- copies of the Software, and to permit persons to whom the Software is 12 | -- furnished to do so, subject to the following conditions: 13 | -- 14 | -- The above copyright notice and this permission notice shall be included in all 15 | -- copies or substantial portions of the Software. 16 | -- 17 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | -- SOFTWARE. 24 | 25 | --============================================================================= 26 | -- USB DEVICE EMULATOR -- moonusb.emulator.lua 27 | --============================================================================= 28 | -- Emulates a USB device by implementing a USB/IP server exporting a fake device. 29 | 30 | local socket = require("socket") 31 | local usb = require("moonusb") -- for utilities only 32 | local has_timers, timers = pcall(require, "moontimers") -- optional 33 | 34 | -- Utilities 35 | local fmt = string.format 36 | local function printf(...) io.write(fmt(...)) end 37 | local rep, pack, unpack = string.rep, string.pack, string.unpack 38 | local doubleface = usb.doubleface 39 | local hex, bcd2str, str2bcd = usb.hex, usb.bcd2str, usb.str2bcd 40 | local zeropad, packbytes, unpackbytes = usb.zeropad, usb.packbytes, usb.unpackbytes 41 | 42 | local client -- the currently connected client 43 | 44 | -- Configurable parameters 45 | local IP, PORT, USBIP_VER 46 | local ATTACHED, DETACHED, RECEIVE_SUBMIT, RECEIVE_UNLINK -- user callbacks 47 | local VENDOR_ID, PRODUCT_ID, RELEASE_NUMBER 48 | local PATH, SPEED, BUSNUM, DEVNUM, BUSID, DEVID 49 | local DEVICE_CLASS, DEVICE_SUBCLASS, DEVICE_PROTOCOL 50 | local NUM_CONFIGURATIONS, CONFIGURATION_VALUE, INTERFACES 51 | 52 | -- usbip opcodes 53 | local OP_REQ_DEVLIST = 0x8005 54 | local OP_REP_DEVLIST = 0x0005 55 | local OP_REQ_IMPORT = 0x8003 56 | local OP_REP_IMPORT = 0x0003 57 | local USBIP_CMD_SUBMIT = 0x00000001 58 | local USBIP_CMD_UNLINK = 0x00000002 59 | local USBIP_RET_SUBMIT = 0x00000003 60 | local USBIP_RET_UNLINK = 0x00000004 61 | 62 | local USB_DIR = doubleface({ 63 | ['out'] = 0, 64 | ['in'] = 1, 65 | }) 66 | 67 | local USB_CLASS = doubleface({ 68 | ['per interface'] = 0x00, 69 | ['audio'] = 0x01, 70 | ['cdc'] = 0x02, 71 | ['hid'] = 0x03, 72 | ['physical'] = 0x05, 73 | ['image'] = 0x06, 74 | ['printer'] = 0x07, 75 | ['mass storage'] = 0x08, 76 | ['hub'] = 0x09, 77 | ['cdc data'] = 0x0a, 78 | ['smart card'] = 0x0b, 79 | ['content security'] = 0x0d, 80 | ['video'] = 0x0e, 81 | ['personal healthcare'] = 0x0f, 82 | ['audio video'] = 0x10, 83 | ['billboard'] = 0x11, 84 | ['type c bridge'] = 0x12, 85 | ['diagnostic'] = 0xdc, 86 | ['wireless'] = 0xe0, 87 | ['miscellaneous'] = 0xef, 88 | ['application specific'] = 0xfe, 89 | ['vendor specific'] = 0xff, 90 | }) 91 | 92 | local USB_SPEED = doubleface({ 93 | ['unknown'] = 0, 94 | ['low'] = 1, 95 | ['full'] = 2, 96 | ['high'] = 3, 97 | ['super'] = 4, 98 | ['super plus'] = 5, 99 | }) 100 | 101 | -- USBIP protocol -------------------------------------------------------------- 102 | 103 | local function send_submit_response(submit, status, error_count, data) 104 | -- Send a USBIP_RET_SUBMIT response. 105 | -- submit: the unmodified submit received via the receive_submit() callback, 106 | -- status: 0 for success, non-zero for error @@ codes from ? 107 | -- error_count: integer 108 | -- data: binary string containing the URB response, or nil if none 109 | local t = {} 110 | -- truncate data if too long to fit (the driver will repeat the 111 | -- submit request with the appropriate length) 112 | if data and #data > submit.transfer_buffer_length then 113 | data = data:sub(1, submit.transfer_buffer_length) 114 | end 115 | t[#t+1] = pack(">I4", USBIP_RET_SUBMIT) 116 | t[#t+1] = pack(">I4", submit.seqnum) 117 | t[#t+1] = pack(">I4", submit.devid) 118 | t[#t+1] = pack(">I4", USB_DIR[submit.direction]) 119 | t[#t+1] = pack(">I4", submit.ep) 120 | t[#t+1] = pack(">i4", status or 0) 121 | t[#t+1] = pack(">I4", data and #data or 0) -- actual_length 122 | t[#t+1] = pack(">I4", submit.start_frame) 123 | t[#t+1] = pack(">I4", submit.number_of_packets) 124 | t[#t+1] = pack(">I4", error_count or 0) 125 | t[#t+1] = zeropad(8) -- setup 126 | t[#t+1] = data 127 | client:send(table.concat(t)) 128 | end 129 | 130 | local function send_unlink_response(unlink, status) 131 | -- Send a USBIP_RET_UNLINK response. 132 | -- unlink: the unmodified unlink received via the receive_unlink() callback, 133 | -- status: 0 for success, non-zero for error @@ codes from ? 134 | local t = {} 135 | t[#t+1] = pack(">I4", USBIP_RET_UNLINK) 136 | t[#t+1] = pack(">I4", unlink.victim_seqnum) 137 | t[#t+1] = pack(">I4", unlink.devid) 138 | t[#t+1] = pack(">I4", USB_DIR[unlink.direction]) 139 | t[#t+1] = pack(">I4", unlink.ep) 140 | t[#t+1] = pack(">i4", status or 0) 141 | t[#t+1] = zeropad(24) 142 | client:send(table.concat(t)) 143 | end 144 | 145 | local function send_devlist_response() 146 | -- Sends an OP_REP_DEVLIST response to an OP_REQ_DEVLIST. 147 | local t = {} 148 | t[#t+1] = pack(">I2I2I4I4", USBIP_VER, OP_REP_DEVLIST, 0, 1) -- 1 device only 149 | t[#t+1] = PATH 150 | t[#t+1] = BUSID 151 | t[#t+1] = pack(">I4", BUSNUM) 152 | t[#t+1] = pack(">I4", DEVNUM) 153 | t[#t+1] = pack(">I4", SPEED) 154 | t[#t+1] = pack(">I2I2", VENDOR_ID, PRODUCT_ID) 155 | t[#t+1] = pack(">I2", RELEASE_NUMBER) 156 | t[#t+1] = pack("I1", USB_CLASS[DEVICE_CLASS]) 157 | t[#t+1] = pack("I1", DEVICE_SUBCLASS) 158 | t[#t+1] = pack("I1", DEVICE_PROTOCOL) 159 | t[#t+1] = pack("I1", CONFIGURATION_VALUE) 160 | t[#t+1] = pack("I1", NUM_CONFIGURATIONS) 161 | t[#t+1] = pack("I1", #INTERFACES) 162 | for _, itf in ipairs(INTERFACES) do 163 | t[#t+1] = pack("I1I1I1I1", USB_CLASS[itf.class], itf.subclass or 0, itf.protocol or 0, 0) 164 | end 165 | client:send(table.concat(t)) 166 | end 167 | 168 | local function send_import_response(busid) 169 | -- Sends an OP_REP_IMPORT response to an OP_REQ_IMPORT for busid. 170 | -- Returns true if the request can be accepted, false otherwise. 171 | local t = {} 172 | t[#t+1] = pack(">I2I2", USBIP_VER, OP_REP_IMPORT) 173 | if busid ~= BUSID then 174 | t[#t+1] = pack(">I4", 1) -- status = not ok 175 | client:send(table.concat(t)) 176 | return false 177 | end 178 | t[#t+1] = pack(">I4", 0) -- status = ok 179 | t[#t+1] = PATH 180 | t[#t+1] = BUSID 181 | t[#t+1] = pack(">I4", BUSNUM) 182 | t[#t+1] = pack(">I4", DEVNUM) 183 | t[#t+1] = pack(">I4", SPEED) 184 | t[#t+1] = pack(">I2I2", VENDOR_ID, PRODUCT_ID) 185 | t[#t+1] = pack(">I2", RELEASE_NUMBER) 186 | t[#t+1] = pack("I1", USB_CLASS[DEVICE_CLASS]) 187 | t[#t+1] = pack("I1", DEVICE_SUBCLASS) 188 | t[#t+1] = pack("I1", DEVICE_PROTOCOL) 189 | t[#t+1] = pack("I1", CONFIGURATION_VALUE) 190 | t[#t+1] = pack("I1", NUM_CONFIGURATIONS) 191 | t[#t+1] = pack("I1", #INTERFACES) 192 | client:send(table.concat(t)) 193 | return true 194 | end 195 | 196 | local function receive_cmd() 197 | -- Receives a command (in attached state), and handles it to the user. 198 | local hdr = client:receive(48) 199 | if not hdr then return false end 200 | local cmd = unpack(">I4", hdr) 201 | if cmd == USBIP_CMD_SUBMIT then 202 | local submit = {} 203 | submit.seqnum = unpack(">I4", hdr, 5) 204 | submit.devid = unpack(">I4", hdr, 9) 205 | submit.direction = USB_DIR[unpack(">I4", hdr, 13)] 206 | submit.ep = unpack(">I4", hdr, 17) 207 | submit.transfer_flags = unpack(">I4", hdr, 21) 208 | submit.transfer_buffer_length = unpack(">I4", hdr, 25) 209 | submit.start_frame = unpack(">I4", hdr, 29) 210 | submit.number_of_packets = unpack(">I4", hdr, 33) 211 | submit.interval = unpack(">I4", hdr, 37) 212 | submit.setup = unpack("c8", hdr, 41) 213 | local len = submit.transfer_buffer_length 214 | if submit.direction == 'out' and len > 0 then 215 | submit.data = client:receive(len) 216 | if not submit.data then return false end 217 | end 218 | RECEIVE_SUBMIT(submit) 219 | return true 220 | elseif cmd == USBIP_CMD_UNLINK then 221 | local unlink = {} 222 | unlink.seqnum = unpack(">I4", hdr, 5) 223 | unlink.devid = unpack(">I4", hdr, 9) 224 | unlink.direction = USB_DIR[unpack(">I4", hdr, 13)] 225 | unlink.ep = unpack(">I4", hdr, 17) 226 | unlink.victim_seqnum = unpack(">I4", hdr, 21) 227 | RECEIVE_UNLINK(unlink) 228 | return true 229 | else 230 | printf("received unknown cmd=0x%.8x\n", cmd) 231 | return false 232 | end 233 | end 234 | 235 | local function receive_op() 236 | local hdr = client:receive(8) 237 | if not hdr then return false end 238 | local ver, op = unpack(">I2I2", hdr) 239 | if op == OP_REQ_DEVLIST then 240 | send_devlist_response() 241 | return false 242 | elseif op == OP_REQ_IMPORT then 243 | local busid = client:receive(32) 244 | if not busid then return false end 245 | return send_import_response(busid) 246 | else 247 | printf("received unknown op=0x%.4x\n", op) 248 | return false 249 | end 250 | end 251 | 252 | local function start(cfg) 253 | -- Configure the module and start operations. 254 | USBIP_VER = str2bcd(cfg.usbip_ver or "01.11") 255 | IP = cfg.ip or 'localhost' 256 | PORT = cfg.port or 3240 257 | BUSNUM = cfg.busnum or 1 258 | DEVNUM = cfg.devnum or 1 259 | PATH = pack("c256", cfg.path or "moonusb emulated device") 260 | VENDOR_ID = cfg.vendor_id or 0x0000 261 | PRODUCT_ID = cfg.product_id or 0x0000 262 | RELEASE_NUMBER = str2bcd(cfg.release_number or "00.00") 263 | SPEED = USB_SPEED[cfg.speed or 'high'] 264 | DEVICE_CLASS = cfg.device_class or 'per interface' 265 | DEVICE_SUBCLASS = cfg.device_subclass or 0 266 | DEVICE_PROTOCOL = cfg.device_protocol or 0 267 | CONFIGURATION_VALUE = cfg.configuration_value or 1 268 | NUM_CONFIGURATIONS = cfg.num_configurations or 1 269 | INTERFACES = cfg.interfaces or {} 270 | ATTACHED = cfg.attached or function() end 271 | DETACHED = cfg.detached or function() end 272 | RECEIVE_SUBMIT = cfg.receive_submit 273 | RECEIVE_UNLINK = cfg.receive_unlink 274 | DEVID = (BUSNUM << 16 | DEVNUM) -- see usbip_common.h in the Linux kernel 275 | BUSID = pack("c32", BUSNUM.."-"..DEVNUM) 276 | -- Create server socket and start listening for client connections 277 | printf("starting moonusb device emulator on ip=%s:%d\n", IP, PORT) 278 | local server = assert(socket.bind(IP, PORT)) 279 | assert(server:setoption('reuseaddr', true)) 280 | while true do 281 | client = assert(server:accept()) 282 | local ip, port = client:getpeername() 283 | printf("client %s:%d connected\n", ip, port) 284 | local attached = receive_op() 285 | if attached then -- keep the connection up for submit and unlink commands 286 | printf("device attached\n") 287 | ATTACHED() -- notify the user 288 | local recvt, r = { client }, nil 289 | while true do 290 | if has_timers then timers.trigger() end 291 | r = socket.select(recvt, nil, 0) 292 | if r and r[client] then 293 | if not receive_cmd() then break end 294 | end 295 | end 296 | printf("device detached\n") 297 | DETACHED() -- notify the user 298 | end 299 | client:close() 300 | printf("client %s:%d disconnected\n", ip, port) 301 | end 302 | server:close() 303 | end 304 | 305 | return { 306 | start = start, 307 | send_submit_response = send_submit_response, 308 | send_unlink_response = send_unlink_response, 309 | } 310 | 311 | -------------------------------------------------------------------------------- /moonusb/utils.lua: -------------------------------------------------------------------------------- 1 | -- The MIT License (MIT) 2 | -- 3 | -- Copyright (c) 2021 Stefano Trettel 4 | -- 5 | -- Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | -- 7 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 8 | -- of this software and associated documentation files (the "Software"), to deal 9 | -- in the Software without restriction, including without limitation the rights 10 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | -- copies of the Software, and to permit persons to whom the Software is 12 | -- furnished to do so, subject to the following conditions: 13 | -- 14 | -- The above copyright notice and this permission notice shall be included in all 15 | -- copies or substantial portions of the Software. 16 | -- 17 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | -- SOFTWARE. 24 | -- 25 | 26 | -- ********************************************************************* 27 | -- DO NOT require() THIS MODULE (it is loaded automatically by MoonUSB) 28 | -- ********************************************************************* 29 | 30 | -- Assorted utilties for data handling and logs 31 | 32 | do 33 | local usb = moonusb -- require("moonusb") 34 | local fmt = string.format 35 | local rep, pack, unpack = string.rep, string.pack, string.unpack 36 | local table_unpack, table_concat = table.unpack, table.concat 37 | 38 | usb.hex = function(bytes) 39 | -- Convert a binary string to a readable string of hexadecimal bytes (eg. "00 21 f3 54") 40 | local t = { unpack(rep("B", #bytes), bytes) } 41 | t[#t] = nil -- this entry is the index of the next unread byte, we don't want it! 42 | for i, x in ipairs(t) do t[i] = fmt("%.2x", x) end 43 | return table_concat(t, " ") 44 | end 45 | 46 | usb.bcd2str = function(x) -- e.g. 0x1234 --> "12.34" 47 | return fmt("%d%d.%d%d", (x>>12)&0x0f, (x>>8)&0x0f, (x>>4)&0xf, x&0xf) 48 | end 49 | 50 | usb.str2bcd = function(s) -- e.g. "12.34" --> 0x1234 51 | local function f(n) return tonumber(s:sub(n, n)) end 52 | return (f(1)<<12)|(f(2)<<8)|(f(4)<<4)|f(5) 53 | end 54 | 55 | local ZERO = pack("I1", 0) 56 | usb.zeropad = function(n) 57 | -- returns a binary string of length n filled with zeros 58 | return rep(ZERO, n) 59 | end 60 | 61 | -- pack/unpack an array of bytes 62 | usb.packbytes = function(t) return pack(rep("I1", #t), table_unpack(t)) end 63 | usb.unpackbytes = function(s) 64 | local t = {unpack(rep("I1", #s), s)} 65 | t[#t]=nil -- the last entry is an index in the string, not a byte value 66 | return t 67 | end 68 | 69 | usb.doubleface = function(t) 70 | -- Creates a double-keyed version of the table t, 71 | -- i.e. one where if t[k]=v, then also t[v]=k 72 | local tt = {} 73 | for k, v in pairs(t) do 74 | if tt[v] then 75 | error(fmt("duplicated value %s %s", tostring(k), tostring(v))) 76 | end 77 | tt[k]=v 78 | tt[v]=k 79 | end 80 | return tt 81 | end 82 | 83 | end 84 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | 2 | ifdef MINGW_PREFIX 3 | MINGW=1 4 | else 5 | LINUX=1 6 | endif 7 | 8 | # Lua version 9 | LUAVER?=$(shell lua -e 'print(string.match(_VERSION, "%d+%.%d+") or "5.3")') 10 | ifeq ($(LUAVER),) 11 | # lua-interpreter not found 12 | LUAVER=5.3 13 | endif 14 | 15 | # Base install directory 16 | ifdef LINUX 17 | PREFIX?=/usr/local 18 | endif 19 | ifdef MINGW 20 | PREFIX?=$(MINGW_PREFIX) 21 | endif 22 | 23 | # Directory where to install Lua modules 24 | L_DIR=$(PREFIX)/share/lua/$(LUAVER) 25 | # Directory where to install Lua C modules 26 | C_DIR=$(PREFIX)/lib/lua/$(LUAVER) 27 | # Directory where to install C headers 28 | H_DIR=$(PREFIX)/include 29 | # Directory where to install C libraries 30 | S_DIR=$(PREFIX)/lib 31 | 32 | ifeq ($(D),1) 33 | DEBUG=1 34 | endif 35 | 36 | ifdef LINUX 37 | LIBS = -lusb-1.0 38 | endif 39 | ifdef MINGW 40 | LIBS = -lusb-1.0 -llua 41 | endif 42 | 43 | Tgt := moonusb 44 | Src := $(wildcard *.c) 45 | Objs := $(Src:.c=.o) 46 | 47 | INCDIR = -I. -I/usr/include/lua$(LUAVER) 48 | 49 | COPT += -O2 50 | #COPT += -O0 -g 51 | #COPT += -m32 52 | COPT += -Wfatal-errors 53 | COPT += -Wall -Wextra -Wpedantic 54 | COPT += -DCOMPAT53_PREFIX=moonusb_compat_ 55 | COPT += -std=gnu99 56 | COPT += -DLUAVER=$(LUAVER) 57 | ifdef LINUX 58 | COPT += -fpic 59 | COPT += -DLINUX 60 | endif 61 | ifdef MINGW 62 | COPT += -DMINGW 63 | endif 64 | ifdef DEBUG 65 | COPT += -DDEBUG 66 | COPT += -Wshadow -Wsign-compare -Wundef -Wwrite-strings 67 | COPT += -Wdisabled-optimization -Wdeclaration-after-statement 68 | COPT += -Wmissing-prototypes -Wstrict-prototypes -Wnested-externs 69 | COPT += -Wold-style-definition 70 | #COPT += -Wc++-compat 71 | endif 72 | 73 | override CFLAGS = $(COPT) $(INCDIR) 74 | 75 | default: build 76 | 77 | where: 78 | @echo "PREFIX="$(PREFIX) 79 | @echo "LUAVER="$(LUAVER) 80 | @echo $(L_DIR) 81 | @echo $(C_DIR) 82 | @echo $(H_DIR) 83 | @echo $(S_DIR) 84 | 85 | clean: 86 | @-rm -f *.so *.dll *.o *.err *.map *.S *~ *.log 87 | @-rm -f $(Tgt).symbols 88 | 89 | install: 90 | @-mkdir -pv $(H_DIR) 91 | @-mkdir -pv $(C_DIR) 92 | @-mkdir -pv $(S_DIR) 93 | @-mkdir -pv $(L_DIR) 94 | @-cp -fpv $(Tgt).h $(H_DIR) 95 | @-cp -fpvr ../$(Tgt) $(L_DIR) 96 | ifdef LINUX 97 | @-cp -fpv $(Tgt).so $(C_DIR) 98 | @-ln -fsv $(C_DIR)/$(Tgt).so $(S_DIR)/lib$(Tgt).so 99 | endif 100 | ifdef MINGW 101 | @-cp -fpv $(Tgt).dll $(C_DIR) 102 | endif 103 | 104 | uninstall: 105 | @-rm -f $(H_DIR)/$(Tgt).h 106 | @-rm -f $(C_DIR)/$(Tgt).so 107 | @-rm -f $(S_DIR)/lib$(Tgt).so 108 | @-rm -fr $(L_DIR)/$(Tgt) 109 | @-rm -f $(C_DIR)/$(Tgt).dll 110 | 111 | build: clean $(Tgt) 112 | 113 | symbols: build 114 | @objdump -T $(Tgt).so > $(Tgt).symbols 115 | 116 | $(Tgt): $(Objs) 117 | ifdef LINUX 118 | @$(CXX) -shared -o $(Tgt).so $(Objs) $(LIBDIR) $(LIBS) 119 | endif 120 | ifdef MINGW 121 | @-$(CXX) -shared -o $(Tgt).dll $(Objs) $(LIBDIR) $(LIBS) 122 | endif 123 | @-rm -f $(Objs) 124 | @echo 125 | 126 | -------------------------------------------------------------------------------- /src/_make: -------------------------------------------------------------------------------- 1 | sudo make LUAVER=5.4 uninstall && make LUAVER=5.4 D=$1 && sudo make LUAVER=5.4 install 2 | -------------------------------------------------------------------------------- /src/context.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2021 Stefano Trettel 4 | * 5 | * Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #include "internal.h" 27 | 28 | static int freecontext(lua_State *L, ud_t *ud) 29 | { 30 | context_t *context = (context_t*)ud->handle; 31 | freechildren(L, HOTPLUG_MT, ud); 32 | freechildren(L, DEVICE_MT, ud); 33 | if(!freeuserdata(L, ud, "context")) return 0; 34 | libusb_exit(context); 35 | return 0; 36 | } 37 | 38 | static int Create(lua_State *L) 39 | { 40 | ud_t *ud; 41 | context_t *context; 42 | int ec = libusb_init(&context); 43 | CheckError(L, ec); 44 | ud = newuserdata(L, context, CONTEXT_MT, "context"); 45 | ud->parent_ud = NULL; 46 | ud->destructor = freecontext; 47 | return 1; 48 | } 49 | 50 | static int Set_option(lua_State *L) 51 | { 52 | int level, ec=0; 53 | context_t *context; 54 | int option = checkoption(L, 2); 55 | switch(option) 56 | { 57 | case LIBUSB_OPTION_LOG_LEVEL: 58 | context = checkcontext(L, 1, NULL); 59 | level = checkloglevel(L, 3); 60 | ec = libusb_set_option(context, option, level); 61 | break; 62 | case LIBUSB_OPTION_USE_USBDK: 63 | context = checkcontext(L, 1, NULL); 64 | ec = libusb_set_option(context, option); 65 | break; 66 | case LIBUSB_OPTION_WEAK_AUTHORITY: 67 | ec = libusb_set_option(NULL, option); 68 | break; 69 | default: 70 | return unexpected(L); 71 | } 72 | CheckError(L, ec); 73 | return 0; 74 | } 75 | 76 | static int log_cb_ref = LUA_NOREF; /* reference for global log cb */ 77 | static void LogCallback(context_t *context, enum libusb_log_level level, const char *str) 78 | { 79 | #define L moonusb_L 80 | ud_t *ud; 81 | int top = lua_gettop(L); 82 | if(context) /* context callback */ 83 | { 84 | ud = userdata(context); 85 | if(!ud) { unexpected(L); return; } 86 | lua_rawgeti(L, LUA_REGISTRYINDEX, ud->ref1); 87 | pushcontext(L, context); 88 | } 89 | else /* global callback */ 90 | { 91 | lua_rawgeti(L, LUA_REGISTRYINDEX, log_cb_ref); 92 | lua_pushnil(L); 93 | } 94 | pushloglevel(L, level); 95 | lua_pushstring(L, str); 96 | if(lua_pcall(L, 3, 0, 0) != LUA_OK) 97 | { lua_error(L); return; } 98 | lua_settop(L, top); 99 | return; 100 | #undef L 101 | } 102 | 103 | static int Set_log_cb(lua_State *L) 104 | { 105 | ud_t *ud; 106 | int mode; 107 | context_t *context = optcontext(L, 1, &ud); 108 | if(!lua_isfunction(L, 2)) 109 | { return argerror(L, 2, ERR_FUNCTION); } 110 | if(context) 111 | { 112 | mode = LIBUSB_LOG_CB_CONTEXT; 113 | Reference(L, 2, ud->ref1); 114 | } 115 | else 116 | { 117 | mode = LIBUSB_LOG_CB_GLOBAL; 118 | Reference(L, 2, log_cb_ref); 119 | } 120 | libusb_set_log_cb(context, LogCallback, mode); 121 | return 0; 122 | } 123 | 124 | static int Get_device_list(lua_State *L) 125 | { 126 | device_t **list; 127 | context_t *context = checkcontext(L, 1, NULL); 128 | ssize_t i, n = libusb_get_device_list(context, &list); 129 | if(n < 0) CheckError(L, n); 130 | lua_newtable(L); 131 | for(i=0; ihandle; 33 | context_t *context = ud->context; 34 | freechildren(L, HOSTMEM_MT, ud); 35 | freechildren(L, TRANSFER_MT, ud); 36 | freechildren(L, INTERFACE_MT, ud); 37 | if(!freeuserdata(L, ud, "devhandle")) return 0; 38 | if(lock_on_close) 39 | { 40 | libusb_lock_events(context); 41 | libusb_close(devhandle); 42 | libusb_unlock_events(context); 43 | } 44 | else 45 | libusb_close(devhandle); 46 | return 0; 47 | } 48 | 49 | int newdevhandle(lua_State *L, device_t *device, devhandle_t *devhandle) 50 | { 51 | ud_t *ud; 52 | ud = newuserdata(L, devhandle, DEVHANDLE_MT, "devhandle"); 53 | ud->parent_ud = userdata(device); 54 | ud->context = userdata(device)->context; 55 | ud->destructor = freedevhandle; 56 | // Automatically detach the kernel driver when an interface is claimed, 57 | // and re-attach it when the interface is released (only relevant on linux): 58 | (void)libusb_set_auto_detach_kernel_driver(devhandle, 1); 59 | return 1; 60 | } 61 | 62 | static int ClaimInterface(lua_State *L) 63 | { 64 | devhandle_t *devhandle = checkdevhandle(L, 1, NULL); 65 | int interface_number = luaL_checkinteger(L, 2); 66 | newinterface(L, devhandle, interface_number); 67 | return 1; 68 | } 69 | 70 | static int Get_descriptor(lua_State *L) 71 | { 72 | int ec; 73 | char *ptr=NULL; 74 | devhandle_t *devhandle = checkdevhandle(L, 1, NULL); 75 | uint8_t desc_type = luaL_checkinteger(L, 2); /* libusb_descriptor_type */ 76 | uint8_t desc_index = luaL_checkinteger(L, 3); 77 | char *buf = (char*)optlightuserdata(L, 4); 78 | size_t len = luaL_checkinteger(L, 5); 79 | if(len == 0) 80 | return argerror(L, 5, ERR_VALUE); 81 | if(!buf) 82 | { 83 | ptr = Malloc(L, len*sizeof(char)); 84 | buf = ptr; 85 | } 86 | ec = libusb_get_descriptor(devhandle, desc_type, desc_index, (unsigned char*)ptr, (int)len); 87 | if(ec<0) 88 | { Free(L, ptr); CheckError(L, ec); } 89 | lua_pushlstring(L, buf, ec); // ec = no. of bytes received 90 | Free(L, ptr); 91 | return 1; 92 | } 93 | 94 | static int Get_string_descriptor(lua_State *L) 95 | { 96 | #define maxlen 256 /* the length field of the descriptor is 8 bits long */ 97 | int ec; 98 | uint16_t langid; 99 | unsigned char data[maxlen]; 100 | devhandle_t *devhandle = checkdevhandle(L, 1, NULL); 101 | uint8_t index = luaL_checkinteger(L, 2); 102 | if(index==0) 103 | { lua_pushnil(L); return 1; } 104 | if(lua_isnoneornil(L, 3)) 105 | ec = libusb_get_string_descriptor_ascii(devhandle, index, data, maxlen); 106 | else 107 | { 108 | langid = luaL_checkinteger(L, 3); 109 | ec = libusb_get_string_descriptor(devhandle, index, langid, data, maxlen); 110 | } 111 | if(ec<0) CheckError(L, ec); 112 | lua_pushlstring(L, (char*)data, ec); 113 | return 1; 114 | #undef maxlen 115 | } 116 | 117 | static int Get_configuration(lua_State *L) 118 | { 119 | int value; 120 | devhandle_t *devhandle = checkdevhandle(L, 1, NULL); 121 | int ec = libusb_get_configuration(devhandle, &value); 122 | CheckError(L, ec); 123 | lua_pushinteger(L, value); 124 | return 1; 125 | } 126 | 127 | static int Set_configuration(lua_State *L) 128 | { 129 | devhandle_t *devhandle = checkdevhandle(L, 1, NULL); 130 | int value = luaL_checkinteger(L, 2); 131 | int ec = libusb_set_configuration(devhandle, value); 132 | CheckError(L, ec); 133 | return 0; 134 | } 135 | 136 | static int Clear_halt(lua_State *L) 137 | { 138 | devhandle_t *devhandle = checkdevhandle(L, 1, NULL); 139 | unsigned char endpoint = luaL_checkinteger(L, 2); 140 | int ec = libusb_clear_halt(devhandle, endpoint); 141 | CheckError(L, ec); 142 | return 0; 143 | } 144 | 145 | static int Reset_device(lua_State *L) 146 | { 147 | devhandle_t *devhandle = checkdevhandle(L, 1, NULL); 148 | int ec = libusb_reset_device(devhandle); 149 | CheckError(L, ec); 150 | return 0; 151 | } 152 | 153 | static int Get_bos_descriptor(lua_State *L) 154 | { 155 | ud_t *ud; 156 | devhandle_t *devhandle = checkdevhandle(L, 1, &ud); 157 | struct libusb_bos_descriptor *bos; 158 | int ec = libusb_get_bos_descriptor(devhandle, &bos); 159 | CheckError(L, ec); 160 | pushbosdescriptor(L, bos, ud->context); 161 | libusb_free_bos_descriptor(bos); 162 | return 1; 163 | } 164 | 165 | static int Alloc_streams(lua_State *L) 166 | { 167 | int num_endpoints, err, ec; 168 | devhandle_t *devhandle = checkdevhandle(L, 1, NULL); 169 | uint32_t num_streams = luaL_checkinteger(L, 2); 170 | unsigned char *endpoints = checkucharlist(L, 3, &num_endpoints, &err); 171 | /* All endpoints must belong to the same interface, otherwise the function fails */ 172 | if(err) return argerror(L, 3, err); 173 | ec = libusb_alloc_streams(devhandle, num_streams, endpoints, num_endpoints); 174 | Free(L, endpoints); 175 | if(ec<0) CheckError(L, ec); 176 | lua_pushinteger(L, ec); // number of streams, valid stream ids are 1, 2,..., ec. 177 | return 1; 178 | } 179 | 180 | static int Free_streams(lua_State *L) 181 | { 182 | int num_endpoints, err, ec; 183 | devhandle_t *devhandle = checkdevhandle(L, 1, NULL); 184 | unsigned char *endpoints = checkucharlist(L, 2, &num_endpoints, &err); 185 | if(err) return argerror(L, 2, err); 186 | /* Note that streams are automatically free'd when the interface is released, so there 187 | * is no need for automatic cleanup here */ 188 | ec = libusb_free_streams(devhandle, endpoints, num_endpoints); 189 | Free(L, endpoints); 190 | CheckError(L, ec); 191 | return 0; 192 | } 193 | 194 | static int LockOnClose(lua_State *L) 195 | { 196 | lock_on_close = checkboolean(L, 1); 197 | return 0; 198 | } 199 | 200 | 201 | RAW_FUNC(devhandle) 202 | DESTROY_FUNC(devhandle) 203 | 204 | static const struct luaL_Reg Methods[] = 205 | { 206 | { "raw", Raw }, 207 | { "close", Destroy }, 208 | { "claim_interface", ClaimInterface }, 209 | { "get_descriptor", Get_descriptor }, 210 | { "get_string_descriptor", Get_string_descriptor }, 211 | { "get_configuration", Get_configuration }, 212 | { "set_configuration", Set_configuration }, 213 | { "clear_halt", Clear_halt }, 214 | { "reset_device", Reset_device }, 215 | { "get_bos_descriptor", Get_bos_descriptor }, 216 | { "alloc_streams", Alloc_streams }, 217 | { "free_streams", Free_streams }, 218 | { NULL, NULL } /* sentinel */ 219 | }; 220 | 221 | static const struct luaL_Reg MetaMethods[] = 222 | { 223 | { "__gc", Destroy }, 224 | { NULL, NULL } /* sentinel */ 225 | }; 226 | 227 | static const struct luaL_Reg Functions[] = 228 | { 229 | { "lock_on_close", LockOnClose }, 230 | { NULL, NULL } /* sentinel */ 231 | }; 232 | 233 | void moonusb_open_devhandle(lua_State *L) 234 | { 235 | udata_define(L, DEVHANDLE_MT, Methods, MetaMethods); 236 | luaL_setfuncs(L, Functions, 0); 237 | } 238 | 239 | #if 0 240 | //device_t * libusb_get_device(devhandle_t *devhandle); 241 | #endif 242 | -------------------------------------------------------------------------------- /src/device.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2021 Stefano Trettel 4 | * 5 | * Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #include "internal.h" 27 | 28 | static int freedevice(lua_State *L, ud_t *ud) 29 | { 30 | device_t *device = (device_t*)ud->handle; 31 | freechildren(L, DEVHANDLE_MT, ud); 32 | if(!freeuserdata(L, ud, "device")) return 0; 33 | /* After having deleted all devhandles, now the ref count should be 1 34 | * and by decrementing it to 0 libusb should delete it */ 35 | libusb_unref_device(device); 36 | return 0; 37 | } 38 | 39 | int newdevice(lua_State *L, context_t *context, device_t *device) 40 | { 41 | ud_t *ud; 42 | libusb_ref_device(device); 43 | ud = newuserdata(L, device, DEVICE_MT, "device"); 44 | ud->parent_ud = userdata(context); 45 | ud->context = context; 46 | ud->destructor = freedevice; 47 | return 1; 48 | } 49 | 50 | static int Open(lua_State *L) 51 | { 52 | int ec; 53 | ud_t *ud; 54 | devhandle_t *devhandle; 55 | device_t *device = checkdevice(L, 1, &ud); 56 | ec = libusb_open(device, &devhandle); // this increases device's ref count 57 | CheckError(L, ec); 58 | newdevhandle(L, device, devhandle); 59 | return 1; 60 | } 61 | 62 | static int Get_bus_number(lua_State *L) 63 | { 64 | device_t *device = checkdevice(L, 1, NULL); 65 | uint8_t n = libusb_get_bus_number(device); 66 | lua_pushinteger(L, n); 67 | return 1; 68 | } 69 | 70 | static int Get_port_number(lua_State *L) 71 | { 72 | device_t *device = checkdevice(L, 1, NULL); 73 | uint8_t n = libusb_get_port_number(device); 74 | lua_pushinteger(L, n); 75 | return 1; 76 | } 77 | 78 | static int Get_port_numbers(lua_State *L) 79 | { 80 | int i, rc; 81 | uint8_t *n; 82 | device_t *device = checkdevice(L, 1, NULL); 83 | int len = luaL_optinteger(L, 2, 8); 84 | if(len <= 0) return argerror(L, 2, ERR_LENGTH); 85 | n = Malloc(L, len*sizeof(uint8_t)); 86 | rc = libusb_get_port_numbers(device, n, len); 87 | if(rc<0) 88 | { Free(L, n); CheckError(L, rc); } 89 | lua_newtable(L); 90 | for(i=0; icontext, parent); 108 | else 109 | pushdevice(L, parent); 110 | } 111 | else 112 | lua_pushnil(L); 113 | return 1; 114 | } 115 | 116 | static int Get_device_address(lua_State *L) 117 | { 118 | device_t *device = checkdevice(L, 1, NULL); 119 | uint8_t address = libusb_get_device_address(device); 120 | lua_pushinteger(L, address); 121 | return 1; 122 | } 123 | 124 | static int Get_device_speed(lua_State *L) 125 | { 126 | device_t *device = checkdevice(L, 1, NULL); 127 | int speed = libusb_get_device_speed(device); 128 | pushspeed(L, speed); 129 | return 1; 130 | } 131 | 132 | #define F(Func, func) \ 133 | static int Func(lua_State *L) \ 134 | { \ 135 | device_t *device = checkdevice(L, 1, NULL); \ 136 | unsigned char endpoint = luaL_checkinteger(L, 2); \ 137 | int rc = func(device, endpoint); \ 138 | CheckError(L, rc); \ 139 | lua_pushinteger(L, rc); \ 140 | return 1; \ 141 | } 142 | F(Get_max_packet_size, libusb_get_max_packet_size) 143 | F(Get_max_iso_packet_size, libusb_get_max_iso_packet_size) 144 | #undef F 145 | 146 | static int Get_device_descriptor(lua_State *L) 147 | { 148 | ud_t *ud; 149 | struct libusb_device_descriptor desc; 150 | device_t *device = checkdevice(L, 1, &ud); 151 | int ec = libusb_get_device_descriptor(device, &desc); 152 | CheckError(L, ec); 153 | pushdevicedescriptor(L, &desc, device, ud->context); 154 | return 1; 155 | } 156 | 157 | static int Get_active_config_descriptor(lua_State *L) 158 | { 159 | ud_t *ud; 160 | struct libusb_config_descriptor *desc; 161 | device_t *device = checkdevice(L, 1, &ud); 162 | int ec = libusb_get_active_config_descriptor(device, &desc); 163 | CheckError(L, ec); 164 | pushconfigdescriptor(L, desc, ud->context); 165 | libusb_free_config_descriptor(desc); 166 | return 1; 167 | } 168 | 169 | #define F(Func, func) \ 170 | static int Func(lua_State *L) \ 171 | { \ 172 | ud_t *ud; \ 173 | struct libusb_config_descriptor *desc; \ 174 | device_t *device = checkdevice(L, 1, &ud); \ 175 | uint8_t val = luaL_checkinteger(L, 2); \ 176 | int ec = func(device, val, &desc); \ 177 | CheckError(L, ec); \ 178 | pushconfigdescriptor(L, desc, ud->context); \ 179 | libusb_free_config_descriptor(desc); \ 180 | return 1; \ 181 | } 182 | F(Get_config_descriptor, libusb_get_config_descriptor) 183 | F(Get_config_descriptor_by_value, libusb_get_config_descriptor_by_value) 184 | #undef F 185 | 186 | RAW_FUNC(device) 187 | DESTROY_FUNC(device) 188 | 189 | static const struct luaL_Reg Methods[] = 190 | { 191 | { "raw", Raw }, 192 | { "open", Open }, 193 | { "free", Destroy }, 194 | { "get_bus_number", Get_bus_number }, 195 | { "get_port_number", Get_port_number }, 196 | { "get_port_numbers", Get_port_numbers }, 197 | { "get_parent", Get_parent }, 198 | { "get_address", Get_device_address }, 199 | { "get_speed", Get_device_speed }, 200 | { "get_max_packet_size", Get_max_packet_size }, 201 | { "get_max_iso_packet_size", Get_max_iso_packet_size }, 202 | { "get_device_descriptor", Get_device_descriptor }, 203 | { "get_active_config_descriptor", Get_active_config_descriptor }, 204 | { "get_config_descriptor", Get_config_descriptor }, 205 | { "get_config_descriptor_by_value", Get_config_descriptor_by_value }, 206 | { NULL, NULL } /* sentinel */ 207 | }; 208 | 209 | static const struct luaL_Reg MetaMethods[] = 210 | { 211 | { "__gc", Destroy }, 212 | { NULL, NULL } /* sentinel */ 213 | }; 214 | 215 | static const struct luaL_Reg Functions[] = 216 | { 217 | { NULL, NULL } /* sentinel */ 218 | }; 219 | 220 | void moonusb_open_device(lua_State *L) 221 | { 222 | udata_define(L, DEVICE_MT, Methods, MetaMethods); 223 | luaL_setfuncs(L, Functions, 0); 224 | } 225 | 226 | #if 0 227 | //device_t * libusb_ref_device(device_t *dev); 228 | //void libusb_unref_device(device_t *dev); 229 | #endif 230 | -------------------------------------------------------------------------------- /src/hotplug.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2021 Stefano Trettel 4 | * 5 | * Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #include "internal.h" 27 | 28 | static int freehotplug(lua_State *L, ud_t *ud) 29 | { 30 | hotplug_t *hotplug = (hotplug_t*)ud->handle; 31 | context_t *context = ud->context; 32 | // freechildren(L, _MT, ud); 33 | if(!freeuserdata(L, ud, "hotplug")) return 0; 34 | libusb_hotplug_deregister_callback(context, hotplug->cb_handle); 35 | Free(L, hotplug); 36 | return 0; 37 | } 38 | 39 | static int Callback(context_t *context, device_t *device, libusb_hotplug_event event, void *user_data) 40 | { 41 | #define L moonusb_L 42 | #define hotplug ((hotplug_t*)(user_data)) 43 | int rc; 44 | ud_t *ud, *device_ud; 45 | int top = lua_gettop(L); 46 | ud = userdata(hotplug); 47 | if(!ud) 48 | { unexpected(L); return 0; } 49 | lua_rawgeti(L, LUA_REGISTRYINDEX, ud->ref1); 50 | pushcontext(L, context); 51 | device_ud = userdata(device); 52 | if(!device_ud) /* new device, create it */ 53 | newdevice(L, context, device); /* this also pushes the device on the stack */ 54 | else 55 | pushdevice(L, device); 56 | pushhotplugevent(L, event); 57 | rc = lua_pcall(L, 3, 1, 0); 58 | if(rc!=LUA_OK) 59 | { lua_error(L); return 0; } 60 | rc = lua_toboolean(L, -1); /* true -> deregister the callback, i.e. delete this hotplug */ 61 | if(rc) ud->destructor(L, ud); 62 | lua_settop(L, top); 63 | return 0; /* returning 1 would cause the callback to be dereferenced */ 64 | #undef hotplug 65 | #undef L 66 | } 67 | 68 | static int Hotplug_register(lua_State *L) 69 | { 70 | ud_t *ud; 71 | int ec, ref = LUA_NOREF; 72 | hotplug_t *hotplug; 73 | context_t *context = checkcontext(L, 1, NULL); 74 | int events = checkhotplugevent(L, 2); //libusb_hotplug_event 75 | int enumerate = optboolean(L, 4, 0); 76 | int vendor_id = luaL_optinteger(L, 5, LIBUSB_HOTPLUG_MATCH_ANY); 77 | int product_id = luaL_optinteger(L, 6, LIBUSB_HOTPLUG_MATCH_ANY); 78 | int dev_class = luaL_optinteger(L, 7, LIBUSB_HOTPLUG_MATCH_ANY); 79 | int flags = enumerate ? LIBUSB_HOTPLUG_ENUMERATE : 0; 80 | if(!lua_isfunction(L, 3)) 81 | return argerror(L, 3, ERR_FUNCTION); 82 | Reference(L, 3, ref); 83 | hotplug = Malloc(L, sizeof(hotplug_t)); 84 | ud = newuserdata(L, hotplug, HOTPLUG_MT, "hotplug"); 85 | ud->parent_ud = userdata(context); 86 | ud->destructor = freehotplug; 87 | ud->context = context; 88 | ud->ref1 = ref; 89 | /* Note: if the LIBUSB_HOTPLUG_ENUMERATE flag is set, the callback is executed 90 | * repeatedly (once per attached device) before the register function returns, so 91 | * we must create the userdata before registering the callback. 92 | */ 93 | ec = libusb_hotplug_register_callback(context, events, flags, vendor_id, product_id, 94 | dev_class, Callback, hotplug, &(hotplug->cb_handle)); 95 | if(ec) 96 | { ud->destructor(L, ud); CheckError(L, ec); return 0; } 97 | return 1; 98 | } 99 | 100 | DESTROY_FUNC(hotplug) 101 | 102 | static const struct luaL_Reg ContextMethods[] = 103 | { 104 | { "hotplug_register", Hotplug_register }, 105 | { NULL, NULL } /* sentinel */ 106 | }; 107 | 108 | static const struct luaL_Reg Methods[] = 109 | { 110 | { "deregister", Destroy }, 111 | { NULL, NULL } /* sentinel */ 112 | }; 113 | 114 | static const struct luaL_Reg MetaMethods[] = 115 | { 116 | { "__gc", Destroy }, 117 | { NULL, NULL } /* sentinel */ 118 | }; 119 | 120 | static const struct luaL_Reg Functions[] = 121 | { 122 | { NULL, NULL } /* sentinel */ 123 | }; 124 | 125 | void moonusb_open_hotplug(lua_State *L) 126 | { 127 | udata_define(L, HOTPLUG_MT, Methods, MetaMethods); 128 | udata_addmethods(L, CONTEXT_MT, ContextMethods); 129 | luaL_setfuncs(L, Functions, 0); 130 | } 131 | 132 | -------------------------------------------------------------------------------- /src/interface.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2021 Stefano Trettel 4 | * 5 | * Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #include "internal.h" 27 | 28 | static int freeinterface(lua_State *L, ud_t *ud) 29 | { 30 | interface_t *interface = (interface_t*)ud->handle; 31 | int isclaimed = IsClaimed(ud); 32 | // freechildren(L, _MT, ud); 33 | if(!freeuserdata(L, ud, "interface")) return 0; 34 | if(isclaimed) 35 | libusb_release_interface(interface->devhandle, interface->number); 36 | Free(L, interface); 37 | return 0; 38 | } 39 | 40 | int newinterface(lua_State *L, devhandle_t *devhandle, int interface_number) 41 | { 42 | int ec; 43 | ud_t *ud; 44 | interface_t *interface; 45 | interface = Malloc(L, sizeof(interface_t)); 46 | interface->devhandle = devhandle; 47 | interface->number = interface_number; 48 | ud = newuserdata(L, interface, INTERFACE_MT, "interface"); 49 | ud->parent_ud = userdata(devhandle); 50 | ud->context = userdata(devhandle)->context; 51 | ud->destructor = freeinterface; 52 | CancelClaimed(ud); 53 | ec = libusb_claim_interface(interface->devhandle, interface->number); 54 | if(ec) 55 | { 56 | ud->destructor(L, ud); 57 | lua_pop(L, 1); 58 | CheckError(L, ec); 59 | } 60 | MarkClaimed(ud); 61 | return 1; 62 | } 63 | 64 | static int Set_alt_setting(lua_State *L) 65 | { 66 | interface_t *interface = checkinterface(L, 1, NULL); 67 | int alt_setting = luaL_checkinteger(L, 2); 68 | int ec = libusb_set_interface_alt_setting(interface->devhandle, interface->number, alt_setting); 69 | CheckError(L, ec); 70 | return 0; 71 | } 72 | 73 | #if 0 74 | static int Get_number(lua_State *L) 75 | { 76 | interface_t *interface = checkinterface(L, 1, NULL); 77 | lua_pushinteger(L, interface->number); 78 | return 1; 79 | } 80 | #endif 81 | 82 | DESTROY_FUNC(interface) 83 | //PARENT_FUNC(interface) 84 | 85 | static const struct luaL_Reg Methods[] = 86 | { 87 | { "release", Destroy }, 88 | { "set_alt_setting", Set_alt_setting }, 89 | // { "get_number", Get_number }, 90 | // { "get_devhandle", Parent }, 91 | { NULL, NULL } /* sentinel */ 92 | }; 93 | 94 | static const struct luaL_Reg MetaMethods[] = 95 | { 96 | { "__gc", Destroy }, 97 | { NULL, NULL } /* sentinel */ 98 | }; 99 | 100 | static const struct luaL_Reg Functions[] = 101 | { 102 | { NULL, NULL } /* sentinel */ 103 | }; 104 | 105 | void moonusb_open_interface(lua_State *L) 106 | { 107 | udata_define(L, INTERFACE_MT, Methods, MetaMethods); 108 | luaL_setfuncs(L, Functions, 0); 109 | } 110 | 111 | #if 0 112 | // Not exposed (auto_detach is set when opening the device, see newdevhandle): 113 | int libusb_set_auto_detach_kernel_driver(devhandle_t *devhandle, int enable); 114 | int libusb_kernel_driver_active(devhandle_t *devhandle, int interface_number); 115 | int libusb_detach_kernel_driver(devhandle_t *devhandle, int interface_number); 116 | int libusb_attach_kernel_driver(devhandle_t *devhandle, int interface_number); 117 | #endif 118 | -------------------------------------------------------------------------------- /src/internal.h: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2021 Stefano Trettel 4 | * 5 | * Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #ifndef internalDEFINED 27 | #define internalDEFINED 28 | 29 | #ifdef LINUX 30 | #define _ISOC11_SOURCE /* see man aligned_alloc(3) */ 31 | #endif 32 | #include 33 | #include 34 | #include 35 | #include "moonusb.h" 36 | 37 | #define TOSTR_(x) #x 38 | #define TOSTR(x) TOSTR_(x) 39 | 40 | #include "tree.h" 41 | #include "objects.h" 42 | #include "enums.h" 43 | 44 | /* Note: all the dynamic symbols of this library (should) start with 'moonusb_' . 45 | * The only exception is the luaopen_moonusb() function, which is searched for 46 | * with that name by Lua. 47 | * MoonUSB's string references on the Lua registry also start with 'moonusb_'. 48 | */ 49 | 50 | #if 0 51 | /* .c */ 52 | #define moonusb_ 53 | #endif 54 | 55 | /* flags.c */ 56 | #define checkflags(L, arg) luaL_checkinteger((L), (arg)) 57 | #define optflags(L, arg, defval) luaL_optinteger((L), (arg), (defval)) 58 | #define pushflags(L, val) lua_pushinteger((L), (val)) 59 | 60 | /* utils.c */ 61 | void moonusb_utils_init(lua_State *L); 62 | #define copytable moonusb_copytable 63 | int copytable(lua_State *L); 64 | #define noprintf moonusb_noprintf 65 | int noprintf(const char *fmt, ...); 66 | #define now moonusb_now 67 | double now(void); 68 | #define sleeep moonusb_sleeep 69 | void sleeep(double seconds); 70 | #define since(t) (now() - (t)) 71 | #define notavailable moonusb_notavailable 72 | int notavailable(lua_State *L, ...); 73 | #define Malloc moonusb_Malloc 74 | void *Malloc(lua_State *L, size_t size); 75 | #define MallocNoErr moonusb_MallocNoErr 76 | void *MallocNoErr(lua_State *L, size_t size); 77 | #define Strdup moonusb_Strdup 78 | char *Strdup(lua_State *L, const char *s); 79 | #define Free moonusb_Free 80 | void Free(lua_State *L, void *ptr); 81 | #define checkboolean moonusb_checkboolean 82 | int checkboolean(lua_State *L, int arg); 83 | #define testboolean moonusb_testboolean 84 | int testboolean(lua_State *L, int arg, int *err); 85 | #define optboolean moonusb_optboolean 86 | int optboolean(lua_State *L, int arg, int d); 87 | #define checklightuserdata moonusb_checklightuserdata 88 | void *checklightuserdata(lua_State *L, int arg); 89 | #define checklightuserdataorzero moonusb_checklightuserdataorzero 90 | void *checklightuserdataorzero(lua_State *L, int arg); 91 | #define optlightuserdata moonusb_optlightuserdata 92 | void *optlightuserdata(lua_State *L, int arg); 93 | #define testindex moonusb_testindex 94 | int testindex(lua_State *L, int arg, int *err); 95 | #define checkindex moonusb_checkindex 96 | int checkindex(lua_State *L, int arg); 97 | #define optindex moonusb_optindex 98 | int optindex(lua_State *L, int arg, int optval); 99 | #define pushindex moonusb_pushindex 100 | void pushindex(lua_State *L, int val); 101 | 102 | /* Internal error codes */ 103 | #define ERR_NOTPRESENT 1 104 | #define ERR_SUCCESS 0 105 | #define ERR_GENERIC -1 106 | #define ERR_TYPE -2 107 | #define ERR_ELEMTYPE -3 108 | #define ERR_VALUE -4 109 | #define ERR_ELEMVALUE -5 110 | #define ERR_TABLE -6 111 | #define ERR_FUNCTION -7 112 | #define ERR_EMPTY -8 113 | #define ERR_MEMORY -9 114 | #define ERR_MALLOC_ZERO -10 115 | #define ERR_LENGTH -11 116 | #define ERR_POOL -12 117 | #define ERR_BOUNDARIES -13 118 | #define ERR_RANGE -14 119 | #define ERR_FOPEN -15 120 | #define ERR_OPERATION -16 121 | #define ERR_UNKNOWN -17 122 | #define errstring moonusb_errstring 123 | const char* errstring(int err); 124 | 125 | /* device.c */ 126 | #define newdevice moonusb_newdevice 127 | int newdevice(lua_State *L, context_t *context, device_t *device); 128 | 129 | /* devhandle.c */ 130 | #define newdevhandle moonusb_newdevhandle 131 | int newdevhandle(lua_State *L, device_t *device, devhandle_t *devhandle); 132 | 133 | /* interface.c */ 134 | #define newinterface moonusb_newinterface 135 | int newinterface(lua_State *L, devhandle_t *devhandle, int interface_number); 136 | 137 | /* datahandling.c */ 138 | #define sizeoftype moonusb_sizeoftype 139 | size_t sizeoftype(int type); 140 | #define toflattable moonusb_toflattable 141 | int toflattable(lua_State *L, int arg); 142 | #define testdata moonusb_testdata 143 | int testdata(lua_State *L, int type, size_t n, void *dst, size_t dstsize); 144 | #define checkdata moonusb_checkdata 145 | int checkdata(lua_State *L, int arg, int type, void *dst, size_t dstsize); 146 | #define pushdata moonusb_pushdata 147 | int pushdata(lua_State *L, int type, void *data, size_t datalen); 148 | 149 | /* datastructs.c */ 150 | #define checkucharlist moonusb_checkucharlist 151 | unsigned char* checkucharlist(lua_State *L, int arg, int *count, int *err); 152 | #define checkcontrolsetup moonusb_checkcontrolsetup 153 | int checkcontrolsetup(lua_State *L, int arg, struct libusb_control_setup *dst); 154 | #define pushcontrolsetup moonusb_pushcontrolsetup 155 | void pushcontrolsetup(lua_State *L, const struct libusb_control_setup *s); 156 | #define pushdevicedescriptor moonusb_pushdevicedescriptor 157 | int pushdevicedescriptor(lua_State *L, const struct libusb_device_descriptor *s, device_t *device, context_t *context); 158 | #define pushconfigdescriptor moonusb_pushconfigdescriptor 159 | void pushconfigdescriptor(lua_State *L, const struct libusb_config_descriptor *s, context_t *context); 160 | #define pushbosdescriptor moonusb_pushbosdescriptor 161 | void pushbosdescriptor(lua_State *L, struct libusb_bos_descriptor *s, context_t *context); 162 | 163 | /* tracing.c */ 164 | #define pusherrcode moonusb_pusherrcode 165 | int pusherrcode(lua_State *L, int ec); 166 | #define trace_objects moonusb_trace_objects 167 | extern int trace_objects; 168 | 169 | /* main.c */ 170 | extern lua_State *moonusb_L; 171 | int luaopen_moonusb(lua_State *L); 172 | void moonusb_open_enums(lua_State *L); 173 | //void moonusb_open_flags(lua_State *L); 174 | void moonusb_open_tracing(lua_State *L); 175 | void moonusb_open_context(lua_State *L); 176 | void moonusb_open_device(lua_State *L); 177 | void moonusb_open_devhandle(lua_State *L); 178 | void moonusb_open_synch(lua_State *L); 179 | void moonusb_open_transfer(lua_State *L); 180 | void moonusb_open_polling(lua_State *L); 181 | void moonusb_open_hotplug(lua_State *L); 182 | void moonusb_open_interface(lua_State *L); 183 | void moonusb_open_datahandling(lua_State *L); 184 | void moonusb_open_hostmem(lua_State *L); 185 | 186 | /*------------------------------------------------------------------------------* 187 | | Debug and other utilities | 188 | *------------------------------------------------------------------------------*/ 189 | 190 | #define CheckError(L, ec) \ 191 | do { if(ec != LIBUSB_SUCCESS) { pusherrcode((L), (ec)); return lua_error(L); } } while(0) 192 | 193 | /* If this is printed, it denotes a suspect bug: */ 194 | #define UNEXPECTED_ERROR "unexpected error (%s, %d)", __FILE__, __LINE__ 195 | #define unexpected(L) luaL_error((L), UNEXPECTED_ERROR) 196 | 197 | /* Errors with internal error code (ERR_XXX) */ 198 | #define failure(L, errcode) luaL_error((L), errstring((errcode))) 199 | #define argerror(L, arg, errcode) luaL_argerror((L), (arg), errstring((errcode))) 200 | #define errmemory(L) luaL_error((L), errstring((ERR_MEMORY))) 201 | 202 | #define notsupported(L) luaL_error((L), "operation not supported") 203 | #define badvalue(L, s) lua_pushfstring((L), "invalid value '%s'", (s)) 204 | 205 | /* Reference/unreference variables on the Lua registry */ 206 | #define Unreference(L, ref) do { \ 207 | if((ref)!= LUA_NOREF) \ 208 | { \ 209 | luaL_unref((L), LUA_REGISTRYINDEX, (ref)); \ 210 | (ref) = LUA_NOREF; \ 211 | } \ 212 | } while(0) 213 | 214 | #define Reference(L, arg, ref) do { \ 215 | Unreference((L), (ref)); \ 216 | lua_pushvalue(L, (arg)); \ 217 | (ref) = luaL_ref(L, LUA_REGISTRYINDEX); \ 218 | } while(0) 219 | 220 | /* DEBUG -------------------------------------------------------- */ 221 | #if defined(DEBUG) 222 | 223 | #define DBG printf 224 | #define TR() do { printf("trace %s %d\n",__FILE__,__LINE__); } while(0) 225 | #define BK() do { printf("break %s %d\n",__FILE__,__LINE__); getchar(); } while(0) 226 | #define TSTART double ts = now(); 227 | #define TSTOP do { \ 228 | ts = since(ts); ts = ts*1e6; \ 229 | printf("%s %d %.3f us\n", __FILE__, __LINE__, ts); \ 230 | ts = now(); \ 231 | } while(0); 232 | 233 | #else 234 | 235 | #define DBG noprintf 236 | #define TR() 237 | #define BK() 238 | #define TSTART do {} while(0) 239 | #define TSTOP do {} while(0) 240 | 241 | #endif /* DEBUG ------------------------------------------------- */ 242 | 243 | #endif /* internalDEFINED */ 244 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2021 Stefano Trettel 4 | * 5 | * Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #include "internal.h" 27 | 28 | lua_State *moonusb_L; 29 | 30 | static void AtExit(void) 31 | { 32 | if(moonusb_L) 33 | { 34 | enums_free_all(moonusb_L); 35 | moonusb_L = NULL; 36 | } 37 | } 38 | 39 | 40 | int luaopen_moonusb(lua_State *L) 41 | /* Lua calls this function to load the module */ 42 | { 43 | moonusb_L = L; 44 | 45 | moonusb_utils_init(L); 46 | atexit(AtExit); 47 | 48 | lua_newtable(L); /* the module table */ 49 | moonusb_open_enums(L); 50 | // moonusb_open_flags(L); 51 | moonusb_open_tracing(L); 52 | moonusb_open_context(L); 53 | moonusb_open_device(L); 54 | moonusb_open_devhandle(L); 55 | moonusb_open_synch(L); 56 | moonusb_open_transfer(L); 57 | moonusb_open_polling(L); 58 | moonusb_open_hotplug(L); 59 | moonusb_open_interface(L); 60 | moonusb_open_datahandling(L); 61 | moonusb_open_hostmem(L); 62 | 63 | /* Add functions implemented in Lua */ 64 | lua_pushvalue(L, -1); lua_setglobal(L, "moonusb"); 65 | if(luaL_dostring(L, "require('moonusb.bosdescriptors')") != 0) lua_error(L); 66 | if(luaL_dostring(L, "require('moonusb.utils')") != 0) lua_error(L); 67 | lua_pushnil(L); lua_setglobal(L, "moonusb"); 68 | 69 | return 1; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /src/moonusb.h: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2021 Stefano Trettel 4 | * 5 | * Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #ifndef moonusbDEFINED 27 | #define moonusbDEFINED 28 | 29 | #include 30 | #include "lualib.h" 31 | #include "lauxlib.h" 32 | #include "compat-5.3.h" 33 | #include 34 | 35 | #define MOONUSB_VERSION "0.1" 36 | 37 | #endif /* moonusbDEFINED */ 38 | 39 | -------------------------------------------------------------------------------- /src/objects.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2021 Stefano Trettel 4 | * 5 | * Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #include "internal.h" 27 | 28 | ud_t *newuserdata(lua_State *L, void *handle, const char *mt, const char *tracename) 29 | { 30 | ud_t *ud; 31 | /* we use handle as search key */ 32 | ud = (ud_t*)udata_new(L, sizeof(ud_t), (uint64_t)(uintptr_t)handle, mt); 33 | memset(ud, 0, sizeof(ud_t)); 34 | ud->handle = handle; 35 | MarkValid(ud); 36 | if(trace_objects) 37 | printf("create %s %p (%p)\n", tracename, (void*)ud, handle); 38 | return ud; 39 | } 40 | 41 | int freeuserdata(lua_State *L, ud_t *ud, const char *tracename) 42 | { 43 | /* The 'Valid' mark prevents double calls when an object is explicitly destroyed, 44 | * and subsequently deleted also by the GC (the ud sticks around until the GC 45 | * collects it, so we mark it as invalid when the object is explicitly destroyed 46 | * by the script, or implicitly destroyed because child of a destroyed object). */ 47 | if(!IsValid(ud)) return 0; 48 | CancelValid(ud); 49 | if(ud->info) 50 | Free(L, ud->info); 51 | if(ud->ref1!=LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, ud->ref1); 52 | if(ud->ref2!=LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, ud->ref2); 53 | if(ud->ref3!=LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, ud->ref3); 54 | if(trace_objects) 55 | printf("delete %s %p (%p)\n", tracename, (void*)ud, ud->handle); 56 | udata_free(L, (uint64_t)(uintptr_t)ud->handle); 57 | return 1; 58 | } 59 | 60 | static int freeifchild(lua_State *L, const void *mem, const char *mt, const void *parent_ud) 61 | /* callback for udata_scan */ 62 | { 63 | ud_t *ud = (ud_t*)mem; 64 | (void)mt; 65 | if(IsValid(ud) && (ud->parent_ud == parent_ud)) 66 | ud->destructor(L, ud); 67 | return 0; 68 | } 69 | 70 | int freechildren(lua_State *L, const char *mt, ud_t *parent_ud) 71 | /* calls the self destructor for all 'mt' objects that are children of the given parent_ud */ 72 | { 73 | return udata_scan(L, mt, parent_ud, freeifchild); 74 | } 75 | 76 | int pushuserdata(lua_State *L, ud_t *ud) 77 | { 78 | if(!IsValid(ud)) return unexpected(L); 79 | return udata_push(L, (uint64_t)(uintptr_t)ud->handle); 80 | } 81 | 82 | ud_t *userdata(const void *handle) 83 | { 84 | ud_t *ud = (ud_t*)udata_mem((uint64_t)(uintptr_t)handle); 85 | if(ud && IsValid(ud)) return ud; 86 | return NULL; 87 | } 88 | 89 | void *testxxx(lua_State *L, int arg, ud_t **udp, const char *mt) 90 | { 91 | ud_t *ud = (ud_t*)udata_test(L, arg, mt); 92 | if(ud && IsValid(ud)) { if(udp) *udp=ud; return ud->handle; } 93 | if(udp) *udp = NULL; 94 | return 0; 95 | } 96 | 97 | #if 0 98 | void *testoneofxxx(lua_State *L, int arg, ud_t **udp, char **mtp) 99 | { 100 | void *handle = NULL; 101 | int i = 0; 102 | char *mt = NULL; 103 | while((mt = mtp[i++]) != NULL) 104 | { 105 | handle = testxxx(L, arg, udp, mt); 106 | if(handle) return handle; 107 | } 108 | if(udp) *udp = NULL; 109 | return 0; 110 | } 111 | #endif 112 | 113 | 114 | void *checkxxx(lua_State *L, int arg, ud_t **udp, const char *mt) 115 | { 116 | ud_t *ud = (ud_t*)udata_test(L, arg, mt); 117 | if(ud && IsValid(ud)) 118 | { if(udp) *udp = ud; return ud->handle; } 119 | lua_pushfstring(L, "not a %s", mt); 120 | luaL_argerror(L, arg, lua_tostring(L, -1)); 121 | return 0; 122 | } 123 | 124 | void *optxxx(lua_State *L, int arg, ud_t **udp, const char *mt) 125 | /* This differs from testxxx in that it fails in case the argument is 126 | * something different than either none/nil or the expected userdata type 127 | */ 128 | { 129 | if(lua_isnoneornil(L, arg)) 130 | { if(udp) *udp = NULL; return 0; } 131 | return checkxxx(L, arg, udp, mt); 132 | } 133 | 134 | int pushxxx(lua_State *L, void *handle) 135 | { return udata_push(L, (uint64_t)(uintptr_t)handle); } 136 | 137 | 138 | void** checkxxxlist(lua_State *L, int arg, int *count, int *err, const char *mt) 139 | /* xxx* checkxxxlist(lua_State *L, int arg, int *count, int *err) 140 | * Checks if the variable at arg on the Lua stack is a list of xxx objects. 141 | * On success, returns an array of xxx handles and sets its length in *count. 142 | * The array s Malloc'd and must be released by the caller using Free(L, ...). 143 | * On error, sets *err to ERR_XXX, *count to 0, and returns NULL. 144 | */ 145 | { 146 | void** list; 147 | int i; 148 | 149 | *count = 0; 150 | *err = 0; 151 | if(lua_isnoneornil(L, arg)) 152 | { *err = ERR_NOTPRESENT; return NULL; } 153 | if(lua_type(L, arg) != LUA_TTABLE) 154 | { *err = ERR_TABLE; return NULL; } 155 | *count = luaL_len(L, arg); 156 | if(*count == 0) 157 | { *err = ERR_EMPTY; return NULL; } 158 | list = (void**)MallocNoErr(L, sizeof(void*) * (*count)); 159 | 160 | if(!list) 161 | { *count = 0; *err = ERR_MEMORY; return NULL; } 162 | 163 | for(i=0; i<*count; i++) 164 | { 165 | lua_rawgeti(L, arg, i+1); 166 | list[i] = (void*)(uintptr_t)testxxx(L, -1, NULL, mt); 167 | if(!list[i]) 168 | { Free(L, list); *count = 0; *err = ERR_TYPE; return NULL; } 169 | lua_pop(L, 1); 170 | } 171 | return list; 172 | } 173 | 174 | 175 | int setmetatable(lua_State *L, const char *mt) 176 | /* Sets the metatable of the table on top of the stack */ 177 | { 178 | luaL_getmetatable(L, mt); 179 | lua_setmetatable(L, -2); 180 | return 0; 181 | } 182 | 183 | -------------------------------------------------------------------------------- /src/objects.h: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2021 Stefano Trettel 4 | * 5 | * Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #ifndef objectsDEFINED 27 | #define objectsDEFINED 28 | 29 | #include "tree.h" 30 | #include "udata.h" 31 | 32 | /* libusb renaming (for internal use) */ 33 | #define context_t libusb_context 34 | #define device_t libusb_device 35 | #define devhandle_t libusb_device_handle 36 | #define transfer_t struct libusb_transfer 37 | #define hotplug_t moonusb_hotplug_t 38 | #define interface_t moonusb_interface_t 39 | #define hostmem_t moonusb_hostmem_t 40 | 41 | typedef struct { 42 | libusb_hotplug_callback_handle cb_handle; /* this is unique per context */ 43 | } moonusb_hotplug_t; 44 | 45 | typedef struct { 46 | int number; /* bInterfaceNumber */ 47 | devhandle_t *devhandle; 48 | } moonusb_interface_t; 49 | 50 | /* host accessible memory: */ 51 | typedef struct { 52 | unsigned char *ptr; 53 | size_t size; 54 | } moonusb_hostmem_t; 55 | 56 | /* Objects' metatable names */ 57 | #define CONTEXT_MT "moonusb_context" 58 | #define DEVICE_MT "moonusb_device" 59 | #define DEVHANDLE_MT "moonusb_devhandle" 60 | #define TRANSFER_MT "moonusb_transfer" 61 | #define HOTPLUG_MT "moonusb_hotplug" 62 | #define INTERFACE_MT "moonusb_interface" 63 | #define HOSTMEM_MT "moonusb_hostmem" 64 | 65 | /* Userdata memory associated with objects */ 66 | #define ud_t moonusb_ud_t 67 | typedef struct moonusb_ud_s ud_t; 68 | 69 | struct moonusb_ud_s { 70 | void *handle; /* the object handle bound to this userdata */ 71 | int (*destructor)(lua_State *L, ud_t *ud); /* self destructor */ 72 | ud_t *parent_ud; /* the ud of the parent object */ 73 | context_t *context; 74 | uint32_t marks; 75 | int ref1, ref2, ref3, ref4; /* refs for callbacks, automatically unreferenced at destruction */ 76 | void *info; /* object specific info (ud_info_t, subject to Free() at destruction, if not NULL) */ 77 | }; 78 | 79 | /* Marks. m_ = marks word (uint32_t) , i_ = bit number (0 .. 31) */ 80 | #define MarkGet(m_,i_) (((m_) & ((uint32_t)1<<(i_))) == ((uint32_t)1<<(i_))) 81 | #define MarkSet(m_,i_) do { (m_) = ((m_) | ((uint32_t)1<<(i_))); } while(0) 82 | #define MarkReset(m_,i_) do { (m_) = ((m_) & (~((uint32_t)1<<(i_)))); } while(0) 83 | 84 | #define IsValid(ud) MarkGet((ud)->marks, 0) 85 | #define MarkValid(ud) MarkSet((ud)->marks, 0) 86 | #define CancelValid(ud) MarkReset((ud)->marks, 0) 87 | 88 | #define IsBorrowed(ud) MarkGet((ud)->marks, 1) 89 | #define MarkBorrowed(ud) MarkSet((ud)->marks, 1) 90 | #define CancelBorrowed(ud) MarkReset((ud)->marks, 1) 91 | 92 | #define IsClaimed(ud) MarkGet((ud)->marks, 2) 93 | #define MarkClaimed(ud) MarkSet((ud)->marks, 2) 94 | #define CancelClaimed(ud) MarkReset((ud)->marks, 2) 95 | 96 | #define IsAllocated(ud) MarkGet((ud)->marks, 3) 97 | #define MarkAllocated(ud) MarkSet((ud)->marks, 3) 98 | #define CancelAllocated(ud) MarkReset((ud)->marks, 3) 99 | 100 | #define IsDma(ud) MarkGet((ud)->marks, 4) 101 | #define MarkDma(ud) MarkSet((ud)->marks, 4) 102 | #define CancelDma(ud) MarkReset((ud)->marks, 4) 103 | 104 | #define IsSubmitted(ud) MarkGet((ud)->marks, 5) 105 | #define MarkSubmitted(ud) MarkSet((ud)->marks, 5) 106 | #define CancelSubmitted(ud) MarkReset((ud)->marks, 5) 107 | 108 | #if 0 109 | /* .c */ 110 | #define moonusb_ 111 | #endif 112 | 113 | #define setmetatable moonusb_setmetatable 114 | int setmetatable(lua_State *L, const char *mt); 115 | 116 | #define newuserdata moonusb_newuserdata 117 | ud_t *newuserdata(lua_State *L, void *handle, const char *mt, const char *tracename); 118 | #define freeuserdata moonusb_freeuserdata 119 | int freeuserdata(lua_State *L, ud_t *ud, const char *tracename); 120 | #define pushuserdata moonusb_pushuserdata 121 | int pushuserdata(lua_State *L, ud_t *ud); 122 | 123 | #define freechildren moonusb_freechildren 124 | int freechildren(lua_State *L, const char *mt, ud_t *parent_ud); 125 | 126 | #define userdata_unref(L, handle) udata_unref((L),(handle)) 127 | 128 | #define UD(handle) userdata((handle)) /* dispatchable objects only */ 129 | #define userdata moonusb_userdata 130 | ud_t *userdata(const void *handle); 131 | #define testxxx moonusb_testxxx 132 | void *testxxx(lua_State *L, int arg, ud_t **udp, const char *mt); 133 | #define checkxxx moonusb_checkxxx 134 | void *checkxxx(lua_State *L, int arg, ud_t **udp, const char *mt); 135 | #define optxxx moonusb_optxxx 136 | void *optxxx(lua_State *L, int arg, ud_t **udp, const char *mt); 137 | #define pushxxx moonusb_pushxxx 138 | int pushxxx(lua_State *L, void *handle); 139 | #define checkxxxlist moonusb_checkxxxlist 140 | void** checkxxxlist(lua_State *L, int arg, int *count, int *err, const char *mt); 141 | 142 | /* context.c */ 143 | #define checkcontext(L, arg, udp) (context_t*)checkxxx((L), (arg), (udp), CONTEXT_MT) 144 | #define testcontext(L, arg, udp) (context_t*)testxxx((L), (arg), (udp), CONTEXT_MT) 145 | #define optcontext(L, arg, udp) (context_t*)optxxx((L), (arg), (udp), CONTEXT_MT) 146 | #define pushcontext(L, handle) pushxxx((L), (void*)(handle)) 147 | #define checkcontextlist(L, arg, count, err) checkxxxlist((L), (arg), (count), (err), CONTEXT_MT) 148 | 149 | /* device.c */ 150 | #define checkdevice(L, arg, udp) (device_t*)checkxxx((L), (arg), (udp), DEVICE_MT) 151 | #define testdevice(L, arg, udp) (device_t*)testxxx((L), (arg), (udp), DEVICE_MT) 152 | #define optdevice(L, arg, udp) (device_t*)optxxx((L), (arg), (udp), DEVICE_MT) 153 | #define pushdevice(L, handle) pushxxx((L), (void*)(handle)) 154 | #define checkdevicelist(L, arg, count, err) checkxxxlist((L), (arg), (count), (err), DEVICE_MT) 155 | 156 | /* devhandle.c */ 157 | #define checkdevhandle(L, arg, udp) (devhandle_t*)checkxxx((L), (arg), (udp), DEVHANDLE_MT) 158 | #define testdevhandle(L, arg, udp) (devhandle_t*)testxxx((L), (arg), (udp), DEVHANDLE_MT) 159 | #define optdevhandle(L, arg, udp) (devhandle_t*)optxxx((L), (arg), (udp), DEVHANDLE_MT) 160 | #define pushdevhandle(L, handle) pushxxx((L), (void*)(handle)) 161 | #define checkdevhandlelist(L, arg, count, err) checkxxxlist((L), (arg), (count), (err), DEVHANDLE_MT) 162 | 163 | /* transfer.c */ 164 | #define checktransfer(L, arg, udp) (transfer_t*)checkxxx((L), (arg), (udp), TRANSFER_MT) 165 | #define testtransfer(L, arg, udp) (transfer_t*)testxxx((L), (arg), (udp), TRANSFER_MT) 166 | #define opttransfer(L, arg, udp) (transfer_t*)optxxx((L), (arg), (udp), TRANSFER_MT) 167 | #define pushtransfer(L, handle) pushxxx((L), (void*)(handle)) 168 | #define checktransferlist(L, arg, count, err) checkxxxlist((L), (arg), (count), (err), TRANSFER_MT) 169 | 170 | /* hotplug.c */ 171 | #define checkhotplug(L, arg, udp) (hotplug_t*)checkxxx((L), (arg), (udp), HOTPLUG_MT) 172 | #define testhotplug(L, arg, udp) (hotplug_t*)testxxx((L), (arg), (udp), HOTPLUG_MT) 173 | #define opthotplug(L, arg, udp) (hotplug_t*)optxxx((L), (arg), (udp), HOTPLUG_MT) 174 | #define pushhotplug(L, handle) pushxxx((L), (void*)(handle)) 175 | #define checkhotpluglist(L, arg, count, err) checkxxxlist((L), (arg), (count), (err), HOTPLUG_MT) 176 | 177 | /* interface.c */ 178 | #define checkinterface(L, arg, udp) (interface_t*)checkxxx((L), (arg), (udp), INTERFACE_MT) 179 | #define testinterface(L, arg, udp) (interface_t*)testxxx((L), (arg), (udp), INTERFACE_MT) 180 | #define optinterface(L, arg, udp) (interface_t*)optxxx((L), (arg), (udp), INTERFACE_MT) 181 | #define pushinterface(L, handle) pushxxx((L), (void*)(handle)) 182 | #define checkinterfacelist(L, arg, count, err) checkxxxlist((L), (arg), (count), (err), INTERFACE_MT) 183 | 184 | /* hostmem.c */ 185 | #define checkhostmem(L, arg, udp) (hostmem_t*)checkxxx((L), (arg), (udp), HOSTMEM_MT) 186 | #define testhostmem(L, arg, udp) (hostmem_t*)testxxx((L), (arg), (udp), HOSTMEM_MT) 187 | #define opthostmem(L, arg, udp) (hostmem_t*)optxxx((L), (arg), (udp), HOSTMEM_MT) 188 | #define pushhostmem(L, handle) pushxxx((L), (void*)(handle)) 189 | #define checkhostmemlist(L, arg, count, err) checkxxxlist((L), (arg), (count), (err), HOSTMEM_MT) 190 | 191 | #if 0 // 7yy 192 | /* zzz.c */ 193 | #define checkzzz(L, arg, udp) (zzz_t*)checkxxx((L), (arg), (udp), ZZZ_MT) 194 | #define testzzz(L, arg, udp) (zzz_t*)testxxx((L), (arg), (udp), ZZZ_MT) 195 | #define optzzz(L, arg, udp) (zzz_t*)optxxx((L), (arg), (udp), ZZZ_MT) 196 | #define pushzzz(L, handle) pushxxx((L), (void*)(handle)) 197 | #define checkzzzlist(L, arg, count, err) checkxxxlist((L), (arg), (count), (err), ZZZ_MT) 198 | 199 | #endif 200 | 201 | #define RAW_FUNC(xxx) \ 202 | static int Raw(lua_State *L) \ 203 | { \ 204 | lua_pushinteger(L, (uintptr_t)check##xxx(L, 1, NULL)); \ 205 | return 1; \ 206 | } 207 | 208 | #define TYPE_FUNC(xxx) /* */ \ 209 | static int Type(lua_State *L) \ 210 | { \ 211 | (void)check##xxx(L, 1, NULL); \ 212 | lua_pushstring(L, ""#xxx); \ 213 | return 1; \ 214 | } 215 | 216 | #define DESTROY_FUNC(xxx) \ 217 | static int Destroy(lua_State *L) \ 218 | { \ 219 | ud_t *ud; \ 220 | (void)test##xxx(L, 1, &ud); \ 221 | if(!ud) return 0; /* already deleted */ \ 222 | return ud->destructor(L, ud); \ 223 | } 224 | 225 | #define PARENT_FUNC(xxx) \ 226 | static int Parent(lua_State *L) \ 227 | { \ 228 | ud_t *ud; \ 229 | (void)check##xxx(L, 1, &ud); \ 230 | if(!ud->parent_ud) return 0; \ 231 | return pushuserdata(L, ud->parent_ud); \ 232 | } 233 | 234 | #endif /* objectsDEFINED */ 235 | -------------------------------------------------------------------------------- /src/polling.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2021 Stefano Trettel 4 | * 5 | * Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #include "internal.h" 27 | 28 | static void sectotv(struct timeval *tv, double seconds) 29 | { 30 | tv->tv_sec=(time_t)seconds; 31 | tv->tv_usec=(long)((seconds-((double)tv->tv_sec))*1.0e6); 32 | } 33 | 34 | static double tvtosec(const struct timeval *tv) 35 | { 36 | return tv->tv_sec*1.0+tv->tv_usec*1.0e-6; 37 | } 38 | 39 | static int Handle_events(lua_State *L) 40 | { 41 | int ec; 42 | double seconds; 43 | struct timeval tv; 44 | context_t *context = checkcontext(L, 1, NULL); 45 | if(lua_isnoneornil(L, 2)) /* blocking */ 46 | ec = libusb_handle_events(context); 47 | else 48 | { 49 | seconds = luaL_checknumber(L, 2); 50 | sectotv(&tv, seconds); 51 | ec = libusb_handle_events_timeout(context, &tv); 52 | } 53 | CheckError(L, ec); 54 | return 0; 55 | } 56 | 57 | static int Get_next_timeout(lua_State *L) 58 | { 59 | struct timeval tv; 60 | context_t *context = checkcontext(L, 1, NULL); 61 | int ec = libusb_get_next_timeout(context, &tv); 62 | if(ec==0) { lua_pushnil(L); return 1; } // no pending timeouts 63 | if(ec==1) { lua_pushnumber(L, tvtosec(&tv)); return 1; } 64 | CheckError(L, ec); 65 | return 0; 66 | } 67 | 68 | static const struct luaL_Reg Methods[] = 69 | { 70 | { "handle_events", Handle_events }, 71 | { "get_next_timeout", Get_next_timeout }, 72 | { NULL, NULL } /* sentinel */ 73 | }; 74 | 75 | static const struct luaL_Reg Functions[] = 76 | { 77 | { NULL, NULL } /* sentinel */ 78 | }; 79 | 80 | void moonusb_open_polling(lua_State *L) 81 | { 82 | udata_addmethods(L, CONTEXT_MT, Methods); 83 | luaL_setfuncs(L, Functions, 0); 84 | } 85 | 86 | #if 0 // 87 | /* File descriptor for polling */ 88 | struct libusb_pollfd { 89 | int fd; 90 | short events; 91 | }; 92 | typedef void (*libusb_pollfd_added_cb)(int fd, short events, void *user_data); 93 | typedef void (*libusb_pollfd_removed_cb)(int fd, void *user_data); 94 | //int libusb_pollfds_handle_timeouts(context_t *context); 95 | //const struct libusb_pollfd ** libusb_get_pollfds(context_t *context); 96 | //void libusb_free_pollfds(const struct libusb_pollfd **pollfds); 97 | //void libusb_set_pollfd_notifiers(context_t *context, libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, void *user_data); 98 | //int libusb_try_lock_events(context_t *context); 99 | //void libusb_lock_events(context_t *context); 100 | //void libusb_unlock_events(context_t *context); 101 | //void libusb_interrupt_event_handler(context_t *context); 102 | //void libusb_lock_event_waiters(context_t *context); 103 | //void libusb_unlock_event_waiters(context_t *context); 104 | //int libusb_event_handling_ok(context_t *context); 105 | //int libusb_event_handler_active(context_t *context); 106 | //int libusb_wait_for_event(context_t *context, struct timeval *tv); 107 | //int libusb_handle_events_locked(context_t *context, struct timeval *tv); 108 | //int libusb_handle_events_timeout_completed(context_t *context, struct timeval *tv, int *completed); 109 | //int libusb_handle_events_completed(context_t *context, int *completed); 110 | #endif 111 | 112 | -------------------------------------------------------------------------------- /src/synch.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2021 Stefano Trettel 4 | * 5 | * Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #include "internal.h" 27 | 28 | static int Control_transfer(lua_State *L) 29 | // actual_length = f(devhandle, ptr, timeout) 30 | // expects the setup packet in the first 8 bytes 31 | { 32 | int ec; 33 | devhandle_t *devhandle = checkdevhandle(L, 1, NULL); 34 | unsigned char *ptr = (unsigned char*)checklightuserdata(L, 2); 35 | int length = luaL_checkinteger(L, 3); 36 | unsigned int timeout = luaL_checkinteger(L, 4); 37 | struct libusb_control_setup *s = (struct libusb_control_setup*)ptr; 38 | unsigned char *data = ptr + sizeof(struct libusb_control_setup); 39 | if(length < (s->wLength + 8)) 40 | return argerror(L, 3, ERR_VALUE); 41 | ec = libusb_control_transfer(devhandle, s->bmRequestType, s->bRequest, s->wValue, s->wIndex, 42 | data, s->wLength, timeout); 43 | if(ec>=0) 44 | { 45 | lua_pushinteger(L, ec); // actual length of data starting from ptr+8 46 | return 1; 47 | } 48 | CheckError(L, ec); 49 | return 0; 50 | } 51 | 52 | // actual_length = f(devhandle, endpoint, ptr, length, timeout, datastring) 53 | #define F(Func, func) \ 54 | static int Func(lua_State *L) \ 55 | { \ 56 | int ec, transferred; \ 57 | devhandle_t *devhandle = checkdevhandle(L, 1, NULL); \ 58 | unsigned char endpoint = luaL_checknumber(L, 2); \ 59 | unsigned char *ptr = (unsigned char*)checklightuserdata(L, 3); \ 60 | int length = luaL_checkinteger(L, 4); \ 61 | unsigned int timeout = luaL_checkinteger(L, 5); \ 62 | ec = func(devhandle, endpoint, ptr, length, &transferred, timeout); \ 63 | CheckError(L, ec); \ 64 | lua_pushinteger(L, transferred); \ 65 | return 1; \ 66 | } 67 | F(Bulk_transfer, libusb_bulk_transfer) 68 | F(Interrupt_transfer, libusb_interrupt_transfer) 69 | #undef F 70 | 71 | static const struct luaL_Reg Methods[] = 72 | { 73 | { "control_transfer", Control_transfer }, 74 | { "bulk_transfer", Bulk_transfer }, 75 | { "interrupt_transfer", Interrupt_transfer }, 76 | { NULL, NULL } /* sentinel */ 77 | }; 78 | 79 | static const struct luaL_Reg Functions[] = 80 | { 81 | { NULL, NULL } /* sentinel */ 82 | }; 83 | 84 | void moonusb_open_synch(lua_State *L) 85 | { 86 | luaL_setfuncs(L, Functions, 0); 87 | udata_addmethods(L, DEVHANDLE_MT, Methods); 88 | } 89 | 90 | -------------------------------------------------------------------------------- /src/tracing.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2021 Stefano Trettel 4 | * 5 | * Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #include "internal.h" 27 | 28 | int trace_objects = 0; 29 | 30 | static int TraceObjects(lua_State *L) 31 | { 32 | trace_objects = checkboolean(L, 1); 33 | return 0; 34 | } 35 | 36 | static int Now(lua_State *L) 37 | { 38 | lua_pushnumber(L, now()); 39 | return 1; 40 | } 41 | 42 | static int Since(lua_State *L) 43 | { 44 | double t = luaL_checknumber(L, 1); 45 | lua_pushnumber(L, since(t)); 46 | return 1; 47 | } 48 | 49 | /* ----------------------------------------------------------------------- */ 50 | 51 | static int Setlocale(lua_State *L) 52 | { 53 | const char* locale = luaL_checkstring(L, 1); 54 | int ec = libusb_setlocale(locale); 55 | CheckError(L, ec); 56 | return 0; 57 | } 58 | 59 | static int has_cap = 0; 60 | static int Has_capability(lua_State *L) 61 | { 62 | uint32_t cap = checkcapability(L, 1); 63 | if(!has_cap) 64 | return luaL_error(L, "the libusb_has_capability() API is not available"); 65 | lua_pushboolean(L, libusb_has_capability(cap)); 66 | return 1; 67 | } 68 | 69 | static int Version(lua_State *L) 70 | { 71 | const struct libusb_version *v = libusb_get_version(); 72 | lua_pushinteger(L, v->major); 73 | lua_pushinteger(L, v->minor); 74 | lua_pushinteger(L, v->micro); 75 | lua_pushinteger(L, v->nano); 76 | lua_pushfstring(L, "%s", v->rc ? v->rc : "???"); 77 | return 5; 78 | } 79 | 80 | //const char *libusb_error_name(int errcode); 81 | //const char *libusb_strerror(int errcode); 82 | int pusherrcode(lua_State *L, int ec) 83 | { 84 | #if 0 85 | const char *s = libusb_strerror(ec); 86 | if(s) 87 | lua_pushstring(L, s); 88 | else 89 | lua_pushfstring(L, "unknown libusb error code %d", ec); 90 | return 1; 91 | #endif 92 | switch(ec) 93 | { 94 | #define CASE(code, s) case code: lua_pushstring(L, s); break 95 | CASE(LIBUSB_SUCCESS, "success"); 96 | CASE(LIBUSB_ERROR_IO, "io error"); 97 | CASE(LIBUSB_ERROR_INVALID_PARAM, "invalid param"); 98 | CASE(LIBUSB_ERROR_ACCESS, "access"); 99 | CASE(LIBUSB_ERROR_NO_DEVICE, "no device"); 100 | CASE(LIBUSB_ERROR_NOT_FOUND, "not found"); 101 | CASE(LIBUSB_ERROR_BUSY, "busy"); 102 | CASE(LIBUSB_ERROR_TIMEOUT, "timeout"); 103 | CASE(LIBUSB_ERROR_OVERFLOW, "overflow"); 104 | CASE(LIBUSB_ERROR_PIPE, "pipe"); 105 | CASE(LIBUSB_ERROR_INTERRUPTED, "interrupted"); 106 | CASE(LIBUSB_ERROR_NO_MEM, "no mem"); 107 | CASE(LIBUSB_ERROR_NOT_SUPPORTED, "not supported"); 108 | CASE(LIBUSB_ERROR_OTHER, "other"); 109 | // positive values 110 | // CASE(LIBUSB_TRANSFER_COMPLETED, "success"); 111 | CASE(LIBUSB_TRANSFER_ERROR, "error"); 112 | CASE(LIBUSB_TRANSFER_TIMED_OUT, "timeout"); 113 | CASE(LIBUSB_TRANSFER_CANCELLED, "cancelled"); 114 | CASE(LIBUSB_TRANSFER_STALL, "stall"); 115 | CASE(LIBUSB_TRANSFER_NO_DEVICE, "no device"); 116 | CASE(LIBUSB_TRANSFER_OVERFLOW, "overflow"); 117 | #undef CASE 118 | default: 119 | lua_pushstring(L, "unknown"); 120 | } 121 | return 1; 122 | } 123 | 124 | static int AddVersions(lua_State *L) 125 | { 126 | const struct libusb_version *v; 127 | lua_pushstring(L, "_VERSION"); 128 | lua_pushstring(L, "MoonUSB "MOONUSB_VERSION); 129 | lua_settable(L, -3); 130 | 131 | v = libusb_get_version(); 132 | lua_pushstring(L, "_LIBUSB_VERSION"); 133 | lua_pushfstring(L, "libusb %d.%d.%d", v->major, v->minor, v->micro); 134 | lua_settable(L, -3); 135 | return 0; 136 | } 137 | 138 | static const struct luaL_Reg Functions[] = 139 | { 140 | { "trace_objects", TraceObjects }, 141 | { "now", Now }, 142 | { "since", Since }, 143 | { "setlocale", Setlocale }, 144 | { "has_capability", Has_capability }, 145 | { "get_version", Version }, 146 | { NULL, NULL } /* sentinel */ 147 | }; 148 | 149 | void moonusb_open_tracing(lua_State *L) 150 | { 151 | AddVersions(L); 152 | luaL_setfuncs(L, Functions, 0); 153 | has_cap = libusb_has_capability(LIBUSB_CAP_HAS_CAPABILITY); 154 | } 155 | 156 | -------------------------------------------------------------------------------- /src/udata.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2021 Stefano Trettel 4 | * 5 | * Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include "tree.h" 29 | #include "udata.h" 30 | #include "lua.h" 31 | #include "lauxlib.h" 32 | #include "lualib.h" 33 | 34 | struct moonusb_udata_s { 35 | RB_ENTRY(moonusb_udata_s) entry; 36 | uint64_t id; /* object id (search key) */ 37 | /* references on the Lua registry */ 38 | int ref; /* the correspoding userdata */ 39 | void *mem; /* userdata memory area allocated and released by Lua */ 40 | const char *mt; 41 | }; 42 | 43 | #define UNEXPECTED_ERROR "unexpected error (%s, %d)", __FILE__, __LINE__ 44 | 45 | static int cmp(udata_t *udata1, udata_t *udata2) /* the compare function */ 46 | { return (udata1->id < udata2->id ? -1 : udata1->id > udata2->id); } 47 | 48 | static RB_HEAD(udatatree_s, udata_s) Head = RB_INITIALIZER(&Head); 49 | 50 | RB_PROTOTYPE_STATIC(udatatree_s, udata_s, entry, cmp) 51 | RB_GENERATE_STATIC(udatatree_s, udata_s, entry, cmp) 52 | 53 | static udata_t *udata_remove(udata_t *udata) 54 | { return RB_REMOVE(udatatree_s, &Head, udata); } 55 | static udata_t *udata_insert(udata_t *udata) 56 | { return RB_INSERT(udatatree_s, &Head, udata); } 57 | static udata_t *udata_search(uint64_t id) 58 | { udata_t tmp; tmp.id = id; return RB_FIND(udatatree_s, &Head, &tmp); } 59 | static udata_t *udata_first(uint64_t id) 60 | { udata_t tmp; tmp.id = id; return RB_NFIND(udatatree_s, &Head, &tmp); } 61 | #if 0 62 | static udata_t *udata_next(udata_t *udata) 63 | { return RB_NEXT(udatatree_s, &Head, udata); } 64 | static udata_t *udata_prev(udata_t *udata) 65 | { return RB_PREV(udatatree_s, &Head, udata); } 66 | static udata_t *udata_min(void) 67 | { return RB_MIN(udatatree_s, &Head); } 68 | static udata_t *udata_max(void) 69 | { return RB_MAX(udatatree_s, &Head); } 70 | static udata_t *udata_root(void) 71 | { return RB_ROOT(&Head); } 72 | #endif 73 | 74 | void *udata_new(lua_State *L, size_t size, uint64_t id_, const char *mt) 75 | /* Creates a new Lua userdata, optionally sets its metatable to mt (if != NULL), 76 | * associates the userdata with the passed id and pushes the userdata on the stack. 77 | * 78 | * The userdata can be subsquently pushed on the Lua stack with udata_push(L, id). 79 | * (This is useful to bind C/C++ objects to Lua userdata). 80 | * 81 | * If id=0, the pointer to the memory area allocated by Lua is used as identifier 82 | * (this function returnes it). 83 | */ 84 | { 85 | udata_t *udata; 86 | if((udata = (udata_t*)Malloc(L, sizeof(udata_t))) == NULL) 87 | { luaL_error(L, "cannot allocate memory"); return NULL; } 88 | memset(udata, 0, sizeof(udata_t)); 89 | udata->mem = lua_newuserdata(L, size); 90 | if(!udata->mem) 91 | { 92 | Free(L, udata); 93 | luaL_error(L, "lua_newuserdata error"); 94 | return NULL; 95 | } 96 | udata->id = id_ != 0 ? id_ : (uint64_t)(uintptr_t)(udata->mem); 97 | if(udata_search(udata->id)) 98 | { 99 | Free(L, udata); 100 | luaL_error(L, "duplicated object %I", id_); 101 | return NULL; 102 | } 103 | /* create a reference for later push's */ 104 | lua_pushvalue(L, -1); /* the newly created userdata */ 105 | udata->ref = luaL_ref(L, LUA_REGISTRYINDEX); 106 | udata_insert(udata); 107 | if(mt) 108 | { 109 | udata->mt = mt; 110 | luaL_getmetatable(L, mt); 111 | lua_setmetatable(L, -2); 112 | } 113 | return udata->mem; 114 | } 115 | 116 | void *udata_mem(uint64_t id) 117 | { 118 | udata_t *udata = udata_search(id); 119 | return udata ? udata->mem : NULL; 120 | } 121 | 122 | int udata_unref(lua_State *L, uint64_t id) 123 | /* unreference udata so that it will be garbage collected */ 124 | { 125 | // printf("unref object %lu\n", id); 126 | udata_t *udata = udata_search(id); 127 | if(!udata) 128 | return luaL_error(L, "invalid object identifier %p", id); 129 | if(udata->ref != LUA_NOREF) 130 | { 131 | luaL_unref(L, LUA_REGISTRYINDEX, udata->ref); 132 | udata->ref = LUA_NOREF; 133 | } 134 | return 0; 135 | } 136 | 137 | int udata_free(lua_State *L, uint64_t id) 138 | /* this should be called in the __gc metamethod 139 | */ 140 | { 141 | udata_t *udata = udata_search(id); 142 | // printf("free object %lu\n", id); 143 | if(!udata) 144 | return luaL_error(L, "invalid object identifier %p", id); 145 | /* release all references */ 146 | if(udata->ref != LUA_NOREF) 147 | luaL_unref(L, LUA_REGISTRYINDEX, udata->ref); 148 | udata_remove(udata); 149 | Free(L, udata); 150 | /* mem is released by Lua at garbage collection */ 151 | return 0; 152 | } 153 | 154 | 155 | int udata_push(lua_State *L, uint64_t id) 156 | { 157 | udata_t *udata = udata_search(id); 158 | if(!udata) 159 | return luaL_error(L, "invalid object identifier %p", id); 160 | if(udata->ref == LUA_NOREF) 161 | return luaL_error(L, "unreferenced object"); 162 | if(lua_rawgeti(L, LUA_REGISTRYINDEX, udata->ref) != LUA_TUSERDATA) 163 | return luaL_error(L, UNEXPECTED_ERROR); 164 | return 1; /* one value pushed */ 165 | } 166 | 167 | void udata_free_all(lua_State *L) 168 | /* free all without unreferencing (for atexit()) */ 169 | { 170 | udata_t *udata; 171 | while((udata = udata_first(0))) 172 | { 173 | udata_remove(udata); 174 | Free(L, udata); 175 | } 176 | } 177 | 178 | int udata_scan(lua_State *L, const char *mt, 179 | void *info, int (*func)(lua_State *L, const void *mem, const char* mt, const void *info)) 180 | /* scans the udata database, and calls the func callback for every 'mt' object found 181 | * (the object may be deleted in the callback). 182 | * func must return 0 to continue the scan, !=0 to interrupt it. 183 | * returns 1 if interrupted, 0 otherwise 184 | */ 185 | { 186 | int stop = 0; 187 | uint64_t id = 0; 188 | udata_t *udata; 189 | while((udata = udata_first(id))) 190 | { 191 | id = udata->id + 1; 192 | if(mt == udata->mt) 193 | { 194 | stop = func(L, (const void*)(udata->mem), mt, info); 195 | if(stop) return 1; 196 | } 197 | } 198 | return 0; 199 | } 200 | 201 | 202 | static int is_subclass(lua_State *L, int arg, int mt_index) 203 | { 204 | int ok; 205 | if(luaL_getmetafield(L, arg, "__index") == LUA_TNIL) 206 | return 0; 207 | if(lua_rawequal(L, mt_index, lua_gettop(L))) 208 | { 209 | lua_pop(L, 1); 210 | return 1; 211 | } 212 | ok = is_subclass(L, lua_gettop(L), mt_index); 213 | lua_pop(L, 1); 214 | return ok; 215 | } 216 | 217 | void *udata_test(lua_State *L, int arg, const char *mt) 218 | /* same as luaL_testudata(), but succeeds also if the userdata has not 219 | * mt as metatable but as ancestor 220 | */ 221 | { 222 | void *mem; 223 | int ok; 224 | if((mem = luaL_testudata(L, arg, mt))) 225 | return mem; 226 | 227 | if((mem = lua_touserdata(L, arg)) == NULL) 228 | return NULL; 229 | 230 | if(luaL_getmetatable(L, mt)!=LUA_TTABLE) 231 | { luaL_error(L, "cannot find metatable '%s'", mt); return NULL; } 232 | ok = is_subclass(L, arg, lua_gettop(L)); 233 | lua_pop(L, 1); 234 | return ok ? mem : NULL; 235 | } 236 | 237 | /*------------------------------------------------------------------------------* 238 | | newmetatable for 'class' userdata | 239 | *------------------------------------------------------------------------------*/ 240 | 241 | int udata_define(lua_State *L, const char* mt, const luaL_Reg* methods, const luaL_Reg* metamethods) 242 | { 243 | /* create the metatable */ 244 | if(!luaL_newmetatable(L, mt)) 245 | return luaL_error(L, "cannot create metatable '%s'", mt); 246 | lua_pushvalue(L, -1); 247 | lua_setfield(L, -2, "__index"); 248 | if(metamethods) 249 | /* add metamethods */ 250 | luaL_setfuncs(L, metamethods, 0); 251 | if(methods) 252 | { 253 | /* add methods */ 254 | luaL_getsubtable(L, -1, "__index"); 255 | luaL_setfuncs(L, methods, 0); 256 | lua_pop(L, 1); 257 | } 258 | lua_pop(L, 1); 259 | return 0; 260 | } 261 | 262 | int udata_inherit(lua_State *L, const char *sub, const char *super) 263 | /* Sets metatable(sub).__index = metatable(super) 264 | * 265 | * This way, if one accesses a field/method of a 'sub' object which is not defined in 266 | * the 'sub' metatable, Lua searches for it in the 'super' metatable (i.e., the 'sub' 267 | * type inherits from 'super'). 268 | */ 269 | { 270 | if(luaL_getmetatable(L, sub)!=LUA_TTABLE) 271 | return luaL_error(L, "cannot find metatable '%s'", sub); 272 | luaL_getsubtable(L, -1, "__index"); 273 | if(luaL_getmetatable(L, super)!=LUA_TTABLE) 274 | return luaL_error(L, "cannot find metatable '%s'", super); 275 | lua_setmetatable(L, -2); 276 | lua_pop(L, 2); 277 | return 0; 278 | } 279 | 280 | int udata_addmethods(lua_State *L, const char* mt, const luaL_Reg* methods) 281 | /* Adds methods to the metatable mt */ 282 | { 283 | if(luaL_getmetatable(L, mt)!=LUA_TTABLE) 284 | return luaL_error(L, "cannot find metatable '%s'", mt); 285 | if(methods) 286 | { 287 | /* add methods */ 288 | luaL_getsubtable(L, -1, "__index"); 289 | luaL_setfuncs(L, methods, 0); 290 | lua_pop(L, 1); 291 | } 292 | lua_pop(L, 1); 293 | return 0; 294 | } 295 | 296 | -------------------------------------------------------------------------------- /src/udata.h: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2021 Stefano Trettel 4 | * 5 | * Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | #include 32 | #include "lua.h" 33 | #include "lauxlib.h" 34 | #include "compat-5.3.h" 35 | 36 | #ifndef Malloc 37 | #define Malloc moonusb_Malloc 38 | void *Malloc(lua_State *L, size_t size); 39 | #define Free moonusb_Free 40 | void Free(lua_State *L, void *ptr); 41 | #endif 42 | 43 | #define udata_t moonusb_udata_t 44 | #define udata_s moonusb_udata_s 45 | #define moonusb_udata_t struct moonusb_udata_s 46 | 47 | #define udata_new moonusb_udata_new 48 | void *udata_new(lua_State*, size_t, uint64_t, const char*); 49 | #define udata_unref moonusb_udata_unref 50 | int udata_unref(lua_State *L, uint64_t); 51 | #define udata_free moonusb_udata_free 52 | int udata_free(lua_State*, uint64_t); 53 | #define udata_mem moonusb_udata_mem 54 | void *udata_mem(uint64_t); 55 | #define udata_push moonusb_udata_push 56 | int udata_push(lua_State*, uint64_t); 57 | #define udata_free_all moonusb_udata_free_all 58 | void udata_free_all(lua_State *L); 59 | #define udata_scan moonusb_udata_scan 60 | int udata_scan(lua_State *L, const char *mt, 61 | void *info, int (*func)(lua_State *L, const void *mem, const char* mt, const void *info)); 62 | #define udata_define moonusb_udata_define 63 | int udata_define(lua_State*, const char*, const luaL_Reg*, const luaL_Reg*); 64 | #define udata_inherit moonusb_udata_inherit 65 | int udata_inherit(lua_State*, const char*, const char*); 66 | #define udata_test moonusb_udata_test 67 | void *udata_test(lua_State*, int, const char*); 68 | #define udata_addmethods moonusb_udata_addmethods 69 | int udata_addmethods(lua_State*, const char*, const luaL_Reg*); 70 | 71 | #ifdef __cplusplus 72 | } 73 | #endif 74 | 75 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2021 Stefano Trettel 4 | * 5 | * Software repository: MoonUSB, https://github.com/stetre/moonusb 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | 27 | #include "internal.h" 28 | 29 | /*------------------------------------------------------------------------------* 30 | | Misc utilities | 31 | *------------------------------------------------------------------------------*/ 32 | 33 | int noprintf(const char *fmt, ...) 34 | { (void)fmt; return 0; } 35 | 36 | int notavailable(lua_State *L, ...) 37 | { 38 | return luaL_error(L, "function not available in this CL version"); 39 | } 40 | 41 | /*------------------------------------------------------------------------------* 42 | | Malloc | 43 | *------------------------------------------------------------------------------*/ 44 | 45 | /* We do not use malloc(), free() etc directly. Instead, we inherit the memory 46 | * allocator from the main Lua state instead (see lua_getallocf in the Lua manual) 47 | * and use that. 48 | * 49 | * By doing so, we can use an alternative malloc() implementation without recompiling 50 | * this library (we have needs to recompile lua only, or execute it with LD_PRELOAD 51 | * set to the path to the malloc library we want to use). 52 | */ 53 | static lua_Alloc Alloc = NULL; 54 | static void* AllocUd = NULL; 55 | 56 | static void malloc_init(lua_State *L) 57 | { 58 | if(Alloc) unexpected(L); 59 | Alloc = lua_getallocf(L, &AllocUd); 60 | } 61 | 62 | static void* Malloc_(size_t size) 63 | { return Alloc ? Alloc(AllocUd, NULL, 0, size) : NULL; } 64 | 65 | static void Free_(void *ptr) 66 | { if(Alloc) Alloc(AllocUd, ptr, 0, 0); } 67 | 68 | void *Malloc(lua_State *L, size_t size) 69 | { 70 | void *ptr; 71 | if(size == 0) 72 | { luaL_error(L, errstring(ERR_MALLOC_ZERO)); return NULL; } 73 | ptr = Malloc_(size); 74 | if(ptr==NULL) 75 | { luaL_error(L, errstring(ERR_MEMORY)); return NULL; } 76 | memset(ptr, 0, size); 77 | //DBG("Malloc %p\n", ptr); 78 | return ptr; 79 | } 80 | 81 | void *MallocNoErr(lua_State *L, size_t size) /* do not raise errors (check the retval) */ 82 | { 83 | void *ptr = Malloc_(size); 84 | (void)L; 85 | if(ptr==NULL) 86 | return NULL; 87 | memset(ptr, 0, size); 88 | //DBG("MallocNoErr %p\n", ptr); 89 | return ptr; 90 | } 91 | 92 | char *Strdup(lua_State *L, const char *s) 93 | { 94 | size_t len = strnlen(s, 256); 95 | char *ptr = (char*)Malloc(L, len + 1); 96 | if(len>0) 97 | memcpy(ptr, s, len); 98 | ptr[len]='\0'; 99 | return ptr; 100 | } 101 | 102 | 103 | void Free(lua_State *L, void *ptr) 104 | { 105 | (void)L; 106 | //DBG("Free %p\n", ptr); 107 | if(ptr) Free_(ptr); 108 | } 109 | 110 | /*------------------------------------------------------------------------------* 111 | | Time utilities | 112 | *------------------------------------------------------------------------------*/ 113 | 114 | #if defined(LINUX) 115 | 116 | #if 0 117 | static double tstosec(const struct timespec *ts) 118 | { 119 | return ts->tv_sec*1.0+ts->tv_nsec*1.0e-9; 120 | } 121 | #endif 122 | 123 | static void sectots(struct timespec *ts, double seconds) 124 | { 125 | ts->tv_sec=(time_t)seconds; 126 | ts->tv_nsec=(long)((seconds-((double)ts->tv_sec))*1.0e9); 127 | } 128 | 129 | double now(void) 130 | { 131 | #if _POSIX_C_SOURCE >= 199309L 132 | struct timespec ts; 133 | if(clock_gettime(CLOCK_MONOTONIC,&ts)!=0) 134 | { printf("clock_gettime error\n"); return -1; } 135 | return ts.tv_sec + ts.tv_nsec*1.0e-9; 136 | #else 137 | struct timeval tv; 138 | if(gettimeofday(&tv, NULL) != 0) 139 | { printf("gettimeofday error\n"); return -1; } 140 | return tv.tv_sec + tv.tv_usec*1.0e-6; 141 | #endif 142 | } 143 | 144 | void sleeep(double seconds) 145 | { 146 | #if _POSIX_C_SOURCE >= 199309L 147 | struct timespec ts, ts1; 148 | struct timespec *req, *rem, *tmp; 149 | sectots(&ts, seconds); 150 | req = &ts; 151 | rem = &ts1; 152 | while(1) 153 | { 154 | if(nanosleep(req, rem) == 0) 155 | return; 156 | tmp = req; 157 | req = rem; 158 | rem = tmp; 159 | } 160 | #else 161 | usleep((useconds_t)(seconds*1.0e6)); 162 | #endif 163 | } 164 | 165 | #define time_init(L) do { (void)L; /* do nothing */ } while(0) 166 | 167 | #elif defined(MINGW) 168 | 169 | #include 170 | 171 | static LARGE_INTEGER Frequency; 172 | double now(void) 173 | { 174 | LARGE_INTEGER ts; 175 | QueryPerformanceCounter(&ts); 176 | return ((double)(ts.QuadPart))/Frequency.QuadPart; 177 | } 178 | 179 | void sleeep(double seconds) 180 | { 181 | DWORD msec = (DWORD)seconds * 1000; 182 | //if(msec < 0) return; DWORD seems to be unsigned 183 | Sleep(msec); 184 | } 185 | 186 | static void time_init(lua_State *L) 187 | { 188 | (void)L; 189 | QueryPerformanceFrequency(&Frequency); 190 | } 191 | 192 | #endif 193 | 194 | 195 | /*------------------------------------------------------------------------------* 196 | | Light userdata | 197 | *------------------------------------------------------------------------------*/ 198 | 199 | void *checklightuserdata(lua_State *L, int arg) 200 | { 201 | if(lua_type(L, arg) != LUA_TLIGHTUSERDATA) 202 | { luaL_argerror(L, arg, "expected lightuserdata"); return NULL; } 203 | return lua_touserdata(L, arg); 204 | } 205 | 206 | void *optlightuserdata(lua_State *L, int arg) 207 | { 208 | if(lua_isnoneornil(L, arg)) 209 | return NULL; 210 | return checklightuserdata(L, arg); 211 | } 212 | 213 | void *checklightuserdataorzero(lua_State *L, int arg) 214 | { 215 | int val, isnum; 216 | val = lua_tointegerx(L, arg, &isnum); 217 | if(!isnum) 218 | return checklightuserdata(L, arg); 219 | if(val != 0) 220 | luaL_argerror(L, arg, "expected lightuserdata or 0"); 221 | return NULL; 222 | } 223 | 224 | /*------------------------------------------------------------------------------* 225 | | Deep copy of a table | 226 | *------------------------------------------------------------------------------*/ 227 | 228 | int copytable(lua_State *L) 229 | /* Deep-copies the contents of the table at arg=-1 to the table at arg=-2 230 | * Leaves them on the stack. 231 | */ 232 | { 233 | int src = lua_gettop(L); 234 | int dst = src - 1; 235 | lua_pushnil(L); 236 | while(lua_next(L, src)) 237 | { 238 | lua_pushvalue(L, -2); // key 239 | if(lua_type(L, -1)==LUA_TTABLE) /* nested */ 240 | { 241 | lua_newtable(L); //dst 242 | lua_pushvalue(L, -3); // value 243 | copytable(L); 244 | lua_pop(L, 1); 245 | } 246 | else 247 | lua_pushvalue(L, -2); // value 248 | lua_rawset(L, dst); 249 | lua_pop(L, 1); // value 250 | } 251 | return 0; 252 | } 253 | 254 | /*------------------------------------------------------------------------------* 255 | | Custom luaL_checkxxx() style functions | 256 | *------------------------------------------------------------------------------*/ 257 | 258 | int checkboolean(lua_State *L, int arg) 259 | { 260 | if(!lua_isboolean(L, arg)) 261 | return (int)luaL_argerror(L, arg, "boolean expected"); 262 | return lua_toboolean(L, arg); // ? 1 : 0; 263 | } 264 | 265 | 266 | int testboolean(lua_State *L, int arg, int *err) 267 | { 268 | if(!lua_isboolean(L, arg)) 269 | { *err = ERR_TYPE; return 0; } 270 | *err = 0; 271 | return lua_toboolean(L, arg); // ? 1 : 0; 272 | } 273 | 274 | 275 | int optboolean(lua_State *L, int arg, int d) 276 | { 277 | if(!lua_isboolean(L, arg)) 278 | return d; 279 | return lua_toboolean(L, arg); 280 | } 281 | 282 | /* 1-based index to 0-based ------------------------------------------*/ 283 | 284 | int testindex(lua_State *L, int arg, int *err) 285 | { 286 | int val; 287 | if(!lua_isinteger(L, arg)) 288 | { *err = ERR_TYPE; return 0; } 289 | val = lua_tointeger(L, arg); 290 | if(val < 1) 291 | { *err = ERR_GENERIC; return 0; } 292 | *err = 0; 293 | return val - 1; 294 | } 295 | 296 | int checkindex(lua_State *L, int arg) 297 | { 298 | int val = luaL_checkinteger(L, arg); 299 | if(val < 1) 300 | return luaL_argerror(L, arg, "positive integer expected"); 301 | return val - 1; 302 | } 303 | 304 | int optindex(lua_State *L, int arg, int optval /* 0-based */) 305 | { 306 | int val = luaL_optinteger(L, arg, optval + 1); 307 | if(val < 1) 308 | return luaL_argerror(L, arg, "positive integer expected"); 309 | return val - 1; 310 | } 311 | 312 | void pushindex(lua_State *L, int val) 313 | { lua_pushinteger((L), (val) + 1); } 314 | 315 | /*------------------------------------------------------------------------------* 316 | | Internal error codes | 317 | *------------------------------------------------------------------------------*/ 318 | 319 | const char* errstring(int err) 320 | { 321 | switch(err) 322 | { 323 | case ERR_NOTPRESENT: return "missing"; 324 | case ERR_SUCCESS: return "success"; 325 | case ERR_GENERIC: return "generic error"; 326 | case ERR_TABLE: return "not a table"; 327 | case ERR_FUNCTION: return "not a function"; 328 | case ERR_EMPTY: return "empty list"; 329 | case ERR_TYPE: return "invalid type"; 330 | case ERR_ELEMTYPE: return "invalid element type"; 331 | case ERR_VALUE: return "invalid value"; 332 | case ERR_ELEMVALUE: return "invalid element value"; 333 | case ERR_MEMORY: return "out of memory"; 334 | case ERR_MALLOC_ZERO: return "zero bytes malloc"; 335 | case ERR_LENGTH: return "invalid length"; 336 | case ERR_POOL: return "elements are not from the same pool"; 337 | case ERR_BOUNDARIES: return "invalid boundaries"; 338 | case ERR_RANGE: return "out of range"; 339 | case ERR_FOPEN: return "cannot open file"; 340 | case ERR_OPERATION: return "operation failed"; 341 | case ERR_UNKNOWN: return "unknown field name"; 342 | default: 343 | return "???"; 344 | } 345 | return NULL; /* unreachable */ 346 | } 347 | 348 | 349 | /*------------------------------------------------------------------------------* 350 | | Inits | 351 | *------------------------------------------------------------------------------*/ 352 | 353 | void moonusb_utils_init(lua_State *L) 354 | { 355 | malloc_init(L); 356 | time_init(L); 357 | } 358 | 359 | -------------------------------------------------------------------------------- /thirdparty/asciidoctor-styles-license: -------------------------------------------------------------------------------- 1 | The CSS used for the Reference Manual is taken from the 2 | Asciidoctor styles project 3 | (https://github.com/asciidoctor/asciidoctor-stylesheet-factory ). 4 | 5 | Below is a copy of the Asciidoctor styles copyright notice. 6 | 7 | Stefano Trettel 8 | 9 | ======================================================================== 10 | 11 | Asciidoctor styles 12 | ------------------ 13 | 14 | Copyright (c) 2013 Dan Allen 15 | 16 | MIT License 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining 19 | a copy of this software and associated documentation files (the 20 | "Software"), to deal in the Software without restriction, including 21 | without limitation the rights to use, copy, modify, merge, publish, 22 | distribute, sublicense, and/or sell copies of the Software, and to 23 | permit persons to whom the Software is furnished to do so, subject to 24 | the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be 27 | included in all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 30 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 31 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 32 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 33 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 34 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 35 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 | 37 | 38 | Other licensed work 39 | ------------------- 40 | 41 | - Foundation 4 by Zurb, on which the themes are built, is licensed under the 42 | Apache License, v2.0: 43 | 44 | http://apache.org/licenses/LICENSE-2.0 45 | http://foundation.zurb.com 46 | 47 | - The riak theme is derived from the Riak documentation theme by Basho, 48 | licensed under the Creative Commons Attribution 3.0 Unported License: 49 | 50 | http://creativecommons.org/licenses/by/3.0/us 51 | http://docs.basho.org 52 | 53 | - The iconic theme is inspired by O'Reilly typography and Atlas manual. 54 | 55 | http://oreilly.com 56 | -------------------------------------------------------------------------------- /thirdparty/lua-compat-5.3-license: -------------------------------------------------------------------------------- 1 | The src/compat-5.3.h and src/compat-5.3.c files are taken from: 2 | https://github.com/keplerproject/lua-compat-5.3 3 | 4 | Below is a copy of the original copyright notice, from the same source. 5 | 6 | Stefano Trettel 7 | 8 | =========================================================================== 9 | 10 | The MIT License (MIT) 11 | 12 | Copyright (c) 2015 Kepler Project. 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy of 15 | this software and associated documentation files (the "Software"), to deal in 16 | the Software without restriction, including without limitation the rights to 17 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 18 | the Software, and to permit persons to whom the Software is furnished to do so, 19 | subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all 22 | copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 26 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 27 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 28 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | -------------------------------------------------------------------------------- /thirdparty/openbsd-tree-license: -------------------------------------------------------------------------------- 1 | The src/tree.h module is taken from: 2 | http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/sys/tree.h 3 | 4 | Below is a copy of the copyright notice included in the module. 5 | 6 | Stefano Trettel 7 | 8 | =========================================================================== 9 | /* 10 | * Copyright 2002 Niels Provos 11 | * All rights reserved. 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 1. Redistributions of source code must retain the above copyright 17 | * notice, this list of conditions and the following disclaimer. 18 | * 2. Redistributions in binary form must reproduce the above copyright 19 | * notice, this list of conditions and the following disclaimer in the 20 | * documentation and/or other materials provided with the distribution. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | -------------------------------------------------------------------------------- /thirdparty/powered-by-lua-license: -------------------------------------------------------------------------------- 1 | The 'powered by Lua' logo (doc/powered-by-lua.gif ) is taken from: 2 | http://www.lua.org/images . 3 | 4 | Below is a copy of the copyright notice from the same web-page. 5 | 6 | Stefano Trettel 7 | 8 | =========================================================================== 9 | Copyright © 1998 Lua.org. Graphic design by Alexandre Nakonechnyj. 10 | 11 | Permission is hereby granted, without written agreement and without license 12 | or royalty fees, to use, copy, and distribute this logo for any purpose, 13 | including commercial applications, subject to the following conditions: 14 | 15 | - The origin of this logo must not be misrepresented; you must not claim that 16 | you drew the original logo. 17 | - The only modification you can make is to adapt the orbiting text to your 18 | product name. 19 | - The logo can be used in any scale as long as the relative proportions of its 20 | elements are maintained. 21 | --------------------------------------------------------------------------------