添加modem以及相关依赖
This commit is contained in:
4
applications/luci-app-modem/.gitattributes
vendored
Normal file
4
applications/luci-app-modem/.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
#指定换行符
|
||||
/luasrc/** text eol=lf
|
||||
# /old/* text eol=lf
|
||||
/root/** text eol=lf
|
||||
49
applications/luci-app-modem/.gitignore
vendored
Normal file
49
applications/luci-app-modem/.gitignore
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
674
applications/luci-app-modem/LICENSE
Normal file
674
applications/luci-app-modem/LICENSE
Normal file
@@ -0,0 +1,674 @@
|
||||
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>.
|
||||
41
applications/luci-app-modem/Makefile
Normal file
41
applications/luci-app-modem/Makefile
Normal file
@@ -0,0 +1,41 @@
|
||||
# Copyright (C) 2020 Lienol <lawlienol@gmail.com>
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v3.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-modem
|
||||
LUCI_TITLE:=LuCI support for Modem
|
||||
LUCI_PKGARCH:=all
|
||||
PKG_VERSION:=20231207-1.0
|
||||
PKG_LICENSE:=GPLv3
|
||||
PKG_LINCESE_FILES:=LICENSE
|
||||
PKF_MAINTAINER:=siriling <siriling@qq.com>
|
||||
LUCI_DEPENDS:=+luci-compat +kmod-usb-net +kmod-usb-net-cdc-ether +kmod-usb-acm \
|
||||
+kmod-usb-net-qmi-wwan +kmod-usb-net-rndis +kmod-usb-serial-qualcomm \
|
||||
+kmod-usb-net-sierrawireless +kmod-usb-ohci +kmod-usb-serial \
|
||||
+kmod-usb-serial-option +kmod-usb-wdm \
|
||||
+kmod-usb2 +kmod-usb3 \
|
||||
+kmod-usb-net-cdc-mbim \
|
||||
+usbutils \
|
||||
+kmod-pcie_mhi \
|
||||
+pciutils \
|
||||
+quectel-CM-5G \
|
||||
+sms-tool \
|
||||
+jq +grep\
|
||||
|
||||
define Package/luci-app-modem/conffiles
|
||||
/etc/config/modem
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/config
|
||||
# shown in make menuconfig <Help>
|
||||
help
|
||||
$(LUCI_TITLE)
|
||||
Version: $(PKG_VERSION)
|
||||
endef
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
14
applications/luci-app-modem/README.md
Normal file
14
applications/luci-app-modem/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# luci-app-modem
|
||||
|
||||
- 原项目地址:https://github.com/momokind/luci-app-hypermodem
|
||||
|
||||
## 说明
|
||||
|
||||
- 在原项目的基础上修改代码逻辑
|
||||
|
||||
- 添加USB和PCIe等依赖支持
|
||||
|
||||
- 添加多模块支持
|
||||
|
||||
- 支持USB和PCIE两种连接模式的5G模块用QMI模式进行IPv6拨号,然后下发给内网设备
|
||||
|
||||
575
applications/luci-app-modem/luasrc/controller/modem.lua
Normal file
575
applications/luci-app-modem/luasrc/controller/modem.lua
Normal file
@@ -0,0 +1,575 @@
|
||||
-- Copyright 2024 Siriling <siriling@qq.com>
|
||||
module("luci.controller.modem", package.seeall)
|
||||
local http = require "luci.http"
|
||||
local fs = require "nixio.fs"
|
||||
local json = require("luci.jsonc")
|
||||
uci = luci.model.uci.cursor()
|
||||
local script_path="/usr/share/modem/"
|
||||
|
||||
function index()
|
||||
if not nixio.fs.access("/etc/config/modem") then
|
||||
return
|
||||
end
|
||||
|
||||
entry({"admin", "network", "modem"}, alias("admin", "network", "modem", "modem_info"), translate("Modem"), 100).dependent = true
|
||||
|
||||
--模块信息
|
||||
entry({"admin", "network", "modem", "modem_info"}, template("modem/modem_info"), translate("Modem Information"),10).leaf = true
|
||||
entry({"admin", "network", "modem", "get_at_port"}, call("getATPort"), nil).leaf = true
|
||||
entry({"admin", "network", "modem", "get_modem_info"}, call("getModemInfo")).leaf = true
|
||||
|
||||
--拨号配置
|
||||
entry({"admin", "network", "modem", "index"},cbi("modem/index"),translate("Dial Config"),20).leaf = true
|
||||
entry({"admin", "network", "modem", "config"}, cbi("modem/config")).leaf = true
|
||||
entry({"admin", "network", "modem", "get_modems"}, call("getModems"), nil).leaf = true
|
||||
entry({"admin", "network", "modem", "status"}, call("act_status")).leaf = true
|
||||
|
||||
--模块调试
|
||||
entry({"admin", "network", "modem", "modem_debug"},template("modem/modem_debug"),translate("Modem Debug"),30).leaf = true
|
||||
entry({"admin", "network", "modem", "get_quick_commands"}, call("getQuickCommands"), nil).leaf = true
|
||||
entry({"admin", "network", "modem", "send_at_command"}, call("sendATCommand"), nil).leaf = true
|
||||
entry({"admin", "network", "modem", "get_modem_debug_info"}, call("getModemDebugInfo"), nil).leaf = true
|
||||
entry({"admin", "network", "modem", "set_mode"}, call("setMode"), nil).leaf = true
|
||||
entry({"admin", "network", "modem", "set_network_prefer"}, call("setNetworkPrefer"), nil).leaf = true
|
||||
entry({"admin", "network", "modem", "quick_commands_config"}, cbi("modem/quick_commands_config")).leaf = true
|
||||
|
||||
--AT命令旧界面
|
||||
entry({"admin", "network", "modem", "at_command_old"},template("modem/at_command_old")).leaf = true
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 判断字符串是否含有字母
|
||||
@Params
|
||||
str 字符串
|
||||
]]
|
||||
function hasLetters(str)
|
||||
local pattern = "%a" -- 匹配字母的正则表达式
|
||||
return string.find(str, pattern) ~= nil
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 执行AT命令
|
||||
@Params
|
||||
at_port AT串口
|
||||
at_command AT命令
|
||||
]]
|
||||
function at(at_port,at_command)
|
||||
local odpall = io.popen("cd "..script_path.." && source "..script_path.."modem_debug.sh && at "..at_port.." "..at_command)
|
||||
local odp = odpall:read("*a")
|
||||
odpall:close()
|
||||
odp=string.gsub(odp, "\r", "")
|
||||
return odp
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 获取模组连接状态
|
||||
@Params
|
||||
at_port AT串口
|
||||
manufacturer 制造商
|
||||
]]
|
||||
function getModemConnectStatus(at_port,manufacturer)
|
||||
|
||||
local connect_status="unknown"
|
||||
|
||||
if at_port and manufacturer~="unknown" then
|
||||
local odpall = io.popen("cd "..script_path.." && source "..script_path..manufacturer..".sh && "..manufacturer.."_get_connect_status "..at_port)
|
||||
opd = odpall:read("*a")
|
||||
odpall:close()
|
||||
connect_status = string.gsub(opd, "\n", "")
|
||||
end
|
||||
|
||||
return connect_status
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 获取模组设备信息
|
||||
@Params
|
||||
at_port AT串口
|
||||
]]
|
||||
function getModemDeviceInfo(at_port)
|
||||
local modem_device_info={}
|
||||
|
||||
uci:foreach("modem", "modem-device", function (modem_device)
|
||||
if at_port == modem_device["at_port"] then
|
||||
--获取数据接口
|
||||
local data_interface=modem_device["data_interface"]:upper()
|
||||
--获取连接状态
|
||||
local connect_status=getModemConnectStatus(modem_device["at_port"],modem_device["manufacturer"])
|
||||
|
||||
--设置值
|
||||
modem_device_info=modem_device
|
||||
modem_device_info["data_interface"]=data_interface
|
||||
modem_device_info["connect_status"]=connect_status
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
return modem_device_info
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 获取模组更多信息
|
||||
@Params
|
||||
at_port AT串口
|
||||
manufacturer 制造商
|
||||
]]
|
||||
function getModemMoreInfo(at_port,manufacturer)
|
||||
|
||||
--获取模组信息
|
||||
local odpall = io.popen("sh "..script_path.."modem_info.sh".." "..at_port.." "..manufacturer)
|
||||
local opd = odpall:read("*a")
|
||||
odpall:close()
|
||||
|
||||
--设置值
|
||||
local modem_more_info=json.parse(opd)
|
||||
return modem_more_info
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 模块状态获取
|
||||
]]
|
||||
function getModemInfo()
|
||||
|
||||
--获取AT串口
|
||||
local at_port = http.formvalue("port")
|
||||
|
||||
--获取信息
|
||||
local modem_device_info
|
||||
local modem_more_info
|
||||
if at_port then
|
||||
modem_device_info=getModemDeviceInfo(at_port)
|
||||
modem_more_info=getModemMoreInfo(at_port,modem_device_info["manufacturer"])
|
||||
end
|
||||
|
||||
--设置信息
|
||||
local modem_info={}
|
||||
modem_info["device_info"]=modem_device_info
|
||||
modem_info["more_info"]=modem_more_info
|
||||
|
||||
--设置翻译
|
||||
local translation={}
|
||||
--设备信息翻译
|
||||
-- if modem_device_info then
|
||||
-- local name=modem_device_info["name"]
|
||||
-- translation[name]=luci.i18n.translate(name)
|
||||
-- local manufacturer=modem_device_info["manufacturer"]
|
||||
-- translation[manufacturer]=luci.i18n.translate(manufacturer)
|
||||
-- local mode=modem_device_info["mode"]
|
||||
-- translation[mode]=luci.i18n.translate(mode)
|
||||
-- local data_interface=modem_device_info["data_interface"]
|
||||
-- translation[data_interface]=luci.i18n.translate(data_interface)
|
||||
-- local network=modem_device_info["network"]
|
||||
-- translation[network]=luci.i18n.translate(network)
|
||||
-- end
|
||||
|
||||
--基本信息翻译
|
||||
-- if modem_more_info["base_info"] then
|
||||
-- for key in pairs(modem_more_info["base_info"]) do
|
||||
-- local value=modem_more_info["base_info"][key]
|
||||
-- --翻译值
|
||||
-- translation[value]=luci.i18n.translate(value)
|
||||
-- end
|
||||
-- end
|
||||
--SIM卡信息翻译
|
||||
if modem_more_info["sim_info"] then
|
||||
local sim_info=modem_more_info["sim_info"]
|
||||
for i = 1, #sim_info do
|
||||
local info = sim_info[i]
|
||||
for key in pairs(info) do
|
||||
--翻译键
|
||||
translation[key]=luci.i18n.translate(key)
|
||||
-- local value=info[key]
|
||||
-- if hasLetters(value) then
|
||||
-- --翻译值
|
||||
-- translation[value]=luci.i18n.translate(value)
|
||||
-- end
|
||||
end
|
||||
end
|
||||
end
|
||||
--网络信息翻译
|
||||
if modem_more_info["network_info"] then
|
||||
local network_info=modem_more_info["network_info"]
|
||||
for i = 1, #network_info do
|
||||
local info = network_info[i]
|
||||
for key in pairs(info) do
|
||||
--翻译键
|
||||
translation[key]=luci.i18n.translate(key)
|
||||
-- local value=info[key]
|
||||
-- if hasLetters(value) then
|
||||
-- --翻译值
|
||||
-- translation[value]=luci.i18n.translate(value)
|
||||
-- end
|
||||
end
|
||||
end
|
||||
end
|
||||
--小区信息翻译
|
||||
if modem_more_info["cell_info"] then
|
||||
for network_mode_key in pairs(modem_more_info["cell_info"]) do
|
||||
--翻译网络模式
|
||||
translation[network_mode_key]=luci.i18n.translate(network_mode_key)
|
||||
if network_mode_key == "EN-DC Mode" then
|
||||
local network_mode=modem_more_info["cell_info"][network_mode_key]
|
||||
for i = 1, #network_mode do
|
||||
for key in pairs(network_mode[i]) do
|
||||
--获取每个网络类型信息
|
||||
local network_type=network_mode[i][key]
|
||||
for j = 1, #network_type do
|
||||
local info = network_type[j]
|
||||
for key in pairs(info) do
|
||||
translation[key]=luci.i18n.translate(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
--获取网络类型信息
|
||||
local network_type=modem_more_info["cell_info"][network_mode_key]
|
||||
for i = 1, #network_type do
|
||||
local info = network_type[i]
|
||||
for key in pairs(info) do
|
||||
translation[key]=luci.i18n.translate(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--整合数据
|
||||
local data={}
|
||||
data["modem_info"]=modem_info
|
||||
data["translation"]=translation
|
||||
|
||||
-- 写入Web界面
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(data)
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 获取模组信息
|
||||
]]
|
||||
function getModems()
|
||||
|
||||
-- 获取所有模组
|
||||
local modems={}
|
||||
local translation={}
|
||||
uci:foreach("modem", "modem-device", function (modem_device)
|
||||
-- 获取连接状态
|
||||
local connect_status=getModemConnectStatus(modem_device["at_port"],modem_device["manufacturer"])
|
||||
|
||||
-- 获取翻译
|
||||
translation[connect_status]=luci.i18n.translate(connect_status)
|
||||
translation[modem_device["name"]]=luci.i18n.translate(modem_device["name"])
|
||||
translation[modem_device["mode"]]=luci.i18n.translate(modem_device["mode"])
|
||||
|
||||
-- 设置值
|
||||
local modem=modem_device
|
||||
modem["connect_status"]=connect_status
|
||||
|
||||
local modem_tmp={}
|
||||
modem_tmp[modem_device[".name"]]=modem
|
||||
table.insert(modems,modem_tmp)
|
||||
end)
|
||||
|
||||
-- 设置值
|
||||
local data={}
|
||||
data["modems"]=modems
|
||||
data["translation"]=translation
|
||||
|
||||
-- 写入Web界面
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(data)
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 模块列表状态函数
|
||||
]]
|
||||
function act_status()
|
||||
local e = {}
|
||||
e.index = luci.http.formvalue("index")
|
||||
e.status = luci.sys.call(string.format("busybox ps -w | grep -v 'grep' | grep '/var/etc/socat/%s' >/dev/null", luci.http.formvalue("id"))) == 0
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 获取模组的备注
|
||||
@Params
|
||||
network 移动网络
|
||||
]]
|
||||
function getModemRemarks(network)
|
||||
local remarks=""
|
||||
uci:foreach("modem", "config", function (config)
|
||||
---配置启用,且备注存在
|
||||
if network == config["network"] and config["enable"] == "1" then
|
||||
if config["remarks"] then
|
||||
remarks=" ("..config["remarks"]..")" --" (备注)"
|
||||
|
||||
return true --跳出循环
|
||||
end
|
||||
end
|
||||
end)
|
||||
return remarks
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 获取AT串口
|
||||
]]
|
||||
function getATPort()
|
||||
|
||||
local at_ports={}
|
||||
local translation={}
|
||||
|
||||
uci:foreach("modem", "modem-device", function (modem_device)
|
||||
--获取模组的备注
|
||||
local network=modem_device["network"]
|
||||
local remarks=getModemRemarks(network)
|
||||
|
||||
--设置模组AT串口
|
||||
if modem_device["name"] and modem_device["at_port"] then
|
||||
|
||||
local name=modem_device["name"]:upper()..remarks
|
||||
if modem_device["name"] == "unknown" then
|
||||
translation[modem_device["name"]]=luci.i18n.translate(modem_device["name"])
|
||||
name=modem_device["name"]..remarks
|
||||
end
|
||||
|
||||
local at_port = modem_device["at_port"]
|
||||
--排序插入
|
||||
at_port_tmp={}
|
||||
at_port_tmp[at_port]=name
|
||||
table.insert(at_ports, at_port_tmp)
|
||||
end
|
||||
end)
|
||||
|
||||
-- 设置值
|
||||
local data={}
|
||||
data["at_ports"]=at_ports
|
||||
data["translation"]=translation
|
||||
|
||||
-- 写入Web界面
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(data)
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 获取快捷命令
|
||||
]]
|
||||
function getQuickCommands()
|
||||
|
||||
--获取快捷命令选项
|
||||
local quick_option = http.formvalue("option")
|
||||
--获取AT串口
|
||||
local at_port = http.formvalue("port")
|
||||
|
||||
--获取制造商
|
||||
local manufacturer
|
||||
uci:foreach("modem", "modem-device", function (modem_device)
|
||||
--设置模组AT串口
|
||||
if at_port == modem_device["at_port"] then
|
||||
--获取制造商
|
||||
manufacturer=modem_device["manufacturer"]
|
||||
return true --跳出循环
|
||||
end
|
||||
end)
|
||||
|
||||
--未适配模组时,快捷命令选项为自定义
|
||||
if manufacturer=="unknown" then
|
||||
quick_option="custom"
|
||||
end
|
||||
|
||||
local quick_commands={}
|
||||
local commands={}
|
||||
if quick_option=="auto" then
|
||||
--获取模组AT命令
|
||||
-- local odpall = io.popen("cd "..script_path.." && source "..script_path.."modem_debug.sh && get_quick_commands "..quick_option.." "..manufacturer)
|
||||
local odpall = io.popen("cat "..script_path..manufacturer.."_at_commands.json")
|
||||
local opd = odpall:read("*a")
|
||||
odpall:close()
|
||||
quick_commands=json.parse(opd)
|
||||
else
|
||||
uci:foreach("modem", "custom-commands", function (custom_commands)
|
||||
local command={}
|
||||
command[custom_commands["description"]]=custom_commands["command"]
|
||||
table.insert(commands,command)
|
||||
end)
|
||||
quick_commands["quick_commands"]=commands
|
||||
end
|
||||
|
||||
-- 写入Web界面
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(quick_commands)
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 发送AT命令
|
||||
]]
|
||||
function sendATCommand()
|
||||
local at_port = http.formvalue("port")
|
||||
local at_command = http.formvalue("command")
|
||||
|
||||
local response={}
|
||||
if at_port and at_command then
|
||||
response["response"]=at(at_port,at_command)
|
||||
response["time"]=os.date("%Y-%m-%d %H:%M:%S")
|
||||
end
|
||||
|
||||
-- 写入Web界面
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(response)
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 设置网络偏好
|
||||
]]
|
||||
function setNetworkPrefer()
|
||||
local at_port = http.formvalue("port")
|
||||
local network_prefer_config = json.stringify(http.formvalue("prefer_config"))
|
||||
|
||||
--获取制造商
|
||||
local manufacturer
|
||||
uci:foreach("modem", "modem-device", function (modem_device)
|
||||
--设置模组AT串口
|
||||
if at_port == modem_device["at_port"] then
|
||||
--获取制造商
|
||||
manufacturer=modem_device["manufacturer"]
|
||||
return true --跳出循环
|
||||
end
|
||||
end)
|
||||
|
||||
--设置模组网络偏好
|
||||
local odpall = io.popen("cd "..script_path.." && source "..script_path..manufacturer..".sh && "..manufacturer.."_set_network_prefer "..at_port.." "..network_prefer_config)
|
||||
odpall:close()
|
||||
|
||||
--获取设置好后的模组网络偏好
|
||||
local network_prefer={}
|
||||
if at_port and manufacturer and manufacturer~="unknown" then
|
||||
local odpall = io.popen("cd "..script_path.." && source "..script_path..manufacturer..".sh && "..manufacturer.."_get_network_prefer "..at_port)
|
||||
local opd = odpall:read("*a")
|
||||
network_prefer=json.parse(opd)
|
||||
odpall:close()
|
||||
end
|
||||
|
||||
-- 写入Web界面
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(network_prefer)
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 设置拨号模式
|
||||
]]
|
||||
function setMode()
|
||||
local at_port = http.formvalue("port")
|
||||
local mode_config = http.formvalue("mode_config")
|
||||
|
||||
--获取制造商
|
||||
local manufacturer
|
||||
uci:foreach("modem", "modem-device", function (modem_device)
|
||||
--设置模组AT串口
|
||||
if at_port == modem_device["at_port"] then
|
||||
--获取制造商
|
||||
manufacturer=modem_device["manufacturer"]
|
||||
return true --跳出循环
|
||||
end
|
||||
end)
|
||||
|
||||
--设置模组拨号模式
|
||||
local odpall = io.popen("cd "..script_path.." && source "..script_path..manufacturer..".sh && "..manufacturer.."_set_mode "..at_port.." "..mode_config)
|
||||
odpall:close()
|
||||
|
||||
--获取设置好后的模组拨号模式
|
||||
local mode
|
||||
if at_port and manufacturer and manufacturer~="unknown" then
|
||||
local odpall = io.popen("cd "..script_path.." && source "..script_path..manufacturer..".sh && "..manufacturer.."_get_mode "..at_port)
|
||||
mode = odpall:read("*a")
|
||||
mode=string.gsub(mode, "\n", "")
|
||||
odpall:close()
|
||||
end
|
||||
|
||||
-- 写入Web界面
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(mode)
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 获取拨号模式信息
|
||||
@Params
|
||||
at_port AT串口
|
||||
manufacturer 制造商
|
||||
]]
|
||||
function getModeInfo(at_port,manufacturer)
|
||||
|
||||
--获取支持的拨号模式
|
||||
local modes
|
||||
uci:foreach("modem", "modem-device", function (modem_device)
|
||||
--设置模组AT串口
|
||||
if at_port == modem_device["at_port"] then
|
||||
modes=modem_device["modes"]
|
||||
return true --跳出循环
|
||||
end
|
||||
end)
|
||||
|
||||
--获取模组拨号模式
|
||||
local odpall = io.popen("cd "..script_path.." && source "..script_path..manufacturer..".sh && "..manufacturer.."_get_mode "..at_port)
|
||||
local opd = odpall:read("*a")
|
||||
odpall:close()
|
||||
local mode=string.gsub(opd, "\n", "")
|
||||
|
||||
-- 设置值
|
||||
local mode_info={}
|
||||
mode_info["mode"]=mode
|
||||
mode_info["modes"]=modes
|
||||
|
||||
return mode_info
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 获取网络偏好信息
|
||||
@Params
|
||||
at_port AT串口
|
||||
manufacturer 制造商
|
||||
]]
|
||||
function getNetworkPreferInfo(at_port,manufacturer)
|
||||
|
||||
--获取模组网络偏好
|
||||
local odpall = io.popen("cd "..script_path.." && source "..script_path..manufacturer..".sh && "..manufacturer.."_get_network_prefer "..at_port)
|
||||
local opd = odpall:read("*a")
|
||||
odpall:close()
|
||||
local network_prefer_info=json.parse(opd)
|
||||
|
||||
return network_prefer_info
|
||||
end
|
||||
|
||||
--[[
|
||||
@Description 获取模组调试信息
|
||||
]]
|
||||
function getModemDebugInfo()
|
||||
local at_port = http.formvalue("port")
|
||||
|
||||
--获取制造商
|
||||
local manufacturer
|
||||
uci:foreach("modem", "modem-device", function (modem_device)
|
||||
--设置模组AT串口
|
||||
if at_port == modem_device["at_port"] then
|
||||
--获取制造商
|
||||
manufacturer=modem_device["manufacturer"]
|
||||
return true --跳出循环
|
||||
end
|
||||
end)
|
||||
|
||||
--获取值
|
||||
local mode_info={}
|
||||
local network_prefer_info={}
|
||||
if manufacturer~="unknown" then
|
||||
mode_info=getModeInfo(at_port,manufacturer)
|
||||
network_prefer_info=getNetworkPreferInfo(at_port,manufacturer)
|
||||
end
|
||||
|
||||
--设置值
|
||||
local modem_debug_info={}
|
||||
modem_debug_info["mode_info"]=mode_info
|
||||
modem_debug_info["network_prefer_info"]=network_prefer_info
|
||||
|
||||
-- 写入Web界面
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(modem_debug_info)
|
||||
end
|
||||
111
applications/luci-app-modem/luasrc/model/cbi/modem/config.lua
Normal file
111
applications/luci-app-modem/luasrc/model/cbi/modem/config.lua
Normal file
@@ -0,0 +1,111 @@
|
||||
local dispatcher = require "luci.dispatcher"
|
||||
local uci = require "luci.model.uci".cursor()
|
||||
local http = require "luci.http"
|
||||
|
||||
m = Map("modem", translate("Modem Config"))
|
||||
m.redirect = dispatcher.build_url("admin", "network", "modem","index")
|
||||
|
||||
s = m:section(NamedSection, arg[1], "config", "")
|
||||
s.addremove = false
|
||||
s.dynamic = false
|
||||
s:tab("general", translate("General Settings"))
|
||||
s:tab("advanced", translate("Advanced Settings"))
|
||||
|
||||
--------general--------
|
||||
|
||||
-- 是否启用
|
||||
enable = s:taboption("general", Flag, "enable", translate("Enable"))
|
||||
enable.default = "0"
|
||||
enable.rmempty = false
|
||||
|
||||
-- 配置ID
|
||||
uci:set('modem',arg[1],'id',arg[1])
|
||||
|
||||
-- 备注
|
||||
remarks = s:taboption("general", Value, "remarks", translate("Remarks"))
|
||||
remarks.rmempty = true
|
||||
|
||||
-- 移动网络
|
||||
-- network = s:taboption("general", Value, "network", translate("Mobile Network"))
|
||||
network = s:taboption("general", ListValue, "network", translate("Mobile Network"))
|
||||
-- network.default = ""
|
||||
network.rmempty = false
|
||||
|
||||
-- 获取移动网络,并显示设备名
|
||||
function getMobileNetwork()
|
||||
local modem_number=uci:get('modem','global','modem_number')
|
||||
if modem_number == "0" then
|
||||
network:value("",translate("Mobile network not found"))
|
||||
end
|
||||
|
||||
for i=0,modem_number-1 do
|
||||
--获取模块名
|
||||
local modem_name = uci:get('modem','modem'..i,'name')
|
||||
if modem_name == nil then
|
||||
modem_name = "unknown"
|
||||
end
|
||||
--设置网络
|
||||
modem_network = uci:get('modem','modem'..i,'network')
|
||||
if modem_network ~= nil then
|
||||
network:value(modem_network,modem_network.." ("..translate(modem_name:upper())..")")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
getMobileNetwork()
|
||||
|
||||
-- 拨号模式
|
||||
-- mode = s:taboption("general", ListValue, "mode", translate("Mode"))
|
||||
-- mode.rmempty = false
|
||||
-- mode.description = translate("Only display the modes available for the adaptation modem")
|
||||
-- local modes = {"qmi","gobinet","ecm","mbim","rndis","ncm"}
|
||||
-- for i in ipairs(modes) do
|
||||
-- mode:value(modes[i],string.upper(modes[i]))
|
||||
-- end
|
||||
|
||||
-- 添加获取拨号模式信息
|
||||
-- m:append(Template("modem/mode_info"))
|
||||
|
||||
--------advanced--------
|
||||
|
||||
-- 拨号工具
|
||||
dial_tool = s:taboption("advanced", Value, "dial_tool", translate("Dial Tool"))
|
||||
dial_tool.rmempty = true
|
||||
dial_tool:value("", translate("Auto Choose"))
|
||||
dial_tool:value("quectel-CM", translate("quectel-CM"))
|
||||
|
||||
-- 网络类型
|
||||
pdp_type= s:taboption("advanced", ListValue, "pdp_type", translate("PDP Type"))
|
||||
pdp_type.default = "ipv4_ipv6"
|
||||
pdp_type.rmempty = false
|
||||
pdp_type:value("ipv4", translate("IPv4"))
|
||||
pdp_type:value("ipv6", translate("IPv6"))
|
||||
pdp_type:value("ipv4_ipv6", translate("IPv4/IPv6"))
|
||||
|
||||
-- 接入点
|
||||
apn = s:taboption("advanced", Value, "apn", translate("APN"))
|
||||
apn.default = ""
|
||||
apn.rmempty = true
|
||||
apn:value("", translate("Auto Choose"))
|
||||
apn:value("cmnet", translate("China Mobile"))
|
||||
apn:value("3gnet", translate("China Unicom"))
|
||||
apn:value("ctnet", translate("China Telecom"))
|
||||
apn:value("cbnet", translate("China Broadcast"))
|
||||
apn:value("5gscuiot", translate("Skytone"))
|
||||
|
||||
username = s:taboption("advanced", Value, "username", translate("PAP/CHAP Username"))
|
||||
username.rmempty = true
|
||||
|
||||
password = s:taboption("advanced", Value, "password", translate("PAP/CHAP Password"))
|
||||
password.rmempty = true
|
||||
|
||||
auth = s:taboption("advanced", Value, "auth", translate("Authentication Type"))
|
||||
auth.default = ""
|
||||
auth.rmempty = true
|
||||
auth:value("", translate("NONE"))
|
||||
auth:value("both", "PAP/CHAP (both)")
|
||||
auth:value("pap", "PAP")
|
||||
auth:value("chap", "CHAP")
|
||||
-- auth:value("none", "NONE")
|
||||
|
||||
return m
|
||||
88
applications/luci-app-modem/luasrc/model/cbi/modem/index.lua
Normal file
88
applications/luci-app-modem/luasrc/model/cbi/modem/index.lua
Normal file
@@ -0,0 +1,88 @@
|
||||
local d = require "luci.dispatcher"
|
||||
local uci = luci.model.uci.cursor()
|
||||
|
||||
m = Map("modem")
|
||||
m.title = translate("Dial Config")
|
||||
m.description = translate("Add dialing configuration to all modules on this page")
|
||||
|
||||
--全局配置
|
||||
s = m:section(NamedSection, "global", "global", translate("Global Config"))
|
||||
s.anonymous = true
|
||||
s.addremove = false
|
||||
|
||||
o = s:option(Flag, "enable", translate("Enable"))
|
||||
o.rmempty = false
|
||||
o.description = translate("Check to enable all configurations")
|
||||
|
||||
-- 添加模块状态
|
||||
m:append(Template("modem/modem_status"))
|
||||
|
||||
s = m:section(TypedSection, "config", translate("Config List"))
|
||||
s.anonymous = true
|
||||
s.addremove = true
|
||||
s.template = "modem/tblsection"
|
||||
s.extedit = d.build_url("admin", "network", "modem", "config", "%s")
|
||||
|
||||
function s.create(uci, t)
|
||||
local uuid = string.gsub(luci.sys.exec("echo -n $(cat /proc/sys/kernel/random/uuid)"), "-", "")
|
||||
t = uuid
|
||||
TypedSection.create(uci, t)
|
||||
luci.http.redirect(uci.extedit:format(t))
|
||||
end
|
||||
function s.remove(uci, t)
|
||||
uci.map.proceed = true
|
||||
uci.map:del(t)
|
||||
luci.http.redirect(d.build_url("admin", "network", "modem","index"))
|
||||
end
|
||||
|
||||
o = s:option(Flag, "enable", translate("Enable"))
|
||||
o.width = "5%"
|
||||
o.rmempty = false
|
||||
|
||||
-- o = s:option(DummyValue, "status", translate("Status"))
|
||||
-- o.template = "modem/status"
|
||||
-- o.value = translate("Collecting data...")
|
||||
|
||||
o = s:option(DummyValue, "remarks", translate("Remarks"))
|
||||
|
||||
o = s:option(DummyValue, "network", translate("Mobile Network"))
|
||||
o.cfgvalue = function(t, n)
|
||||
-- 检测移动网络是否存在
|
||||
local network = (Value.cfgvalue(t, n) or "")
|
||||
local odpall = io.popen("ls /sys/class/net/ | grep -w "..network.." | wc -l")
|
||||
local odp = odpall:read("*a"):gsub("\n","")
|
||||
odpall:close()
|
||||
if odp ~= "0" then
|
||||
return network
|
||||
else
|
||||
return translate("The network device was not found")
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "dial_tool", translate("Dial Tool"))
|
||||
o.cfgvalue = function(t, n)
|
||||
local dial_tool = (Value.cfgvalue(t, n) or "")
|
||||
if dial_tool == "" then
|
||||
dial_tool=translate("Auto Choose")
|
||||
end
|
||||
return dial_tool
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "pdp_type", translate("PDP Type"))
|
||||
o.cfgvalue = function(t, n)
|
||||
local pdp_type = (Value.cfgvalue(t, n) or ""):gsub("_","/"):upper():gsub("V","v")
|
||||
return pdp_type
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "apn", translate("APN"))
|
||||
o.cfgvalue = function(t, n)
|
||||
local apn = (Value.cfgvalue(t, n) or "")
|
||||
if apn == "" then
|
||||
apn=translate("Auto Choose")
|
||||
end
|
||||
return apn
|
||||
end
|
||||
|
||||
-- m:append(Template("modem/list_status"))
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,30 @@
|
||||
-- Copyright 2024 Siriling <siriling@qq.com>
|
||||
|
||||
local dispatcher = require "luci.dispatcher"
|
||||
local fs = require "nixio.fs"
|
||||
local http = require "luci.http"
|
||||
local uci = require "luci.model.uci".cursor()
|
||||
|
||||
m = Map("modem")
|
||||
m.title = translate("Custom quick commands")
|
||||
m.description = translate("Customize your quick commands")
|
||||
m.redirect = dispatcher.build_url("admin", "network", "modem","modem_debug")
|
||||
|
||||
-- 自定义命令 --
|
||||
s = m:section(TypedSection, "custom-commands", translate("Custom Commands"))
|
||||
s.anonymous = true
|
||||
s.addremove = true
|
||||
s.sortable = true
|
||||
s.template = "modem/tblsection_command"
|
||||
|
||||
description = s:option(Value, "description", translate("Description"))
|
||||
description.placeholder = translate("Not Null")
|
||||
description.rmempty = true
|
||||
description.optional = false
|
||||
|
||||
command = s:option(Value, "command", translate("Command"))
|
||||
command.placeholder = translate("Not Null")
|
||||
command.rmempty = true
|
||||
command.optional = false
|
||||
|
||||
return m
|
||||
253
applications/luci-app-modem/luasrc/view/modem/at_command_old.htm
Normal file
253
applications/luci-app-modem/luasrc/view/modem/at_command_old.htm
Normal file
@@ -0,0 +1,253 @@
|
||||
<%+header%>
|
||||
|
||||
<style type="text/css">
|
||||
#modem_status_view > div {
|
||||
display: inline-block;
|
||||
margin: 1rem;
|
||||
padding: 1rem;
|
||||
width: 15rem;
|
||||
float: left;
|
||||
line-height: 125%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
//第一次打开界面
|
||||
first_start=1;
|
||||
|
||||
// 获取快捷命令
|
||||
function get_quick_commands()
|
||||
{
|
||||
//获取快捷选项
|
||||
var quick_option = "custom";
|
||||
//获取选中的模组
|
||||
var at_port = document.getElementById("modem_select").value;
|
||||
|
||||
//获取AT命令
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "network", "modem", "get_quick_commands")%>', {"option":quick_option,"port":at_port},
|
||||
function(x, data)
|
||||
{
|
||||
//获取模组选择框元素
|
||||
var command_select = document.getElementById('command_select');
|
||||
//获取快捷命令
|
||||
var quick_commands=data["quick_commands"];
|
||||
//遍历每一条信息
|
||||
for (var info of quick_commands)
|
||||
{
|
||||
//遍历每一条信息里的键
|
||||
for (var key in info)
|
||||
{
|
||||
var option = document.createElement('option');
|
||||
option.text = key;
|
||||
option.value = info[key];
|
||||
command_select.appendChild(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 定时触发更新AT串口
|
||||
XHR.poll(5,'<%=luci.dispatcher.build_url("admin", "network", "modem", "get_at_port")%>', null,
|
||||
function(x, data)
|
||||
{
|
||||
var at_ports=data["at_ports"];
|
||||
|
||||
//获取模块选择框元素
|
||||
var modem_select = document.getElementById('modem_select');
|
||||
// 记录所选
|
||||
var selected=modem_select.value;
|
||||
// 删除原来的选项
|
||||
modem_select.options.length=0;
|
||||
//遍历每一个AT串口
|
||||
for (var port of at_ports)
|
||||
{
|
||||
//更新(key:AT串口,value:模块名称)
|
||||
for (var key in port)
|
||||
{
|
||||
var option = document.createElement('option');
|
||||
option.value = key;
|
||||
var language=navigator.language;
|
||||
if (port[key].includes("unknown"))
|
||||
{
|
||||
option.text = port[key].replace("unknown", key);
|
||||
}
|
||||
else
|
||||
{
|
||||
option.text = port[key];
|
||||
}
|
||||
modem_select.appendChild(option);
|
||||
}
|
||||
}
|
||||
// 恢复原来的选择
|
||||
for (let i = 0; i < modem_select.options.length; i++)
|
||||
{
|
||||
if(modem_select.options[i].value == selected)
|
||||
{
|
||||
modem_select.selectedIndex=i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 界面显示控制
|
||||
if (Object.keys(at_ports).length==0)
|
||||
{
|
||||
// 更新提示信息
|
||||
document.getElementById("info_message").innerHTML="<strong><%:No modems found%></strong>";
|
||||
// 显示提示信息
|
||||
document.getElementById("cbi-info").style.display="block";
|
||||
// 隐藏AT命令界面
|
||||
document.getElementById("at_command_view").style.display="none";
|
||||
}
|
||||
else
|
||||
{
|
||||
//获取快捷命令
|
||||
if (first_start)
|
||||
{
|
||||
get_quick_commands();
|
||||
first_start=0
|
||||
}
|
||||
|
||||
// 显示AT命令界面
|
||||
document.getElementById("at_command_view").style.display="block";
|
||||
// 隐藏提示信息
|
||||
document.getElementById("cbi-info").style.display="none";
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
//自动填写到命令输入框
|
||||
function copy_to_input()
|
||||
{
|
||||
var node = document.getElementById('response');
|
||||
node.style.visibility = 'hidden';
|
||||
|
||||
var command_select = document.getElementById("command_select").value;
|
||||
document.getElementById("at_command").value = command_select;
|
||||
document.getElementById("response").innerHTML = "";
|
||||
}
|
||||
|
||||
function post_command(at_port,at_command)
|
||||
{
|
||||
XHR.post('<%=luci.dispatcher.build_url("admin", "network", "modem", "send_at_command")%>', {"port":at_port,"command":at_command},
|
||||
function(x, data)
|
||||
{
|
||||
responseElement=document.getElementById("response");
|
||||
if ("response" in data) {
|
||||
//显示当前时间
|
||||
// responseElement.value+=data["time"];
|
||||
//显示返回值
|
||||
responseElement.innerHTML+=data["response"];
|
||||
}
|
||||
}
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function (ev) {
|
||||
var button = document.getElementById("sendcmd");
|
||||
button.addEventListener("click", function () {
|
||||
|
||||
//获取AT串口
|
||||
var at_port = document.getElementById("modem_select").value;
|
||||
if ( at_port.length == 0 )
|
||||
{
|
||||
document.getElementById("response").innerHTML = "";
|
||||
alert("<%:Please choose a Modem%>");
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取AT命令
|
||||
var at_command = document.getElementById("at_command").value;
|
||||
if ( at_command.length == 0 )
|
||||
{
|
||||
document.getElementById("response").innerHTML = "";
|
||||
alert("<%:Please enter a AT Command%>");
|
||||
return false;
|
||||
}
|
||||
|
||||
//发送AT命令
|
||||
post_command(at_port,at_command);
|
||||
at_command = "";
|
||||
|
||||
var node = document.getElementById('response');
|
||||
if (node.style.visibility=='visible') {
|
||||
node.style.visibility = 'hidden';
|
||||
}
|
||||
else
|
||||
node.style.visibility = 'visible'
|
||||
|
||||
return true;
|
||||
});
|
||||
}, true);
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<h2 name="content"><%:AT Commands%></h2>
|
||||
<div class="cbi-map-descr"><%:Debug Your Module%></div>
|
||||
|
||||
<fieldset class="cbi-section" id="cbi-info" style="display: block;">
|
||||
<div class="cbi-section fade-in">
|
||||
<h3><%:Message%></h3>
|
||||
<table class="table" id="message">
|
||||
<tr class="tr">
|
||||
<td colspan="2" class="td left">
|
||||
<div align="left" id="info_message" style="font-size:1.875em">
|
||||
<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle"/>
|
||||
<%:Loading modem%>...
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div id="at_command_view" style="display: none;">
|
||||
<h4><br/></h4>
|
||||
<div class="table" width="100%">
|
||||
<div class="tr">
|
||||
<div class="td left" style="width:25%;"><%:Modem Select%></div>
|
||||
<div class="td left" style="width:50%;">
|
||||
<select name="modem_select" id="modem_select"></select>
|
||||
</div>
|
||||
<!-- <div class="td left" style="width:50%;"></div> -->
|
||||
</div>
|
||||
|
||||
<div class="tr">
|
||||
<div class="td left" style="width:25%;"><%:Quick Commands%></div>
|
||||
<div class="td left" style="width:50%;">
|
||||
<select name="command_select" id="command_select" onclick="copy_to_input()"></select>
|
||||
</div>
|
||||
<!-- <div class="td left" style="width:50%;"></div> -->
|
||||
</div>
|
||||
|
||||
<div class="tr">
|
||||
<div class="td left" style="width:25%;"><%:Enter Command%></div>
|
||||
<div class="td left" style="width:50%;">
|
||||
<input type="text" id="at_command" required size="20" >
|
||||
</div>
|
||||
<!-- <div class="td left" style="width:50%;"></div> -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="table" width="100%">
|
||||
|
||||
<div class="td left" style="width:25%;"><%:Response%>:
|
||||
<pre id="response" style="visibility: hidden; width:100%;"></pre>
|
||||
</div>
|
||||
|
||||
<div class="tr cbi-rowstyle-2">
|
||||
<div class="td right"><input type="button" style="margin-right: 26%"; id="sendcmd" class="btn cbi-button cbi-button-neutral" value="<%:Send Command%>" /></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cbi-page-actions">
|
||||
<input class="btn cbi-button cbi-button-link" type="button" value="<%:Return to modem debug%>" onclick="location.href='/cgi-bin/luci/admin/network/modem/modem_debug'">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%+footer%>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var _status = document.getElementsByClassName('_status');
|
||||
for(var i = 0; i < _status.length; i++) {
|
||||
var id = _status[i].parentElement.parentElement.parentElement.id;
|
||||
id = id.substr(id.lastIndexOf("-") + 1);
|
||||
XHR.poll(1,'<%=url([[admin]], [[network]], [[modem]], [[status]])%>', {
|
||||
index: i,
|
||||
id: id
|
||||
},
|
||||
function(x, result) {
|
||||
_status[result.index].setAttribute("style","font-weight:bold;");
|
||||
_status[result.index].setAttribute("color",result.status ? "green":"red");
|
||||
_status[result.index].innerHTML = (result.status ? '✓' : 'X');
|
||||
}
|
||||
);
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
51
applications/luci-app-modem/luasrc/view/modem/mode_info.htm
Normal file
51
applications/luci-app-modem/luasrc/view/modem/mode_info.htm
Normal file
@@ -0,0 +1,51 @@
|
||||
<script type="text/javascript">
|
||||
window.onload=function()
|
||||
{
|
||||
var url=window.location.pathname
|
||||
var lastSlashIndex = url.lastIndexOf("/");
|
||||
var uuid = url.substring(lastSlashIndex + 1);
|
||||
|
||||
// 查询network元素
|
||||
var network = document.getElementById("widget.cbid.modem."+uuid+".network");
|
||||
if (network===null)
|
||||
{
|
||||
// 查询所有以.network结尾的元素
|
||||
var elements = document.querySelectorAll('[id$=".network"]');
|
||||
network=elements[0];
|
||||
}
|
||||
|
||||
//页面加载完成时触发
|
||||
getMode(network,uuid);
|
||||
|
||||
// 更换移动网络时触发
|
||||
network.addEventListener('change', function() {
|
||||
// 获取对应的拨号模式,并设置到页面选项中
|
||||
getMode(network,uuid);
|
||||
});
|
||||
};
|
||||
|
||||
// 获取对应的拨号模式,并设置到页面选项中
|
||||
function getMode(network,uuid)
|
||||
{
|
||||
// 获取当前选中的值
|
||||
var selected=network.options[network.selectedIndex].value;
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "network", "modem", "mode_info")%>', {"network":selected},
|
||||
function(x, json)
|
||||
{
|
||||
modeSelect = document.getElementById("widget.cbid.modem."+uuid+".mode");
|
||||
if (modeSelect===null)
|
||||
{
|
||||
// 查询所有以.network结尾的元素
|
||||
var elements = document.querySelectorAll('[id$=".mode"]');
|
||||
modeSelect=elements[0];
|
||||
}
|
||||
|
||||
// 删除原来的选项
|
||||
modeSelect.options.length=0;
|
||||
for (var key in json) {
|
||||
modeSelect.add(new Option(json[key].toUpperCase(),json[key]));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
</script>
|
||||
839
applications/luci-app-modem/luasrc/view/modem/modem_debug.htm
Normal file
839
applications/luci-app-modem/luasrc/view/modem/modem_debug.htm
Normal file
@@ -0,0 +1,839 @@
|
||||
<%+header%>
|
||||
<script type="text/javascript" src="<%=resource%>/xhr.js"></script>
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
|
||||
window.onload=function()
|
||||
{
|
||||
//获取模组选择框元素
|
||||
var modem_select = document.getElementById('modem_select');
|
||||
|
||||
//更换模组(AT串口)时触发
|
||||
modem_select.addEventListener('change', function() {
|
||||
//获取快捷命令
|
||||
get_quick_commands();
|
||||
//获取模组调试信息
|
||||
get_modem_debug_info();
|
||||
});
|
||||
|
||||
//获取快捷选项父元素
|
||||
var quick_option_element = document.getElementById('quick_option_td');
|
||||
//更换快捷选项时触发
|
||||
quick_option_element.addEventListener('change', function(event) {
|
||||
var target = event.target;
|
||||
if (target.matches('input[type="radio"]')) {
|
||||
//获取快捷命令
|
||||
get_quick_commands();
|
||||
}
|
||||
});
|
||||
|
||||
//获取网络偏好选项元素
|
||||
var prefer_option_auto = document.getElementById('prefer_option_auto');
|
||||
var prefer_option_custom = document.getElementById('prefer_option_custom');
|
||||
//网络偏好选项为自动时触发
|
||||
prefer_option_auto.addEventListener('change', function() {
|
||||
if (prefer_option_auto.checked)
|
||||
{
|
||||
//禁用偏好复选框
|
||||
disabled_prefer_custom_config(true);
|
||||
//全选偏好复选框
|
||||
all_choose_prefer_custom_config(true);
|
||||
}
|
||||
});
|
||||
//网络偏好选项为自定义时触发
|
||||
prefer_option_custom.addEventListener('change', function() {
|
||||
if (prefer_option_custom.checked)
|
||||
{
|
||||
//禁用偏好复选框
|
||||
disabled_prefer_custom_config(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 发送AT命令
|
||||
function send(at_port,at_command)
|
||||
{
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "network", "modem", "send_at_command")%>', {"port":at_port,"command":at_command},
|
||||
function(x, data)
|
||||
{
|
||||
responseElement=document.getElementById("response");
|
||||
if ("response" in data) {
|
||||
//显示当前时间
|
||||
responseElement.value+=data["time"]+"\n";
|
||||
//显示返回值
|
||||
responseElement.value+=data["response"];
|
||||
//滚动到底部
|
||||
responseElement.scrollTop = responseElement.scrollHeight;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 发送AT命令前处理
|
||||
function send_at_command(event)
|
||||
{
|
||||
//获取选中的模组(选中的AT串口)
|
||||
var at_port = document.getElementById("modem_select").value;
|
||||
if ( at_port.length == 0 )
|
||||
{
|
||||
alert("<%:Please choose a Modem%>");
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取AT命令
|
||||
var at_command = document.getElementById("at_command").value;
|
||||
if ( at_command.length == 0 )
|
||||
{
|
||||
alert("<%:Please enter a AT Command%>");
|
||||
return false;
|
||||
}
|
||||
|
||||
//对双引号进行特殊处理
|
||||
at_command=at_command.replaceAll("\"","\\\"");
|
||||
|
||||
//发送AT命令
|
||||
send(at_port,at_command);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 清除AT命令
|
||||
function clean_at_command(event)
|
||||
{
|
||||
document.getElementById("at_command").value='';
|
||||
}
|
||||
|
||||
// 清除AT响应
|
||||
function clean_response(event)
|
||||
{
|
||||
document.getElementById("response").value='';
|
||||
}
|
||||
|
||||
// 设置AT串口选项
|
||||
function set_at_port(at_ports,translation)
|
||||
{
|
||||
// 获取模组选择框元素
|
||||
var modem_select = document.getElementById('modem_select');
|
||||
// 记录所选
|
||||
var selected=modem_select.value;
|
||||
// 删除原来的选项
|
||||
modem_select.options.length=0;
|
||||
//遍历每一个AT串口
|
||||
for (var port of at_ports)
|
||||
{
|
||||
//更新(key:AT串口,value:模块名称)
|
||||
for (var key in port)
|
||||
{
|
||||
var option = document.createElement('option');
|
||||
option.value = key;
|
||||
var language=navigator.language;
|
||||
if (port[key].includes("unknown"))
|
||||
{
|
||||
option.text = port[key].replace("unknown", key);
|
||||
}
|
||||
else
|
||||
{
|
||||
option.text = port[key];
|
||||
}
|
||||
modem_select.appendChild(option);
|
||||
}
|
||||
}
|
||||
// 恢复原来的选择
|
||||
for (let i = 0; i < modem_select.options.length; i++)
|
||||
{
|
||||
if(modem_select.options[i].value == selected)
|
||||
{
|
||||
modem_select.selectedIndex=i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 自动填写到命令输入框
|
||||
function copy_to_input()
|
||||
{
|
||||
var command_select = document.getElementById("command_select").value;
|
||||
document.getElementById("at_command").value = command_select;
|
||||
}
|
||||
|
||||
// 无模组界面
|
||||
function no_modems_view()
|
||||
{
|
||||
//更新提示信息
|
||||
document.getElementById("info_message").innerHTML="<strong><%:No modems found%></strong>";
|
||||
//显示提示信息
|
||||
document.getElementById("cbi-info").style.display="block";
|
||||
//隐藏模组选择界面
|
||||
document.getElementById("cbi-modem").style.display="none";
|
||||
//隐藏拨号模式界面
|
||||
document.getElementById("cbi-mode").style.display="none";
|
||||
//隐藏网络偏好界面
|
||||
document.getElementById("cbi-network-prefer").style.display="none";
|
||||
//隐藏AT界面
|
||||
document.getElementById("cbi-at").style.display="none";
|
||||
}
|
||||
|
||||
// 有模组界面
|
||||
function modems_view()
|
||||
{
|
||||
//显示模组选择界面
|
||||
document.getElementById("cbi-modem").style.display="block";
|
||||
//显示AT命令界面
|
||||
document.getElementById("cbi-at").style.display="block";
|
||||
//隐藏提示信息
|
||||
// document.getElementById("cbi-info").style.display="none";
|
||||
}
|
||||
|
||||
// 未适配模组界面
|
||||
function not_adapted_modems_view()
|
||||
{
|
||||
// 更新提示信息
|
||||
document.getElementById("info_message").innerHTML="<strong><%:Not adapted to this modem%></strong>";
|
||||
// 显示提示信息
|
||||
document.getElementById("cbi-info").style.display="block";
|
||||
//隐藏拨号模式界面
|
||||
document.getElementById("cbi-mode").style.display="none";
|
||||
//隐藏网络偏好界面
|
||||
document.getElementById("cbi-network-prefer").style.display="none";
|
||||
}
|
||||
|
||||
// 全功能界面
|
||||
function all_function_view()
|
||||
{
|
||||
//显示拨号模式界面
|
||||
document.getElementById("cbi-mode").style.display="block";
|
||||
//显示网络偏好界面
|
||||
document.getElementById("cbi-network-prefer").style.display="block";
|
||||
//隐藏提示信息
|
||||
document.getElementById("cbi-info").style.display="none";
|
||||
}
|
||||
|
||||
|
||||
// 更新选项
|
||||
function update_option(select_element,data,order)
|
||||
{
|
||||
//记录所选
|
||||
var selected=select_element.value;
|
||||
//删除原来的选项
|
||||
select_element.options.length=0;
|
||||
|
||||
//是否有顺序
|
||||
if (order)
|
||||
{
|
||||
//遍历每一条信息
|
||||
for (var info of data)
|
||||
{
|
||||
//遍历每一条信息里的键
|
||||
for (var key in info)
|
||||
{
|
||||
var option = document.createElement('option');
|
||||
option.text = key;
|
||||
option.value = info[key];
|
||||
select_element.appendChild(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//遍历每一条信息里的键
|
||||
for (var key of data)
|
||||
{
|
||||
var option = document.createElement('option');
|
||||
option.text = key;
|
||||
option.value = info[key];
|
||||
select_element.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
//恢复原来的选择
|
||||
for (let i = 0; i < select_element.options.length; i++)
|
||||
{
|
||||
if(select_element.options[i].value == selected)
|
||||
{
|
||||
select_element.selectedIndex=i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取快捷命令
|
||||
function get_quick_commands()
|
||||
{
|
||||
//获取快捷选项
|
||||
var quick_option = document.querySelector('input[name="quick_option"]:checked').value;
|
||||
//获取选中的模组
|
||||
var at_port = document.getElementById("modem_select").value;
|
||||
|
||||
//获取AT命令
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "network", "modem", "get_quick_commands")%>', {"option":quick_option,"port":at_port},
|
||||
function(x, data)
|
||||
{
|
||||
//获取模组选择框元素
|
||||
var command_select = document.getElementById('command_select');
|
||||
//更新选项
|
||||
update_option(command_select,data["quick_commands"],true);
|
||||
|
||||
//显示有模组界面
|
||||
modems_view();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 全选网络偏好复选框
|
||||
function all_choose_prefer_custom_config(status)
|
||||
{
|
||||
var checkboxes=document.getElementById('prefer_custom_config').querySelectorAll('input[type="checkbox"]');
|
||||
for(checkbox of checkboxes)
|
||||
{
|
||||
//设置网络偏好复选框状态
|
||||
checkbox.checked=status;
|
||||
}
|
||||
}
|
||||
|
||||
// 禁用网络偏好复选框
|
||||
function disabled_prefer_custom_config(status)
|
||||
{
|
||||
var checkboxes=document.getElementById('prefer_custom_config').querySelectorAll('input[type="checkbox"]');
|
||||
for(checkbox of checkboxes)
|
||||
{
|
||||
//禁用
|
||||
checkbox.disabled=status;
|
||||
}
|
||||
}
|
||||
|
||||
// 禁用功能
|
||||
function disabled_function(function_name,status)
|
||||
{
|
||||
//拨号模式
|
||||
if (function_name=="mode")
|
||||
{
|
||||
//模式选项
|
||||
document.getElementById('mode_option_qmi').disabled=status;
|
||||
document.getElementById('mode_option_ecm').disabled=status;
|
||||
document.getElementById('mode_option_mbim').disabled=status;
|
||||
document.getElementById('mode_option_rndis').disabled=status;
|
||||
document.getElementById('mode_option_ncm').disabled=status;
|
||||
|
||||
//模式按钮
|
||||
document.getElementById('mode_button').disabled=status;
|
||||
}
|
||||
//网络偏好
|
||||
else if (function_name=="network_prefer")
|
||||
{
|
||||
//偏好选项
|
||||
document.getElementById('prefer_option_auto').disabled=status;
|
||||
document.getElementById('prefer_option_custom').disabled=status;
|
||||
|
||||
//网络偏好为自动则不启用
|
||||
var prefer_option_auto = document.getElementById('prefer_option_auto');
|
||||
if (!prefer_option_auto.checked)
|
||||
{
|
||||
//偏好复选框
|
||||
disabled_prefer_custom_config(status);
|
||||
}
|
||||
|
||||
//偏好按钮
|
||||
document.getElementById('network_prefer_button').disabled=status;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置拨号模式信息
|
||||
function set_mode_info(mode_info)
|
||||
{
|
||||
//获取当前拨号模式
|
||||
var current_mode=mode_info["mode"];
|
||||
//获取支持的拨号模式
|
||||
var modes=mode_info["modes"];
|
||||
|
||||
//获取模式视图
|
||||
var current_mode_view=current_mode.toUpperCase();
|
||||
|
||||
//设置当前拨号模式
|
||||
document.getElementById('current_mode').innerHTML=current_mode_view;
|
||||
|
||||
var first_element=document.getElementById('first-checked');
|
||||
if (first_element.value=="true")
|
||||
{
|
||||
//设置支持的拨号模式
|
||||
var mode_option_view='';
|
||||
for(mode of modes)
|
||||
{
|
||||
if (mode=="gobinet")
|
||||
{
|
||||
mode_option_view=mode_option_view.replace("QMI","QMI/GobiNet");
|
||||
}
|
||||
else
|
||||
{
|
||||
mode_option_view+='<span class="cbi-radio"><input type="radio" name="mode_option" id="mode_option_'+mode+'" value="'+mode+'"><span>'+mode.toUpperCase()+'</span></span> ';
|
||||
}
|
||||
}
|
||||
document.getElementById('mode_option').innerHTML=mode_option_view;
|
||||
|
||||
//设置拨号模式选项
|
||||
document.getElementById('mode_option_'+current_mode).checked=true;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置拨号模式
|
||||
function set_mode()
|
||||
{
|
||||
//禁用功能
|
||||
disabled_function("mode",true);
|
||||
|
||||
//获取模式选项
|
||||
var mode_config = document.querySelector('input[name="mode_option"]:checked').value;
|
||||
//获取选中的模组
|
||||
var at_port = document.getElementById("modem_select").value;
|
||||
|
||||
//设置偏好
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "network", "modem", "set_mode")%>', {"port":at_port,"mode_config":mode_config},
|
||||
function(x, data)
|
||||
{
|
||||
console.log(data);
|
||||
|
||||
//获取模组拨号模式
|
||||
var current_mode=data;
|
||||
|
||||
//获取模式视图
|
||||
var current_mode_view=current_mode.toUpperCase();
|
||||
|
||||
//设置当前拨号模式
|
||||
document.getElementById('current_mode').innerHTML=current_mode_view;
|
||||
|
||||
//启用功能
|
||||
disabled_function("mode",false);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 获取当前网络视图
|
||||
function get_current_prefer_view(network_prefer)
|
||||
{
|
||||
var current_prefer_view="";
|
||||
|
||||
//自动状态判断(全部选中为自动)
|
||||
if (network_prefer["3G"]&&network_prefer["4G"]&&network_prefer["5G"])
|
||||
{
|
||||
//更新当前偏好
|
||||
current_prefer_view="AUTO";
|
||||
}
|
||||
else
|
||||
{
|
||||
//更新当前偏好
|
||||
for(key in network_prefer)
|
||||
{
|
||||
if (network_prefer[key]) {
|
||||
current_prefer_view+=key+" ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return current_prefer_view;
|
||||
}
|
||||
|
||||
// 设置网络偏好信息
|
||||
function set_network_prefer_info(network_prefer_info)
|
||||
{
|
||||
//获取模组网络偏好
|
||||
var network_prefer=network_prefer_info["network_prefer"];
|
||||
|
||||
//获取偏好视图
|
||||
var current_prefer_view=get_current_prefer_view(network_prefer);
|
||||
|
||||
//设置当前网络偏好
|
||||
document.getElementById('current_prefer').innerHTML=current_prefer_view;
|
||||
|
||||
//设置偏好选项和复选框
|
||||
var first_element=document.getElementById('first-checked');
|
||||
if (first_element.value=="true")
|
||||
{
|
||||
if (network_prefer["3G"]&&network_prefer["4G"]&&network_prefer["5G"])
|
||||
{
|
||||
//设置偏好选项
|
||||
document.getElementById('prefer_option_auto').checked=true;
|
||||
//更新偏好配置
|
||||
all_choose_prefer_custom_config(true);
|
||||
//禁用用偏好复选框
|
||||
disabled_prefer_custom_config(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
//设置偏好选项
|
||||
document.getElementById('prefer_option_custom').checked=true;
|
||||
//更新偏好配置
|
||||
for (key in network_prefer)
|
||||
{
|
||||
//设置偏好配置
|
||||
var prefer_config_element=document.getElementById('prefer_config_'+key.toLowerCase());
|
||||
if (prefer_config_element!=null) {
|
||||
prefer_config_element.checked=network_prefer[key];
|
||||
}
|
||||
}
|
||||
//启用偏好复选框
|
||||
disabled_prefer_custom_config(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置网络偏好
|
||||
function set_network_prefer()
|
||||
{
|
||||
//禁用功能
|
||||
disabled_function("network_prefer",true);
|
||||
|
||||
//获取偏好选项
|
||||
var prefer_option = document.querySelector('input[name="prefer_option"]:checked').value;
|
||||
//获取选中的模组
|
||||
var at_port = document.getElementById("modem_select").value;
|
||||
|
||||
//获取偏好配置
|
||||
var network_prefer_config={};
|
||||
if (prefer_option=="auto")
|
||||
{
|
||||
network_prefer_config["3G"]=1;
|
||||
network_prefer_config["4G"]=1;
|
||||
network_prefer_config["5G"]=1;
|
||||
}
|
||||
else
|
||||
{
|
||||
var checkboxes=document.getElementById('prefer_custom_config').querySelectorAll('input[type="checkbox"]');
|
||||
for(checkbox of checkboxes)
|
||||
{
|
||||
network_prefer_config[checkbox.value.toUpperCase()]=Number(checkbox.checked);
|
||||
}
|
||||
}
|
||||
|
||||
//设置偏好
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "network", "modem", "set_network_prefer")%>', {"port":at_port,"prefer_config":network_prefer_config},
|
||||
function(x, data)
|
||||
{
|
||||
//获取模组网络偏好
|
||||
var network_prefer=data["network_prefer"];
|
||||
|
||||
//获取当前偏好视图
|
||||
var current_prefer_view=get_current_prefer_view(network_prefer);
|
||||
|
||||
//设置模组当前网络偏好
|
||||
document.getElementById('current_prefer').innerHTML=current_prefer_view;
|
||||
|
||||
//启用功能
|
||||
disabled_function("network_prefer",false);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 获取模组调试信息
|
||||
function get_modem_debug_info(params)
|
||||
{
|
||||
//获取选中的模组
|
||||
var at_port = document.getElementById("modem_select").value;
|
||||
|
||||
//获取调试信息
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "network", "modem", "get_modem_debug_info")%>', {"port":at_port},
|
||||
function(x, data)
|
||||
{
|
||||
|
||||
var mode_info=data["mode_info"];
|
||||
var network_prefer_info=data["network_prefer_info"];
|
||||
if (Object.keys(mode_info).length==0||Object.keys(network_prefer_info).length==0) {
|
||||
//显示未适配模组界面
|
||||
not_adapted_modems_view();
|
||||
return false
|
||||
}
|
||||
|
||||
//设置模式信息
|
||||
set_mode_info(mode_info);
|
||||
|
||||
//设置网络偏好信息
|
||||
set_network_prefer_info(network_prefer_info);
|
||||
|
||||
//设置第一次获取数据标志
|
||||
document.getElementById('first-checked').value=false;
|
||||
|
||||
//显示全功能界面
|
||||
all_function_view();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 定时触发更新AT串口
|
||||
XHR.poll(5,'<%=luci.dispatcher.build_url("admin", "network", "modem", "get_at_port")%>', null,
|
||||
function(x, data)
|
||||
{
|
||||
var at_ports=data["at_ports"];
|
||||
var translation=data["translation"];
|
||||
|
||||
//设置AT串口选项
|
||||
set_at_port(at_ports,translation);
|
||||
|
||||
//获取快捷命令
|
||||
if (Object.keys(at_ports).length==0)
|
||||
{
|
||||
//显示无模组界面
|
||||
no_modems_view();
|
||||
}
|
||||
else
|
||||
{
|
||||
//获取快捷选项
|
||||
var quick_option = document.querySelector('input[name="quick_option"]:checked').value;
|
||||
if (quick_option=="auto") {
|
||||
get_quick_commands();
|
||||
}
|
||||
//获取模组调试信息
|
||||
get_modem_debug_info();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div class="cbi-map" id="cbi-modem-debug">
|
||||
<h2 id="content" name="content"><%:Modem Debug%></h2>
|
||||
<div class="cbi-map-descr"><%:Debug Your Module%></div>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
/* table {
|
||||
width: 100%;
|
||||
border-spacing: 10px;
|
||||
border: 0px;
|
||||
} */
|
||||
|
||||
tr td:first-child {
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
/* AT命令响应 */
|
||||
/* #response_label {
|
||||
font-size: 15px;
|
||||
} */
|
||||
|
||||
/* 网络偏好第一次复选框 */
|
||||
[name="first-checked"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 终端 */
|
||||
textarea {
|
||||
background:#373737;
|
||||
border:none;
|
||||
color:#FFF;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#popup {
|
||||
width:560px;
|
||||
height:190px;
|
||||
padding:20px;
|
||||
background-color:gainsboro;
|
||||
border-style : solid;
|
||||
position:fixed;
|
||||
top : 40%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
display:none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<fieldset class="cbi-section" id="cbi-info" style="display: block;">
|
||||
<div class="cbi-section fade-in">
|
||||
<h3><%:Message%></h3>
|
||||
<table class="table" id="message">
|
||||
<tr class="tr">
|
||||
<td colspan="2" class="td left">
|
||||
<div align="left" id="info_message" style="font-size:1.875em">
|
||||
<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle"/>
|
||||
<%:Loading modem%>...
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="cbi-section" id="cbi-modem" style="display: none;">
|
||||
<div class="cbi-section fade-in">
|
||||
<!-- <legend><%:Modem Select%></legend> -->
|
||||
<h3><%:Modem Select%></h3>
|
||||
<div class="cbi-section-node">
|
||||
<div class="cbi-value cbi-value-last">
|
||||
<label class="cbi-value-title"><%:Modem Name%></label>
|
||||
<div class="cbi-value-field">
|
||||
<div class="cbi-checkbox">
|
||||
<select name="modem_select" id="modem_select" class="cbi-input-select"></select>
|
||||
</div>
|
||||
<div class="cbi-value-description">
|
||||
<%:Select a modem for debugging%>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div name="first-checked">
|
||||
<input type="hidden" id="first-checked" value="true">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="cbi-section" id="cbi-mode" style="display: none;">
|
||||
<div class="cbi-section cbi-tblsection">
|
||||
<!-- <legend><%:Mode%></legend> -->
|
||||
<h3><%:Mode%></h3>
|
||||
<table class="table cbi-section-table">
|
||||
<tbody>
|
||||
<tr class="tr cbi-section-table-titles anonymous">
|
||||
<th class="th cbi-section-table-cell"><%:Current%></th>
|
||||
<th class="th cbi-section-table-cell"><%:Config%></th>
|
||||
<th class="th cbi-section-table-cell cbi-section-actions"></th>
|
||||
</tr>
|
||||
<tr class="tr cbi-section-table-row cbi-rowstyle-1">
|
||||
<td class="td cbi-value-field" data-title="<%:Current%>" id="current_mode"></td>
|
||||
<td class="td cbi-value-field" data-title="<%:Config%>" id="mode_option">
|
||||
<!-- <div>
|
||||
<span class="cbi-radio">
|
||||
<input type="radio" name="mode_option" id="mode_option_qmi" value="qmi" checked="true">
|
||||
<span>QMI</span>
|
||||
</span>
|
||||
<span class="cbi-radio">
|
||||
<input type="radio" name="mode_option" id="mode_option_ecm" value="ecm">
|
||||
<span>ECM</span>
|
||||
</span>
|
||||
<span class="cbi-radio">
|
||||
<input type="radio" name="mode_option" id="mode_option_mbim" value="mbim">
|
||||
<span>MBIM</span>
|
||||
</span>
|
||||
<span class="cbi-radio">
|
||||
<input type="radio" name="mode_option" id="mode_option_rndis" value="rndis">
|
||||
<span>RNDIS</span>
|
||||
</span>
|
||||
<span class="cbi-radio">
|
||||
<input type="radio" name="mode_option" id="mode_option_ncm" value="ncm">
|
||||
<span>NCM</span>
|
||||
</span>
|
||||
</div> -->
|
||||
</td>
|
||||
<td class="td">
|
||||
<div>
|
||||
<button class="btn cbi-button cbi-button-apply" id="mode_button" onclick="set_mode()" alt="<%:Apply%>" title="<%:Apply%>"><%:Apply%></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="cbi-section" id="cbi-network-prefer" style="display: none;">
|
||||
<div class="cbi-section cbi-tblsection">
|
||||
<!-- <legend><%:Network Preferences%></legend> -->
|
||||
<h3><%:Network Preferences%></h3>
|
||||
<table class="table cbi-section-table">
|
||||
<tbody>
|
||||
<tr class="tr cbi-section-table-titles anonymous">
|
||||
<th class="th cbi-section-table-cell"><%:Current%></th>
|
||||
<th class="th cbi-section-table-cell"><%:Option%></th>
|
||||
<th class="th cbi-section-table-cell"><%:Config%></th>
|
||||
<th class="th cbi-section-table-cell cbi-section-actions"></th>
|
||||
</tr>
|
||||
<tr class="tr cbi-section-table-row cbi-rowstyle-1">
|
||||
<td class="td cbi-value-field" data-title="<%:Current%>" id="current_prefer"></td>
|
||||
<td class="td cbi-value-field" data-title="<%:Option%>" id="prefer_option">
|
||||
<div>
|
||||
<span class="cbi-radio">
|
||||
<input type="radio" name="prefer_option" id="prefer_option_auto" value="auto" checked="true">
|
||||
<span><%:Auto%></span>
|
||||
</span>
|
||||
<span class="cbi-radio">
|
||||
<input type="radio" name="prefer_option" id="prefer_option_custom" value="custom">
|
||||
<span><%:Custom%></span>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="td cbi-value-field" data-title="<%:Config%>" id="prefer_custom_config">
|
||||
<div>
|
||||
<span class="cbi-checkbox">
|
||||
<input id="prefer_config_3g" type="checkbox" class="cbi-input-checkbox" value="3g">
|
||||
<span><%:3G%></span>
|
||||
</span>
|
||||
<span class="cbi-checkbox">
|
||||
<input id="prefer_config_4g" type="checkbox" class="cbi-input-checkbox" value="4g">
|
||||
<span><%:4G%></span>
|
||||
</span>
|
||||
<span class="cbi-checkbox">
|
||||
<input id="prefer_config_5g" type="checkbox" class="cbi-input-checkbox" value="5g">
|
||||
<span><%:5G%></span>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="td">
|
||||
<div>
|
||||
<button class="btn cbi-button cbi-button-apply" id="network_prefer_button" onclick="set_network_prefer()" alt="<%:Apply%>" title="<%:Apply%>"><%:Apply%></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="cbi-section" id="cbi-at" style="display: none;">
|
||||
<div class="cbi-section fade-in">
|
||||
<!-- <legend><%:AT Command%></legend> -->
|
||||
<h3><%:AT Command%></h3>
|
||||
<table class="table" id="at_command_info">
|
||||
<tbody>
|
||||
<!-- <tr class="tr">
|
||||
<td class="td left"><%:Modem Select%></td>
|
||||
<td class="td left"><select name="modem_select" id="modem_select" class="cbi-input-select"></select></td>
|
||||
</tr> -->
|
||||
<tr class="tr">
|
||||
<td class="td left"><%:Quick Option%></td>
|
||||
<td class="td left" id="quick_option_td">
|
||||
<div>
|
||||
<span class="cbi-radio">
|
||||
<input type="radio" name="quick_option" value="auto" checked="true">
|
||||
<span><%:Auto%></span>
|
||||
</span>
|
||||
<span class="cbi-radio">
|
||||
<input type="radio" name="quick_option" value="custom">
|
||||
<span><%:Custom%></span>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="tr">
|
||||
<td class="td left"><%:Quick Commands%></td>
|
||||
<td class="td left"><select name="command_select" id="command_select" class="cbi-input-select" onclick="copy_to_input()"></select></td>
|
||||
</tr>
|
||||
<tr class="tr">
|
||||
<td class="td left"><%:Enter Command%></td>
|
||||
<td class="td left">
|
||||
<div>
|
||||
<input type="text" id="at_command" class="cbi-input-text"></input>
|
||||
</div>
|
||||
<div>
|
||||
<input class="cbi-button cbi-button-apply" type="button" value="<%:Send%>" onclick="send_at_command()" alt="<%:Send%>" title="<%:Send%>">
|
||||
<input class="cbi-button cbi-button-reset" type="button" value="<%:Clean%>" onclick="clean_at_command()" alt="<%:Clean%>" title="<%:Clean%>">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="tr">
|
||||
<td colspan="2" class="td left">
|
||||
<div id="response_label"><%:Response%></div><br/>
|
||||
<div><textarea readonly="readonly" id="response" rows="20" maxlength="160"></textarea></div>
|
||||
<div class="cbi-page-actions">
|
||||
<input class="btn cbi-button cbi-button-link" type="button" value="<%:Return to old page%>" onclick="location.href='/cgi-bin/luci/admin/network/modem/at_command_old'">
|
||||
<input class="btn cbi-button cbi-button-link" type="button" value="<%:Custom quick commands%>" onclick="location.href='/cgi-bin/luci/admin/network/modem/quick_commands_config'">
|
||||
<input class="cbi-button cbi-button-reset" type="button" value="<%:Clean%>" onclick="clean_response()" alt="<%:Clean%>" title="<%:Clean%>">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
</div>
|
||||
|
||||
<%+footer%>
|
||||
623
applications/luci-app-modem/luasrc/view/modem/modem_info.htm
Normal file
623
applications/luci-app-modem/luasrc/view/modem/modem_info.htm
Normal file
@@ -0,0 +1,623 @@
|
||||
<%+header%>
|
||||
<%
|
||||
local fs = require "nixio.fs"
|
||||
local uci = luci.model.uci.cursor()
|
||||
|
||||
nosms = 1
|
||||
if not fs.stat("/etc/nosim") then
|
||||
nosms = 0
|
||||
end
|
||||
havegps = 0
|
||||
if fs.stat("/etc/havegps") then
|
||||
havegps = 1
|
||||
end
|
||||
%>
|
||||
<style>
|
||||
g {color:grey; font-size:75%; vertical-align: super;}
|
||||
|
||||
/*移动端显示优化*/
|
||||
/* @media (max-width: 768px) {
|
||||
tr td:first-child {
|
||||
width: 30%;
|
||||
}
|
||||
} */
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-spacing: 10px;
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
tr td:first-child {
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
/* 清除表格标题的样式 */
|
||||
/* caption {
|
||||
all: unset;
|
||||
} */
|
||||
</style>
|
||||
<script type="text/javascript" src="<%=resource%>/xhr.js"></script>
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
|
||||
window.onload=function()
|
||||
{
|
||||
//获取模组选择框元素
|
||||
var modem_select = document.getElementById('modem_select');
|
||||
|
||||
//更换模组(AT串口)时触发
|
||||
modem_select.addEventListener('change', function() {
|
||||
//更新数据
|
||||
update();
|
||||
});
|
||||
}
|
||||
|
||||
//获取SIM卡信息视图
|
||||
function get_rate_view(rate)
|
||||
{
|
||||
const b=parseFloat(rate);
|
||||
const kb=b/1024;
|
||||
const mb=kb/1024;
|
||||
|
||||
var rate_view;
|
||||
if (mb>=1) {
|
||||
console.log(mb);
|
||||
rate_view=mb.toFixed(2)+" MB/s";
|
||||
}
|
||||
else if (kb>=1) {
|
||||
rate_view=kb.toFixed(2)+" KB/s";
|
||||
}
|
||||
else {
|
||||
rate_view=b+" B/s";
|
||||
}
|
||||
|
||||
return rate_view;
|
||||
}
|
||||
|
||||
//获取SIM卡信息视图
|
||||
function get_sim_info_view(sim_info,translation)
|
||||
{
|
||||
//初始化视图
|
||||
var sim_info_view='';
|
||||
//遍历每一条信息
|
||||
for (var info of sim_info)
|
||||
{
|
||||
//遍历每一条信息里的键
|
||||
for (var key in info)
|
||||
{
|
||||
//跳过全名
|
||||
if (key=="full_name")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//获取全名
|
||||
var full_name=info["full_name"];
|
||||
if (full_name==null)
|
||||
{
|
||||
full_name='';
|
||||
}
|
||||
//写入视图(不显示空的信息)
|
||||
var value=info[key];
|
||||
if (value!="-"&&value!=""&&value!="ready")
|
||||
{
|
||||
// value=translation[value]; //是否翻译
|
||||
sim_info_view+='<tr class="tr"><td class="td left" title="'+full_name+'">'+translation[key]+'</td><td class="td left" id="'+key+'">'+value+'</td></tr>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sim_info_view;
|
||||
}
|
||||
|
||||
//设置SIM卡信息
|
||||
function set_sim_info(sim_info,translation)
|
||||
{
|
||||
//获取SIM卡信息视图
|
||||
var sim_info_view=get_sim_info_view(sim_info,translation);
|
||||
//获取SIM卡信息表格
|
||||
var sim_info_Element=document.getElementById("sim_info");
|
||||
sim_info_Element.innerHTML=sim_info_view;
|
||||
}
|
||||
|
||||
//获取网络信息视图
|
||||
function get_network_info_view(network_info,translation)
|
||||
{
|
||||
//初始化视图
|
||||
var network_info_view='';
|
||||
//遍历每一条信息
|
||||
for (var info of network_info)
|
||||
{
|
||||
//遍历每一条信息里的键
|
||||
for (var key in info)
|
||||
{
|
||||
//跳过全名
|
||||
if (key=="full_name")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//获取全名
|
||||
var full_name=info["full_name"];
|
||||
if (full_name==null)
|
||||
{
|
||||
full_name='';
|
||||
}
|
||||
//写入视图(不显示空的信息)
|
||||
var value=info[key];
|
||||
if (value!="-"&&value!="")
|
||||
{
|
||||
//添加单位
|
||||
if (key=="RSSI") {
|
||||
value=value+" dBm";
|
||||
}
|
||||
else if (key=="Tx Rate"||key=="Rx Rate")
|
||||
{
|
||||
value=get_rate_view(value);
|
||||
}
|
||||
// value=translation[value]; //是否翻译
|
||||
network_info_view+='<tr class="tr"><td class="td left" title="'+full_name+'">'+translation[key]+'</td><td class="td left" id="'+key+'">'+value+'</td></tr>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return network_info_view;
|
||||
}
|
||||
|
||||
//设置网络信息
|
||||
function set_network_info(network_info,translation)
|
||||
{
|
||||
//获取网络信息视图
|
||||
var network_info_view=get_network_info_view(network_info,translation);
|
||||
//获取网络信息表格
|
||||
var network_info_Element=document.getElementById("network_info");
|
||||
network_info_Element.innerHTML=network_info_view;
|
||||
}
|
||||
|
||||
//获取小区信息视图
|
||||
function get_cell_info_view(network_mode_info,network_type,translation)
|
||||
{
|
||||
//初始化视图
|
||||
var cell_info_view='';
|
||||
//遍历每一条信息
|
||||
for (var info of network_mode_info)
|
||||
{
|
||||
//遍历每一条信息里的键
|
||||
for (var key in info)
|
||||
{
|
||||
//跳过全名
|
||||
if (key=="full_name")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//获取全名
|
||||
var full_name=info["full_name"];
|
||||
if (full_name==null)
|
||||
{
|
||||
full_name='';
|
||||
}
|
||||
//写入视图(不显示空的信息)
|
||||
var value=info[key];
|
||||
if (value!="-"&&value!="")
|
||||
{
|
||||
//添加单位
|
||||
if (key=="Band") {
|
||||
if (network_type.includes("NR")) {
|
||||
value="N"+value
|
||||
}
|
||||
else if (network_type.includes("LTE")) {
|
||||
value="B"+value
|
||||
}
|
||||
else if (network_type.includes("WCDMA")) {
|
||||
value="B"+value
|
||||
}
|
||||
}
|
||||
else if (key=="UL Bandwidth"||key=="DL Bandwidth") {
|
||||
value=value+" MHz"
|
||||
}
|
||||
else if (key=="RSRP"||key=="TX Power"||key=="RxLev") {
|
||||
value=value+" dBm"
|
||||
}
|
||||
else if (key=="RSRQ"||key=="SINR"||key=="RSSNR"||key=="Ec/Io") {
|
||||
value=value+" dB"
|
||||
}
|
||||
else if (key=="SCS") {
|
||||
value=value+" KHz"
|
||||
}
|
||||
cell_info_view+='<tr class="tr"><td class="td left" title="'+full_name+'">'+translation[key]+'</td><td class="td left" id="'+key+'">'+value+'</td></tr>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cell_info_view;
|
||||
}
|
||||
|
||||
//设置小区信息
|
||||
function set_cell_info(cell_info,translation)
|
||||
{
|
||||
//获取网络模式
|
||||
var network_mode=Object.keys(cell_info)[0];
|
||||
//获取视图
|
||||
var cell_info_view='<caption>'+translation[network_mode]+'</caption>'; //网络模式视图
|
||||
// var cell_info_view='<tr class="tr"><td class="td center" colspan="2">'+translation[network_mode]+'</td></tr>'; //网络模式视图
|
||||
//获取网络模式下的信息
|
||||
var network_mode_info=cell_info[network_mode];
|
||||
if (network_mode=="EN-DC Mode")
|
||||
{
|
||||
var lte=network_mode_info[0]["LTE"];
|
||||
cell_info_view+='<tr class="tr"><td class="td left" colspan="2">LTE</td></tr>';
|
||||
cell_info_view+=get_cell_info_view(lte,"LTE",translation);
|
||||
var nsa=network_mode_info[1]["NR5G-NSA"];
|
||||
cell_info_view+='<tr class="tr"><td class="td left" colspan="2">NR5G-NSA</td></tr>';
|
||||
cell_info_view+=get_cell_info_view(nsa,"NR",translation);
|
||||
}
|
||||
else
|
||||
{
|
||||
// cell_info_view+='<tr><td colspan="3">NR5G-NSA</td></tr>';
|
||||
cell_info_view+=get_cell_info_view(network_mode_info,network_mode,translation);
|
||||
}
|
||||
|
||||
//获取表格
|
||||
var cell_info_Element=document.getElementById("cell_info");
|
||||
cell_info_Element.innerHTML=cell_info_view;
|
||||
}
|
||||
|
||||
//显示信息
|
||||
function set_info(info)
|
||||
{
|
||||
for (var key in info)
|
||||
{
|
||||
var info_Element=document.getElementById(key);
|
||||
if (info_Element!=null)
|
||||
{
|
||||
info_Element.innerHTML=info[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//基本信息界面
|
||||
function base_info_view(manufacturer)
|
||||
{
|
||||
if (manufacturer!="unknown")
|
||||
{
|
||||
// 隐藏提示信息
|
||||
document.getElementById("cbi-info").style.display="none";
|
||||
// 显示基本信息
|
||||
document.getElementById("cbi-baseinfo").style.display="block";
|
||||
}
|
||||
else //未适配模组
|
||||
{
|
||||
// 更新提示信息
|
||||
document.getElementById("info_message").innerHTML="<strong><%:Not adapted to this modem%></strong>";
|
||||
// 显示提示信息
|
||||
document.getElementById("cbi-info").style.display="block";
|
||||
// 显示基本信息
|
||||
document.getElementById("cbi-baseinfo").style.display="block";
|
||||
// 隐藏SIM卡信息
|
||||
document.getElementById("cbi-siminfo").style.display="none";
|
||||
// 隐藏网络信息
|
||||
document.getElementById("cbi-networkinfo").style.display="none";
|
||||
// 隐藏小区信息
|
||||
document.getElementById("cbi-cellinfo").style.display="none";
|
||||
}
|
||||
}
|
||||
|
||||
//SIM卡信息界面
|
||||
function sim_info_view(sim_status,connect_status)
|
||||
{
|
||||
//SIM卡状态未知
|
||||
if (sim_status=="unknown")
|
||||
{
|
||||
// 更新提示信息
|
||||
document.getElementById("info_message").innerHTML="<strong><%:Unknown SIM card status%></strong>";
|
||||
// 显示提示信息
|
||||
document.getElementById("cbi-info").style.display="block";
|
||||
// 显示SIM卡信息
|
||||
document.getElementById("cbi-siminfo").style.display="block";
|
||||
}
|
||||
//未插入SIM卡
|
||||
else if (sim_status=="miss")
|
||||
{
|
||||
// 更新提示信息
|
||||
document.getElementById("info_message").innerHTML="<strong><%:SIM card not inserted%></strong>";
|
||||
// 显示提示信息
|
||||
document.getElementById("cbi-info").style.display="block";
|
||||
// 显示SIM卡信息
|
||||
document.getElementById("cbi-siminfo").style.display="block";
|
||||
}
|
||||
//SIM卡就绪
|
||||
else if (sim_status=="ready")
|
||||
{
|
||||
// 隐藏提示信息
|
||||
document.getElementById("cbi-info").style.display="none";
|
||||
// 显示SIM卡信息
|
||||
document.getElementById("cbi-siminfo").style.display="block";
|
||||
}
|
||||
//SIM卡其他状态(锁定等)
|
||||
else
|
||||
{
|
||||
// 更新提示信息
|
||||
document.getElementById("info_message").innerHTML="<strong>"+sim_status+"</strong>";
|
||||
// 显示提示信息
|
||||
document.getElementById("cbi-info").style.display="block";
|
||||
// 显示SIM卡信息
|
||||
document.getElementById("cbi-siminfo").style.display="block";
|
||||
}
|
||||
|
||||
//SIM卡未准备或网络未连接
|
||||
if (sim_status!="ready"||connect_status!="connect")
|
||||
{
|
||||
// 隐藏网络信息
|
||||
document.getElementById("cbi-networkinfo").style.display="none";
|
||||
// 隐藏小区信息
|
||||
document.getElementById("cbi-cellinfo").style.display="none";
|
||||
}
|
||||
}
|
||||
|
||||
//网络信息界面和小区信息界面
|
||||
function network_info_view(connect_status)
|
||||
{
|
||||
//已连接
|
||||
if (connect_status=="connect")
|
||||
{
|
||||
// 显示网络信息
|
||||
document.getElementById("cbi-networkinfo").style.display="block";
|
||||
// 显示小区信息
|
||||
document.getElementById("cbi-cellinfo").style.display="block";
|
||||
}
|
||||
//未连接
|
||||
else
|
||||
{
|
||||
// 隐藏网络信息
|
||||
document.getElementById("cbi-networkinfo").style.display="none";
|
||||
// 隐藏小区信息
|
||||
document.getElementById("cbi-cellinfo").style.display="none";
|
||||
}
|
||||
}
|
||||
|
||||
// 更新模组信息
|
||||
function update()
|
||||
{
|
||||
//获取选中的AT串口
|
||||
var at_port=modem_select.options[modem_select.selectedIndex].value;
|
||||
|
||||
//获取模组信息
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin", "network", "modem", "get_modem_info")%>', {"port":at_port},
|
||||
function(x, data)
|
||||
{
|
||||
console.log(data);
|
||||
|
||||
var modem_info=data["modem_info"];
|
||||
var translation=data["translation"];
|
||||
|
||||
// 设备信息
|
||||
var device_info=modem_info["device_info"];
|
||||
set_info(device_info);
|
||||
|
||||
// 更多信息
|
||||
var more_info=modem_info["more_info"];
|
||||
//基本信息
|
||||
var base_info=more_info["base_info"];
|
||||
set_info(base_info);
|
||||
|
||||
//基本信息界面控制
|
||||
base_info_view(base_info["manufacturer"]);
|
||||
//未适配模组
|
||||
if (base_info["manufacturer"]=="unknown") {
|
||||
return
|
||||
}
|
||||
|
||||
//SIM卡信息
|
||||
var sim_info=more_info["sim_info"];
|
||||
set_sim_info(sim_info,translation);
|
||||
|
||||
//SIM卡信息显示控制
|
||||
var sim_status=sim_info[0]["SIM Status"];
|
||||
sim_info_view(sim_status,device_info["connect_status"]);
|
||||
//SIM卡未插入或SIM卡被锁定
|
||||
if (sim_status!="ready"||device_info["connect_status"]!="connect") {
|
||||
return
|
||||
}
|
||||
|
||||
//网络信息
|
||||
var network_info=more_info["network_info"];
|
||||
set_network_info(network_info,translation);
|
||||
|
||||
//小区信息
|
||||
var cell_info=more_info["cell_info"];
|
||||
set_cell_info(cell_info,translation);
|
||||
|
||||
//网络信息和小区信息界面显示控制
|
||||
network_info_view(device_info["connect_status"]);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 设置AT串口选项
|
||||
function set_at_port(at_ports,translation)
|
||||
{
|
||||
//获取模块选择框元素
|
||||
var modem_select = document.getElementById('modem_select');
|
||||
// 记录所选
|
||||
var selected=modem_select.value;
|
||||
// 删除原来的选项
|
||||
modem_select.options.length=0;
|
||||
//遍历每一个AT串口
|
||||
for (var port of at_ports)
|
||||
{
|
||||
//更新(key:AT串口,value:模块名称)
|
||||
for (var key in port)
|
||||
{
|
||||
var option = document.createElement('option');
|
||||
option.value = key;
|
||||
var language=navigator.language;
|
||||
if (port[key].includes("unknown"))
|
||||
{
|
||||
option.text = translation[port[key]];
|
||||
}
|
||||
else
|
||||
{
|
||||
option.text = port[key];
|
||||
}
|
||||
modem_select.appendChild(option);
|
||||
}
|
||||
}
|
||||
// 恢复原来的选择
|
||||
for (let i = 0; i < modem_select.options.length; i++)
|
||||
{
|
||||
if(modem_select.options[i].value == selected)
|
||||
{
|
||||
modem_select.selectedIndex=i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 定时触发更新AT串口和模组数据
|
||||
XHR.poll(5,'<%=luci.dispatcher.build_url("admin", "network", "modem", "get_at_port")%>', null,
|
||||
function(x, data)
|
||||
{
|
||||
var at_ports=data["at_ports"];
|
||||
var translation=data["translation"];
|
||||
|
||||
//设置AT串口选项
|
||||
set_at_port(at_ports,translation);
|
||||
//更新模组信息
|
||||
if (Object.keys(at_ports).length==0)
|
||||
{
|
||||
no_modems_view();
|
||||
}
|
||||
else
|
||||
{
|
||||
update();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
//无模块界面
|
||||
function no_modems_view()
|
||||
{
|
||||
// 更新提示信息
|
||||
document.getElementById("info_message").innerHTML="<strong><%:No modems found%></strong>";
|
||||
// 显示提示信息
|
||||
document.getElementById("cbi-info").style.display="block";
|
||||
// 隐藏基本信息
|
||||
document.getElementById("cbi-baseinfo").style.display="none";
|
||||
// 隐藏SIM卡信息
|
||||
document.getElementById("cbi-siminfo").style.display="none";
|
||||
// 隐藏网络信息
|
||||
document.getElementById("cbi-networkinfo").style.display="none";
|
||||
// 隐藏小区信息
|
||||
document.getElementById("cbi-cellinfo").style.display="none";
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div class="cbi-map" id="cbi-modem">
|
||||
<h2 name="content"><%:Modem Information%></h2>
|
||||
<div class="cbi-map-descr"><%:Check information about adapted modem on this page%></div>
|
||||
|
||||
<fieldset class="cbi-section" id="cbi-info" style="display: block;">
|
||||
<div class="cbi-section fade-in">
|
||||
<h3><%:Message%></h3>
|
||||
<table class="table">
|
||||
<tbody id="message">
|
||||
<tr class="tr">
|
||||
<td class="td left">
|
||||
<div align="left" id="info_message" style="font-size:1.875em">
|
||||
<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle"/>
|
||||
<%:Loading modem information%>...
|
||||
</div>
|
||||
</td>
|
||||
<td class="td left"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="cbi-section" id="cbi-baseinfo" style="display: none;">
|
||||
<div class="cbi-section fade-in">
|
||||
<h3><%:Base Information%></h3>
|
||||
<table class="table" id="base_info">
|
||||
<tbody>
|
||||
<tr class="tr">
|
||||
<td class="td left"><%:Modem Name%></td>
|
||||
<td class="td left" id="modem_name">
|
||||
<select name="modem_select" id="modem_select"></select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="tr"><td class="td left"><%:Manufacturer%></td><td class="td left" id="manufacturer"></td></tr>
|
||||
<tr class="tr"><td class="td left"><%:Revision%></td><td class="td left" id="revision"></td></tr>
|
||||
<tr class="tr"><td class="td left"><%:Data Interface%></td><td class="td left" id="data_interface"></td></tr>
|
||||
<tr class="tr"><td class="td left"><%:Mode%></td><td class="td left" id="mode"></td></tr>
|
||||
<tr class="tr"><td class="td left"><%:AT Port%></td><td class="td left" id="at_port"></td></tr>
|
||||
<tr class="tr"><td class="td left"><%:Mobile Network%></td><td class="td left" id="network"></td></tr>
|
||||
<tr class="tr"><td class="td left"><%:Temperature%></td><td class="td left" id="temperature"></td></tr>
|
||||
<tr class="tr"><td class="td left"><%:Update Time%></td><td class="td left" id="update_time"></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<% if nosms == 0 then %>
|
||||
<% end %>
|
||||
|
||||
<fieldset class="cbi-section" id="cbi-siminfo" style="display: none;">
|
||||
<div class="cbi-section fade-in">
|
||||
<h3><%:SIM Information%></h3>
|
||||
<table class="table" id="sim_info"></table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="cbi-section" id="cbi-networkinfo" style="display: none;">
|
||||
<div class="cbi-section fade-in">
|
||||
<h3><%:Network Information%></h3>
|
||||
<table id="network_info" class="table"></table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="cbi-section" id="cbi-cellinfo" style="display: none;">
|
||||
<div class="cbi-section fade-in">
|
||||
<h3><%:Cell Information%></h3>
|
||||
<table class="table" id="cell_info"></table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- <fieldset class="cbi-section" id="cbi-stationinfo" style="display: none;">
|
||||
<h3><%:基站信息%></h3>
|
||||
<table width="100%" cellspacing="10">
|
||||
<tr><td width="20%"><%:MCC/MNC 国家码/网络码 %> :</td><td id="mcc"></td><td id="mnc"></td></tr>
|
||||
<tr><td width="20%"><%:eNB ID : %></td><td><ul><span id="rnc" class="r"></span><span id="rncn" class="r"></span></ul></td><td></td></tr>
|
||||
<tr><td width="20%"><%:TAC : %></td><td><ul><span id="lac" class="r"></span><span id="lacn" class="r"></span></ul></td><td></td></tr>
|
||||
<tr><td width="20%"><%:Cell ID : %></td><td><ul><span id="cid" class="r"></span><span id="cidn" class="r"></span></ul></td><td></td></tr>
|
||||
<tr><td width="20%"><%:Band 频段 : %></td><td id="lband"></td><td></td></tr>
|
||||
<tr><td width="20%"><%:Channel 频点 : %></td><td id="channel"></td><td></td></tr>
|
||||
<tr><td width="20%"><%:PCI 物理小区标识 : %></td><td id="pci"></td><td></td></tr>
|
||||
<tr><td width="20%"><%:Maximum Qos 最大Qos级别 : %></td><td><ul><span id="down" class="r"></span><span id="up" class="r"></span></ul></td><td></td></tr>
|
||||
|
||||
</table>
|
||||
</fieldset> -->
|
||||
|
||||
<% if havegps == 1 then %>
|
||||
<!-- <fieldset class="cbi-section" id="cbi-gpsinfo">
|
||||
<h3><%:GPS 定位%></h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td width="30%"><div align="right"><%:纬度 %> :</div></td>
|
||||
<td><ul id="lat"></ul></td>
|
||||
<td width="1%"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div align="right"><%:经度 %> :</div></td>
|
||||
<td><ul id="long"></ul></td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset> -->
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
<%+footer%>
|
||||
|
||||
155
applications/luci-app-modem/luasrc/view/modem/modem_status.htm
Normal file
155
applications/luci-app-modem/luasrc/view/modem/modem_status.htm
Normal file
@@ -0,0 +1,155 @@
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
|
||||
XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "network", "modem", "get_modems")%>', null,
|
||||
function(x, data)
|
||||
{
|
||||
var modems=data["modems"];
|
||||
var translation=data["translation"];
|
||||
|
||||
var modems_div=document.getElementById('modem_status_view');
|
||||
if (Object.keys(modems).length!=0)
|
||||
{
|
||||
var modem_view = "";
|
||||
//遍历每一个模组
|
||||
for (var modem of modems)
|
||||
{
|
||||
//遍历模组里面的信息
|
||||
for ( var key in modem)
|
||||
{
|
||||
var modem=modem[key];
|
||||
|
||||
var language=navigator.language;
|
||||
// 检查模组名
|
||||
var name=modem["name"];
|
||||
if (name==null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (name=="unknown")
|
||||
{
|
||||
language=="en" ? name=name.toUpperCase() : name=translation[name];
|
||||
}
|
||||
else
|
||||
{
|
||||
name=name.toUpperCase();
|
||||
}
|
||||
|
||||
// 检查拨号模式
|
||||
var mode=modem["mode"]
|
||||
if (mode==null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (mode=="unknown")
|
||||
{
|
||||
language=="en" ? mode=mode.toUpperCase() : mode=translation[mode];
|
||||
}
|
||||
else
|
||||
{
|
||||
mode=mode.toUpperCase();
|
||||
}
|
||||
|
||||
// 获取连接状态
|
||||
var connect_status=modem["connect_status"];
|
||||
if (modem["connect_status"]!=null)
|
||||
{
|
||||
if (language=="en") {
|
||||
// 首字母大写
|
||||
connect_status=connect_status.charAt(0).toUpperCase() + modem["connect_status"].slice(1);
|
||||
}
|
||||
else if (language=="zh-CN"||language=="zh") {
|
||||
connect_status=translation[connect_status];
|
||||
}
|
||||
}
|
||||
|
||||
// 设置显示样式
|
||||
var state = '';
|
||||
var css = '';
|
||||
switch (modem["connect_status"])
|
||||
{
|
||||
case 'connect':
|
||||
state = '<%:Connect%>';
|
||||
css = 'success';
|
||||
break;
|
||||
case 'disconnect':
|
||||
state = '<%:Disconnect%>';
|
||||
css = 'danger';
|
||||
break;
|
||||
default:
|
||||
state = '<%:unknown%>';
|
||||
css = 'warning';
|
||||
break;
|
||||
}
|
||||
|
||||
// 设置显示内容
|
||||
modem_view += String.format(
|
||||
'<div class="alert-message %s">',
|
||||
css
|
||||
);
|
||||
modem_view += String.format(
|
||||
'<div><strong>No: </strong>%s</div>',
|
||||
modem[".name"].slice(-1)
|
||||
);
|
||||
modem_view += String.format(
|
||||
'<div><strong><%:Modem Name%>: </strong>%s</div>',
|
||||
name
|
||||
);
|
||||
modem_view += String.format(
|
||||
'<div><strong><%:Data Interface%>: </strong>%s</div>',
|
||||
modem.data_interface.toUpperCase()
|
||||
);
|
||||
modem_view += String.format(
|
||||
'<div><strong><%:Mode%>: </strong>%s</div>',
|
||||
mode
|
||||
);
|
||||
modem_view += String.format(
|
||||
'<div><strong><%:Mobile Network%>: </strong>%s</div>',
|
||||
modem.network
|
||||
);
|
||||
modem_view += String.format(
|
||||
'<div><strong><%:Connect Status%>: </strong>%s</div>',
|
||||
connect_status
|
||||
);
|
||||
modem_view += '</div>';
|
||||
}
|
||||
}
|
||||
// 有参数不存在,则不显示模块状态
|
||||
if (modem_view != "")
|
||||
{
|
||||
modems_div.innerHTML=modem_view;
|
||||
// 显示模块状态(状态加载完成才显示)
|
||||
document.getElementById("modem_status_field").style.display="block";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var modem_view="<strong><%:No modems found%></strong>";
|
||||
modems_div.innerHTML=modem_view;
|
||||
// 隐藏模块状态
|
||||
document.getElementById("modem_status_field").style.display="none";
|
||||
}
|
||||
}
|
||||
);
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
#modem_status_view > div {
|
||||
display: inline-block;
|
||||
margin: 1rem;
|
||||
padding: 1rem;
|
||||
width: 15rem;
|
||||
float: left;
|
||||
line-height: 125%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 默认隐藏模块状态 -->
|
||||
<fieldset id="modem_status_field" class="cbi-section" style="display: none;">
|
||||
<!-- <legend><%:Modem Status%></legend> -->
|
||||
<h3><%:Modem Status%></h3>
|
||||
<div id="modem_status_view">
|
||||
<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle"/>
|
||||
<%:Loading modem status%>...
|
||||
</div>
|
||||
</fieldset>
|
||||
3
applications/luci-app-modem/luasrc/view/modem/status.htm
Normal file
3
applications/luci-app-modem/luasrc/view/modem/status.htm
Normal file
@@ -0,0 +1,3 @@
|
||||
<%+cbi/valueheader%>
|
||||
<font class="_status" hint="<%=self:cfgvalue(section)%>">--</font>
|
||||
<%+cbi/valuefooter%>
|
||||
203
applications/luci-app-modem/luasrc/view/modem/tblsection.htm
Normal file
203
applications/luci-app-modem/luasrc/view/modem/tblsection.htm
Normal file
@@ -0,0 +1,203 @@
|
||||
<%-
|
||||
local rowcnt = 0
|
||||
|
||||
function rowstyle()
|
||||
rowcnt = rowcnt + 1
|
||||
if rowcnt % 2 == 0 then
|
||||
return " cbi-rowstyle-1"
|
||||
else
|
||||
return " cbi-rowstyle-2"
|
||||
end
|
||||
end
|
||||
|
||||
function width(o)
|
||||
if o.width then
|
||||
if type(o.width) == 'number' then
|
||||
return ' style="width:%dpx"' % o.width
|
||||
end
|
||||
return ' style="width:%s"' % o.width
|
||||
end
|
||||
return ''
|
||||
end
|
||||
|
||||
local has_titles = false
|
||||
local has_descriptions = false
|
||||
|
||||
local anonclass = (not self.anonymous or self.sectiontitle) and "named" or "anonymous"
|
||||
local titlename = ifattr(not self.anonymous or self.sectiontitle, "data-title", translate("Name"))
|
||||
|
||||
local i, k
|
||||
for i, k in pairs(self.children) do
|
||||
if not k.typename then
|
||||
k.typename = k.template and k.template:gsub("^.+/", "") or ""
|
||||
end
|
||||
|
||||
if not has_titles and k.title and #k.title > 0 then
|
||||
has_titles = true
|
||||
end
|
||||
|
||||
if not has_descriptions and k.description and #k.description > 0 then
|
||||
has_descriptions = true
|
||||
end
|
||||
end
|
||||
|
||||
function render_titles()
|
||||
if not has_titles then
|
||||
return
|
||||
end
|
||||
|
||||
%><tr class="tr cbi-section-table-titles <%=anonclass%>"<%=titlename%>><%
|
||||
|
||||
local i, k
|
||||
for i, k in ipairs(self.children) do
|
||||
if not k.optional then
|
||||
%><th class="th cbi-section-table-cell"<%=
|
||||
width(k) .. attr('data-widget', k.typename) %>><%
|
||||
|
||||
if k.titleref then
|
||||
%><a title="<%=self.titledesc or translate('Go to relevant configuration page')%>" class="cbi-title-ref" href="<%=k.titleref%>"><%
|
||||
end
|
||||
|
||||
write(k.title)
|
||||
|
||||
if k.titleref then
|
||||
%></a><%
|
||||
end
|
||||
|
||||
%></th><%
|
||||
end
|
||||
end
|
||||
|
||||
if self.sortable or self.extedit or self.addremove then
|
||||
%><th class="th cbi-section-table-cell cbi-section-actions"></th><%
|
||||
end
|
||||
|
||||
%></tr><%
|
||||
|
||||
rowcnt = rowcnt + 1
|
||||
end
|
||||
|
||||
function render_descriptions()
|
||||
if not has_descriptions then
|
||||
return
|
||||
end
|
||||
|
||||
%><tr class="tr cbi-section-table-descr <%=anonclass%>"><%
|
||||
|
||||
local i, k
|
||||
for i, k in ipairs(self.children) do
|
||||
if not k.optional then
|
||||
%><th class="th cbi-section-table-cell"<%=
|
||||
width(k) .. attr("data-widget", k.typename) %>><%
|
||||
|
||||
write(k.description)
|
||||
|
||||
%></th><%
|
||||
end
|
||||
end
|
||||
|
||||
if self.sortable or self.extedit or self.addremove then
|
||||
%><th class="th cbi-section-table-cell cbi-section-actions"></th><%
|
||||
end
|
||||
|
||||
%></tr><%
|
||||
|
||||
rowcnt = rowcnt + 1
|
||||
end
|
||||
|
||||
-%>
|
||||
|
||||
<!-- tblsection -->
|
||||
<div class="cbi-section cbi-tblsection" id="cbi-<%=self.config%>-<%=self.sectiontype%>">
|
||||
<% if self.title and #self.title > 0 then -%>
|
||||
<h3><%=self.title%></h3>
|
||||
<%- end %>
|
||||
<%- if self.sortable then -%>
|
||||
<input type="hidden" id="cbi.sts.<%=self.config%>.<%=self.sectiontype%>" name="cbi.sts.<%=self.config%>.<%=self.sectiontype%>" value="" />
|
||||
<%- end -%>
|
||||
<div class="cbi-section-descr"><%=self.description%></div>
|
||||
<table class="table cbi-section-table">
|
||||
<%-
|
||||
render_titles()
|
||||
render_descriptions()
|
||||
|
||||
local isempty, section, i, k = true, nil, nil
|
||||
for i, k in ipairs(self:cfgsections()) do
|
||||
isempty = false
|
||||
section = k
|
||||
|
||||
local sectionname = striptags((type(self.sectiontitle) == "function") and self:sectiontitle(section) or k)
|
||||
local sectiontitle = ifattr(sectionname and (not self.anonymous or self.sectiontitle), "data-title", sectionname, true)
|
||||
local colorclass = (self.extedit or self.rowcolors) and rowstyle() or ""
|
||||
local scope = {
|
||||
valueheader = "cbi/cell_valueheader",
|
||||
valuefooter = "cbi/cell_valuefooter"
|
||||
}
|
||||
-%>
|
||||
<tr class="tr cbi-section-table-row<%=colorclass%>" id="cbi-<%=self.config%>-<%=section%>"<%=sectiontitle%>>
|
||||
<%-
|
||||
local node
|
||||
for k, node in ipairs(self.children) do
|
||||
if not node.optional then
|
||||
node:render(section, scope or {})
|
||||
end
|
||||
end
|
||||
-%>
|
||||
|
||||
<%- if self.sortable or self.extedit or self.addremove then -%>
|
||||
<td class="td cbi-section-table-cell nowrap cbi-section-actions">
|
||||
<div>
|
||||
<%- if self.sortable then -%>
|
||||
<input class="btn cbi-button cbi-button-up" type="button" value="<%:Up%>" onclick="return cbi_row_swap(this, true, 'cbi.sts.<%=self.config%>.<%=self.sectiontype%>')" title="<%:Move up%>" />
|
||||
<input class="btn cbi-button cbi-button-down" type="button" value="<%:Down%>" onclick="return cbi_row_swap(this, false, 'cbi.sts.<%=self.config%>.<%=self.sectiontype%>')" title="<%:Move down%>" />
|
||||
<% end; if self.extedit then -%>
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" value="<%:Edit%>"
|
||||
<%- if type(self.extedit) == "string" then
|
||||
%> onclick="location.href='<%=self.extedit:format(section)%>'"
|
||||
<%- elseif type(self.extedit) == "function" then
|
||||
%> onclick="location.href='<%=self:extedit(section)%>'"
|
||||
<%- end
|
||||
%> alt="<%:Edit%>" title="<%:Edit%>" />
|
||||
<% end; if self.addremove then %>
|
||||
<input class="btn cbi-button cbi-button-remove" type="submit" value="<%:Delete%>" onclick="this.form.cbi_state='del-section'; return true" name="cbi.rts.<%=self.config%>.<%=k%>" alt="<%:Delete%>" title="<%:Delete%>" />
|
||||
<%- end -%>
|
||||
</div>
|
||||
</td>
|
||||
<%- end -%>
|
||||
</tr>
|
||||
<%- end -%>
|
||||
|
||||
<%- if isempty then -%>
|
||||
<tr class="tr cbi-section-table-row placeholder">
|
||||
<td class="td"><em><%:This section contains no values yet%></em></td>
|
||||
</tr>
|
||||
<%- end -%>
|
||||
</table>
|
||||
|
||||
<% if self.error then %>
|
||||
<div class="cbi-section-error">
|
||||
<ul><% for _, c in pairs(self.error) do for _, e in ipairs(c) do -%>
|
||||
<li><%=pcdata(e):gsub("\n","<br />")%></li>
|
||||
<%- end end %></ul>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%- if self.addremove then -%>
|
||||
<% if self.template_addremove then include(self.template_addremove) else -%>
|
||||
<div class="cbi-section-create cbi-tblsection-create">
|
||||
<% if self.anonymous then %>
|
||||
<input class="btn cbi-button cbi-button-add" type="submit" value="<%:Add%>" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" title="<%:Add%>" />
|
||||
<% else %>
|
||||
<% if self.invalid_cts then -%>
|
||||
<div class="cbi-section-error"><%:Invalid%></div>
|
||||
<%- end %>
|
||||
<div>
|
||||
<input type="text" class="cbi-section-create-name" id="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" data-type="uciname" data-optional="true" onkeyup="cbi_validate_named_section_add(this)"/>
|
||||
</div>
|
||||
<input class="btn cbi-button cbi-button-add" type="submit" onclick="this.form.cbi_state='add-section'; return true" value="<%:Add%>" title="<%:Add%>" disabled="" />
|
||||
<% end %>
|
||||
</div>
|
||||
<%- end %>
|
||||
<%- end -%>
|
||||
</div>
|
||||
<!-- /tblsection -->
|
||||
@@ -0,0 +1,208 @@
|
||||
<%-
|
||||
local rowcnt = 0
|
||||
|
||||
function rowstyle()
|
||||
rowcnt = rowcnt + 1
|
||||
if rowcnt % 2 == 0 then
|
||||
return " cbi-rowstyle-1"
|
||||
else
|
||||
return " cbi-rowstyle-2"
|
||||
end
|
||||
end
|
||||
|
||||
function width(o)
|
||||
if o.width then
|
||||
if type(o.width) == 'number' then
|
||||
return ' style="width:%dpx"' % o.width
|
||||
end
|
||||
return ' style="width:%s"' % o.width
|
||||
end
|
||||
return ''
|
||||
end
|
||||
|
||||
local has_titles = false
|
||||
local has_descriptions = false
|
||||
|
||||
local anonclass = (not self.anonymous or self.sectiontitle) and "named" or "anonymous"
|
||||
local titlename = ifattr(not self.anonymous or self.sectiontitle, "data-title", translate("Name"))
|
||||
|
||||
local i, k
|
||||
for i, k in pairs(self.children) do
|
||||
if not k.typename then
|
||||
k.typename = k.template and k.template:gsub("^.+/", "") or ""
|
||||
end
|
||||
|
||||
if not has_titles and k.title and #k.title > 0 then
|
||||
has_titles = true
|
||||
end
|
||||
|
||||
if not has_descriptions and k.description and #k.description > 0 then
|
||||
has_descriptions = true
|
||||
end
|
||||
end
|
||||
|
||||
function render_titles()
|
||||
if not has_titles then
|
||||
return
|
||||
end
|
||||
|
||||
%><tr class="tr cbi-section-table-titles <%=anonclass%>"<%=titlename%>>
|
||||
<th class="th cbi-section-table-cell"><%:Serial Number%></th>
|
||||
<%
|
||||
local i, k
|
||||
for i, k in ipairs(self.children) do
|
||||
if not k.optional then
|
||||
%><th class="th cbi-section-table-cell"<%=
|
||||
width(k) .. attr('data-widget', k.typename) %>><%
|
||||
|
||||
if k.titleref then
|
||||
%><a title="<%=self.titledesc or translate('Go to relevant configuration page')%>" class="cbi-title-ref" href="<%=k.titleref%>"><%
|
||||
end
|
||||
|
||||
write(k.title)
|
||||
|
||||
if k.titleref then
|
||||
%></a><%
|
||||
end
|
||||
|
||||
%></th><%
|
||||
end
|
||||
end
|
||||
|
||||
if self.sortable or self.extedit or self.addremove then
|
||||
%><th class="th cbi-section-table-cell cbi-section-actions"></th><%
|
||||
end
|
||||
|
||||
%></tr><%
|
||||
|
||||
rowcnt = rowcnt + 1
|
||||
end
|
||||
|
||||
function render_descriptions()
|
||||
if not has_descriptions then
|
||||
return
|
||||
end
|
||||
|
||||
%><tr class="tr cbi-section-table-descr <%=anonclass%>"><%
|
||||
|
||||
local i, k
|
||||
for i, k in ipairs(self.children) do
|
||||
if not k.optional then
|
||||
%><th class="th cbi-section-table-cell"<%=
|
||||
width(k) .. attr("data-widget", k.typename) %>><%
|
||||
|
||||
write(k.description)
|
||||
|
||||
%></th><%
|
||||
end
|
||||
end
|
||||
|
||||
if self.sortable or self.extedit or self.addremove then
|
||||
%><th class="th cbi-section-table-cell cbi-section-actions"></th><%
|
||||
end
|
||||
|
||||
%></tr><%
|
||||
|
||||
rowcnt = rowcnt + 1
|
||||
end
|
||||
|
||||
-%>
|
||||
|
||||
<!-- tblsection -->
|
||||
<div class="cbi-section cbi-tblsection" id="cbi-<%=self.config%>-<%=self.sectiontype%>">
|
||||
<% if self.title and #self.title > 0 then -%>
|
||||
<h3><%=self.title%></h3>
|
||||
<%- end %>
|
||||
<%- if self.sortable then -%>
|
||||
<input type="hidden" id="cbi.sts.<%=self.config%>.<%=self.sectiontype%>" name="cbi.sts.<%=self.config%>.<%=self.sectiontype%>" value="" />
|
||||
<%- end -%>
|
||||
<div class="cbi-section-descr"><%=self.description%></div>
|
||||
<table class="table cbi-section-table">
|
||||
<%-
|
||||
render_titles()
|
||||
render_descriptions()
|
||||
|
||||
local num = 1
|
||||
local isempty, section, i, k = true, nil, nil
|
||||
for i, k in ipairs(self:cfgsections()) do
|
||||
isempty = false
|
||||
section = k
|
||||
|
||||
local sectionname = striptags((type(self.sectiontitle) == "function") and self:sectiontitle(section) or k)
|
||||
local sectiontitle = ifattr(sectionname and (not self.anonymous or self.sectiontitle), "data-title", sectionname, true)
|
||||
local colorclass = (self.extedit or self.rowcolors) and rowstyle() or ""
|
||||
local scope = {
|
||||
valueheader = "cbi/cell_valueheader",
|
||||
valuefooter = "cbi/cell_valuefooter"
|
||||
}
|
||||
-%>
|
||||
<tr class="tr cbi-section-table-row<%=colorclass%>" id="cbi-<%=self.config%>-<%=section%>"<%=sectiontitle%>>
|
||||
<td class="td cbi-value-field" data-title="<%:Serial Number%>">
|
||||
<p><%=num%></p>
|
||||
<% num = num + 1 -%>
|
||||
</td>
|
||||
<%-
|
||||
local node
|
||||
for k, node in ipairs(self.children) do
|
||||
if not node.optional then
|
||||
node:render(section, scope or {})
|
||||
end
|
||||
end
|
||||
-%>
|
||||
<%- if self.sortable or self.extedit or self.addremove then -%>
|
||||
<td class="td cbi-section-table-cell nowrap cbi-section-actions">
|
||||
<div>
|
||||
<%- if self.sortable then -%>
|
||||
<input class="btn cbi-button cbi-button-up" type="button" value="<%:Up%>" onclick="return cbi_row_swap(this, true, 'cbi.sts.<%=self.config%>.<%=self.sectiontype%>')" title="<%:Move up%>" />
|
||||
<input class="btn cbi-button cbi-button-down" type="button" value="<%:Down%>" onclick="return cbi_row_swap(this, false, 'cbi.sts.<%=self.config%>.<%=self.sectiontype%>')" title="<%:Move down%>" />
|
||||
<% end; if self.extedit then -%>
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" value="<%:Edit%>"
|
||||
<%- if type(self.extedit) == "string" then
|
||||
%> onclick="location.href='<%=self.extedit:format(section)%>'"
|
||||
<%- elseif type(self.extedit) == "function" then
|
||||
%> onclick="location.href='<%=self:extedit(section)%>'"
|
||||
<%- end
|
||||
%> alt="<%:Edit%>" title="<%:Edit%>" />
|
||||
<% end; if self.addremove then %>
|
||||
<input class="btn cbi-button cbi-button-remove" type="submit" value="<%:Delete%>" onclick="this.form.cbi_state='del-section'; return true" name="cbi.rts.<%=self.config%>.<%=k%>" alt="<%:Delete%>" title="<%:Delete%>" />
|
||||
<%- end -%>
|
||||
</div>
|
||||
</td>
|
||||
<%- end -%>
|
||||
</tr>
|
||||
<%- end -%>
|
||||
|
||||
<%- if isempty then -%>
|
||||
<tr class="tr cbi-section-table-row placeholder">
|
||||
<td class="td"><em><%:This section contains no values yet%></em></td>
|
||||
</tr>
|
||||
<%- end -%>
|
||||
</table>
|
||||
|
||||
<% if self.error then %>
|
||||
<div class="cbi-section-error">
|
||||
<ul><% for _, c in pairs(self.error) do for _, e in ipairs(c) do -%>
|
||||
<li><%=pcdata(e):gsub("\n","<br />")%></li>
|
||||
<%- end end %></ul>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%- if self.addremove then -%>
|
||||
<% if self.template_addremove then include(self.template_addremove) else -%>
|
||||
<div class="cbi-section-create cbi-tblsection-create">
|
||||
<% if self.anonymous then %>
|
||||
<input class="btn cbi-button cbi-button-add" type="submit" value="<%:Add%>" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" title="<%:Add%>" />
|
||||
<% else %>
|
||||
<% if self.invalid_cts then -%>
|
||||
<div class="cbi-section-error"><%:Invalid%></div>
|
||||
<%- end %>
|
||||
<div>
|
||||
<input type="text" class="cbi-section-create-name" id="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" data-type="uciname" data-optional="true" onkeyup="cbi_validate_named_section_add(this)"/>
|
||||
</div>
|
||||
<input class="btn cbi-button cbi-button-add" type="submit" onclick="this.form.cbi_state='add-section'; return true" value="<%:Add%>" title="<%:Add%>" disabled="" />
|
||||
<% end %>
|
||||
</div>
|
||||
<%- end %>
|
||||
<%- end -%>
|
||||
</div>
|
||||
<!-- /tblsection -->
|
||||
446
applications/luci-app-modem/po/zh-cn/modem.po
Normal file
446
applications/luci-app-modem/po/zh-cn/modem.po
Normal file
@@ -0,0 +1,446 @@
|
||||
msgid "Base Setting"
|
||||
msgstr "基本设置"
|
||||
|
||||
msgid "Modem"
|
||||
msgstr "移动通信模组"
|
||||
|
||||
msgid "Modem Config"
|
||||
msgstr "模组配置"
|
||||
|
||||
msgid "Modem Status"
|
||||
msgstr "模组状态"
|
||||
|
||||
msgid "Modem Name"
|
||||
msgstr "模组名称"
|
||||
|
||||
msgid "Modem Debug"
|
||||
msgstr "模组调试"
|
||||
|
||||
msgid "Modem Select"
|
||||
msgstr "模组选择"
|
||||
|
||||
msgid "Check information about adapted modem on this page"
|
||||
msgstr "在此页面查看已适配模组的信息"
|
||||
|
||||
msgid "Not adapted to this modem"
|
||||
msgstr "未适配该模组"
|
||||
|
||||
msgid "Loading modem information"
|
||||
msgstr "正在加载模组信息"
|
||||
|
||||
msgid "Loading modem status"
|
||||
msgstr "正在加载模组状态"
|
||||
|
||||
msgid "Loading modem"
|
||||
msgstr "正在加载模组"
|
||||
|
||||
msgid "Dial Config"
|
||||
msgstr "拨号配置"
|
||||
|
||||
msgid "Add dialing configuration to all modules on this page"
|
||||
msgstr "在此页面给所有模组添加拨号配置"
|
||||
|
||||
msgid "Global Config"
|
||||
msgstr "全局配置"
|
||||
|
||||
msgid "connect"
|
||||
msgstr "已连接"
|
||||
|
||||
msgid "disconnect"
|
||||
msgstr "未连接"
|
||||
|
||||
msgid "disabled"
|
||||
msgstr "未启用"
|
||||
|
||||
msgid "Data Interface"
|
||||
msgstr "数据接口"
|
||||
|
||||
msgid "Mode"
|
||||
msgstr "模式"
|
||||
|
||||
msgid "Connect Status"
|
||||
msgstr "连接状态"
|
||||
|
||||
msgid "Config List"
|
||||
msgstr "配置列表"
|
||||
|
||||
msgid "Debug Your Module"
|
||||
msgstr "调试你的模组"
|
||||
|
||||
msgid "Select a modem for debugging"
|
||||
msgstr "选择一个模组进行调试"
|
||||
|
||||
msgid "Network Preferences"
|
||||
msgstr "网络偏好"
|
||||
|
||||
msgid "Current"
|
||||
msgstr "当前"
|
||||
|
||||
msgid "Option"
|
||||
msgstr "选项"
|
||||
|
||||
msgid "Config"
|
||||
msgstr "配置"
|
||||
|
||||
msgid "AT Command"
|
||||
msgstr "AT命令"
|
||||
|
||||
msgid "Quick Option"
|
||||
msgstr "快捷选项"
|
||||
|
||||
msgid "Auto"
|
||||
msgstr "自动"
|
||||
|
||||
msgid "Custom"
|
||||
msgstr "自定义"
|
||||
|
||||
msgid "Quick Commands"
|
||||
msgstr "快捷命令"
|
||||
|
||||
msgid "Enter Command"
|
||||
msgstr "输入命令"
|
||||
|
||||
msgid "Apply"
|
||||
msgstr "应用"
|
||||
|
||||
msgid "Send"
|
||||
msgstr "发送"
|
||||
|
||||
msgid "Clean"
|
||||
msgstr "清空"
|
||||
|
||||
msgid "Response"
|
||||
msgstr "响应"
|
||||
|
||||
msgid "Return to old page"
|
||||
msgstr "返回旧界面"
|
||||
|
||||
msgid "Custom quick commands"
|
||||
msgstr "自定义快捷命令"
|
||||
|
||||
msgid "Customize your quick commands"
|
||||
msgstr "自定义你的快捷命令"
|
||||
|
||||
msgid "Custom Commands"
|
||||
msgstr "自定义命令"
|
||||
|
||||
msgid "Serial Number"
|
||||
msgstr "序号"
|
||||
|
||||
msgid "Description"
|
||||
msgstr "描述"
|
||||
|
||||
msgid "Command"
|
||||
msgstr "命令"
|
||||
|
||||
msgid "Modem Information"
|
||||
msgstr "模组信息"
|
||||
|
||||
msgid "No modems found"
|
||||
msgstr "没有找到模组"
|
||||
|
||||
msgid "Check to enable all configurations"
|
||||
msgstr "勾选启用全部配置"
|
||||
|
||||
msgid "General Settings"
|
||||
msgstr "通用配置"
|
||||
|
||||
msgid "Advanced Settings"
|
||||
msgstr "高级配置"
|
||||
|
||||
msgid "Remarks"
|
||||
msgstr "备注"
|
||||
|
||||
msgid "Mobile Network"
|
||||
msgstr "移动网络"
|
||||
|
||||
msgid "UNKNOWN"
|
||||
msgstr "未知"
|
||||
|
||||
msgid "unknown"
|
||||
msgstr "未知"
|
||||
|
||||
msgid "Mobile network not found"
|
||||
msgstr "未发现移动网络"
|
||||
|
||||
msgid "The network device was not found"
|
||||
msgstr "找不到网络设备"
|
||||
|
||||
msgid "Only display the modes available for the adaptation modem"
|
||||
msgstr "仅显示适配模组可用的拨号模式"
|
||||
|
||||
msgid "Dial Tool"
|
||||
msgstr "拨号工具"
|
||||
|
||||
msgid "Auto Choose"
|
||||
msgstr "自动选择"
|
||||
|
||||
msgid "quectel-CM"
|
||||
msgstr "移远模组拨号工具"
|
||||
|
||||
msgid "PDP Type"
|
||||
msgstr "网络类型"
|
||||
|
||||
msgid "APN"
|
||||
msgstr "接入点"
|
||||
|
||||
msgid "China Mobile"
|
||||
msgstr "中国移动"
|
||||
|
||||
msgid "China Unicom"
|
||||
msgstr "中国联通"
|
||||
|
||||
msgid "China Telecom"
|
||||
msgstr "中国电信"
|
||||
|
||||
msgid "China Broadcast"
|
||||
msgstr "中国广电"
|
||||
|
||||
msgid "Skytone"
|
||||
msgstr "天际通"
|
||||
|
||||
msgid "PAP/CHAP Username"
|
||||
msgstr "PAP/CHAP 用户名"
|
||||
|
||||
msgid "PAP/CHAP Password"
|
||||
msgstr "PAP/CHAP 密码"
|
||||
|
||||
msgid "Authentication Type"
|
||||
msgstr "认证类型"
|
||||
|
||||
msgid "NONE"
|
||||
msgstr "无"
|
||||
|
||||
msgid "Message"
|
||||
msgstr "信息"
|
||||
|
||||
msgid "Base Information"
|
||||
msgstr "基本信息"
|
||||
|
||||
msgid "Manufacturer"
|
||||
msgstr "制造商"
|
||||
|
||||
msgid "Revision"
|
||||
msgstr "固件版本"
|
||||
|
||||
msgid "AT Port"
|
||||
msgstr "AT串口"
|
||||
|
||||
msgid "Temperature"
|
||||
msgstr "温度"
|
||||
|
||||
msgid "Update Time"
|
||||
msgstr "更新时间"
|
||||
|
||||
msgid "SIM Information"
|
||||
msgstr "SIM卡信息"
|
||||
|
||||
msgid "Unknown SIM card status"
|
||||
msgstr "未知SIM卡状态"
|
||||
|
||||
msgid "SIM card not inserted"
|
||||
msgstr "SIM卡未插入"
|
||||
|
||||
msgid "ISP"
|
||||
msgstr "运营商"
|
||||
|
||||
msgid "SIM Status"
|
||||
msgstr "SIM卡状态"
|
||||
|
||||
msgid "miss"
|
||||
msgstr "未插入"
|
||||
|
||||
msgid "locked"
|
||||
msgstr "锁定"
|
||||
|
||||
msgid "SIM Slot"
|
||||
msgstr "SIM卡卡槽"
|
||||
|
||||
msgid "SIM Number"
|
||||
msgstr "SIM卡号码"
|
||||
|
||||
msgid "IMEI"
|
||||
msgstr "国际移动设备识别码"
|
||||
|
||||
msgid "IMSI"
|
||||
msgstr "国际移动用户识别码"
|
||||
|
||||
msgid "ICCID"
|
||||
msgstr "集成电路卡识别码"
|
||||
|
||||
msgid "Network Information"
|
||||
msgstr "网络信息"
|
||||
|
||||
msgid "Network Type"
|
||||
msgstr "网络类型"
|
||||
|
||||
msgid "Tx Rate"
|
||||
msgstr "上传速率"
|
||||
|
||||
msgid "Rx Rate"
|
||||
msgstr "下载速率"
|
||||
|
||||
msgid "RSSI"
|
||||
msgstr "接收信号强度指示"
|
||||
|
||||
msgid "BER"
|
||||
msgstr "信道误码率"
|
||||
|
||||
msgid "Cell Information"
|
||||
msgstr "小区信息"
|
||||
|
||||
msgid "Network Mode"
|
||||
msgstr "网络模式"
|
||||
|
||||
msgid "NR5G-SA Mode"
|
||||
msgstr "NR5G-SA 模式"
|
||||
|
||||
msgid "EN-DC Mode"
|
||||
msgstr "EN-DC 模式"
|
||||
|
||||
msgid "LTE Mode"
|
||||
msgstr "LTE 模式"
|
||||
|
||||
msgid "WCDMA Mode"
|
||||
msgstr "WCDMA 模式"
|
||||
|
||||
msgid "MCC"
|
||||
msgstr "移动国家代码"
|
||||
|
||||
msgid "MNC"
|
||||
msgstr "移动网络代码"
|
||||
|
||||
msgid "Duplex Mode"
|
||||
msgstr "双工模式"
|
||||
|
||||
msgid "LAC"
|
||||
msgstr "位置区码"
|
||||
|
||||
msgid "Cell ID"
|
||||
msgstr "小区ID"
|
||||
|
||||
msgid "Physical Cell ID"
|
||||
msgstr "物理小区ID"
|
||||
|
||||
msgid "TAC"
|
||||
msgstr "跟踪区编码"
|
||||
|
||||
msgid "ARFCN"
|
||||
msgstr "绝对射频信道号"
|
||||
|
||||
msgid "EARFCN"
|
||||
msgstr "E-UTRA绝对射频信道号"
|
||||
|
||||
msgid "UARFCN"
|
||||
msgstr "UTRA绝对射频信道号"
|
||||
|
||||
msgid "Band"
|
||||
msgstr "频段"
|
||||
|
||||
msgid "Freq band indicator"
|
||||
msgstr "频带指示"
|
||||
|
||||
msgid "UL Bandwidth"
|
||||
msgstr "上行带宽"
|
||||
|
||||
msgid "DL Bandwidth"
|
||||
msgstr "下行带宽"
|
||||
|
||||
msgid "RSRP"
|
||||
msgstr "参考信号接收功率"
|
||||
|
||||
msgid "RSRQ"
|
||||
msgstr "参考信号接收质量"
|
||||
|
||||
msgid "RSSI"
|
||||
msgstr "接收信号强度指示"
|
||||
|
||||
msgid "SINR"
|
||||
msgstr "信号与干扰加噪声比"
|
||||
|
||||
msgid "RSSNR"
|
||||
msgstr "信号干扰比"
|
||||
|
||||
msgid "SCS"
|
||||
msgstr "NR子载波间隔"
|
||||
|
||||
msgid "CQI"
|
||||
msgstr "信道质量指示"
|
||||
|
||||
msgid "TX Power"
|
||||
msgstr "TX 功率"
|
||||
|
||||
msgid "PSC"
|
||||
msgstr "主扰码"
|
||||
|
||||
msgid "RAC"
|
||||
msgstr "路由区域码"
|
||||
|
||||
msgid "RSCP"
|
||||
msgstr "接收信号码功率"
|
||||
|
||||
msgid "每比特能量与干扰功率密度(干扰比)之比"
|
||||
msgstr "Eb/Io"
|
||||
|
||||
msgid "每比特能量与噪声功率密度(噪声比)之比"
|
||||
msgstr "Eb/No"
|
||||
|
||||
msgid "每码片能量与干扰功率密度(干扰比)之比"
|
||||
msgstr "Ec/Io"
|
||||
|
||||
msgid "每码片能量与噪声功率密度(噪声比)之比"
|
||||
msgstr "Ec/No"
|
||||
|
||||
msgid "Physical Channel"
|
||||
msgstr "物理信道"
|
||||
|
||||
msgid "Spreading Factor"
|
||||
msgstr "扩频因子"
|
||||
|
||||
msgid "Slot"
|
||||
msgstr "插槽格式"
|
||||
|
||||
msgid "Speech Code"
|
||||
msgstr "语音编码"
|
||||
|
||||
msgid "Compression Mode"
|
||||
msgstr "压缩模式"
|
||||
|
||||
msgid "RxLev"
|
||||
msgstr "接收信号功率"
|
||||
|
||||
msgid "CHN-CMCC"
|
||||
msgstr "中国移动"
|
||||
|
||||
msgid "CMCC"
|
||||
msgstr "中国移动"
|
||||
|
||||
msgid "46000"
|
||||
msgstr "中国移动"
|
||||
|
||||
msgid "CHN-UNICOM"
|
||||
msgstr "中国联通"
|
||||
|
||||
msgid "UNICOM"
|
||||
msgstr "中国联通"
|
||||
|
||||
msgid "CUCC"
|
||||
msgstr "中国联通"
|
||||
|
||||
msgid "46001"
|
||||
msgstr "中国联通"
|
||||
|
||||
msgid "CHN-CT"
|
||||
msgstr "中国电信"
|
||||
|
||||
msgid "CHN-TELECOM"
|
||||
msgstr "中国电信"
|
||||
|
||||
msgid "CTCC"
|
||||
msgstr "中国电信"
|
||||
|
||||
msgid "CT"
|
||||
msgstr "中国电信"
|
||||
|
||||
msgid "46011"
|
||||
msgstr "中国电信"
|
||||
240
applications/luci-app-modem/root/etc/config/modem
Normal file
240
applications/luci-app-modem/root/etc/config/modem
Normal file
@@ -0,0 +1,240 @@
|
||||
|
||||
config global 'global'
|
||||
option enable '1'
|
||||
option modem_number '0'
|
||||
|
||||
config custom-commands
|
||||
option description '****************通用****************'
|
||||
option command 'ATI'
|
||||
|
||||
config custom-commands
|
||||
option description '模组信息 > ATI'
|
||||
option command 'ATI'
|
||||
|
||||
config custom-commands
|
||||
option description '查询SIM卡状态 > AT+CPIN?'
|
||||
option command 'AT+CSQ'
|
||||
|
||||
config custom-commands
|
||||
option description '查询此时信号强度 > AT+CSQ'
|
||||
option command 'AT+CSQ'
|
||||
|
||||
config custom-commands
|
||||
option description '查询网络信息 > AT+COPS?'
|
||||
option command 'AT+COPS?'
|
||||
|
||||
config custom-commands
|
||||
option description '查询PDP信息 > AT+CGDCONT?'
|
||||
option command 'AT+CGDCONT?'
|
||||
|
||||
config custom-commands
|
||||
option description '最小功能模式 > AT+CFUN=0'
|
||||
option command 'AT+CFUN=0'
|
||||
|
||||
config custom-commands
|
||||
option description '全功能模式 > AT+CFUN=1'
|
||||
option command 'AT+CFUN=1'
|
||||
|
||||
config custom-commands
|
||||
option description '重启模组 > AT+CFUN=1,1'
|
||||
option command 'AT+CFUN=1,1'
|
||||
|
||||
config custom-commands
|
||||
option description '****************移远****************'
|
||||
option command 'ATI'
|
||||
|
||||
config custom-commands
|
||||
option description 'SIM卡状态上报 > AT+QSIMSTAT?'
|
||||
option command 'AT+QSIMSTAT?'
|
||||
|
||||
config custom-commands
|
||||
option description '设置当前使用的为卡1 > AT+QUIMSLOT=1'
|
||||
option command 'AT+QUIMSLOT=1'
|
||||
|
||||
config custom-commands
|
||||
option description '设置当前使用的为卡2 > AT+QUIMSLOT=2'
|
||||
option command 'AT+QUIMSLOT=2'
|
||||
|
||||
config custom-commands
|
||||
option description '查询网络信息 > AT+QNWINFO'
|
||||
option command 'AT+QNWINFO'
|
||||
|
||||
config custom-commands
|
||||
option description '查询SIM卡签约速率 > AT+QNWCFG="nr5g_ambr"'
|
||||
option command 'AT+QNWCFG="nr5g_ambr"'
|
||||
|
||||
config custom-commands
|
||||
option description '查询载波聚合参数 > AT+QCAINFO'
|
||||
option command 'AT+QCAINFO'
|
||||
|
||||
config custom-commands
|
||||
option description '查询当前拨号模式 > AT+QCFG="usbnet"'
|
||||
option command 'AT+QCFG="usbnet"'
|
||||
|
||||
config custom-commands
|
||||
option description 'QMI/GobiNet拨号 > AT+QCFG="usbnet",0'
|
||||
option command 'AT+QCFG="usbnet",0'
|
||||
|
||||
config custom-commands
|
||||
option description 'ECM拨号 > AT+QCFG="usbnet",1'
|
||||
option command 'AT+QCFG="usbnet",1'
|
||||
|
||||
config custom-commands
|
||||
option description 'MBIM拨号 > AT+QCFG="usbnet",2'
|
||||
option command 'AT+QCFG="usbnet",2'
|
||||
|
||||
config custom-commands
|
||||
option description 'RNDIS拨号 > AT+QCFG="usbnet",3'
|
||||
option command 'AT+QCFG="usbnet",3'
|
||||
|
||||
config custom-commands
|
||||
option description 'NCM拨号 > AT+QCFG="usbnet",5'
|
||||
option command 'AT+QCFG="usbnet",5'
|
||||
|
||||
config custom-commands
|
||||
option description '锁4G > AT+QNWPREFCFG="mode_pref",LTE'
|
||||
option command 'AT+QNWPREFCFG="mode_pref",LTE'
|
||||
|
||||
config custom-commands
|
||||
option description '锁5G > AT+QNWPREFCFG="mode_pref",NR5G'
|
||||
option command 'AT+QNWPREFCFG="mode_pref",NR5G'
|
||||
|
||||
config custom-commands
|
||||
option description '锁5G NSA > AT+QNWPREFCFG="mode_pref",NR5G-NSA'
|
||||
option command 'AT+QNWPREFCFG="mode_pref",NR5G-NSA'
|
||||
|
||||
config custom-commands
|
||||
option description '锁5G SA > AT+QNWPREFCFG="mode_pref",NR5G-SA'
|
||||
option command 'AT+QNWPREFCFG="mode_pref",NR5G-SA'
|
||||
|
||||
config custom-commands
|
||||
option description '恢复自动搜索网络 > AT+QNWPREFCFG="mode_pref",AUTO'
|
||||
option command 'AT+QNWPREFCFG="mode_pref",AUTO'
|
||||
|
||||
config custom-commands
|
||||
option description '查询模组IMEI > AT+CGSN'
|
||||
option command 'AT+CGSN'
|
||||
|
||||
config custom-commands
|
||||
option description '查询模组IMEI > AT+GSN'
|
||||
option command 'AT+GSN'
|
||||
|
||||
config custom-commands
|
||||
option description '更改模组IMEI > AT+EGMR=1,7,"IMEI"'
|
||||
option command 'AT+EGMR=1,7,"在此设置IMEI"'
|
||||
|
||||
config custom-commands
|
||||
option description '获取模组温度 > AT+QTEMP'
|
||||
option command 'AT+QTEMP'
|
||||
|
||||
config custom-commands
|
||||
option description '切换为USB通信端口 > AT+QCFG="data_interface",0,0'
|
||||
option command 'AT+QCFG="data_interface",0,0'
|
||||
|
||||
config custom-commands
|
||||
option description '切换为PCIE通信端口 > AT+QCFG="data_interface",1,0'
|
||||
option command 'AT+QCFG="data_interface",1,0'
|
||||
|
||||
config custom-commands
|
||||
option description '查看当前USB速率 > AT+QCFG="usbspeed"'
|
||||
option command 'AT+QCFG="usbspeed"'
|
||||
|
||||
config custom-commands
|
||||
option description '切换为USB2.0 > AT+QCFG="usbspeed","20"'
|
||||
option command 'AT+QCFG="usbspeed","20"'
|
||||
|
||||
config custom-commands
|
||||
option description '切换为USB3.1 Gen1(5Gbps) > AT+QCFG="usbspeed","311"'
|
||||
option command 'AT+QCFG="usbspeed","311"'
|
||||
|
||||
config custom-commands
|
||||
option description '切换为USB3.1 Gen1(10Gbps) > AT+QCFG="usbspeed","312"'
|
||||
option command 'AT+QCFG="usbspeed","312"'
|
||||
|
||||
config custom-commands
|
||||
option description '重置模组 > AT+QCFG="ResetFactory"'
|
||||
option command 'AT+QCFG="ResetFactory"'
|
||||
|
||||
config custom-commands
|
||||
option description '****************广和通****************'
|
||||
option command 'ATI'
|
||||
|
||||
config custom-commands
|
||||
option description '设置当前使用的为卡1 > AT+GTDUALSIM=0'
|
||||
option command 'AT+GTDUALSIM=0'
|
||||
|
||||
config custom-commands
|
||||
option description '设置当前使用的为卡2 > AT+GTDUALSIM=1'
|
||||
option command 'AT+GTDUALSIM=1'
|
||||
|
||||
config custom-commands
|
||||
option description 'ECM手动拨号 > AT+GTRNDIS=1,1'
|
||||
option command 'AT+GTRNDIS=1,1'
|
||||
|
||||
config custom-commands
|
||||
option description 'ECM拨号断开 > AT+GTRNDIS=0,1'
|
||||
option command 'AT+GTRNDIS=0,1'
|
||||
|
||||
config custom-commands
|
||||
option description '查询当前端口模式 > AT+GTUSBMODE?'
|
||||
option command 'AT+GTUSBMODE?'
|
||||
|
||||
config custom-commands
|
||||
option description 'QMI/GobiNet拨号 > AT+GTUSBMODE=32'
|
||||
option command 'AT+GTUSBMODE=32'
|
||||
|
||||
config custom-commands
|
||||
option description 'ECM拨号 > AT+GTUSBMODE=18'
|
||||
option command 'AT+GTUSBMODE=18'
|
||||
|
||||
config custom-commands
|
||||
option description 'MBIM拨号 > AT+GTUSBMODE=30'
|
||||
option command 'AT+GTUSBMODE=30'
|
||||
|
||||
config custom-commands
|
||||
option description 'RNDIS拨号 > AT+GTUSBMODE=24'
|
||||
option command 'AT+GTUSBMODE=24'
|
||||
|
||||
config custom-commands
|
||||
option description 'NCM拨号 > AT+GTUSBMODE=18'
|
||||
option command 'AT+GTUSBMODE=18'
|
||||
|
||||
config custom-commands
|
||||
option description '锁4G > AT+GTACT=2":"AT+GTACT=2'
|
||||
option command 'AT+GTACT=2'
|
||||
|
||||
config custom-commands
|
||||
option description '锁5G > AT+GTACT=14'
|
||||
option command 'AT+GTACT=14'
|
||||
|
||||
config custom-commands
|
||||
option description '恢复自动搜索网络 > AT+GTACT=20'
|
||||
option command 'AT+GTACT=20'
|
||||
|
||||
config custom-commands
|
||||
option description '查询当前连接的网络类型 > AT+PSRAT?'
|
||||
option command 'AT+PSRAT?'
|
||||
|
||||
config custom-commands
|
||||
option description '查询模组IMEI > AT+CGSN?'
|
||||
option command 'AT+CGSN?'
|
||||
|
||||
config custom-commands
|
||||
option description '查询模组IMEI > AT+GSN?'
|
||||
option command 'AT+GSN?'
|
||||
|
||||
config custom-commands
|
||||
option description '更改模组IMEI > AT+GTSN=1,7,"IMEI"'
|
||||
option command 'AT+GTSN=1,7,"在此设置IMEI"'
|
||||
|
||||
config custom-commands
|
||||
option description '报告一次当前BBIC的温度 > AT+MTSM=1,6'
|
||||
option command 'AT+MTSM=1,6'
|
||||
|
||||
config custom-commands
|
||||
option description '报告一次当前射频的温度 > AT+MTSM=1,7'
|
||||
option command 'AT+MTSM=1,7'
|
||||
|
||||
config custom-commands
|
||||
option description '重置模组 > AT+CFUN=15'
|
||||
option command 'AT+CFUN=15'
|
||||
511
applications/luci-app-modem/root/etc/init.d/modem
Normal file
511
applications/luci-app-modem/root/etc/init.d/modem
Normal file
@@ -0,0 +1,511 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (C) 2006-2014 OpenWrt.org
|
||||
|
||||
START=21
|
||||
STOP=13
|
||||
USE_PROCD=1
|
||||
|
||||
#设置拨号模式
|
||||
# $1:拨号模式
|
||||
# set_mode()
|
||||
# {
|
||||
# #获取AT串口、制造商、模块名
|
||||
# local at_port=$(uci -q get modem.modem$modem_no.at_port)
|
||||
# local manufacturer=$(uci -q get modem.modem$modem_no.manufacturer)
|
||||
# local name=$(uci -q get modem.modem$modem_no.name)
|
||||
|
||||
# #分制造商设置不同的AT命令
|
||||
# local command
|
||||
# if [ "$manufacturer" = "quectel" ]; then
|
||||
# local mode_num
|
||||
# case $1 in
|
||||
# "qmi") mode_num='0' ;;
|
||||
# "gobinet") mode_num='0' ;;
|
||||
# "ecm") mode_num='1' ;;
|
||||
# "mbim") mode_num='2' ;;
|
||||
# "rndis") mode_num='3' ;;
|
||||
# "ncm") mode_num='5' ;;
|
||||
# *) mode_num='0' ;;
|
||||
# esac
|
||||
|
||||
# #查询当前拨号模式
|
||||
# command='AT+QCFG="usbnet"'
|
||||
# local at_result=$(sh /usr/share/modem/modem_at.sh $at_port $command)
|
||||
# if [[ "$at_result" != *"$mode_num"* ]]; then
|
||||
|
||||
# #切换到指定的拨号模式
|
||||
# case $1 in
|
||||
# "qmi") command='AT+QCFG="usbnet",0' ;;
|
||||
# "gobinet") command='AT+QCFG="usbnet",0' ;;
|
||||
# "ecm") command='AT+QCFG="usbnet",1' ;;
|
||||
# "mbim") command='AT+QCFG="usbnet",2' ;;
|
||||
# "rndis") command='AT+QCFG="usbnet",3' ;;
|
||||
# "ncm") command='AT+QCFG="usbnet",5' ;;
|
||||
# *) command='AT+QCFG="usbnet",0' ;;
|
||||
# esac
|
||||
# at_result=$(sh /usr/share/modem/modem_at.sh "$at_port" "$command")
|
||||
# #移远切换模式后,还需要重启模块,待测试
|
||||
# sleep 5
|
||||
# modem_scan
|
||||
# fi
|
||||
# elif [ "$manufacturer" = "fibocom" ]; then
|
||||
# if [ "$name" = "fm150-ae" ]; then
|
||||
# local mode_num
|
||||
# case $1 in
|
||||
# "qmi") mode_num='32' ;;
|
||||
# "gobinet") mode_num='32' ;;
|
||||
# "ecm") mode_num='23' ;;
|
||||
# "mbim") mode_num='29' ;;
|
||||
# "rndis") mode_num='24' ;;
|
||||
# "ncm") mode_num='23' ;;
|
||||
# *) mode_num='32' ;;
|
||||
# esac
|
||||
|
||||
# #查询当前拨号模式
|
||||
# command='AT+GTUSBMODE?'
|
||||
# local at_result=$(sh /usr/share/modem/modem_at.sh $at_port $command)
|
||||
# if [[ "$at_result" != *"$mode_num"* ]]; then
|
||||
# #切换到指定的拨号模式
|
||||
# case $1 in
|
||||
# "qmi") command='AT+GTUSBMODE=32' ;;
|
||||
# "gobinet") command='AT+GTUSBMODE=32' ;;
|
||||
# "ecm") command='AT+GTUSBMODE=23' ;;
|
||||
# "mbim") command='AT+GTUSBMODE=29' ;;
|
||||
# "rndis") command='AT+GTUSBMODE=24' ;;
|
||||
# "ncm") command='AT+GTUSBMODE=23' ;;
|
||||
# *) command='AT+GTUSBMODE=32' ;;
|
||||
# esac
|
||||
# at_result=$(sh /usr/share/modem/modem_at.sh "$at_port" "$command")
|
||||
# sleep 5
|
||||
# modem_scan
|
||||
# fi
|
||||
# elif [ "$name" = "fm650" ]; then
|
||||
# #待处理
|
||||
# echo "fm650"
|
||||
# fi
|
||||
# else
|
||||
# #没有匹配到制造商,需要手动切换模块的拨号模式
|
||||
# echo "请手动切换模块的拨号模式"
|
||||
# fi
|
||||
# }
|
||||
|
||||
#设置防火墙
|
||||
set_firewall()
|
||||
{
|
||||
local num=`uci show firewall | grep "name='wan'" | wc -l`
|
||||
local wwan_num=`uci -q get firewall.@zone[$num].network | grep -w "$1" | wc -l`
|
||||
if [ "$wwan_num" = "0" ]; then
|
||||
uci add_list firewall.@zone[$num].network="$1"
|
||||
fi
|
||||
uci commit firewall
|
||||
}
|
||||
|
||||
#设置IPv4网络接口
|
||||
# $1:网络接口名称
|
||||
# $2:网络接口
|
||||
set_ipv4_interface()
|
||||
{
|
||||
#配置中不存在这个网络接口配置,或这个网络接口配置的设备不同
|
||||
if [ "$(uci -q get network.$1.device)" != "$2" ] && [ "$(uci -q get network.$1.ifname)" != "$2" ]; then
|
||||
uci set network.$1='interface'
|
||||
uci set network.$1.proto='dhcp'
|
||||
uci set network.$1.device="$2"
|
||||
uci set network.$1.ifname="$2"
|
||||
uci commit network
|
||||
|
||||
#加入WAN防火墙
|
||||
set_firewall $1
|
||||
|
||||
#启动网络接口
|
||||
ifup $1
|
||||
fi
|
||||
}
|
||||
|
||||
#设置IPv6网络接口
|
||||
# $1:网络接口名称
|
||||
# $2:网络接口
|
||||
set_ipv6_interface()
|
||||
{
|
||||
if [ "$(uci -q get network.$1.device)" != "$2" ] && [ "$(uci -q get network.$1.ifname)" != "$2" ] ; then
|
||||
uci set network.$1='interface'
|
||||
uci set network.$1.proto='dhcpv6'
|
||||
uci set network.$1.extendprefix='1'
|
||||
uci set network.$1.device="$2"
|
||||
uci set network.$1.ifname="$2"
|
||||
uci commit network
|
||||
|
||||
#加入WAN防火墙
|
||||
set_firewall $1
|
||||
|
||||
#启动网络接口
|
||||
ifup $1
|
||||
else
|
||||
uci set network.$1.extendprefix='1'
|
||||
uci commit network
|
||||
fi
|
||||
}
|
||||
|
||||
#设置网络接口
|
||||
# $1:模块序号
|
||||
# $2:网络接口
|
||||
set_interface()
|
||||
{
|
||||
case $pdp_type in
|
||||
"ipv4") set_ipv4_interface wwan_5g_$1 $2 ;;
|
||||
"ipv6") set_ipv6_interface wwan6_5g_$1 $2 ;;
|
||||
"ipv4_ipv6")
|
||||
set_ipv4_interface "wwan_5g_$1" $2
|
||||
set_ipv6_interface "wwan6_5g_$1" $2
|
||||
;;
|
||||
*)
|
||||
set_ipv4_interface "wwan_5g_$1" $2
|
||||
set_ipv6_interface "wwan6_5g_$1" $2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
qmi()
|
||||
{
|
||||
#设置网络接口
|
||||
local network_interface=$(uci -q get modem.modem$modem_no.network_interface)
|
||||
set_interface $modem_no $network_interface
|
||||
|
||||
#拨号
|
||||
procd_open_instance
|
||||
|
||||
if [ "$dial_tool" = "quectel-CM" ]; then
|
||||
procd_set_param command quectel-CM
|
||||
elif [[ -z "$dial_tool" ]]; then
|
||||
procd_set_param command quectel-CM
|
||||
else
|
||||
procd_set_param command $dial_tool
|
||||
fi
|
||||
|
||||
case $pdp_type in
|
||||
"ipv4") procd_append_param command -4 ;;
|
||||
"ipv6") procd_append_param command -6 ;;
|
||||
"ipv4_ipv6") procd_append_param command -4 -6 ;;
|
||||
*) procd_append_param command -4 -6 ;;
|
||||
esac
|
||||
|
||||
if [ "$apn" != "" ]; then
|
||||
procd_append_param command -s $apn
|
||||
fi
|
||||
if [ "$user" != "" ]; then
|
||||
procd_append_param command $user
|
||||
fi
|
||||
if [ "$password" != "" ]; then
|
||||
procd_append_param command $password
|
||||
fi
|
||||
if [ "$auth" != "" ]; then
|
||||
procd_append_param command $auth
|
||||
fi
|
||||
if [ "$network" != "" ]; then
|
||||
procd_append_param command -i $network
|
||||
fi
|
||||
procd_set_param respawn
|
||||
procd_set_param procd_pid /var/run/modem/modem$modem_no.pid
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
gobinet()
|
||||
{
|
||||
#获取网络接口、AT串口、制造商
|
||||
local network_interface=$(uci -q get modem.modem$modem_no.network_interface)
|
||||
local at_port=$(uci -q get modem.modem$modem_no.at_port)
|
||||
local manufacturer=$(uci -q get modem.modem$modem_no.manufacturer)
|
||||
#设置网络接口
|
||||
set_interface $modem_no $network_interface
|
||||
|
||||
#拨号
|
||||
procd_open_instance
|
||||
procd_set_param command sh /usr/share/modem/modem_usb_network.sh $id $at_port $manufacturer "gobinet"
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
ecm()
|
||||
{
|
||||
#获取网络接口、AT串口、制造商
|
||||
local network_interface=$(uci -q get modem.modem$modem_no.network_interface)
|
||||
local at_port=$(uci -q get modem.modem$modem_no.at_port)
|
||||
local manufacturer=$(uci -q get modem.modem$modem_no.manufacturer)
|
||||
#设置网络接口
|
||||
set_interface $modem_no $network_interface
|
||||
|
||||
#拨号
|
||||
procd_open_instance
|
||||
procd_set_param command sh /usr/share/modem/modem_usb_network.sh $id $at_port $manufacturer "ecm"
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
mbim()
|
||||
{
|
||||
qmi
|
||||
}
|
||||
|
||||
rndis()
|
||||
{
|
||||
ecm
|
||||
#广和通的rndis和ecm不同,后续再测试
|
||||
}
|
||||
|
||||
ncm()
|
||||
{
|
||||
ecm
|
||||
}
|
||||
|
||||
stop_qmi()
|
||||
{
|
||||
#获取modem的实例信息
|
||||
local response=$(ubus call service list '{"name": "modem"}')
|
||||
local instance_number=$(echo "$response" | jq -r '.modem.instances | length')
|
||||
for i in $(seq 1 $((instance_number))); do
|
||||
#获取拨号命令
|
||||
local command=$(echo "$response" | jq -r '.modem.instances.instance$i.command')
|
||||
if [ "$command" = *"$network"* ]; then
|
||||
local pid=$(echo "$response" | jq -r '.modem.instances.$i.pid')
|
||||
kill $pid >/dev/null 2>&1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
stop_gobinet()
|
||||
{
|
||||
#获取AT串口、制造商
|
||||
local at_port=$(uci -q get modem.modem$modem_no.at_port)
|
||||
local manufacturer=$(uci -q get modem.modem$modem_no.manufacturer)
|
||||
|
||||
#停止拨号
|
||||
local command="sh /usr/share/modem/modem_at.sh $at_port"
|
||||
if [ "$manufacturer" = "quectel" ]; then
|
||||
$command 'ATI'
|
||||
elif [ "$manufacturer" = "fibocom" ]; then
|
||||
$command 'AT$QCRMCALL=0,1'
|
||||
else
|
||||
$command 'ATI'
|
||||
fi
|
||||
}
|
||||
|
||||
stop_ecm()
|
||||
{
|
||||
#获取AT串口、制造商
|
||||
local at_port=$(uci -q get modem.modem$modem_no.at_port)
|
||||
local manufacturer=$(uci -q get modem.modem$modem_no.manufacturer)
|
||||
|
||||
#停止拨号
|
||||
local command="sh /usr/share/modem/modem_at.sh $at_port"
|
||||
if [ "$manufacturer" = "quectel" ]; then
|
||||
$command 'ATI'
|
||||
elif [ "$manufacturer" = "fibocom" ]; then
|
||||
$command 'AT+GTRNDIS=0,1'
|
||||
else
|
||||
$command 'ATI'
|
||||
fi
|
||||
}
|
||||
|
||||
stop_mbim()
|
||||
{
|
||||
stop_qmi
|
||||
}
|
||||
|
||||
stop_rndis()
|
||||
{
|
||||
stop_ecm
|
||||
#广和通的rndis和ecm不同,后续再测试
|
||||
}
|
||||
|
||||
stop_ncm()
|
||||
{
|
||||
stop_ecm
|
||||
}
|
||||
|
||||
#获取模块序号
|
||||
# $1:移动网络
|
||||
get_modem_no()
|
||||
{
|
||||
local modem_number=$(uci -q get modem.@global[0].modem_number)
|
||||
local modem_network
|
||||
for i in $(seq 0 $((modem_number-1))); do
|
||||
modem_network=$(uci -q get modem.modem$i.network)
|
||||
if [ "$modem_network" = "$1" ]; then
|
||||
#模块序号
|
||||
modem_no=$i
|
||||
break
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
#获取实例运行状态(未使用)
|
||||
# $1:配置ID
|
||||
get_instance_status()
|
||||
{
|
||||
#获取modem的实例信息
|
||||
local response=$(ubus call service list '{"name": "modem"}')
|
||||
local instance_number=$(echo "$response" | jq -r ".modem.instances | length")
|
||||
for i in $(seq 1 $((instance_number))); do
|
||||
#获取运行状态和拨号命令
|
||||
local running_status=$(echo "$response" | jq -r ".modem.instances.instance$i.running")
|
||||
local command=$(echo "$response" | jq -r ".modem.instances.instance$i.command")
|
||||
if [ "$running_status" = "true" ] && [[ "$command" = *"$network"* ]]; then
|
||||
#查看配置ID是否记录在已运行的文件里
|
||||
local run_config="/tmp/modem/run_config"
|
||||
local run_config_id=$(grep -n "$network" "$run_config" | cut -d ';' -f 2)
|
||||
if [ "$1" = "$run_config_id" ]; then
|
||||
status=2
|
||||
break
|
||||
else
|
||||
status=1
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
#停止拨号
|
||||
# $1:配置ID
|
||||
stop_dial()
|
||||
{
|
||||
local id="$1" #配置ID
|
||||
local network=$(uci -q get modem.$1.network) #移动网络
|
||||
|
||||
#把配置ID从临时列表中移除
|
||||
local run_config="/tmp/modem/run_config"
|
||||
local row_no=$(grep -n "$id" "$run_config" | cut -d ':' -f 1)
|
||||
if [ -z "$row_no" ]; then
|
||||
return 0
|
||||
fi
|
||||
#该配置ID在运行,需要删除记录
|
||||
sed -i "$row_no"d $run_config
|
||||
|
||||
#获取模块序号
|
||||
get_modem_no $network
|
||||
#获取模组的拨号模式
|
||||
local mode=$(uci -q get modem.modem$modem_no.mode)
|
||||
|
||||
#根据不同的拨号模式停止拨号
|
||||
if [ "$mode" = "qmi" ]; then
|
||||
stop_qmi
|
||||
elif [ "$mode" = "gobinet" ]; then
|
||||
stop_gobinet
|
||||
elif [ "$mode" = "ecm" ]; then
|
||||
stop_ecm
|
||||
elif [ "$mode" = "mbim" ]; then
|
||||
stop_mbim
|
||||
elif [ "$mode" = "rndis" ]; then
|
||||
stop_rndis
|
||||
elif [ "$mode" = "ncm" ]; then
|
||||
stop_ncm
|
||||
fi
|
||||
}
|
||||
|
||||
dial()
|
||||
{
|
||||
local enable #启用
|
||||
local id #ID
|
||||
|
||||
config_get enable $1 enable
|
||||
config_get id $1 id
|
||||
[ "$enable" = "0" ] && {
|
||||
stop_dial "$id"
|
||||
return 0
|
||||
}
|
||||
|
||||
local remarks #备注
|
||||
local network #移动网络
|
||||
local dial_tool #拨号工具
|
||||
local pdp_type #网络类型
|
||||
local apn
|
||||
local user
|
||||
local password
|
||||
local auth
|
||||
|
||||
config_get remarks $1 remarks
|
||||
config_get network $1 network
|
||||
config_get dial_tool $1 dial_tool
|
||||
config_get pdp_type $1 pdp_type
|
||||
config_get apn $1 apn
|
||||
config_get user $1 user
|
||||
config_get password $1 password
|
||||
config_get auth $1 auth
|
||||
|
||||
#获取模块序号
|
||||
get_modem_no $network
|
||||
#获取模组的拨号模式
|
||||
[ -z "$modem_no" ] && return 0
|
||||
local mode=$(uci -q get modem.modem$modem_no.mode)
|
||||
if [ "$mode" = "unknown" ]; then
|
||||
mode="qmi"
|
||||
fi
|
||||
|
||||
#查看移动网络是否已经有配置在运行
|
||||
local run_path="/tmp/modem"
|
||||
mkdir -p $run_path
|
||||
local run_config="/tmp/modem/run_config"
|
||||
local row_no=$(grep -n "$network" "$run_config" | cut -d ':' -f 1) #1:wwan0;abc->1
|
||||
if [ -z "$row_no" ]; then #未记录该移动网络
|
||||
#把已运行的配置ID加入到记录中
|
||||
echo "$network;$id" >> "$run_path/run_config"
|
||||
else
|
||||
local run_config_id=$(grep -n "$network" "$run_config" | cut -d ';' -f 2)
|
||||
if [ "$id" != "$run_config_id" ]; then #该移动网络已存在,且已有其他配置运行
|
||||
uci set modem.$1.enable=0
|
||||
uci commit modem
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
#根据不同的拨号模式拨号
|
||||
if [ "$mode" = "qmi" ]; then
|
||||
qmi
|
||||
elif [ "$mode" = "gobinet" ]; then
|
||||
gobinet
|
||||
elif [ "$mode" = "ecm" ]; then
|
||||
ecm
|
||||
elif [ "$mode" = "mbim" ]; then
|
||||
mbim
|
||||
elif [ "$mode" = "rndis" ]; then
|
||||
rndis
|
||||
elif [ "$mode" = "ncm" ]; then
|
||||
ncm
|
||||
fi
|
||||
|
||||
# sleep 15
|
||||
}
|
||||
|
||||
service_triggers()
|
||||
{
|
||||
procd_add_reload_trigger "modem"
|
||||
}
|
||||
|
||||
start_service() {
|
||||
local enable=$(uci -q get modem.@global[0].enable)
|
||||
if [ "$enable" = "0" ]; then
|
||||
stop_service
|
||||
else
|
||||
config_load modem
|
||||
config_foreach dial "config"
|
||||
fi
|
||||
}
|
||||
|
||||
stop_service()
|
||||
{
|
||||
#删除记录文件
|
||||
rm -rf /tmp/modem
|
||||
#停止qmi、mbim拨号
|
||||
killall quectel-CM >/dev/null 2>&1
|
||||
#停止gobinet、ecm、rndis、ncm拨号
|
||||
local modem_number=$(uci -q get modem.@global[0].modem_number)
|
||||
for i in $(seq 0 $((modem_number-1))); do
|
||||
modem_no=$i
|
||||
local mode=$(uci -q get modem.modem$modem_no.mode)
|
||||
case $mode in
|
||||
"gobinet") stop_gobinet ;;
|
||||
"ecm") stop_ecm ;;
|
||||
"rndis") stop_rndis ;;
|
||||
"ncm") stop_ncm ;;
|
||||
*) stop_ecm ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
12
applications/luci-app-modem/root/etc/init.d/modeminit
Normal file
12
applications/luci-app-modem/root/etc/init.d/modeminit
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=99
|
||||
STOP=13
|
||||
USE_PROCD=1
|
||||
|
||||
start_service() {
|
||||
procd_open_instance #启动实例
|
||||
procd_set_param command /bin/sh /usr/share/modem/modem_task.sh
|
||||
procd_set_param respawn # 定义respawn参数,告知procd当task程序退出后尝试进行重启
|
||||
procd_close_instance #关闭实例
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
uci -q get modem.global >/dev/null || uci -q batch <<-EOF >/dev/null
|
||||
set modem.global=global
|
||||
set modem.global.enable=1
|
||||
set modem.global.modem_number=0
|
||||
commit modem
|
||||
EOF
|
||||
|
||||
/etc/init.d/modeminit enable
|
||||
/etc/init.d/modem enable
|
||||
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
delete ucitrack.@modem[-1]
|
||||
add ucitrack modem
|
||||
set ucitrack.@modem[-1].init=modem
|
||||
commit ucitrack
|
||||
EOF
|
||||
|
||||
rm -rf /tmp/luci-*cache
|
||||
exit 0
|
||||
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"quick_commands":[
|
||||
{"****************通用****************":"ATI"},
|
||||
{"模组信息 > ATI":"ATI"},
|
||||
{"查询SIM卡状态 > AT+CPIN?":"AT+CPIN?"},
|
||||
{"查询此时信号强度 > AT+CSQ":"AT+CSQ"},
|
||||
{"查询网络信息 > AT+COPS?":"AT+COPS?"},
|
||||
{"查询PDP信息 > AT+CGDCONT?":"AT+CGDCONT?"},
|
||||
{"最小功能模式 > AT+CFUN=0":"AT+CFUN=0"},
|
||||
{"全功能模式 > AT+CFUN=1":"AT+CFUN=1"},
|
||||
{"重启模组 > AT+CFUN=1,1":"AT+CFUN=1,1"},
|
||||
{"****************移远****************":"ATI"},
|
||||
{"SIM卡状态上报 > AT+QSIMSTAT?":"AT+QSIMSTAT?"},
|
||||
{"设置当前使用的为卡1 > AT+QUIMSLOT=1":"AT+QUIMSLOT=1"},
|
||||
{"设置当前使用的为卡2 > AT+QUIMSLOT=2":"AT+QUIMSLOT=2"},
|
||||
{"查询网络信息 > AT+QNWINFO":"AT+QNWINFO"},
|
||||
{"查询载波聚合参数 > AT+QCAINFO":"AT+QCAINFO"},
|
||||
{"查询当前拨号模式 > AT+QCFG=\"usbnet\"":"AT+QCFG=\"usbnet\""},
|
||||
{"QMI/GobiNet拨号 > AT+QCFG=\"usbnet\",0":"AT+QCFG=\"usbnet\",0"},
|
||||
{"ECM拨号 > AT+QCFG=\"usbnet\",1":"AT+QCFG=\"usbnet\",1"},
|
||||
{"MBIM拨号 > AT+QCFG=\"usbnet\",2":"AT+QCFG=\"usbnet\",2"},
|
||||
{"RNDIS拨号 > AT+QCFG=\"usbnet\",3":"AT+QCFG=\"usbnet\",3"},
|
||||
{"NCM拨号 > AT+QCFG=\"usbnet\",5":"AT+QCFG=\"usbnet\",5"},
|
||||
{"锁4G > AT+QNWPREFCFG=\"mode_pref\",LTE":"AT+QNWPREFCFG=\"mode_pref\",LTE"},
|
||||
{"锁5G > AT+QNWPREFCFG=\"mode_pref\",NR5G":"AT+QNWPREFCFG=\"mode_pref\",NR5G"},
|
||||
{"锁5G NSA > AT+QNWPREFCFG=\"mode_pref\",NR5G-NSA":"AT+QNWPREFCFG=\"mode_pref\",NR5G-NSA"},
|
||||
{"锁5G SA > AT+QNWPREFCFG=\"mode_pref\",NR5G-SA":"AT+QNWPREFCFG=\"mode_pref\",NR5G-SA"},
|
||||
{"恢复自动搜索网络 > AT+QNWPREFCFG=\"mode_pref\",AUTO":"AT+QNWPREFCFG=\"mode_pref\",AUTO"},
|
||||
{"查询模组IMEI > AT+CGSN":"AT+CGSN"},
|
||||
{"查询模组IMEI > AT+GSN":"AT+GSN"},
|
||||
{"更改模组IMEI > AT+EGMR=1,7,\"IMEI\"":"AT+EGMR=1,7,\"在此设置IMEI\""},
|
||||
{"获取模组温度 > AT+QTEMP":"AT+QTEMP"},
|
||||
{"切换为USB通信端口 > AT+QCFG=\"data_interface\",0,0":"AT+QCFG=\"data_interface\",0,0"},
|
||||
{"切换为PCIE通信端口 > AT+QCFG=\"data_interface\",1,0":"AT+QCFG=\"data_interface\",1,0"},
|
||||
{"查看当前USB速率 > AT+QCFG=\"usbspeed\"":"AT+QCFG=\"usbspeed\""},
|
||||
{"切换为USB2.0 > AT+QCFG=\"usbspeed\",\"20\"":"AT+QCFG=\"usbspeed\",\"20\""},
|
||||
{"切换为USB3.1 Gen1(5Gbps) > AT+QCFG=\"usbspeed\",\"311\"":"AT+QCFG=\"usbspeed\",\"311\""},
|
||||
{"切换为USB3.1 Gen1(10Gbps) > AT+QCFG=\"usbspeed\",\"312\"":"AT+QCFG=\"usbspeed\",\"312\""},
|
||||
{"重置模组 > AT+QCFG=\"ResetFactory\"":"AT+QCFG=\"ResetFactory\""},
|
||||
{"****************广和通****************":"ATI"},
|
||||
{"设置当前使用的为卡1 > AT+GTDUALSIM=0":"AT+GTDUALSIM=0"},
|
||||
{"设置当前使用的为卡2 > AT+GTDUALSIM=1":"AT+GTDUALSIM=1"},
|
||||
{"ECM手动拨号 > AT+GTRNDIS=1,1":"AT+GTRNDIS=1,1"},
|
||||
{"ECM拨号断开 > AT+GTRNDIS=0,1":"AT+GTRNDIS=0,1"},
|
||||
{"查询当前端口模式 > AT+GTUSBMODE?":"AT+GTUSBMODE?"},
|
||||
{"QMI/GobiNet拨号 > AT+GTUSBMODE=32":"AT+GTUSBMODE=32"},
|
||||
{"ECM拨号 > AT+GTUSBMODE=18":"AT+GTUSBMODE=18"},
|
||||
{"MBIM拨号 > AT+GTUSBMODE=30":"AT+GTUSBMODE=30"},
|
||||
{"RNDIS拨号 > AT+GTUSBMODE=24":"AT+GTUSBMODE=24"},
|
||||
{"NCM拨号 > AT+GTUSBMODE=18":"AT+GTUSBMODE=18"},
|
||||
{"锁4G > AT+GTACT=2":"AT+GTACT=2"},
|
||||
{"锁5G > AT+GTACT=14":"AT+GTACT=14"},
|
||||
{"恢复自动搜索网络 > AT+GTACT=20":"AT+GTACT=20"},
|
||||
{"查询当前连接的网络类型 > AT+PSRAT?":"AT+PSRAT?"},
|
||||
{"查询模组IMEI > AT+CGSN?":"AT+CGSN?"},
|
||||
{"查询模组IMEI > AT+GSN?":"AT+GSN?"},
|
||||
{"更改模组IMEI > AT+GTSN=1,7,\"IMEI\"":"AT+GTSN=1,7,\"在此设置IMEI\""},
|
||||
{"报告一次当前BBIC的温度 > AT+MTSM=1,6":"AT+MTSM=1,6"},
|
||||
{"报告一次当前射频的温度 > AT+MTSM=1,7":"AT+MTSM=1,7"},
|
||||
{"重置模组 > AT+CFUN=15":"AT+CFUN=15"}
|
||||
]
|
||||
}
|
||||
895
applications/luci-app-modem/root/usr/share/modem/fibocom.sh
Normal file
895
applications/luci-app-modem/root/usr/share/modem/fibocom.sh
Normal file
@@ -0,0 +1,895 @@
|
||||
#!/bin/sh
|
||||
current_dir="$(dirname "$0")"
|
||||
|
||||
#获取拨号模式
|
||||
# $1:AT串口
|
||||
# $2:平台
|
||||
fibocom_get_mode()
|
||||
{
|
||||
local at_port="$1"
|
||||
at_command="AT+GTUSBMODE?"
|
||||
local mode_num=$(sh $current_dir/modem_at.sh $at_port $at_command | grep "+GTUSBMODE:" | sed 's/+GTUSBMODE: //g' | sed 's/\r//g')
|
||||
|
||||
#获取芯片平台
|
||||
local platform="$2"
|
||||
if [ -z "$platform" ]; then
|
||||
local modem_number=$(uci -q get modem.global.modem_number)
|
||||
for i in $(seq 0 $((modem_number-1))); do
|
||||
local at_port_tmp=$(uci -q get modem.modem$i.at_port)
|
||||
if [ "$at_port" = "$at_port_tmp" ]; then
|
||||
platform=$(uci -q get modem.modem$i.platform)
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
local mode
|
||||
case "$platform" in
|
||||
"qualcomm")
|
||||
case "$mode_num" in
|
||||
"17") mode="qmi" ;; #-
|
||||
"31") mode="qmi" ;; #-
|
||||
"32") mode="qmi" ;;
|
||||
"34") mode="qmi" ;;
|
||||
# "32") mode="gobinet" ;;
|
||||
"18") mode="ecm" ;;
|
||||
"23") mode="ecm" ;; #-
|
||||
"33") mode="ecm" ;; #-
|
||||
"35") mode="ecm" ;; #-
|
||||
"29") mode="mbim" ;; #-
|
||||
"30") mode="mbim" ;;
|
||||
"24") mode="rndis" ;;
|
||||
"18") mode="ncm" ;;
|
||||
*) mode="$mode_num" ;;
|
||||
esac
|
||||
;;
|
||||
"unisoc")
|
||||
case "$mode_num" in
|
||||
"34") mode="ecm" ;;
|
||||
"35") mode="ecm" ;; #-
|
||||
"40") mode="mbim" ;;
|
||||
"41") mode="mbim" ;; #-
|
||||
"38") mode="rndis" ;;
|
||||
"39") mode="rndis" ;; #-
|
||||
"36") mode="ncm" ;;
|
||||
"37") mode="ncm" ;; #-
|
||||
*) mode="$mode_num" ;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
mode="$mode_num"
|
||||
;;
|
||||
esac
|
||||
echo "$mode"
|
||||
}
|
||||
|
||||
#设置拨号模式
|
||||
# $1:AT串口
|
||||
# $2:拨号模式配置
|
||||
fibocom_set_mode()
|
||||
{
|
||||
local at_port="$1"
|
||||
|
||||
#获取芯片平台
|
||||
local platform
|
||||
local modem_number=$(uci -q get modem.global.modem_number)
|
||||
for i in $(seq 0 $((modem_number-1))); do
|
||||
local at_port_tmp=$(uci -q get modem.modem$i.at_port)
|
||||
if [ "$at_port" = "$at_port_tmp" ]; then
|
||||
platform=$(uci -q get modem.modem$i.platform)
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
#获取拨号模式配置
|
||||
local mode_num
|
||||
case "$platform" in
|
||||
"qualcomm")
|
||||
case "$2" in
|
||||
"qmi") mode_num="32" ;;
|
||||
# "gobinet") mode_num="32" ;;
|
||||
"ecm") mode_num="18" ;;
|
||||
"mbim") mode_num="30" ;;
|
||||
"rndis") mode_num="24" ;;
|
||||
"ncm") mode_num="18" ;;
|
||||
*) mode_num="32" ;;
|
||||
esac
|
||||
;;
|
||||
"unisoc")
|
||||
case "$2" in
|
||||
"ecm") mode_num="34" ;;
|
||||
"mbim") mode_num="40" ;;
|
||||
"rndis") mode_num="38" ;;
|
||||
"ncm") mode_num="36" ;;
|
||||
*) mode_num="34" ;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
mode_num="32"
|
||||
;;
|
||||
esac
|
||||
|
||||
#设置模组
|
||||
at_command="AT+GTUSBMODE=$mode_num"
|
||||
sh $current_dir/modem_at.sh $at_port "$at_command"
|
||||
}
|
||||
|
||||
#获取网络偏好
|
||||
# $1:AT串口
|
||||
fibocom_get_network_prefer()
|
||||
{
|
||||
local at_port="$1"
|
||||
at_command="AT+GTACT?"
|
||||
local network_prefer_num=$(sh $current_dir/modem_at.sh $at_port $at_command | grep "+GTACT:" | awk -F',' '{print $1}' | sed 's/+GTACT: //g')
|
||||
|
||||
local network_prefer_3g="0";
|
||||
local network_prefer_4g="0";
|
||||
local network_prefer_5g="0";
|
||||
|
||||
#匹配不同的网络类型
|
||||
case "$network_prefer_num" in
|
||||
"1") network_prefer_3g="1" ;;
|
||||
"2") network_prefer_4g="1" ;;
|
||||
"4")
|
||||
network_prefer_3g="1"
|
||||
network_prefer_4g="1"
|
||||
;;
|
||||
"10")
|
||||
network_prefer_3g="1"
|
||||
network_prefer_4g="1"
|
||||
network_prefer_5g="1"
|
||||
;;
|
||||
"14") network_prefer_5g="1" ;;
|
||||
"16")
|
||||
network_prefer_3g="1"
|
||||
network_prefer_5g="1"
|
||||
;;
|
||||
"17")
|
||||
network_prefer_4g="1"
|
||||
network_prefer_5g="1"
|
||||
;;
|
||||
"20")
|
||||
network_prefer_3g="1"
|
||||
network_prefer_4g="1"
|
||||
network_prefer_5g="1"
|
||||
;;
|
||||
*)
|
||||
network_prefer_3g="1"
|
||||
network_prefer_4g="1"
|
||||
network_prefer_5g="1"
|
||||
;;
|
||||
esac
|
||||
|
||||
local network_prefer="{
|
||||
\"network_prefer\":{
|
||||
\"3G\":$network_prefer_3g,
|
||||
\"4G\":$network_prefer_4g,
|
||||
\"5G\":$network_prefer_5g
|
||||
}
|
||||
}"
|
||||
echo "$network_prefer"
|
||||
}
|
||||
|
||||
#设置网络偏好
|
||||
# $1:AT串口
|
||||
# $2:网络偏好配置
|
||||
fibocom_set_network_prefer()
|
||||
{
|
||||
local network_prefer="$2"
|
||||
|
||||
#获取网络偏好数字
|
||||
local network_prefer_num
|
||||
|
||||
#获取选中的数量
|
||||
local count=$(echo "$network_prefer" | grep -o "1" | wc -l)
|
||||
#获取每个偏好的值
|
||||
local network_prefer_3g=$(echo "$network_prefer" | jq -r '.["3G"]')
|
||||
local network_prefer_4g=$(echo "$network_prefer" | jq -r '.["4G"]')
|
||||
local network_prefer_5g=$(echo "$network_prefer" | jq -r '.["5G"]')
|
||||
|
||||
case "$count" in
|
||||
"1")
|
||||
if [ "$network_prefer_3g" = "1" ]; then
|
||||
network_prefer_num="1"
|
||||
elif [ "$network_prefer_4g" = "1" ]; then
|
||||
network_prefer_num="2"
|
||||
elif [ "$network_prefer_5g" = "1" ]; then
|
||||
network_prefer_num="14"
|
||||
fi
|
||||
;;
|
||||
"2")
|
||||
if [ "$network_prefer_3g" = "1" ] && [ "$network_prefer_4g" = "1" ]; then
|
||||
network_prefer_num="4"
|
||||
elif [ "$network_prefer_3g" = "1" ] && [ "$network_prefer_5g" = "1" ]; then
|
||||
network_prefer_num="16"
|
||||
elif [ "$network_prefer_4g" = "1" ] && [ "$network_prefer_5g" = "1" ]; then
|
||||
network_prefer_num="17"
|
||||
fi
|
||||
;;
|
||||
"3") network_prefer_num="20" ;;
|
||||
*) network_prefer_num="10" ;;
|
||||
esac
|
||||
|
||||
#设置模组
|
||||
local at_port="$1"
|
||||
at_command="AT+GTACT=$network_prefer_num"
|
||||
sh $current_dir/modem_at.sh $at_port "$at_command"
|
||||
}
|
||||
|
||||
#获取连接状态
|
||||
# $1:AT串口
|
||||
fibocom_get_connect_status()
|
||||
{
|
||||
local at_port="$1"
|
||||
at_command="AT+CGDCONT?"
|
||||
|
||||
local response=$(sh $current_dir/modem_at.sh $at_port $at_command | sed -n '2p' | awk -F'"' '{print $6}')
|
||||
local not_ip="0.0.0.0,0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0"
|
||||
|
||||
local connect_status
|
||||
if [ "$response" = "$not_ip" ]; then
|
||||
connect_status="disconnect"
|
||||
else
|
||||
connect_status="connect"
|
||||
fi
|
||||
|
||||
echo "$connect_status"
|
||||
}
|
||||
|
||||
#基本信息
|
||||
fibocom_base_info()
|
||||
{
|
||||
debug "Fibocom base info"
|
||||
|
||||
#Name(名称)
|
||||
at_command="AT+CGMM"
|
||||
name=$(sh $current_dir/modem_at.sh $at_port $at_command | sed -n '2p' | sed 's/\r//g')
|
||||
#Manufacturer(制造商)
|
||||
at_command="AT+CGMI"
|
||||
manufacturer=$(sh $current_dir/modem_at.sh $at_port $at_command | sed -n '2p' | sed 's/\r//g')
|
||||
#Revision(固件版本)
|
||||
at_command="AT+CGMR"
|
||||
revision=$(sh $current_dir/modem_at.sh $at_port $at_command | sed -n '2p' | sed 's/\r//g')
|
||||
|
||||
#Mode(拨号模式)
|
||||
mode=$(fibocom_get_mode $at_port | tr 'a-z' 'A-Z')
|
||||
|
||||
#Temperature(温度)
|
||||
at_command="AT+MTSM=1,6"
|
||||
response=$(sh $current_dir/modem_at.sh $at_port $at_command | sed -n '2p' | sed 's/+MTSM: //g' | sed 's/\r//g')
|
||||
if [ -n "$response" ]; then
|
||||
temperature="$response$(printf "\xc2\xb0")C"
|
||||
fi
|
||||
}
|
||||
|
||||
#获取SIM卡状态
|
||||
# $1:SIM卡状态标志
|
||||
fibocom_get_sim_status()
|
||||
{
|
||||
local sim_status
|
||||
case $1 in
|
||||
"") sim_status="miss" ;;
|
||||
*"ERROR"*) sim_status="miss" ;;
|
||||
*"READY"*) sim_status="ready" ;;
|
||||
*"SIM PIN"*) sim_status="MT is waiting SIM PIN to be given" ;;
|
||||
*"SIM PUK"*) sim_status="MT is waiting SIM PUK to be given" ;;
|
||||
*"PH-FSIM PIN"*) sim_status="MT is waiting phone-to-SIM card password to be given" ;;
|
||||
*"PH-FSIM PIN"*) sim_status="MT is waiting phone-to-very first SIM card password to be given" ;;
|
||||
*"PH-FSIM PUK"*) sim_status="MT is waiting phone-to-very first SIM card unblocking password to be given" ;;
|
||||
*"SIM PIN2"*) sim_status="MT is waiting SIM PIN2 to be given" ;;
|
||||
*"SIM PUK2"*) sim_status="MT is waiting SIM PUK2 to be given" ;;
|
||||
*"PH-NET PIN"*) sim_status="MT is waiting network personalization password to be given" ;;
|
||||
*"PH-NET PUK"*) sim_status="MT is waiting network personalization unblocking password to be given" ;;
|
||||
*"PH-NETSUB PIN"*) sim_status="MT is waiting network subset personalization password to be given" ;;
|
||||
*"PH-NETSUB PUK"*) sim_status="MT is waiting network subset personalization unblocking password to be given" ;;
|
||||
*"PH-SP PIN"*) sim_status="MT is waiting service provider personalization password to be given" ;;
|
||||
*"PH-SP PUK"*) sim_status="MT is waiting service provider personalization unblocking password to be given" ;;
|
||||
*"PH-CORP PIN"*) sim_status="MT is waiting corporate personalization password to be given" ;;
|
||||
*"PH-CORP PUK"*) sim_status="MT is waiting corporate personalization unblocking password to be given" ;;
|
||||
*) sim_status="unknown" ;;
|
||||
esac
|
||||
echo "$sim_status"
|
||||
}
|
||||
|
||||
#SIM卡信息
|
||||
fibocom_sim_info()
|
||||
{
|
||||
debug "Fibocom sim info"
|
||||
|
||||
#SIM Slot(SIM卡卡槽)
|
||||
at_command="AT+GTDUALSIM"
|
||||
sim_slot=$(sh $current_dir/modem_at.sh $at_port $at_command | grep "+GTDUALSIM:" | awk -F'"' '{print $2}' | sed 's/SUB//g')
|
||||
|
||||
#IMEI(国际移动设备识别码)
|
||||
at_command="AT+CGSN"
|
||||
imei=$(sh $current_dir/modem_at.sh $at_port $at_command | sed -n '2p' | sed 's/\r//g')
|
||||
|
||||
#SIM Status(SIM状态)
|
||||
at_command="AT+CPIN?"
|
||||
sim_status_flag=$(sh $current_dir/modem_at.sh $at_port $at_command | sed -n '2p')
|
||||
sim_status=$(fibocom_get_sim_status "$sim_status_flag")
|
||||
|
||||
if [ "$sim_status" != "ready" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
#ISP(互联网服务提供商)
|
||||
at_command="AT+COPS?"
|
||||
isp=$(sh $current_dir/modem_at.sh $at_port $at_command | sed -n '2p' | awk -F'"' '{print $2}')
|
||||
# if [ "$isp" = "CHN-CMCC" ] || [ "$isp" = "CMCC" ]|| [ "$isp" = "46000" ]; then
|
||||
# isp="中国移动"
|
||||
# elif [ "$isp" = "CHN-UNICOM" ] || [ "$isp" = "UNICOM" ] || [ "$isp" = "46001" ]; then
|
||||
# isp="中国联通"
|
||||
# elif [ "$isp" = "CHN-CT" ] || [ "$isp" = "CT" ] || [ "$isp" = "46011" ]; then
|
||||
# isp="中国电信"
|
||||
# fi
|
||||
|
||||
#SIM Number(SIM卡号码,手机号)
|
||||
at_command="AT+CNUM?"
|
||||
sim_number=$(sh $current_dir/modem_at.sh $at_port $at_command | sed -n '2p' | awk -F'"' '{print $2}')
|
||||
|
||||
#IMSI(国际移动用户识别码)
|
||||
at_command="AT+CIMI"
|
||||
imsi=$(sh $current_dir/modem_at.sh $at_port $at_command | sed -n '2p' | sed 's/\r//g')
|
||||
|
||||
#ICCID(集成电路卡识别码)
|
||||
at_command="AT+ICCID"
|
||||
iccid=$(sh $current_dir/modem_at.sh $at_port $at_command | grep -o "+ICCID:[ ]*[-0-9]\+" | grep -o "[-0-9]\{1,4\}")
|
||||
}
|
||||
|
||||
#获取信号强度指示
|
||||
# $1:信号强度指示数字
|
||||
fibocom_get_rssi()
|
||||
{
|
||||
local rssi
|
||||
case $1 in
|
||||
"99") rssi="unknown" ;;
|
||||
* ) rssi=$((2 * $1 - 113)) ;;
|
||||
esac
|
||||
echo "$rssi"
|
||||
}
|
||||
|
||||
#网络信息
|
||||
fibocom_network_info()
|
||||
{
|
||||
debug "Fibocom network info"
|
||||
|
||||
#Connect Status(连接状态)
|
||||
connect_status=$(fibocom_get_connect_status $at_port)
|
||||
if [ "$connect_status" != "connect" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
#Network Type(网络类型)
|
||||
at_command="AT+PSRAT?"
|
||||
network_type=$(sh $current_dir/modem_at.sh $at_port $at_command | grep "+PSRAT:" | sed 's/+PSRAT: //g' | sed 's/\r//g')
|
||||
|
||||
#设置网络类型为5G时,信号强度指示用RSRP代替
|
||||
# at_command="AT+GTCSQNREN=1"
|
||||
# sh $current_dir/modem_at.sh $at_port $at_command
|
||||
|
||||
#CSQ(信号强度)
|
||||
at_command="AT+CSQ"
|
||||
response=$(sh $current_dir/modem_at.sh $at_port $at_command | grep "+CSQ:" | sed 's/+CSQ: //g' | sed 's/\r//g')
|
||||
|
||||
#RSSI(信号强度指示)
|
||||
rssi_num=$(echo $response | awk -F',' '{print $1}')
|
||||
rssi=$(fibocom_get_rssi $rssi_num)
|
||||
#BER(信道误码率)
|
||||
ber=$(echo $response | awk -F',' '{print $2}')
|
||||
|
||||
# #PER(信号强度)
|
||||
# if [ -n "$csq" ]; then
|
||||
# per=$(($csq * 100/31))"%"
|
||||
# fi
|
||||
|
||||
#速率统计
|
||||
at_command="AT+GTSTATIS?"
|
||||
response=$(sh $current_dir/modem_at.sh $at_port $at_command | grep "+GTSTATIS:" | sed 's/+GTSTATIS: //g' | sed 's/\r//g')
|
||||
|
||||
#当前上传速率(单位,Byte/s)
|
||||
tx_rate=$(echo $response | awk -F',' '{print $2}')
|
||||
|
||||
#当前下载速率(单位,Byte/s)
|
||||
rx_rate=$(echo $response | awk -F',' '{print $1}')
|
||||
}
|
||||
|
||||
#获取频段
|
||||
# $1:网络类型
|
||||
# $2:频段数字
|
||||
fibocom_get_band()
|
||||
{
|
||||
local band
|
||||
case $1 in
|
||||
"WCDMA") band="$2" ;;
|
||||
"LTE") band="$(($2-100))" ;;
|
||||
"NR") band="$2" band="${band#*50}" ;;
|
||||
esac
|
||||
echo "$band"
|
||||
}
|
||||
|
||||
#获取上行带宽
|
||||
# $1:上行带宽数字
|
||||
fibocom_get_ul_bandwidth()
|
||||
{
|
||||
local ul_bandwidth
|
||||
case $1 in
|
||||
"6") ul_bandwidth="1.4" ;;
|
||||
"15"|"25"|"50"|"75"|"100") ul_bandwidth=$(( $1 / 5 )) ;;
|
||||
esac
|
||||
echo "$ul_bandwidth"
|
||||
}
|
||||
|
||||
#获取下行带宽
|
||||
# $1:下行带宽数字
|
||||
fibocom_get_dl_bandwidth()
|
||||
{
|
||||
local dl_bandwidth
|
||||
case $1 in
|
||||
"6") ul_bandwidth="1.4" ;;
|
||||
"15"|"25"|"50"|"75"|"100") ul_bandwidth=$(( $1 / 5 )) ;;
|
||||
esac
|
||||
echo "$dl_bandwidth"
|
||||
}
|
||||
|
||||
#获取NR下行带宽
|
||||
# $1:下行带宽数字
|
||||
fibocom_get_nr_dl_bandwidth()
|
||||
{
|
||||
local nr_dl_bandwidth
|
||||
case $1 in
|
||||
"0") nr_dl_bandwidth="5" ;;
|
||||
"10"|"15"|"20"|"25"|"30"|"40"|"50"|"60"|"70"|"80"|"90"|"100"|"200"|"400") nr_dl_bandwidth="$1" ;;
|
||||
esac
|
||||
echo "$nr_dl_bandwidth"
|
||||
}
|
||||
|
||||
#获取参考信号接收功率
|
||||
# $1:网络类型
|
||||
# $2:参考信号接收功率数字
|
||||
fibocom_get_rsrp()
|
||||
{
|
||||
local rsrp
|
||||
case $1 in
|
||||
"LTE") rsrp=$(($2-141)) ;;
|
||||
"NR") rsrp=$(($2-157)) ;;
|
||||
esac
|
||||
echo "$rsrp"
|
||||
}
|
||||
|
||||
#获取参考信号接收质量
|
||||
# $1:网络类型
|
||||
# $2:参考信号接收质量数字
|
||||
fibocom_get_rsrq()
|
||||
{
|
||||
local rsrq
|
||||
case $1 in
|
||||
"LTE") rsrq=$(awk "BEGIN{ printf \"%.2f\", $2 * 0.5 - 20 }" | sed 's/\.*0*$//') ;;
|
||||
"NR") rsrq=$(awk -v num="$2" "BEGIN{ printf \"%.2f\", (num+1) * 0.5 - 44 }" | sed 's/\.*0*$//') ;;
|
||||
esac
|
||||
echo "$rsrq"
|
||||
}
|
||||
|
||||
#获取信号干扰比
|
||||
# $1:信号干扰比数字
|
||||
fibocom_get_rssnr()
|
||||
{
|
||||
#去掉小数点后的0
|
||||
local rssnr=$(awk "BEGIN{ printf \"%.2f\", $1 / 2 }" | sed 's/\.*0*$//')
|
||||
echo "$rssnr"
|
||||
}
|
||||
|
||||
#获取接收信号功率
|
||||
# $1:网络类型
|
||||
# $2:接收信号功率数字
|
||||
fibocom_get_rxlev()
|
||||
{
|
||||
local rxlev
|
||||
case $1 in
|
||||
"WCDMA") rxlev=$(($2-121)) ;;
|
||||
"LTE") rxlev=$(($2-141)) ;;
|
||||
"NR") rxlev=$(($2-157)) ;;
|
||||
esac
|
||||
echo "$rxlev"
|
||||
}
|
||||
|
||||
#获取Ec/Io
|
||||
# $1:Ec/Io数字
|
||||
fibocom_get_ecio()
|
||||
{
|
||||
local ecio=$(awk "BEGIN{ printf \"%.2f\", $1 * 0.5 - 24.5 }" | sed 's/\.*0*$//')
|
||||
echo "$ecio"
|
||||
}
|
||||
|
||||
#小区信息
|
||||
fibocom_cell_info()
|
||||
{
|
||||
debug "Fibocom cell info"
|
||||
|
||||
#RSRQ,RSRP,SINR
|
||||
at_command='AT+GTCCINFO?'
|
||||
response=$(sh $current_dir/modem_at.sh $at_port $at_command)
|
||||
|
||||
local rat=$(echo "$response" | grep "service" | awk -F' ' '{print $1}')
|
||||
response=$(echo "$response" | sed -n '4p')
|
||||
case $rat in
|
||||
"NR")
|
||||
network_mode="NR5G-SA Mode"
|
||||
nr_mcc=$(echo "$response" | awk -F',' '{print $3}')
|
||||
nr_mnc=$(echo "$response" | awk -F',' '{print $4}')
|
||||
nr_tac=$(echo "$response" | awk -F',' '{print $5}')
|
||||
nr_cell_id=$(echo "$response" | awk -F',' '{print $6}')
|
||||
nr_arfcn=$(echo "$response" | awk -F',' '{print $7}')
|
||||
nr_physical_cell_id=$(echo "$response" | awk -F',' '{print $8}')
|
||||
nr_band_num=$(echo "$response" | awk -F',' '{print $9}')
|
||||
nr_band=$(fibocom_get_band "NR" $nr_band_num)
|
||||
nr_dl_bandwidth_num=$(echo "$response" | awk -F',' '{print $10}')
|
||||
nr_dl_bandwidth=$(fibocom_get_nr_dl_bandwidth $nr_dl_bandwidth_num)
|
||||
nr_sinr=$(echo "$response" | awk -F',' '{print $11}')
|
||||
nr_rxlev_num=$(echo "$response" | awk -F',' '{print $12}')
|
||||
nr_rxlev=$(fibocom_get_rxlev "NR" $nr_rxlev_num)
|
||||
nr_rsrp_num=$(echo "$response" | awk -F',' '{print $13}')
|
||||
nr_rsrp=$(fibocom_get_rsrp "NR" $nr_rsrp_num)
|
||||
nr_rsrq_num=$(echo "$response" | awk -F',' '{print $14}' | sed 's/\r//g')
|
||||
nr_rsrq=$(fibocom_get_rsrq "NR" $nr_rsrq_num)
|
||||
;;
|
||||
"LTE-NR")
|
||||
network_mode="EN-DC Mode"
|
||||
#LTE
|
||||
endc_lte_mcc=$(echo "$response" | awk -F',' '{print $3}')
|
||||
endc_lte_mnc=$(echo "$response" | awk -F',' '{print $4}')
|
||||
endc_lte_tac=$(echo "$response" | awk -F',' '{print $5}')
|
||||
endc_lte_cell_id=$(echo "$response" | awk -F',' '{print $6}')
|
||||
endc_lte_earfcn=$(echo "$response" | awk -F',' '{print $7}')
|
||||
endc_lte_physical_cell_id=$(echo "$response" | awk -F',' '{print $8}')
|
||||
endc_lte_band_num=$(echo "$response" | awk -F',' '{print $9}')
|
||||
endc_lte_band=$(fibocom_get_band "LTE" $endc_lte_band_num)
|
||||
ul_bandwidth_num=$(echo "$response" | awk -F',' '{print $10}')
|
||||
endc_lte_ul_bandwidth=$(fibocom_get_ul_bandwidth $ul_bandwidth_num)
|
||||
endc_lte_dl_bandwidth="$endc_lte_ul_bandwidth"
|
||||
endc_lte_rssnr_num=$(echo "$response" | awk -F',' '{print $11}')
|
||||
endc_lte_rssnr=$(fibocom_get_rssnr $endc_lte_rssnr_num)
|
||||
endc_lte_rxlev_num=$(echo "$response" | awk -F',' '{print $12}')
|
||||
endc_lte_rxlev=$(fibocom_get_rxlev "LTE" $endc_lte_rxlev_num)
|
||||
endc_lte_rsrp_num=$(echo "$response" | awk -F',' '{print $13}')
|
||||
endc_lte_rsrp=$(fibocom_get_rsrp "LTE" $endc_lte_rsrp_num)
|
||||
endc_lte_rsrq_num=$(echo "$response" | awk -F',' '{print $14}' | sed 's/\r//g')
|
||||
endc_lte_rsrq=$(fibocom_get_rsrq "LTE" $endc_lte_rsrq_num)
|
||||
#NR5G-NSA
|
||||
endc_nr_mcc=$(echo "$response" | awk -F',' '{print $3}')
|
||||
endc_nr_mnc=$(echo "$response" | awk -F',' '{print $4}')
|
||||
endc_nr_tac=$(echo "$response" | awk -F',' '{print $5}')
|
||||
endc_nr_cell_id=$(echo "$response" | awk -F',' '{print $6}')
|
||||
endc_nr_arfcn=$(echo "$response" | awk -F',' '{print $7}')
|
||||
endc_nr_physical_cell_id=$(echo "$response" | awk -F',' '{print $8}')
|
||||
endc_nr_band_num=$(echo "$response" | awk -F',' '{print $9}')
|
||||
endc_nr_band=$(fibocom_get_band "NR" $endc_nr_band_num)
|
||||
nr_dl_bandwidth_num=$(echo "$response" | awk -F',' '{print $10}')
|
||||
endc_nr_dl_bandwidth=$(fibocom_get_nr_dl_bandwidth $nr_dl_bandwidth_num)
|
||||
endc_nr_sinr=$(echo "$response" | awk -F',' '{print $11}')
|
||||
endc_nr_rxlev_num=$(echo "$response" | awk -F',' '{print $12}')
|
||||
endc_nr_rxlev=$(fibocom_get_rxlev "NR" $endc_nr_rxlev_num)
|
||||
endc_nr_rsrp_num=$(echo "$response" | awk -F',' '{print $13}')
|
||||
endc_nr_rsrp=$(fibocom_get_rsrp "NR" $endc_nr_rsrp_num)
|
||||
endc_nr_rsrq_num=$(echo "$response" | awk -F',' '{print $14}' | sed 's/\r//g')
|
||||
endc_nr_rsrq=$(fibocom_get_rsrq "NR" $endc_nr_rsrq_num)
|
||||
;;
|
||||
"LTE"|"eMTC"|"NB-IoT")
|
||||
network_mode="LTE Mode"
|
||||
lte_mcc=$(echo "$response" | awk -F',' '{print $3}')
|
||||
lte_mnc=$(echo "$response" | awk -F',' '{print $4}')
|
||||
lte_tac=$(echo "$response" | awk -F',' '{print $5}')
|
||||
lte_cell_id=$(echo "$response" | awk -F',' '{print $6}')
|
||||
lte_earfcn=$(echo "$response" | awk -F',' '{print $7}')
|
||||
lte_physical_cell_id=$(echo "$response" | awk -F',' '{print $8}')
|
||||
lte_band_num=$(echo "$response" | awk -F',' '{print $9}')
|
||||
lte_band=$(fibocom_get_band "LTE" $lte_band_num)
|
||||
ul_bandwidth_num=$(echo "$response" | awk -F',' '{print $10}')
|
||||
lte_ul_bandwidth=$(fibocom_get_ul_bandwidth $ul_bandwidth_num)
|
||||
lte_dl_bandwidth="$lte_ul_bandwidth"
|
||||
lte_rssnr=$(echo "$response" | awk -F',' '{print $11}')
|
||||
lte_rxlev_num=$(echo "$response" | awk -F',' '{print $12}')
|
||||
lte_rxlev=$(fibocom_get_rxlev "LTE" $lte_rxlev_num)
|
||||
lte_rsrp_num=$(echo "$response" | awk -F',' '{print $13}')
|
||||
lte_rsrp=$(fibocom_get_rsrp "LTE" $lte_rsrp_num)
|
||||
lte_rsrq_num=$(echo "$response" | awk -F',' '{print $14}' | sed 's/\r//g')
|
||||
lte_rsrq=$(fibocom_get_rsrq "LTE" $lte_rsrq_num)
|
||||
;;
|
||||
"WCDMA"|"UMTS")
|
||||
network_mode="WCDMA Mode"
|
||||
wcdma_mcc=$(echo "$response" | awk -F',' '{print $3}')
|
||||
wcdma_mnc=$(echo "$response" | awk -F',' '{print $4}')
|
||||
wcdma_lac=$(echo "$response" | awk -F',' '{print $5}')
|
||||
wcdma_cell_id=$(echo "$response" | awk -F',' '{print $6}')
|
||||
wcdma_uarfcn=$(echo "$response" | awk -F',' '{print $7}')
|
||||
wcdma_psc=$(echo "$response" | awk -F',' '{print $8}')
|
||||
wcdma_band_num=$(echo "$response" | awk -F',' '{print $9}')
|
||||
wcdma_band=$(fibocom_get_band "WCDMA" $wcdma_band_num)
|
||||
wcdma_ecno=$(echo "$response" | awk -F',' '{print $10}')
|
||||
wcdma_rscp=$(echo "$response" | awk -F',' '{print $11}')
|
||||
wcdma_rac=$(echo "$response" | awk -F',' '{print $12}')
|
||||
wcdma_rxlev_num=$(echo "$response" | awk -F',' '{print $13}')
|
||||
wcdma_rxlev=$(fibocom_get_rxlev "WCDMA" $wcdma_rxlev_num)
|
||||
wcdma_reserved=$(echo "$response" | awk -F',' '{print $14}')
|
||||
wcdma_ecio_num=$(echo "$response" | awk -F',' '{print $15}' | sed 's/\r//g')
|
||||
wcdma_ecio=$(fibocom_get_ecio $wcdma_ecio_num)
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
# fibocom获取基站信息
|
||||
Fibocom_Cellinfo()
|
||||
{
|
||||
#baseinfo.gcom
|
||||
OX=$( sh $current_dir/modem_at.sh $at_port "ATI")
|
||||
OX=$( sh $current_dir/modem_at.sh $at_port "AT+CGEQNEG=1")
|
||||
|
||||
#cellinfo0.gcom
|
||||
# OX1=$( sh $current_dir/modem_at.sh $at_port "AT+COPS=3,0;+COPS?")
|
||||
# OX2=$( sh $current_dir/modem_at.sh $at_port "AT+COPS=3,2;+COPS?")
|
||||
OX=$OX1" "$OX2
|
||||
|
||||
#cellinfo.gcom
|
||||
OY1=$( sh $current_dir/modem_at.sh $at_port "AT+CREG=2;+CREG?;+CREG=0")
|
||||
OY2=$( sh $current_dir/modem_at.sh $at_port "AT+CEREG=2;+CEREG?;+CEREG=0")
|
||||
OY3=$( sh $current_dir/modem_at.sh $at_port "AT+C5GREG=2;+C5GREG?;+C5GREG=0")
|
||||
OY=$OY1" "$OY2" "$OY3
|
||||
|
||||
|
||||
OXx=$OX
|
||||
OX=$(echo $OX | tr 'a-z' 'A-Z')
|
||||
OY=$(echo $OY | tr 'a-z' 'A-Z')
|
||||
OX=$OX" "$OY
|
||||
|
||||
#debug "$OX"
|
||||
#debug "$OY"
|
||||
|
||||
COPS="-"
|
||||
COPS_MCC="-"
|
||||
COPS_MNC="-"
|
||||
COPSX=$(echo $OXx | grep -o "+COPS: [01],0,.\+," | cut -d, -f3 | grep -o "[^\"]\+")
|
||||
|
||||
if [ "x$COPSX" != "x" ]; then
|
||||
COPS=$COPSX
|
||||
fi
|
||||
|
||||
COPSX=$(echo $OX | grep -o "+COPS: [01],2,.\+," | cut -d, -f3 | grep -o "[^\"]\+")
|
||||
|
||||
if [ "x$COPSX" != "x" ]; then
|
||||
COPS_MCC=${COPSX:0:3}
|
||||
COPS_MNC=${COPSX:3:3}
|
||||
if [ "$COPS" = "-" ]; then
|
||||
COPS=$(awk -F[\;] '/'$COPS'/ {print $2}' $ROOTER/signal/mccmnc.data)
|
||||
[ "x$COPS" = "x" ] && COPS="-"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$COPS" = "-" ]; then
|
||||
COPS=$(echo "$O" | awk -F[\"] '/^\+COPS: 0,0/ {print $2}')
|
||||
if [ "x$COPS" = "x" ]; then
|
||||
COPS="-"
|
||||
COPS_MCC="-"
|
||||
COPS_MNC="-"
|
||||
fi
|
||||
fi
|
||||
COPS_MNC=" "$COPS_MNC
|
||||
|
||||
OX=$(echo "${OX//[ \"]/}")
|
||||
CID=""
|
||||
CID5=""
|
||||
RAT=""
|
||||
REGV=$(echo "$OX" | grep -o "+C5GREG:2,[0-9],[A-F0-9]\{2,6\},[A-F0-9]\{5,10\},[0-9]\{1,2\}")
|
||||
if [ -n "$REGV" ]; then
|
||||
LAC5=$(echo "$REGV" | cut -d, -f3)
|
||||
LAC5=$LAC5" ($(printf "%d" 0x$LAC5))"
|
||||
CID5=$(echo "$REGV" | cut -d, -f4)
|
||||
CID5L=$(printf "%010X" 0x$CID5)
|
||||
RNC5=${CID5L:1:6}
|
||||
RNC5=$RNC5" ($(printf "%d" 0x$RNC5))"
|
||||
CID5=${CID5L:7:3}
|
||||
CID5="Short $(printf "%X" 0x$CID5) ($(printf "%d" 0x$CID5)), Long $(printf "%X" 0x$CID5L) ($(printf "%d" 0x$CID5L))"
|
||||
RAT=$(echo "$REGV" | cut -d, -f5)
|
||||
fi
|
||||
REGV=$(echo "$OX" | grep -o "+CEREG:2,[0-9],[A-F0-9]\{2,4\},[A-F0-9]\{5,8\}")
|
||||
REGFMT="3GPP"
|
||||
if [ -z "$REGV" ]; then
|
||||
REGV=$(echo "$OX" | grep -o "+CEREG:2,[0-9],[A-F0-9]\{2,4\},[A-F0-9]\{1,3\},[A-F0-9]\{5,8\}")
|
||||
REGFMT="SW"
|
||||
fi
|
||||
if [ -n "$REGV" ]; then
|
||||
LAC=$(echo "$REGV" | cut -d, -f3)
|
||||
LAC=$(printf "%04X" 0x$LAC)" ($(printf "%d" 0x$LAC))"
|
||||
if [ $REGFMT = "3GPP" ]; then
|
||||
CID=$(echo "$REGV" | cut -d, -f4)
|
||||
else
|
||||
CID=$(echo "$REGV" | cut -d, -f5)
|
||||
fi
|
||||
CIDL=$(printf "%08X" 0x$CID)
|
||||
RNC=${CIDL:1:5}
|
||||
RNC=$RNC" ($(printf "%d" 0x$RNC))"
|
||||
CID=${CIDL:6:2}
|
||||
CID="Short $(printf "%X" 0x$CID) ($(printf "%d" 0x$CID)), Long $(printf "%X" 0x$CIDL) ($(printf "%d" 0x$CIDL))"
|
||||
|
||||
else
|
||||
REGV=$(echo "$OX" | grep -o "+CREG:2,[0-9],[A-F0-9]\{2,4\},[A-F0-9]\{2,8\}")
|
||||
if [ -n "$REGV" ]; then
|
||||
LAC=$(echo "$REGV" | cut -d, -f3)
|
||||
CID=$(echo "$REGV" | cut -d, -f4)
|
||||
if [ ${#CID} -gt 4 ]; then
|
||||
LAC=$(printf "%04X" 0x$LAC)" ($(printf "%d" 0x$LAC))"
|
||||
CIDL=$(printf "%08X" 0x$CID)
|
||||
RNC=${CIDL:1:3}
|
||||
CID=${CIDL:4:4}
|
||||
CID="Short $(printf "%X" 0x$CID) ($(printf "%d" 0x$CID)), Long $(printf "%X" 0x$CIDL) ($(printf "%d" 0x$CIDL))"
|
||||
else
|
||||
LAC=""
|
||||
fi
|
||||
else
|
||||
LAC=""
|
||||
fi
|
||||
fi
|
||||
REGSTAT=$(echo "$REGV" | cut -d, -f2)
|
||||
if [ "$REGSTAT" == "5" -a "$COPS" != "-" ]; then
|
||||
COPS_MNC=$COPS_MNC" (Roaming)"
|
||||
fi
|
||||
if [ -n "$CID" -a -n "$CID5" ] && [ "$RAT" == "13" -o "$RAT" == "10" ]; then
|
||||
LAC="4G $LAC, 5G $LAC5"
|
||||
CID="4G $CID<br />5G $CID5"
|
||||
RNC="4G $RNC, 5G $RNC5"
|
||||
elif [ -n "$CID5" ]; then
|
||||
LAC=$LAC5
|
||||
CID=$CID5
|
||||
RNC=$RNC5
|
||||
fi
|
||||
if [ -z "$LAC" ]; then
|
||||
LAC="-"
|
||||
CID="-"
|
||||
RNC="-"
|
||||
fi
|
||||
}
|
||||
|
||||
#获取Fibocom模块信息
|
||||
# $1:AT串口
|
||||
get_fibocom_info()
|
||||
{
|
||||
debug "get fibocom info"
|
||||
#设置AT串口
|
||||
at_port=$1
|
||||
|
||||
#基本信息
|
||||
fibocom_base_info
|
||||
|
||||
#SIM卡信息
|
||||
fibocom_sim_info
|
||||
if [ "$sim_status" != "ready" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
#网络信息
|
||||
fibocom_network_info
|
||||
if [ "$connect_status" != "connect" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
#小区信息
|
||||
fibocom_cell_info
|
||||
|
||||
return
|
||||
|
||||
# Fibocom_Cellinfo
|
||||
|
||||
#基站信息
|
||||
OX=$( sh $current_dir/modem_at.sh $at_port "AT+CPSI?")
|
||||
rec=$(echo "$OX" | grep "+CPSI:")
|
||||
w=$(echo $rec |grep "NO SERVICE"| wc -l)
|
||||
if [ $w -ge 1 ];then
|
||||
debug "NO SERVICE"
|
||||
return
|
||||
fi
|
||||
w=$(echo $rec |grep "NR5G_"| wc -l)
|
||||
if [ $w -ge 1 ];then
|
||||
|
||||
w=$(echo $rec |grep "32768"| wc -l)
|
||||
if [ $w -ge 1 ];then
|
||||
debug "-32768"
|
||||
return
|
||||
fi
|
||||
|
||||
debug "$rec"
|
||||
rec1=${rec##*+CPSI:}
|
||||
#echo "$rec1"
|
||||
MODE="${rec1%%,*}" # MODE="NR5G"
|
||||
rect1=${rec1#*,}
|
||||
rect1s="${rect1%%,*}" #Online
|
||||
rect2=${rect1#*,}
|
||||
rect2s="${rect2%%,*}" #460-11
|
||||
rect3=${rect2#*,}
|
||||
rect3s="${rect3%%,*}" #0xCFA102
|
||||
rect4=${rect3#*,}
|
||||
rect4s="${rect4%%,*}" #55744245764
|
||||
rect5=${rect4#*,}
|
||||
rect5s="${rect5%%,*}" #196
|
||||
rect6=${rect5#*,}
|
||||
rect6s="${rect6%%,*}" #NR5G_BAND78
|
||||
rect7=${rect6#*,}
|
||||
rect7s="${rect7%%,*}" #627264
|
||||
rect8=${rect7#*,}
|
||||
rect8s="${rect8%%,*}" #-940
|
||||
rect9=${rect8#*,}
|
||||
rect9s="${rect9%%,*}" #-110
|
||||
# "${rec1##*,}" #最后一位
|
||||
rect10=${rect9#*,}
|
||||
rect10s="${rect10%%,*}" #最后一位
|
||||
PCI=$rect5s
|
||||
LBAND="n"$(echo $rect6s | cut -d, -f0 | grep -o "BAND[0-9]\{1,3\}" | grep -o "[0-9]\+")
|
||||
CHANNEL=$rect7s
|
||||
RSCP=$(($(echo $rect8s | cut -d, -f0) / 10))
|
||||
ECIO=$(($(echo $rect9s | cut -d, -f0) / 10))
|
||||
if [ "$CSQ_PER" = "-" ]; then
|
||||
CSQ_PER=$((100 - (($RSCP + 31) * 100/-125)))"%"
|
||||
fi
|
||||
SINR=$(($(echo $rect10s | cut -d, -f0) / 10))" dB"
|
||||
fi
|
||||
w=$(echo $rec |grep "LTE"|grep "EUTRAN"| wc -l)
|
||||
if [ $w -ge 1 ];then
|
||||
rec1=${rec#*EUTRAN-}
|
||||
lte_band=${rec1%%,*} #EUTRAN-BAND
|
||||
rec1=${rec1#*,}
|
||||
rec1=${rec1#*,}
|
||||
rec1=${rec1#*,}
|
||||
rec1=${rec1#*,}
|
||||
#rec1=${rec1#*,}
|
||||
rec1=${rec1#*,}
|
||||
lte_rssi=${rec1%%,*} #LTE_RSSI
|
||||
lte_rssi=`expr $lte_rssi / 10` #LTE_RSSI
|
||||
debug "LTE_BAND=$lte_band LTE_RSSI=$lte_rssi"
|
||||
if [ $rssi == 0 ];then
|
||||
rssi=$lte_rssi
|
||||
fi
|
||||
fi
|
||||
w=$(echo $rec |grep "WCDMA"| wc -l)
|
||||
if [ $w -ge 1 ];then
|
||||
w=$(echo $rec |grep "UNKNOWN"|wc -l)
|
||||
if [ $w -ge 1 ];then
|
||||
debug "UNKNOWN BAND"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
#CNMP
|
||||
OX=$( sh $current_dir/modem_at.sh $at_port "AT+CNMP?")
|
||||
CNMP=$(echo "$OX" | grep -o "+CNMP:[ ]*[0-9]\{1,3\}" | grep -o "[0-9]\{1,3\}")
|
||||
if [ -n "$CNMP" ]; then
|
||||
case $CNMP in
|
||||
"2"|"55" )
|
||||
NETMODE="1" ;;
|
||||
"13" )
|
||||
NETMODE="3" ;;
|
||||
"14" )
|
||||
NETMODE="5" ;;
|
||||
"38" )
|
||||
NETMODE="7" ;;
|
||||
"71" )
|
||||
NETMODE="9" ;;
|
||||
"109" )
|
||||
NETMODE="8" ;;
|
||||
* )
|
||||
NETMODE="0" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# CMGRMI 信息
|
||||
OX=$( sh $current_dir/modem_at.sh $at_port "AT+CMGRMI=4")
|
||||
CAINFO=$(echo "$OX" | grep -o "$REGXz" | tr ' ' ':')
|
||||
if [ -n "$CAINFO" ]; then
|
||||
for CASV in $(echo "$CAINFO"); do
|
||||
LBAND=$LBAND"<br />B"$(echo "$CASV" | cut -d, -f4)
|
||||
BW=$(echo "$CASV" | cut -d, -f5)
|
||||
decode_bw
|
||||
LBAND=$LBAND" (CA, Bandwidth $BW MHz)"
|
||||
CHANNEL="$CHANNEL, "$(echo "$CASV" | cut -d, -f2)
|
||||
PCI="$PCI, "$(echo "$CASV" | cut -d, -f7)
|
||||
done
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"quick_commands":[
|
||||
{"模组信息 > ATI":"ATI"},
|
||||
{"查询SIM卡状态 > AT+CPIN?":"AT+CPIN?"},
|
||||
{"查询此时信号强度 > AT+CSQ":"AT+CSQ"},
|
||||
{"查询网络信息 > AT+COPS?":"AT+COPS?"},
|
||||
{"查询PDP信息 > AT+CGDCONT?":"AT+CGDCONT?"},
|
||||
{"最小功能模式 > AT+CFUN=0":"AT+CFUN=0"},
|
||||
{"全功能模式 > AT+CFUN=1":"AT+CFUN=1"},
|
||||
{"设置当前使用的为卡1 > AT+GTDUALSIM=0":"AT+GTDUALSIM=0"},
|
||||
{"设置当前使用的为卡2 > AT+GTDUALSIM=1":"AT+GTDUALSIM=1"},
|
||||
{"ECM手动拨号 > AT+GTRNDIS=1,1":"AT+GTRNDIS=1,1"},
|
||||
{"ECM拨号断开 > AT+GTRNDIS=0,1":"AT+GTRNDIS=0,1"},
|
||||
{"查询当前端口模式 > AT+GTUSBMODE?":"AT+GTUSBMODE?"},
|
||||
{"QMI/GobiNet拨号 > AT+GTUSBMODE=32":"AT+GTUSBMODE=32"},
|
||||
{"ECM拨号 > AT+GTUSBMODE=18":"AT+GTUSBMODE=18"},
|
||||
{"MBIM拨号 > AT+GTUSBMODE=30":"AT+GTUSBMODE=30"},
|
||||
{"RNDIS拨号 > AT+GTUSBMODE=24":"AT+GTUSBMODE=24"},
|
||||
{"NCM拨号 > AT+GTUSBMODE=18":"AT+GTUSBMODE=18"},
|
||||
{"锁4G > AT+GTACT=2":"AT+GTACT=2"},
|
||||
{"锁5G > AT+GTACT=14":"AT+GTACT=14"},
|
||||
{"恢复自动搜索网络 > AT+GTACT=20":"AT+GTACT=20"},
|
||||
{"查询当前连接的网络类型 > AT+PSRAT?":"AT+PSRAT?"},
|
||||
{"查询模组IMEI > AT+CGSN?":"AT+CGSN?"},
|
||||
{"查询模组IMEI > AT+GSN?":"AT+GSN?"},
|
||||
{"更改模组IMEI > AT+GTSN=1,7,\"IMEI\"":"AT+GTSN=1,7,\"在此设置IMEI\""},
|
||||
{"报告一次当前BBIC的温度 > AT+MTSM=1,6":"AT+MTSM=1,6"},
|
||||
{"报告一次当前射频的温度 > AT+MTSM=1,7":"AT+MTSM=1,7"},
|
||||
{"重启模组 > AT+CFUN=1,1":"AT+CFUN=1,1"},
|
||||
{"重置模组 > AT+CFUN=15":"AT+CFUN=15"}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
current_dir="$(dirname "$0")"
|
||||
source "$current_dir/modem_debug.sh"
|
||||
|
||||
#发送at命令
|
||||
# $1 AT串口
|
||||
# $2 AT命令
|
||||
at $1 $2
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/bin/sh
|
||||
current_dir="$(dirname "$0")"
|
||||
source "$current_dir/quectel.sh"
|
||||
source "$current_dir/fibocom.sh"
|
||||
source "$current_dir/simcom.sh"
|
||||
|
||||
#调试开关
|
||||
# 0关闭
|
||||
# 1打开
|
||||
# 2输出到文件
|
||||
switch=0
|
||||
out_file="/tmp/modem.log" #输出文件
|
||||
#日志信息
|
||||
debug()
|
||||
{
|
||||
time=$(date "+%Y-%m-%d %H:%M:%S") #获取系统时间
|
||||
if [ $switch = 1 ]; then
|
||||
echo $time $1 #打印输出
|
||||
elif [ $switch = 2 ]; then
|
||||
echo $time $1 >> $outfile #输出到文件
|
||||
fi
|
||||
}
|
||||
|
||||
#发送at命令
|
||||
# $1 AT串口
|
||||
# $2 AT命令
|
||||
at()
|
||||
{
|
||||
local new_str="${2/[$]/$}"
|
||||
local atCommand="${new_str/\"/\"}"
|
||||
|
||||
#echo
|
||||
# echo -e $2 > $1 2>&1
|
||||
|
||||
#sms_tool
|
||||
sms_tool -d $1 at $atCommand 2>&1
|
||||
}
|
||||
|
||||
#测试时打开
|
||||
# debug $1
|
||||
# at $1 $2
|
||||
|
||||
#获取快捷命令
|
||||
# $1:快捷选项
|
||||
# $2:制造商
|
||||
get_quick_commands()
|
||||
{
|
||||
local quick_commands
|
||||
case $1 in
|
||||
"auto") quick_commands=$(cat $current_dir/$2_at_commands.json) ;;
|
||||
"custom") quick_commands=$(cat /etc/modem/custom_at_commands.json) ;;
|
||||
*) quick_commands=$(cat $current_dir/$2_at_commands.json) ;;
|
||||
esac
|
||||
echo "$quick_commands"
|
||||
}
|
||||
394
applications/luci-app-modem/root/usr/share/modem/modem_info.sh
Normal file
394
applications/luci-app-modem/root/usr/share/modem/modem_info.sh
Normal file
@@ -0,0 +1,394 @@
|
||||
#!/bin/sh
|
||||
current_dir="$(dirname "$0")"
|
||||
source "$current_dir/modem_debug.sh"
|
||||
source "$current_dir/quectel.sh"
|
||||
source "$current_dir/fibocom.sh"
|
||||
# source "$current_dir/simcom.sh"
|
||||
|
||||
#初值化数据结构
|
||||
init_modem_info()
|
||||
{
|
||||
#基本信息
|
||||
name='unknown' #名称
|
||||
manufacturer='unknown' #制造商
|
||||
revision='-' #固件版本
|
||||
at_port='-' #AT串口
|
||||
mode='unknown' #拨号模式
|
||||
temperature="NaN $(printf "\xc2\xb0")C" #温度
|
||||
update_time='-' #更新时间
|
||||
|
||||
#SIM卡信息
|
||||
sim_status="unknown" #SIM卡状态
|
||||
sim_slot="-" #SIM卡卡槽
|
||||
isp="-" #运营商(互联网服务提供商)
|
||||
sim_number='-' #SIM卡号码(手机号)
|
||||
imei='-' #IMEI
|
||||
imsi='-' #IMSI
|
||||
iccid='-' #ICCID
|
||||
|
||||
#网络信息
|
||||
connect_status="disconnect" #SIM卡状态
|
||||
network_type="-" #蜂窝网络类型
|
||||
rssi="" #RSSI
|
||||
ber="" #BER
|
||||
tx_rate="-" #上传速率
|
||||
rx_rate="-" #下载速率
|
||||
|
||||
#小区信息
|
||||
network_mode="-" #网络模式
|
||||
#NR5G-SA模式
|
||||
nr_mcc=''
|
||||
nr_mnc=''
|
||||
nr_duplex_mode=''
|
||||
nr_cell_id=''
|
||||
nr_physical_cell_id=''
|
||||
nr_tac=''
|
||||
nr_arfcn=''
|
||||
nr_band=''
|
||||
nr_dl_bandwidth=''
|
||||
nr_rsrp=''
|
||||
nr_rsrq=''
|
||||
nr_sinr=''
|
||||
nr_scs=''
|
||||
nr_rxlev=''
|
||||
#EN-DC模式(LTE)
|
||||
endc_lte_mcc=''
|
||||
endc_lte_mnc=''
|
||||
endc_lte_duplex_mode=''
|
||||
endc_lte_cell_id=''
|
||||
endc_lte_physical_cell_id=''
|
||||
endc_lte_earfcn=''
|
||||
endc_lte_freq_band_ind=''
|
||||
endc_lte_ul_bandwidth=''
|
||||
endc_lte_dl_bandwidth=''
|
||||
endc_lte_tac=''
|
||||
endc_lte_rsrp=''
|
||||
endc_lte_rsrq=''
|
||||
endc_lte_rssi=''
|
||||
endc_lte_sinr=''
|
||||
endc_lte_cql=''
|
||||
endc_lte_tx_power=''
|
||||
endc_lte_rxlev=''
|
||||
#EN-DC模式(NR5G-NSA)
|
||||
endc_nr_mcc=''
|
||||
endc_nr_mnc=''
|
||||
endc_nr_physical_cell_id=''
|
||||
endc_nr_arfcn=''
|
||||
endc_nr_band=''
|
||||
endc_nr_dl_bandwidth=''
|
||||
endc_nr_rsrp=''
|
||||
endc_nr_rsrq=''
|
||||
endc_nr_sinr=''
|
||||
endc_nr_scs=''
|
||||
#LTE模式
|
||||
lte_mcc=''
|
||||
lte_mnc=''
|
||||
lte_duplex_mode=''
|
||||
lte_cell_id=''
|
||||
lte_physical_cell_id=''
|
||||
lte_earfcn=''
|
||||
lte_freq_band_ind=''
|
||||
lte_ul_bandwidth=''
|
||||
lte_dl_bandwidth=''
|
||||
lte_tac=''
|
||||
lte_rsrp=''
|
||||
lte_rsrq=''
|
||||
lte_rssi=''
|
||||
lte_sinr=''
|
||||
lte_cql=''
|
||||
lte_tx_power=''
|
||||
lte_rxlev=''
|
||||
#WCDMA模式
|
||||
wcdma_mcc=''
|
||||
wcdma_mnc=''
|
||||
wcdma_lac=''
|
||||
wcdma_cell_id=''
|
||||
wcdma_uarfcn=''
|
||||
wcdma_psc=''
|
||||
wcdma_rac=''
|
||||
wcdma_rscp=''
|
||||
wcdma_ecio=''
|
||||
wcdma_phych=''
|
||||
wcdma_sf=''
|
||||
wcdma_slot=''
|
||||
wcdma_speech_code=''
|
||||
wcdma_com_mod=''
|
||||
}
|
||||
|
||||
#设置基本信息
|
||||
set_base_info()
|
||||
{
|
||||
base_info="\"base_info\":{
|
||||
\"manufacturer\":\"$manufacturer\",
|
||||
\"revision\":\"$revision\",
|
||||
\"at_port\":\"$at_port\",
|
||||
\"mode\":\"$mode\",
|
||||
\"temperature\":\"$temperature\",
|
||||
\"update_time\":\"$update_time\"
|
||||
},"
|
||||
}
|
||||
|
||||
#设置SIM卡信息
|
||||
set_sim_info()
|
||||
{
|
||||
if [ "$sim_status" = "ready" ]; then
|
||||
sim_info="\"sim_info\":[
|
||||
{\"SIM Status\":\"$sim_status\", \"full_name\":\"SIM Status\"},
|
||||
{\"ISP\":\"$isp\", \"full_name\":\"Internet Service Provider\"},
|
||||
{\"SIM Slot\":\"$sim_slot\", \"full_name\":\"SIM Slot\"},
|
||||
{\"SIM Number\":\"$sim_number\", \"full_name\":\"SIM Number\"},
|
||||
{\"IMEI\":\"$imei\", \"full_name\":\"International Mobile Equipment Identity\"},
|
||||
{\"IMSI\":\"$imsi\", \"full_name\":\"International Mobile Subscriber Identity\"},
|
||||
{\"ICCID\":\"$iccid\", \"full_name\":\"Integrate Circuit Card Identity\"}
|
||||
],"
|
||||
elif [ "$sim_status" = "miss" ]; then
|
||||
sim_info="\"sim_info\":[
|
||||
{\"SIM Status\":\"$sim_status\", \"full_name\":\"SIM Status\"},
|
||||
{\"IMEI\":\"$imei\", \"full_name\":\"International Mobile Equipment Identity\"}
|
||||
],"
|
||||
elif [ "$sim_status" = "unknown" ]; then
|
||||
sim_info="\"sim_info\":[
|
||||
{\"SIM Status\":\"$sim_status\", \"full_name\":\"SIM Status\"}
|
||||
],"
|
||||
else
|
||||
sim_info="\"sim_info\":[
|
||||
{\"SIM Status\":\"$sim_status\", \"full_name\":\"SIM Status\"},
|
||||
{\"SIM Slot\":\"$sim_slot\", \"full_name\":\"SIM Slot\"},
|
||||
{\"IMEI\":\"$imei\", \"full_name\":\"International Mobile Equipment Identity\"},
|
||||
{\"IMSI\":\"$imsi\", \"full_name\":\"International Mobile Subscriber Identity\"},
|
||||
{\"ICCID\":\"$iccid\", \"full_name\":\"Integrate Circuit Card Identity\"}
|
||||
],"
|
||||
fi
|
||||
}
|
||||
|
||||
#设置网络信息
|
||||
set_network_info()
|
||||
{
|
||||
network_info="\"network_info\":[
|
||||
{\"Network Type\":\"$network_type\", \"full_name\":\"Network Type\"},
|
||||
{\"Tx Rate\":\"$tx_rate\", \"full_name\":\"Transmit Rate\"},
|
||||
{\"Rx Rate\":\"$rx_rate\", \"full_name\":\"Receive Rate\"},
|
||||
{\"RSSI\":\"$rssi\", \"full_name\":\"Received Signal Strength Indicator\"},
|
||||
{\"BER\":\"$ber\", \"full_name\":\"Bit Error Rate\"}
|
||||
],"
|
||||
}
|
||||
|
||||
#设置信号信息
|
||||
set_cell_info()
|
||||
{
|
||||
if [ "$network_mode" = "NR5G-SA Mode" ]; then
|
||||
cell_info="\"cell_info\":{
|
||||
\"NR5G-SA Mode\":[
|
||||
{\"MCC\":\"$nr_mcc\", \"full_name\":\"Mobile Country Code\"},
|
||||
{\"MNC\":\"$nr_mnc\", \"full_name\":\"Mobile Network Code\"},
|
||||
{\"Duplex Mode\":\"$nr_duplex_mode\", \"full_name\":\"Duplex Mode\"},
|
||||
{\"Cell ID\":\"$nr_cell_id\", \"full_name\":\"Cell ID\"},
|
||||
{\"Physical Cell ID\":\"$nr_physical_cell_id\", \"full_name\":\"Physical Cell ID\"},
|
||||
{\"TAC\":\"$nr_tac\", \"full_name\":\"Tracking area code of cell servedby neighbor Enb\"},
|
||||
{\"ARFCN\":\"$nr_arfcn\", \"full_name\":\"Absolute Radio-Frequency Channel Number\"},
|
||||
{\"Band\":\"$nr_band\", \"full_name\":\"Band\"},
|
||||
{\"DL Bandwidth\":\"$nr_dl_bandwidth\", \"full_name\":\"DL Bandwidth\"},
|
||||
{\"RSRP\":\"$nr_rsrp\", \"full_name\":\"Reference Signal Received Power\"},
|
||||
{\"RSRQ\":\"$nr_rsrq\", \"full_name\":\"Reference Signal Received Quality\"},
|
||||
{\"SINR\":\"$nr_sinr\", \"full_name\":\"Signal to Interference plus Noise Ratio Bandwidth\"},
|
||||
{\"SCS\":\"$nr_scs\", \"full_name\":\"SCS\"},
|
||||
{\"RxLev\":\"$nr_rxlev\", \"full_name\":\"Received Signal Level\"}
|
||||
]
|
||||
}"
|
||||
elif [ "$network_mode" = "EN-DC Mode" ]; then
|
||||
cell_info="\"cell_info\":{
|
||||
\"EN-DC Mode\":[
|
||||
{\"LTE\":[
|
||||
{\"MCC\":\"$endc_lte_mcc\", \"full_name\":\"Mobile Country Code\"},
|
||||
{\"MNC\":\"$endc_lte_mnc\", \"full_name\":\"Mobile Network Code\"},
|
||||
{\"Duplex Mode\":\"$endc_lte_duplex_mode\", \"full_name\":\"Duplex Mode\"},
|
||||
{\"Cell ID\":\"$endc_lte_cell_id\", \"full_name\":\"Cell ID\"},
|
||||
{\"Physical Cell ID\":\"$endc_lte_physical_cell_id\", \"full_name\":\"Physical Cell ID\"},
|
||||
{\"EARFCN\":\"$endc_lte_earfcn\", \"full_name\":\"E-UTRA Absolute Radio Frequency Channel Number\"},
|
||||
{\"Freq band indicator\":\"$endc_lte_freq_band_ind\", \"full_name\":\"Freq band indicator\"},
|
||||
{\"Band\":\"$endc_lte_band\", \"full_name\":\"Band\"},
|
||||
{\"UL Bandwidth\":\"$endc_lte_ul_bandwidth\", \"full_name\":\"UL Bandwidth\"},
|
||||
{\"DL Bandwidth\":\"$endc_lte_dl_bandwidth\", \"full_name\":\"DL Bandwidth\"},
|
||||
{\"TAC\":\"$endc_lte_tac\", \"full_name\":\"Tracking area code of cell servedby neighbor Enb\"},
|
||||
{\"RSRP\":\"$endc_lte_rsrp\", \"full_name\":\"Reference Signal Received Power\"},
|
||||
{\"RSRQ\":\"$endc_lte_rsrq\", \"full_name\":\"Reference Signal Received Quality\"},
|
||||
{\"RSSI\":\"$endc_lte_rssi\", \"full_name\":\"Received Signal Strength Indicator\"},
|
||||
{\"SINR\":\"$endc_lte_sinr\", \"full_name\":\"Signal to Interference plus Noise Ratio Bandwidth\"},
|
||||
{\"RSSNR\":\"$endc_lte_rssnr\", \"full_name\":\"Radio Signal Strength Noise Ratio\"},
|
||||
{\"CQI\":\"$endc_lte_cql\", \"full_name\":\"Channel Quality Indicator\"},
|
||||
{\"TX Power\":\"$endc_lte_tx_power\", \"full_name\":\"TX Power\"},
|
||||
{\"RxLev\":\"$endc_lte_rxlev\", \"full_name\":\"Received Signal Level\"}
|
||||
]
|
||||
},
|
||||
|
||||
{\"NR5G-NSA\":[
|
||||
{\"MCC\":\"$endc_nr_mcc\", \"full_name\":\"Mobile Country Code\"},
|
||||
{\"MNC\":\"$endc_nr_mnc\", \"full_name\":\"Mobile Network Code\"},
|
||||
{\"Physical Cell ID\":\"$endc_nr_physical_cell_id\", \"full_name\":\"Physical Cell ID\"},
|
||||
{\"ARFCN\":\"$endc_nr_arfcn\", \"full_name\":\"Absolute Radio-Frequency Channel Number\"},
|
||||
{\"Band\":\"$endc_nr_band\", \"full_name\":\"Band\"},
|
||||
{\"DL Bandwidth\":\"$endc_nr_dl_bandwidth\", \"full_name\":\"DL Bandwidth\"},
|
||||
{\"RSRP\":\"$endc_nr_rsrp\", \"full_name\":\"Reference Signal Received Power\"},
|
||||
{\"RSRQ\":\"$endc_nr_rsrq\", \"full_name\":\"Reference Signal Received Quality\"},
|
||||
{\"SINR\":\"$endc_nr_sinr\", \"full_name\":\"Signal to Interference plus Noise Ratio Bandwidth\"},
|
||||
{\"SCS\":\"$endc_nr_scs\", \"full_name\":\"SCS\"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}"
|
||||
elif [ "$network_mode" = "LTE Mode" ]; then
|
||||
cell_info="\"cell_info\":{
|
||||
\"LTE Mode\":[
|
||||
{\"MCC\":\"$lte_mcc\", \"full_name\":\"Mobile Country Code\"},
|
||||
{\"MNC\":\"$lte_mnc\", \"full_name\":\"Mobile Network Code\"},
|
||||
{\"Duplex Mode\":\"$lte_duplex_mode\", \"full_name\":\"Duplex Mode\"},
|
||||
{\"Cell ID\":\"$lte_cell_id\", \"full_name\":\"Cell ID\"},
|
||||
{\"Physical Cell ID\":\"$lte_physical_cell_id\", \"full_name\":\"Physical Cell ID\"},
|
||||
{\"EARFCN\":\"$lte_earfcn\", \"full_name\":\"E-UTRA Absolute Radio Frequency Channel Number\"},
|
||||
{\"Freq band indicator\":\"$lte_freq_band_ind\", \"full_name\":\"Freq band indicator\"},
|
||||
{\"Band\":\"$lte_band\", \"full_name\":\"Band\"},
|
||||
{\"UL Bandwidth\":\"$lte_ul_bandwidth\", \"full_name\":\"UL Bandwidth\"},
|
||||
{\"DL Bandwidth\":\"$lte_dl_bandwidth\", \"full_name\":\"DL Bandwidth\"},
|
||||
{\"TAC\":\"$lte_tac\", \"full_name\":\"Tracking area code of cell servedby neighbor Enb\"},
|
||||
{\"RSRP\":\"$lte_rsrp\", \"full_name\":\"Reference Signal Received Power\"},
|
||||
{\"RSRQ\":\"$lte_rsrq\", \"full_name\":\"Reference Signal Received Quality\"},
|
||||
{\"RSSI\":\"$lte_rssi\", \"full_name\":\"Received Signal Strength Indicator\"},
|
||||
{\"SINR\":\"$lte_sinr\", \"full_name\":\"Signal to Interference plus Noise Ratio Bandwidth\"},
|
||||
{\"RSSNR\":\"$lte_rssnr\", \"full_name\":\"Radio Signal Strength Noise Ratio\"},
|
||||
{\"CQI\":\"$lte_cql\", \"full_name\":\"Channel Quality Indicator\"},
|
||||
{\"TX Power\":\"$lte_tx_power\", \"full_name\":\"TX Power\"},
|
||||
{\"RxLev\":\"$lte_rxlev\", \"full_name\":\"RxLev\"}
|
||||
]
|
||||
}"
|
||||
elif [ "$network_mode" = "WCDMA Mode" ]; then
|
||||
cell_info="\"cell_info\":{
|
||||
\"WCDMA Mode\":[
|
||||
{\"MCC\":\"$wcdma_mcc\", \"full_name\":\"Mobile Country Code\"},
|
||||
{\"MNC\":\"$wcdma_mnc\", \"full_name\":\"Mobile Network Code\"},
|
||||
{\"LAC\":\"$wcdma_lac\", \"full_name\":\"Location Area Code\"},
|
||||
{\"Cell ID\":\"$wcdma_cell_id\", \"full_name\":\"Cell ID\"},
|
||||
{\"UARFCN\":\"$wcdma_uarfcn\", \"full_name\":\"UTRA Absolute Radio Frequency Channel Number\"},
|
||||
{\"PSC\":\"$wcdma_psc\", \"full_name\":\"Primary Scrambling Code\"},
|
||||
{\"RAC\":\"$wcdma_rac\", \"full_name\":\"Routing Area Code\"},
|
||||
{\"Band\":\"$wcdma_band\", \"full_name\":\"Band\"},
|
||||
{\"RSCP\":\"$wcdma_rscp\", \"full_name\":\"Received Signal Code Power\"},
|
||||
{\"Ec/Io\":\"$wcdma_ecio\", \"full_name\":\"Ec/Io\"},
|
||||
{\"Ec/No\":\"$wcdma_ecno\", \"full_name\":\"Ec/No\"},
|
||||
{\"Physical Channel\":\"$wcdma_phych\", \"full_name\":\"Physical Channel\"},
|
||||
{\"Spreading Factor\":\"$wcdma_sf\", \"full_name\":\"Spreading Factor\"},
|
||||
{\"Slot\":\"$wcdma_slot\", \"full_name\":\"Slot\"},
|
||||
{\"Speech Code\":\"$wcdma_speech_code\", \"full_name\":\"Speech Code\"},
|
||||
{\"Compression Mode\":\"$wcdma_com_mod\", \"full_name\":\"Compression Mode\"},
|
||||
{\"RxLev\":\"$wcdma_rxlev\", \"full_name\":\"RxLev\"}
|
||||
]
|
||||
}"
|
||||
fi
|
||||
}
|
||||
|
||||
#以Json格式保存模组信息
|
||||
info_to_json()
|
||||
{
|
||||
base_info="\"base_info\":{},"
|
||||
sim_info="\"sim_info\":{},"
|
||||
network_info="\"network_info\":{},"
|
||||
cell_info="\"cell_info\":{}"
|
||||
|
||||
#设置基本信息
|
||||
set_base_info
|
||||
|
||||
#判断是否适配
|
||||
if [ "$manufacturer" != "unknown" ]; then
|
||||
#设置SIM卡信息
|
||||
set_sim_info
|
||||
fi
|
||||
|
||||
#判断插卡和连接状态
|
||||
if [ "$sim_status" = "ready" ] && [ "$connect_status" = "connect" ]; then
|
||||
#设置网络信息
|
||||
set_network_info
|
||||
#设置小区信息
|
||||
set_cell_info
|
||||
fi
|
||||
|
||||
#拼接所有信息(不要漏掉最后一个})
|
||||
modem_info="{$base_info$modem_info$sim_info$network_info$cell_info}"
|
||||
}
|
||||
# echo $ECIO #参考信号接收质量 RSRQ ecio
|
||||
# echo $ECIO1 #参考信号接收质量 RSRQ ecio1
|
||||
# echo $RSCP #参考信号接收功率 RSRP rscp0
|
||||
# echo $RSCP1 #参考信号接收功率 RSRP rscp1
|
||||
# echo $SINR #信噪比 SINR rv["sinr"]
|
||||
|
||||
# #基站信息
|
||||
# echo $COPS_MCC #MCC
|
||||
# echo $$COPS_MNC #MNC
|
||||
# echo $LAC #eNB ID
|
||||
# echo '' #LAC_NUM
|
||||
# echo $RNC #TAC
|
||||
# echo '' #RNC_NUM
|
||||
# echo $CID
|
||||
# echo '' #CID_NUM
|
||||
# echo $LBAND
|
||||
# echo $channel
|
||||
# echo $PCI
|
||||
# echo $MODTYPE
|
||||
# echo $QTEMP
|
||||
|
||||
#获取模组信息
|
||||
get_modem_info()
|
||||
{
|
||||
update_time=$(date +"%Y-%m-%d %H:%M:%S")
|
||||
|
||||
debug "检查模组的AT串口"
|
||||
#获取模块AT串口
|
||||
if [ -z "$at_port" ]; then
|
||||
debug "模组没有AT串口"
|
||||
return
|
||||
fi
|
||||
|
||||
#检查模块状态(是否处于重启,重置,串口异常状态)
|
||||
local at_command="ATI"
|
||||
local response=$(sh $current_dir/modem_at.sh $at_port $at_command)
|
||||
if [[ "$response" = *"failed"* ]] || [[ "$response" = *"$at_port"* ]]; then
|
||||
debug "模组AT串口未就绪"
|
||||
return
|
||||
fi
|
||||
|
||||
debug "根据模组的制造商获取信息"
|
||||
#更多信息获取
|
||||
case $manufacturer in
|
||||
"quectel") get_quectel_info $at_port ;;
|
||||
"fibocom") get_fibocom_info $at_port ;;
|
||||
"simcom") get_simcom_info $at_port ;;
|
||||
*) debug "未适配该模组" ;;
|
||||
esac
|
||||
|
||||
#获取更新时间
|
||||
update_time=$(date +"%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
|
||||
#获取模组数据信息
|
||||
# $1:AT串口
|
||||
# $2:制造商
|
||||
modem_info()
|
||||
{
|
||||
#初值化模组信息
|
||||
debug "初值化模组信息"
|
||||
init_modem_info
|
||||
debug "初值化模组信息完成"
|
||||
|
||||
#获取模组信息
|
||||
at_port=$1
|
||||
manufacturer=$2
|
||||
debug "获取模组信息"
|
||||
get_modem_info
|
||||
|
||||
#整合模块信息
|
||||
info_to_json
|
||||
echo $modem_info
|
||||
|
||||
#移动网络联网检查
|
||||
# checkMobileNetwork
|
||||
}
|
||||
|
||||
modem_info $1 $2
|
||||
247
applications/luci-app-modem/root/usr/share/modem/modem_scan.sh
Normal file
247
applications/luci-app-modem/root/usr/share/modem/modem_scan.sh
Normal file
@@ -0,0 +1,247 @@
|
||||
#!/bin/sh
|
||||
current_dir="$(dirname "$0")"
|
||||
source "$current_dir/modem_debug.sh"
|
||||
|
||||
#获取USB串口总线地址
|
||||
# $1:USB串口
|
||||
getUSBDeviceBusPath()
|
||||
{
|
||||
local device_name="$(basename "$1")"
|
||||
local device_path="$(find /sys/class/ -name $device_name)"
|
||||
local device_physical_path="$(readlink -f $device_path/device/)"
|
||||
|
||||
#获取父路径的上两层
|
||||
local tmp=$(dirname "$device_physical_path")
|
||||
local device_bus_path=$(dirname $tmp)
|
||||
|
||||
echo $device_bus_path
|
||||
}
|
||||
|
||||
#获取设备总线地址
|
||||
# $1:网络设备或PCIE串口
|
||||
getDeviceBusPath()
|
||||
{
|
||||
local device_name="$(basename "$1")"
|
||||
local device_path="$(find /sys/class/ -name $device_name)"
|
||||
local device_physical_path="$(readlink -f $device_path/device/)"
|
||||
|
||||
local device_bus_path=$device_physical_path
|
||||
if [ "$device_name" != "mhi_BHI" ]; then #未考虑多个mhi_BHI的情况
|
||||
device_bus_path=$(dirname "$device_physical_path")
|
||||
fi
|
||||
|
||||
echo $device_bus_path
|
||||
}
|
||||
|
||||
#设置模块配置
|
||||
# $1:模块序号
|
||||
# $2:设备数据接口
|
||||
# $3:总线地址
|
||||
setModemConfig()
|
||||
{
|
||||
#判断地址是否为net
|
||||
local path=$(basename "$3")
|
||||
if [ "$path" = "net" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
#处理获取到的地址
|
||||
# local substr="${3/\/sys\/devices\//}" #x86平台,替换掉/sys/devices/
|
||||
# local substr="${3/\/sys\/devices\/platform\//}" #arm平台,替换掉/sys/devices/platform/
|
||||
# local substr="${3/\/sys\/devices\/platform\/soc\//}" #arm平台,替换掉/sys/devices/platform/soc/
|
||||
local substr=$3 #路径存在不同,暂不处理
|
||||
|
||||
#获取网络接口
|
||||
local net_path="$(find $substr -name net | sed -n '1p')"
|
||||
local net_net_interface_path=$net_path
|
||||
|
||||
#子目录下存在网络接口
|
||||
local net_count="$(find $substr -name net | wc -l)"
|
||||
if [ "$net_count" = "2" ]; then
|
||||
net_net_interface_path="$(find $substr -name net | sed -n '2p')"
|
||||
fi
|
||||
local network=$(ls $net_path)
|
||||
local network_interface=$(ls $net_net_interface_path)
|
||||
|
||||
#设置配置
|
||||
uci set modem.modem$1="modem-device"
|
||||
uci set modem.modem$1.data_interface="$2"
|
||||
uci set modem.modem$1.path="$substr"
|
||||
uci set modem.modem$1.network="$network"
|
||||
uci set modem.modem$1.network_interface="$network_interface"
|
||||
|
||||
#增加模组计数
|
||||
modem_count=$((modem_count + 1))
|
||||
}
|
||||
|
||||
#设置模块串口配置
|
||||
# $modem_count:模块计数
|
||||
# $1:总线地址
|
||||
# $2:串口
|
||||
setPortConfig()
|
||||
{
|
||||
#处理获取到的地址
|
||||
# local substr="${1/\/sys\/devices\//}" #x86平台,替换掉/sys/devices/
|
||||
# local substr="${1/\/sys\/devices\/platform\//}" #arm平台,替换掉/sys/devices/platform/
|
||||
# local substr="${1/\/sys\/devices\/platform\/soc\//}" #arm平台,替换掉/sys/devices/platform/soc/
|
||||
local substr=$1 #路径存在不同,暂不处理
|
||||
|
||||
for i in $(seq 0 $((modem_count-1))); do
|
||||
#当前模块的物理地址
|
||||
local path=$(uci -q get modem.modem$i.path)
|
||||
if [ "$substr" = "$path" ]; then
|
||||
#添加新的串口
|
||||
uci add_list modem.modem$i.ports="$2"
|
||||
#写入到配置中(解决老版本luci问题)
|
||||
uci commit modem
|
||||
#判断是不是AT串口
|
||||
local response=$(sh $current_dir/modem_at.sh $2 "ATI")
|
||||
local str1="No" #No response from modem.
|
||||
local str2="failed"
|
||||
if [[ "$response" != *"$str1"* ]] && [[ "$response" != *"$str2"* ]]; then
|
||||
#原先的AT串口会被覆盖掉(是否需要加判断)
|
||||
uci set modem.modem$i.at_port="$2"
|
||||
setModemInfoConfig $i $2
|
||||
fi
|
||||
break
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
#设置模组信息(名称、制造商、拨号模式)
|
||||
# $modem_count:模组计数
|
||||
# $1:模组序号
|
||||
# $2:AT串口
|
||||
setModemInfoConfig()
|
||||
{
|
||||
#获取数据接口
|
||||
local data_interface=$(uci -q get modem.modem$1.data_interface)
|
||||
|
||||
#获取支持的模组
|
||||
local modem_support=$(cat $current_dir/modem_support.json)
|
||||
|
||||
#获取模组名
|
||||
local at_response=$(sh $current_dir/modem_at.sh $2 "AT+CGMM" | sed -n '2p' | sed 's/\r//g' | tr 'A-Z' 'a-z')
|
||||
|
||||
#获取模组信息
|
||||
local modem_info=$(echo $modem_support | jq '.modem_support.'$data_interface'."'$at_response'"')
|
||||
|
||||
local modem_name
|
||||
local manufacturer
|
||||
local platform
|
||||
local mode
|
||||
local modes
|
||||
if [ "$modem_info" = "null" ]; then
|
||||
modem_name="unknown"
|
||||
manufacturer="unknown"
|
||||
platform="unknown"
|
||||
mode="unknown"
|
||||
modes="qmi gobinet ecm mbim rndis ncm"
|
||||
else
|
||||
#获取模组名
|
||||
modem_name="$at_response"
|
||||
#获取制造商
|
||||
manufacturer=$(echo $modem_info | jq -r '.manufacturer')
|
||||
#获取平台
|
||||
platform=$(echo $modem_info | jq -r '.platform')
|
||||
#获取当前的拨号模式
|
||||
mode=$(source $current_dir/$manufacturer.sh && "$manufacturer"_get_mode $2 $platform)
|
||||
#获取支持的拨号模式
|
||||
modes=$(echo $modem_info | jq -r '.modes[]')
|
||||
fi
|
||||
|
||||
#设置模组名
|
||||
uci set modem.modem$1.name="$modem_name"
|
||||
#设置制造商
|
||||
uci set modem.modem$1.manufacturer="$manufacturer"
|
||||
#设置平台
|
||||
uci set modem.modem$1.platform="$platform"
|
||||
#设置当前的拨号模式
|
||||
uci set modem.modem$1.mode="$mode"
|
||||
#设置支持的拨号模式
|
||||
uci -q del modem.modem$1.modes #删除原来的拨号模式列表
|
||||
for mode in $modes; do
|
||||
uci add_list modem.modem$1.modes="$mode"
|
||||
done
|
||||
}
|
||||
|
||||
#设置模块数量
|
||||
setModemCount()
|
||||
{
|
||||
uci set modem.global.modem_number="$modem_count"
|
||||
|
||||
#数量为0时,清空模块列表
|
||||
if [ "$modem_count" = "0" ]; then
|
||||
for i in $(seq 0 $((modem_count-1))); do
|
||||
uci -q del modem.modem$i
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
#模块计数
|
||||
modem_count=0
|
||||
#模块支持文件
|
||||
modem_support_file="$current_dir/modem_support"
|
||||
#设置模块信息
|
||||
modem_scan()
|
||||
{
|
||||
#初始化
|
||||
modem_count=0
|
||||
########设置模块基本信息########
|
||||
#USB
|
||||
local usb_network
|
||||
usb_network=$(find /sys/class/net -name usb*)
|
||||
for network in $usb_network; do
|
||||
local usb_device_bus_path=$(getDeviceBusPath $network)
|
||||
setModemConfig $modem_count "usb" $usb_device_bus_path
|
||||
done
|
||||
usb_network=$(find /sys/class/net -name wwan*)
|
||||
for network in $usb_network; do
|
||||
local usb_device_bus_path=$(getDeviceBusPath $network)
|
||||
setModemConfig $modem_count "usb" $usb_device_bus_path
|
||||
done
|
||||
#PCIE
|
||||
local pcie_network
|
||||
pcie_network=$(find /sys/class/net -name mhi_hwip*) #(通用mhi驱动)
|
||||
for network in $pcie_network; do
|
||||
local pcie_device_bus_path=$(getDeviceBusPath $network)
|
||||
setModemConfig $modem_count "pcie" $pcie_device_bus_path
|
||||
done
|
||||
pcie_network=$(find /sys/class/net -name rmnet_mhi*) #(制造商mhi驱动)
|
||||
for network in $pcie_network; do
|
||||
local pcie_device_bus_path=$(getDeviceBusPath $network)
|
||||
setModemConfig $modem_count "pcie" $pcie_device_bus_path
|
||||
done
|
||||
|
||||
########设置模块串口########
|
||||
#清除原串口配置
|
||||
for i in $(seq 0 $((modem_count-1))); do
|
||||
uci -q del modem.modem$i.ports
|
||||
done
|
||||
#USB串口
|
||||
local usb_port=$(find /dev -name ttyUSB*)
|
||||
for port in $usb_port; do
|
||||
local usb_port_device_bus_path=$(getUSBDeviceBusPath $port)
|
||||
setPortConfig $usb_port_device_bus_path $port
|
||||
done
|
||||
#PCIE串口
|
||||
local pcie_port
|
||||
pcie_port=$(find /dev -name wwan*)
|
||||
for port in $pcie_port; do
|
||||
local pcie_port_device_bus_path=$(getDeviceBusPath $port)
|
||||
setPortConfig $pcie_port_device_bus_path $port
|
||||
done
|
||||
pcie_port=$(find /dev -name mhi*)
|
||||
for port in $pcie_port; do
|
||||
local pcie_port_device_bus_path=$(getDeviceBusPath $port)
|
||||
setPortConfig $pcie_port_device_bus_path $port
|
||||
done
|
||||
########设置模块数量########
|
||||
setModemCount
|
||||
|
||||
#写入到配置中
|
||||
uci commit modem
|
||||
}
|
||||
|
||||
#测试时打开
|
||||
# modem_scan
|
||||
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"modem_support":{
|
||||
"usb":{
|
||||
"rg200u-cn":{
|
||||
"manufacturer":"quectel",
|
||||
"platform":"unisoc",
|
||||
"network_interface":"usb",
|
||||
"modes":["ecm","mbim","rndis","ncm"]
|
||||
},
|
||||
"rm500u-cn":{
|
||||
"manufacturer":"quectel",
|
||||
"platform":"unisoc",
|
||||
"network_interface":"usb",
|
||||
"modes":["ecm","mbim","rndis","ncm"]
|
||||
},
|
||||
"rm500q-gl":{
|
||||
"manufacturer":"quectel",
|
||||
"platform":"qualcomm",
|
||||
"network_interface":"usb",
|
||||
"modes":["qmi","gobinet","ecm","mbim","rndis","ncm"]
|
||||
},
|
||||
"rm502q-gl":{
|
||||
"manufacturer":"quectel",
|
||||
"platform":"qualcomm",
|
||||
"network_interface":"usb",
|
||||
"modes":["qmi","gobinet","ecm","mbim","rndis","ncm"]
|
||||
},
|
||||
"rm502q-ae":{
|
||||
"manufacturer":"quectel",
|
||||
"platform":"qualcomm",
|
||||
"network_interface":"usb",
|
||||
"modes":["qmi","gobinet","ecm","mbim","rndis","ncm"]
|
||||
},
|
||||
"rm520n-cn":{
|
||||
"manufacturer":"quectel",
|
||||
"platform":"qualcomm",
|
||||
"network_interface":"usb",
|
||||
"modes":["qmi","gobinet","ecm","mbim","rndis","ncm"]
|
||||
},
|
||||
"rm520n-gl":{
|
||||
"manufacturer":"quectel",
|
||||
"platform":"qualcomm",
|
||||
"network_interface":"usb",
|
||||
"modes":["qmi","gobinet","ecm","mbim","rndis","ncm"]
|
||||
},
|
||||
"fm650-cn":{
|
||||
"manufacturer":"fibocom",
|
||||
"platform":"unisoc",
|
||||
"network_interface":"usb",
|
||||
"modes":["ecm","mbim","rndis","ncm"]
|
||||
},
|
||||
"fm150-ae":{
|
||||
"manufacturer":"fibocom",
|
||||
"platform":"qualcomm",
|
||||
"network_interface":"usb",
|
||||
"modes":["qmi","gobinet","ecm","mbim","rndis","ncm"]
|
||||
},
|
||||
"fm160-cn":{
|
||||
"manufacturer":"fibocom",
|
||||
"platform":"qualcomm",
|
||||
"network_interface":"usb",
|
||||
"modes":["qmi","gobinet","ecm","mbim","rndis","ncm"]
|
||||
}
|
||||
},
|
||||
"pcie":{
|
||||
"rm500q-gl":{
|
||||
"manufacturer":"quectel",
|
||||
"platform":"qualcomm",
|
||||
"network_interface":"pcie",
|
||||
"modes":["qmi","gobinet","mbim"]
|
||||
},
|
||||
"rm502q-gl":{
|
||||
"manufacturer":"fibocom",
|
||||
"platform":"qualcomm",
|
||||
"network_interface":"pcie",
|
||||
"modes":["qmi","gobinet","mbim"]
|
||||
},
|
||||
"fm150-ae":{
|
||||
"manufacturer":"fibocom",
|
||||
"platform":"qualcomm",
|
||||
"network_interface":"pcie",
|
||||
"modes":["qmi"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
current_dir="$(dirname "$0")"
|
||||
source "$current_dir/modem_debug.sh"
|
||||
source "$current_dir/modem_scan.sh"
|
||||
|
||||
#模组扫描任务
|
||||
modem_scan_task()
|
||||
{
|
||||
sleep 8s #刚开机需要等待移动网络出来
|
||||
while true; do
|
||||
enable=$(uci -q get modem.@global[0].enable)
|
||||
if [ "$enable" = "1" ] ;then
|
||||
#扫描模块
|
||||
debug "开启模块扫描任务"
|
||||
modem_scan
|
||||
debug "结束模块扫描任务"
|
||||
fi
|
||||
sleep 10s
|
||||
done
|
||||
}
|
||||
|
||||
modem_scan_task
|
||||
@@ -0,0 +1,76 @@
|
||||
#!/bin/sh
|
||||
current_dir="$(dirname "$0")"
|
||||
source "$current_dir/modem_debug.sh"
|
||||
source "$current_dir/modem_scan.sh"
|
||||
|
||||
#拨号
|
||||
# $1:AT串口
|
||||
# $2:制造商
|
||||
ecm_dial()
|
||||
{
|
||||
#拨号
|
||||
local manufacturer=$2
|
||||
local at_command
|
||||
if [ "$manufacturer" = "quectel" ]; then
|
||||
at_command='ATI'
|
||||
elif [ "$manufacturer" = "fibocom" ]; then
|
||||
at_command='AT+GTRNDIS=1,1'
|
||||
else
|
||||
at_command='ATI'
|
||||
fi
|
||||
sh "$current_dir/modem_at.sh" $1 $at_command
|
||||
}
|
||||
|
||||
#拨号
|
||||
# $1:AT串口
|
||||
# $2:制造商
|
||||
gobinet_dial()
|
||||
{
|
||||
#拨号
|
||||
local manufacturer=$2
|
||||
local at_command
|
||||
if [ "$manufacturer" = "quectel" ]; then
|
||||
at_command='ATI'
|
||||
elif [ "$manufacturer" = "fibocom" ]; then
|
||||
at_command='AT$QCRMCALL=1,1'
|
||||
else
|
||||
at_command='ATI'
|
||||
fi
|
||||
sh "$current_dir/modem_at.sh" $1 $at_command
|
||||
}
|
||||
|
||||
#检查模组网络连接
|
||||
# $1:配置ID
|
||||
# $2:AT串口
|
||||
# $3:制造商
|
||||
# $4:拨号模式
|
||||
modem_network_task()
|
||||
{
|
||||
while true; do
|
||||
local enable=$(uci -q get modem.@global[0].enable)
|
||||
if [ "$enable" != "1" ] ;then
|
||||
break
|
||||
fi
|
||||
enable=$(uci -q get modem.$1.enable)
|
||||
if [ "$enable" != "1" ] ;then
|
||||
break
|
||||
fi
|
||||
|
||||
#网络连接检查
|
||||
debug "开启网络连接检查任务"
|
||||
local at_port=$2
|
||||
local at_command="AT+COPS?"
|
||||
local connect_status=$(sh $current_dir/modem_at.sh $at_port $at_command | sed -n '2p')
|
||||
if [ "$connect_status" = "0" ]; then
|
||||
case "$4" in
|
||||
"ecm") ecm_dial $at_port $3 ;;
|
||||
"gobinet") gobinet_dial $at_port $3 ;;
|
||||
*) ecm_dial $at_port $3 ;;
|
||||
esac
|
||||
fi
|
||||
debug "结束网络连接检查任务"
|
||||
sleep 10s
|
||||
done
|
||||
}
|
||||
|
||||
modem_network_task $1 $2 $3 $4
|
||||
1038
applications/luci-app-modem/root/usr/share/modem/quectel.sh
Normal file
1038
applications/luci-app-modem/root/usr/share/modem/quectel.sh
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"quick_commands":[
|
||||
{"模组信息 > ATI":"ATI"},
|
||||
{"查询SIM卡状态 > AT+CPIN?":"AT+CPIN?"},
|
||||
{"查询此时信号强度 > AT+CSQ":"AT+CSQ"},
|
||||
{"查询网络信息 > AT+COPS?":"AT+COPS?"},
|
||||
{"查询PDP信息 > AT+CGDCONT?":"AT+CGDCONT?"},
|
||||
{"最小功能模式 > AT+CFUN=0":"AT+CFUN=0"},
|
||||
{"全功能模式 > AT+CFUN=1":"AT+CFUN=1"},
|
||||
{"SIM卡状态上报 > AT+QSIMSTAT?":"AT+QSIMSTAT?"},
|
||||
{"设置当前使用的为卡1 > AT+QUIMSLOT=1":"AT+QUIMSLOT=1"},
|
||||
{"设置当前使用的为卡2 > AT+QUIMSLOT=2":"AT+QUIMSLOT=2"},
|
||||
{"查询网络信息 > AT+QNWINFO":"AT+QNWINFO"},
|
||||
{"查询SIM卡签约速率 > AT+QNWCFG=\"nr5g_ambr\"":"AT+QNWCFG=\"nr5g_ambr\""},
|
||||
{"查询载波聚合参数 > AT+QCAINFO":"AT+QCAINFO"},
|
||||
{"查询当前拨号模式 > AT+QCFG=\"usbnet\"":"AT+QCFG=\"usbnet\""},
|
||||
{"QMI/GobiNet拨号 > AT+QCFG=\"usbnet\",0":"AT+QCFG=\"usbnet\",0"},
|
||||
{"ECM拨号 > AT+QCFG=\"usbnet\",1":"AT+QCFG=\"usbnet\",1"},
|
||||
{"MBIM拨号 > AT+QCFG=\"usbnet\",2":"AT+QCFG=\"usbnet\",2"},
|
||||
{"RNDIS拨号 > AT+QCFG=\"usbnet\",3":"AT+QCFG=\"usbnet\",3"},
|
||||
{"NCM拨号 > AT+QCFG=\"usbnet\",5":"AT+QCFG=\"usbnet\",5"},
|
||||
{"锁4G > AT+QNWPREFCFG=\"mode_pref\",LTE":"AT+QNWPREFCFG=\"mode_pref\",LTE"},
|
||||
{"锁5G > AT+QNWPREFCFG=\"mode_pref\",NR5G":"AT+QNWPREFCFG=\"mode_pref\",NR5G"},
|
||||
{"锁5G NSA > AT+QNWPREFCFG=\"mode_pref\",NR5G-NSA":"AT+QNWPREFCFG=\"mode_pref\",NR5G-NSA"},
|
||||
{"锁5G SA > AT+QNWPREFCFG=\"mode_pref\",NR5G-SA":"AT+QNWPREFCFG=\"mode_pref\",NR5G-SA"},
|
||||
{"恢复自动搜索网络 > AT+QNWPREFCFG=\"mode_pref\",AUTO":"AT+QNWPREFCFG=\"mode_pref\",AUTO"},
|
||||
{"查询模组IMEI > AT+CGSN":"AT+CGSN"},
|
||||
{"查询模组IMEI > AT+GSN":"AT+GSN"},
|
||||
{"更改模组IMEI > AT+EGMR=1,7,\"IMEI\"":"AT+EGMR=1,7,\"在此设置IMEI\""},
|
||||
{"获取模组温度 > AT+QTEMP":"AT+QTEMP"},
|
||||
{"切换为USB通信端口 > AT+QCFG=\"data_interface\",0,0":"AT+QCFG=\"data_interface\",0,0"},
|
||||
{"切换为PCIE通信端口 > AT+QCFG=\"data_interface\",1,0":"AT+QCFG=\"data_interface\",1,0"},
|
||||
{"查看当前USB速率 > AT+QCFG=\"usbspeed\"":"AT+QCFG=\"usbspeed\""},
|
||||
{"切换为USB2.0 > AT+QCFG=\"usbspeed\",\"20\"":"AT+QCFG=\"usbspeed\",\"20\""},
|
||||
{"切换为USB3.1 Gen1(5Gbps) > AT+QCFG=\"usbspeed\",\"311\"":"AT+QCFG=\"usbspeed\",\"311\""},
|
||||
{"切换为USB3.1 Gen1(10Gbps) > AT+QCFG=\"usbspeed\",\"312\"":"AT+QCFG=\"usbspeed\",\"312\""},
|
||||
{"重启模组 > AT+CFUN=1,1":"AT+CFUN=1,1"},
|
||||
{"重置模组 > AT+QCFG=\"ResetFactory\"":"AT+QCFG=\"ResetFactory\""}
|
||||
]
|
||||
}
|
||||
299
applications/luci-app-modem/root/usr/share/modem/simcom.sh
Normal file
299
applications/luci-app-modem/root/usr/share/modem/simcom.sh
Normal file
@@ -0,0 +1,299 @@
|
||||
#!/bin/sh
|
||||
|
||||
#查询信息强度
|
||||
All_CSQ()
|
||||
{
|
||||
debug "All_CSQ"
|
||||
#信号
|
||||
OX=$( sh modem_at.sh $at_port "AT+CSQ" |grep "+CSQ:")
|
||||
OX=$(echo $OX | tr 'a-z' 'A-Z')
|
||||
CSQ=$(echo "$OX" | grep -o "+CSQ: [0-9]\{1,2\}" | grep -o "[0-9]\{1,2\}")
|
||||
if [ $CSQ = "99" ]; then
|
||||
CSQ=""
|
||||
fi
|
||||
if [ -n "$CSQ" ]; then
|
||||
CSQ_PER=$(($CSQ * 100/31))"%"
|
||||
CSQ_RSSI=$((2 * CSQ - 113))" dBm"
|
||||
else
|
||||
CSQ="-"
|
||||
CSQ_PER="-"
|
||||
CSQ_RSSI="-"
|
||||
fi
|
||||
}
|
||||
|
||||
# SIMCOM获取基站信息
|
||||
SIMCOM_Cellinfo()
|
||||
{
|
||||
#baseinfo.gcom
|
||||
OX=$( sh modem_at.sh 2 "ATI")
|
||||
OX=$( sh modem_at.sh 2 "AT+CGEQNEG=1")
|
||||
|
||||
#cellinfo0.gcom
|
||||
OX1=$( sh modem_at.sh 2 "AT+COPS=3,0;+COPS?")
|
||||
OX2=$( sh modem_at.sh 2 "AT+COPS=3,2;+COPS?")
|
||||
OX=$OX1" "$OX2
|
||||
|
||||
#cellinfo.gcom
|
||||
OY1=$( sh modem_at.sh 2 "AT+CREG=2;+CREG?;+CREG=0")
|
||||
OY2=$( sh modem_at.sh 2 "AT+CEREG=2;+CEREG?;+CEREG=0")
|
||||
OY3=$( sh modem_at.sh 2 "AT+C5GREG=2;+C5GREG?;+C5GREG=0")
|
||||
OY=$OY1" "$OY2" "$OY3
|
||||
|
||||
|
||||
OXx=$OX
|
||||
OX=$(echo $OX | tr 'a-z' 'A-Z')
|
||||
OY=$(echo $OY | tr 'a-z' 'A-Z')
|
||||
OX=$OX" "$OY
|
||||
|
||||
#debug "$OX"
|
||||
#debug "$OY"
|
||||
|
||||
COPS="-"
|
||||
COPS_MCC="-"
|
||||
COPS_MNC="-"
|
||||
COPSX=$(echo $OXx | grep -o "+COPS: [01],0,.\+," | cut -d, -f3 | grep -o "[^\"]\+")
|
||||
|
||||
if [ "x$COPSX" != "x" ]; then
|
||||
COPS=$COPSX
|
||||
fi
|
||||
|
||||
COPSX=$(echo $OX | grep -o "+COPS: [01],2,.\+," | cut -d, -f3 | grep -o "[^\"]\+")
|
||||
|
||||
if [ "x$COPSX" != "x" ]; then
|
||||
COPS_MCC=${COPSX:0:3}
|
||||
COPS_MNC=${COPSX:3:3}
|
||||
if [ "$COPS" = "-" ]; then
|
||||
COPS=$(awk -F[\;] '/'$COPS'/ {print $2}' $ROOTER/signal/mccmnc.data)
|
||||
[ "x$COPS" = "x" ] && COPS="-"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$COPS" = "-" ]; then
|
||||
COPS=$(echo "$O" | awk -F[\"] '/^\+COPS: 0,0/ {print $2}')
|
||||
if [ "x$COPS" = "x" ]; then
|
||||
COPS="-"
|
||||
COPS_MCC="-"
|
||||
COPS_MNC="-"
|
||||
fi
|
||||
fi
|
||||
COPS_MNC=" "$COPS_MNC
|
||||
|
||||
OX=$(echo "${OX//[ \"]/}")
|
||||
CID=""
|
||||
CID5=""
|
||||
RAT=""
|
||||
REGV=$(echo "$OX" | grep -o "+C5GREG:2,[0-9],[A-F0-9]\{2,6\},[A-F0-9]\{5,10\},[0-9]\{1,2\}")
|
||||
if [ -n "$REGV" ]; then
|
||||
LAC5=$(echo "$REGV" | cut -d, -f3)
|
||||
LAC5=$LAC5" ($(printf "%d" 0x$LAC5))"
|
||||
CID5=$(echo "$REGV" | cut -d, -f4)
|
||||
CID5L=$(printf "%010X" 0x$CID5)
|
||||
RNC5=${CID5L:1:6}
|
||||
RNC5=$RNC5" ($(printf "%d" 0x$RNC5))"
|
||||
CID5=${CID5L:7:3}
|
||||
CID5="Short $(printf "%X" 0x$CID5) ($(printf "%d" 0x$CID5)), Long $(printf "%X" 0x$CID5L) ($(printf "%d" 0x$CID5L))"
|
||||
RAT=$(echo "$REGV" | cut -d, -f5)
|
||||
fi
|
||||
REGV=$(echo "$OX" | grep -o "+CEREG:2,[0-9],[A-F0-9]\{2,4\},[A-F0-9]\{5,8\}")
|
||||
REGFMT="3GPP"
|
||||
if [ -z "$REGV" ]; then
|
||||
REGV=$(echo "$OX" | grep -o "+CEREG:2,[0-9],[A-F0-9]\{2,4\},[A-F0-9]\{1,3\},[A-F0-9]\{5,8\}")
|
||||
REGFMT="SW"
|
||||
fi
|
||||
if [ -n "$REGV" ]; then
|
||||
LAC=$(echo "$REGV" | cut -d, -f3)
|
||||
LAC=$(printf "%04X" 0x$LAC)" ($(printf "%d" 0x$LAC))"
|
||||
if [ $REGFMT = "3GPP" ]; then
|
||||
CID=$(echo "$REGV" | cut -d, -f4)
|
||||
else
|
||||
CID=$(echo "$REGV" | cut -d, -f5)
|
||||
fi
|
||||
CIDL=$(printf "%08X" 0x$CID)
|
||||
RNC=${CIDL:1:5}
|
||||
RNC=$RNC" ($(printf "%d" 0x$RNC))"
|
||||
CID=${CIDL:6:2}
|
||||
CID="Short $(printf "%X" 0x$CID) ($(printf "%d" 0x$CID)), Long $(printf "%X" 0x$CIDL) ($(printf "%d" 0x$CIDL))"
|
||||
|
||||
else
|
||||
REGV=$(echo "$OX" | grep -o "+CREG:2,[0-9],[A-F0-9]\{2,4\},[A-F0-9]\{2,8\}")
|
||||
if [ -n "$REGV" ]; then
|
||||
LAC=$(echo "$REGV" | cut -d, -f3)
|
||||
CID=$(echo "$REGV" | cut -d, -f4)
|
||||
if [ ${#CID} -gt 4 ]; then
|
||||
LAC=$(printf "%04X" 0x$LAC)" ($(printf "%d" 0x$LAC))"
|
||||
CIDL=$(printf "%08X" 0x$CID)
|
||||
RNC=${CIDL:1:3}
|
||||
CID=${CIDL:4:4}
|
||||
CID="Short $(printf "%X" 0x$CID) ($(printf "%d" 0x$CID)), Long $(printf "%X" 0x$CIDL) ($(printf "%d" 0x$CIDL))"
|
||||
else
|
||||
LAC=""
|
||||
fi
|
||||
else
|
||||
LAC=""
|
||||
fi
|
||||
fi
|
||||
REGSTAT=$(echo "$REGV" | cut -d, -f2)
|
||||
if [ "$REGSTAT" == "5" -a "$COPS" != "-" ]; then
|
||||
COPS_MNC=$COPS_MNC" (Roaming)"
|
||||
fi
|
||||
if [ -n "$CID" -a -n "$CID5" ] && [ "$RAT" == "13" -o "$RAT" == "10" ]; then
|
||||
LAC="4G $LAC, 5G $LAC5"
|
||||
CID="4G $CID<br />5G $CID5"
|
||||
RNC="4G $RNC, 5G $RNC5"
|
||||
elif [ -n "$CID5" ]; then
|
||||
LAC=$LAC5
|
||||
CID=$CID5
|
||||
RNC=$RNC5
|
||||
fi
|
||||
if [ -z "$LAC" ]; then
|
||||
LAC="-"
|
||||
CID="-"
|
||||
RNC="-"
|
||||
fi
|
||||
}
|
||||
SIMCOM_SIMINFO()
|
||||
{
|
||||
debug "Quectel_SIMINFO"
|
||||
# 获取IMEI
|
||||
IMEI=$( sh modem_at.sh $at_port "AT+CGSN" | sed -n '2p' )
|
||||
# 获取IMSI
|
||||
IMSI=$( sh modem_at.sh $at_port "AT+CIMI" | sed -n '2p' )
|
||||
# 获取ICCID
|
||||
ICCID=$( sh modem_at.sh $at_port "AT+ICCID" | grep -o "+ICCID:[ ]*[-0-9]\+" | grep -o "[-0-9]\{1,4\}" )
|
||||
# 获取电话号码
|
||||
phone=$( sh modem_at.sh $at_port "AT+CNUM" | grep "+CNUM:" )
|
||||
}
|
||||
#simcom查找基站AT
|
||||
get_simcom_data()
|
||||
{
|
||||
debug "get simcom data"
|
||||
#设置AT串口
|
||||
at_port=$1
|
||||
|
||||
All_CSQ
|
||||
SIMCOM_SIMINFO
|
||||
SIMCOM_Cellinfo
|
||||
|
||||
#温度
|
||||
OX=$( sh modem_at.sh $at_port "AT+CPMUTEMP")
|
||||
TEMP=$(echo "$OX" | grep -o "+CPMUTEMP:[ ]*[-0-9]\+" | grep -o "[-0-9]\{1,4\}")
|
||||
if [ -n "$TEMP" ]; then
|
||||
TEMP=$(echo $TEMP)$(printf "\xc2\xb0")"C"
|
||||
fi
|
||||
|
||||
|
||||
#基站信息
|
||||
OX=$( sh modem_at.sh $at_port "AT+CPSI?")
|
||||
rec=$(echo "$OX" | grep "+CPSI:")
|
||||
w=$(echo $rec |grep "NO SERVICE"| wc -l)
|
||||
if [ $w -ge 1 ];then
|
||||
debug "NO SERVICE"
|
||||
return
|
||||
fi
|
||||
w=$(echo $rec |grep "NR5G_"| wc -l)
|
||||
if [ $w -ge 1 ];then
|
||||
|
||||
w=$(echo $rec |grep "32768"| wc -l)
|
||||
if [ $w -ge 1 ];then
|
||||
debug "-32768"
|
||||
return
|
||||
fi
|
||||
|
||||
debug "$rec"
|
||||
rec1=${rec##*+CPSI:}
|
||||
#echo "$rec1"
|
||||
MODE="${rec1%%,*}" # MODE="NR5G"
|
||||
rect1=${rec1#*,}
|
||||
rect1s="${rect1%%,*}" #Online
|
||||
rect2=${rect1#*,}
|
||||
rect2s="${rect2%%,*}" #460-11
|
||||
rect3=${rect2#*,}
|
||||
rect3s="${rect3%%,*}" #0xCFA102
|
||||
rect4=${rect3#*,}
|
||||
rect4s="${rect4%%,*}" #55744245764
|
||||
rect5=${rect4#*,}
|
||||
rect5s="${rect5%%,*}" #196
|
||||
rect6=${rect5#*,}
|
||||
rect6s="${rect6%%,*}" #NR5G_BAND78
|
||||
rect7=${rect6#*,}
|
||||
rect7s="${rect7%%,*}" #627264
|
||||
rect8=${rect7#*,}
|
||||
rect8s="${rect8%%,*}" #-940
|
||||
rect9=${rect8#*,}
|
||||
rect9s="${rect9%%,*}" #-110
|
||||
# "${rec1##*,}" #最后一位
|
||||
rect10=${rect9#*,}
|
||||
rect10s="${rect10%%,*}" #最后一位
|
||||
PCI=$rect5s
|
||||
LBAND="n"$(echo $rect6s | cut -d, -f0 | grep -o "BAND[0-9]\{1,3\}" | grep -o "[0-9]\+")
|
||||
CHANNEL=$rect7s
|
||||
RSCP=$(($(echo $rect8s | cut -d, -f0) / 10))
|
||||
ECIO=$(($(echo $rect9s | cut -d, -f0) / 10))
|
||||
if [ "$CSQ_PER" = "-" ]; then
|
||||
CSQ_PER=$((100 - (($RSCP + 31) * 100/-125)))"%"
|
||||
fi
|
||||
SINR=$(($(echo $rect10s | cut -d, -f0) / 10))" dB"
|
||||
fi
|
||||
w=$(echo $rec |grep "LTE"|grep "EUTRAN"| wc -l)
|
||||
if [ $w -ge 1 ];then
|
||||
rec1=${rec#*EUTRAN-}
|
||||
lte_band=${rec1%%,*} #EUTRAN-BAND
|
||||
rec1=${rec1#*,}
|
||||
rec1=${rec1#*,}
|
||||
rec1=${rec1#*,}
|
||||
rec1=${rec1#*,}
|
||||
#rec1=${rec1#*,}
|
||||
rec1=${rec1#*,}
|
||||
lte_rssi=${rec1%%,*} #LTE_RSSI
|
||||
lte_rssi=`expr $lte_rssi / 10` #LTE_RSSI
|
||||
debug "LTE_BAND=$lte_band LTE_RSSI=$lte_rssi"
|
||||
if [ $rssi == 0 ];then
|
||||
rssi=$lte_rssi
|
||||
fi
|
||||
fi
|
||||
w=$(echo $rec |grep "WCDMA"| wc -l)
|
||||
if [ $w -ge 1 ];then
|
||||
w=$(echo $rec |grep "UNKNOWN"|wc -l)
|
||||
if [ $w -ge 1 ];then
|
||||
debug "UNKNOWN BAND"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
#CNMP
|
||||
OX=$( sh modem_at.sh $at_port "AT+CNMP?")
|
||||
CNMP=$(echo "$OX" | grep -o "+CNMP:[ ]*[0-9]\{1,3\}" | grep -o "[0-9]\{1,3\}")
|
||||
if [ -n "$CNMP" ]; then
|
||||
case $CNMP in
|
||||
"2"|"55" )
|
||||
NETMODE="1" ;;
|
||||
"13" )
|
||||
NETMODE="3" ;;
|
||||
"14" )
|
||||
NETMODE="5" ;;
|
||||
"38" )
|
||||
NETMODE="7" ;;
|
||||
"71" )
|
||||
NETMODE="9" ;;
|
||||
"109" )
|
||||
NETMODE="8" ;;
|
||||
* )
|
||||
NETMODE="0" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# CMGRMI 信息
|
||||
OX=$( sh modem_at.sh $at_port "AT+CMGRMI=4")
|
||||
CAINFO=$(echo "$OX" | grep -o "$REGXz" | tr ' ' ':')
|
||||
if [ -n "$CAINFO" ]; then
|
||||
for CASV in $(echo "$CAINFO"); do
|
||||
LBAND=$LBAND"<br />B"$(echo "$CASV" | cut -d, -f4)
|
||||
BW=$(echo "$CASV" | cut -d, -f5)
|
||||
decode_bw
|
||||
LBAND=$LBAND" (CA, Bandwidth $BW MHz)"
|
||||
CHANNEL="$CHANNEL, "$(echo "$CASV" | cut -d, -f2)
|
||||
PCI="$PCI, "$(echo "$CASV" | cut -d, -f7)
|
||||
done
|
||||
fi
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"luci-app-modem": {
|
||||
"description": "Grant UCI access for luci-app-modem",
|
||||
"read": {
|
||||
"uci": [ "modem" ]
|
||||
},
|
||||
"write": {
|
||||
"uci": [ "modem" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
47
applications/quectel_MHI/Makefile
Normal file
47
applications/quectel_MHI/Makefile
Normal file
@@ -0,0 +1,47 @@
|
||||
#
|
||||
# Copyright (C) 2015 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=pcie_mhi
|
||||
PKG_VERSION:=3.2
|
||||
PKG_RELEASE:=1
|
||||
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define KernelPackage/pcie_mhi
|
||||
SUBMENU:=WWAN Support
|
||||
TITLE:=Kernel pcie driver for MHI device
|
||||
DEPENDS:=+pciids +pciutils +quectel-CM-5G
|
||||
FILES:=$(PKG_BUILD_DIR)/pcie_mhi.ko
|
||||
AUTOLOAD:=$(call AutoLoad,90,pcie_mhi)
|
||||
endef
|
||||
|
||||
define KernelPackage/pcie_mhi/description
|
||||
Kernel module for register a custom pciemhi platform device.
|
||||
endef
|
||||
|
||||
MAKE_OPTS:= \
|
||||
ARCH="$(LINUX_KARCH)" \
|
||||
CROSS_COMPILE="$(TARGET_CROSS)" \
|
||||
CXXFLAGS="$(TARGET_CXXFLAGS)" \
|
||||
M="$(PKG_BUILD_DIR)" \
|
||||
$(EXTRA_KCONFIG)
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
$(CP) ./src/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
$(MAKE) -C "$(LINUX_DIR)" \
|
||||
$(MAKE_OPTS) \
|
||||
modules
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,pcie_mhi))
|
||||
34
applications/quectel_MHI/src/Makefile
Normal file
34
applications/quectel_MHI/src/Makefile
Normal file
@@ -0,0 +1,34 @@
|
||||
#ccflags-y += -g
|
||||
obj-m += pcie_mhi.o
|
||||
pcie_mhi-objs := core/mhi_init.o core/mhi_main.o core/mhi_pm.o core/mhi_boot.o core/mhi_dtr.o controllers/mhi_qti.o
|
||||
pcie_mhi-objs += devices/mhi_uci.o
|
||||
|
||||
ifeq (1,1)
|
||||
pcie_mhi-objs += devices/mhi_netdev_quectel.o
|
||||
else
|
||||
pcie_mhi-objs += devices/mhi_netdev.o
|
||||
pcie_mhi-objs += devices/rmnet_handler.o
|
||||
endif
|
||||
|
||||
PWD := $(shell pwd)
|
||||
ifeq ($(ARCH),)
|
||||
ARCH := $(shell uname -m)
|
||||
endif
|
||||
ifeq ($(CROSS_COMPILE),)
|
||||
CROSS_COMPILE :=
|
||||
endif
|
||||
ifeq ($(KDIR),)
|
||||
KDIR := /lib/modules/$(shell uname -r)/build
|
||||
endif
|
||||
|
||||
pcie_mhi: clean
|
||||
$(MAKE) ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} -C $(KDIR) M=$(PWD) modules
|
||||
#cp pcie_mhi.ko /tftpboot/
|
||||
|
||||
clean:
|
||||
$(MAKE) ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} -C $(KDIR) M=$(PWD) clean
|
||||
find . -name *.o.ur-safe | xargs rm -f
|
||||
|
||||
install: pcie_mhi
|
||||
sudo cp pcie_mhi.ko /lib/modules/${shell uname -r}/kernel/drivers/pci/
|
||||
sudo depmod
|
||||
36
applications/quectel_MHI/src/README
Normal file
36
applications/quectel_MHI/src/README
Normal file
@@ -0,0 +1,36 @@
|
||||
1. porting pcie_mhi driver as next
|
||||
|
||||
$ git diff drivers/Makefile
|
||||
diff --git a/drivers/Makefile b/drivers/Makefile
|
||||
index 77fbc52..e45837e 100644
|
||||
--- a/drivers/Makefile
|
||||
+++ b/drivers/Makefile
|
||||
@@ -184,3 +184,4 @@ obj-$(CONFIG_FPGA) += fpga/
|
||||
obj-$(CONFIG_FSI) += fsi/
|
||||
obj-$(CONFIG_TEE) += tee/
|
||||
obj-$(CONFIG_MULTIPLEXER) += mux/
|
||||
+obj-y += pcie_mhi/
|
||||
|
||||
$ tree drivers/pcie_mhi/ -L 1
|
||||
drivers/pcie_mhi/
|
||||
controllers
|
||||
core
|
||||
devices
|
||||
Makefile
|
||||
|
||||
2. check RG500 attach pcie_mhi driver successful
|
||||
|
||||
root@OpenWrt:/# lspci
|
||||
00:00.0 Class 0604: 17cb:0302
|
||||
01:00.0 Class ff00: 17cb:0306
|
||||
|
||||
root@OpenWrt:~# dmesg | grep mhi
|
||||
[ 138.483252] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.6
|
||||
[ 138.492350] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306
|
||||
|
||||
3. how to use, see next logs
|
||||
|
||||
log/QXDM_OVER_PCIE.txt
|
||||
log/AT_OVER_PCIE.txt
|
||||
log/MBIM_OVER_PCIE.txt
|
||||
log/QMI_OVER_PCIE.txt
|
||||
103
applications/quectel_MHI/src/ReleaseNote.txt
Normal file
103
applications/quectel_MHI/src/ReleaseNote.txt
Normal file
@@ -0,0 +1,103 @@
|
||||
Release Notes
|
||||
|
||||
[V1.3.4]
|
||||
Date: 12/8/2022
|
||||
enhancement:
|
||||
1. only allow to enable autosuspend when module is in MHI_EE_AMSS
|
||||
2. show pcie link speed and width when driver probe
|
||||
3. check pcie link status by read pcie vid and pid when driver probe,
|
||||
if pcie link is down, return -EIO
|
||||
4. support RM520 (1eac:1004)
|
||||
5. support qmap command packet
|
||||
fix:
|
||||
1. fix tx queue is wrong stop when do uplink TPUT
|
||||
2. fix after QFirehose, module fail to bootup at very small probability
|
||||
3. mhi uci add mutex lock for concurrent reads/writes
|
||||
|
||||
[V1.3.3]
|
||||
Date: 30/6/2022
|
||||
enhancement:
|
||||
1. remove one un-necessary kmalloc when do qfirehose
|
||||
2. support mhi monitor (like usbmon), usage: cat /sys/kernel/debug/mhi_q/0306_00\:01.00/mhimon
|
||||
3. set ring size of event 0 to 256 (from 1024), required by x6x
|
||||
4. support PCIE local network card mhi_swip0 (chan 46/47), default disabled
|
||||
5. porting IPQ5018 mhi rate controll code from spf11.5
|
||||
6. set pcie rmnet download max qmap packet size to 15KB (same to IPQ MHI Driver)
|
||||
7. support set different mac address for rmnet net card
|
||||
8. when mhi netdev fail to malloc, use delay_work instead work
|
||||
9. optimize code for 'when driver load, modem is still in MHI_EE_PTHRU'
|
||||
fix:
|
||||
1. Fix not synchronize access rp/wp when mhi_queue_xxx and mhi_process_xxx_ring run on different CPU
|
||||
2. set dma mask when driver probe, some SOC like rpi_4 need it
|
||||
|
||||
[V1.3.2]
|
||||
Date: 12/16/2021
|
||||
enhancement:
|
||||
1. support Linux Kernel V5.14
|
||||
2. mhi_netdev_quectel.c do not print log in softirq context
|
||||
|
||||
[V1.3.1]
|
||||
Date: 9/26/2021
|
||||
enhancement:
|
||||
fix:
|
||||
|
||||
[V1.3.0.19]
|
||||
Date: 9/18/2021
|
||||
enhancement:
|
||||
1. support sdx62 (17cb:0308)
|
||||
2. support IPQ5018's NSS
|
||||
3. use 'qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c' instead myself rmnet_nss.c
|
||||
and pcie_mhi.ko must load after then rmnet_nss.ko
|
||||
4. allow bhi irq is not 0 (for ipq5018)
|
||||
fix:
|
||||
|
||||
[V1.3.0.18]
|
||||
Date: 4/14/2021
|
||||
enhancement:
|
||||
1. support mbim multiple call, usage:
|
||||
# insmod pcie_mhi.ko mhi_mbim_enabeld=1 qmap_mode=4
|
||||
# quectel-mbim-proxy -d /dev/mhi_MBIM &
|
||||
# quectel-CM -n X
|
||||
fix:
|
||||
|
||||
[V1.3.0.17]
|
||||
Date: 3/11/2021
|
||||
enhancement:
|
||||
fix:
|
||||
1. fix CPU loading very high when TPUT test when only one MSI interrupt
|
||||
2. fix error on latest X24 modem
|
||||
|
||||
[V1.3.0.16]
|
||||
Date: 11/18/2020
|
||||
enhancement:
|
||||
fix:
|
||||
1. add ring size to 32, for in-bound chan, if one ring is full, modem will not generate MSI interrupt for all chan
|
||||
|
||||
[V1.3.0.15]
|
||||
Date: 10/30/2020
|
||||
enhancement:
|
||||
1. support multi-modems, named as /dev/mhi_<chan_name>X
|
||||
fix:
|
||||
1. fix compile error on kernel v5.8
|
||||
|
||||
[V1.3.0.14]
|
||||
Date: 10/9/2020
|
||||
enhancement:
|
||||
1. suppport EM120&EM160
|
||||
fix:
|
||||
1. fix compile error on kernel v5.6
|
||||
2. support runtime suspend
|
||||
|
||||
[V1.3.0.13]
|
||||
Date: 9/7/2020
|
||||
enhancement:
|
||||
1. suppport EM120&EM160
|
||||
fix:
|
||||
1. fix error on X55 + PCIE2.0(e.g IPQ4019)
|
||||
2. support runtime suspend
|
||||
|
||||
[V1.3.0.12]
|
||||
Date: 7/7/2020
|
||||
enhancement:
|
||||
1. suppport create only none netcard (enabled by marco MHI_NETDEV_ONE_CARD_MODE),
|
||||
fix:
|
||||
13
applications/quectel_MHI/src/controllers/Kconfig
Normal file
13
applications/quectel_MHI/src/controllers/Kconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
menu "MHI controllers"
|
||||
|
||||
config MHI_QTI
|
||||
tristate "MHI QTI"
|
||||
depends on MHI_BUS
|
||||
help
|
||||
If you say yes to this option, MHI bus support for QTI modem chipsets
|
||||
will be enabled. QTI PCIe based modems uses MHI as the communication
|
||||
protocol. MHI control driver is the bus master for such modems. As the
|
||||
bus master driver, it oversees power management operations such as
|
||||
suspend, resume, powering on and off the device.
|
||||
|
||||
endmenu
|
||||
1
applications/quectel_MHI/src/controllers/Makefile
Normal file
1
applications/quectel_MHI/src/controllers/Makefile
Normal file
@@ -0,0 +1 @@
|
||||
obj-$(CONFIG_MHI_QTI) += mhi_qti.o mhi_arch_qti.o
|
||||
275
applications/quectel_MHI/src/controllers/mhi_arch_qti.c
Normal file
275
applications/quectel_MHI/src/controllers/mhi_arch_qti.c
Normal file
@@ -0,0 +1,275 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/
|
||||
|
||||
#include <linux/async.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-direction.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/msm-bus.h>
|
||||
#include <linux/msm_pcie.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include "../core/mhi.h"
|
||||
#include "mhi_qti.h"
|
||||
|
||||
struct arch_info {
|
||||
struct mhi_dev *mhi_dev;
|
||||
struct msm_bus_scale_pdata *msm_bus_pdata;
|
||||
u32 bus_client;
|
||||
struct pci_saved_state *pcie_state;
|
||||
struct pci_saved_state *ref_pcie_state;
|
||||
struct dma_iommu_mapping *mapping;
|
||||
};
|
||||
|
||||
struct mhi_bl_info {
|
||||
struct mhi_device *mhi_device;
|
||||
async_cookie_t cookie;
|
||||
void *ipc_log;
|
||||
};
|
||||
|
||||
/* ipc log markings */
|
||||
#define DLOG "Dev->Host: "
|
||||
#define HLOG "Host: "
|
||||
|
||||
#ifdef CONFIG_MHI_DEBUG
|
||||
|
||||
#define MHI_IPC_LOG_PAGES (100)
|
||||
enum MHI_DEBUG_LEVEL mhi_ipc_log_lvl = MHI_MSG_LVL_VERBOSE;
|
||||
|
||||
#else
|
||||
|
||||
#define MHI_IPC_LOG_PAGES (10)
|
||||
enum MHI_DEBUG_LEVEL mhi_ipc_log_lvl = MHI_MSG_LVL_ERROR;
|
||||
|
||||
#endif
|
||||
|
||||
static int mhi_arch_set_bus_request(struct mhi_controller *mhi_cntrl, int index)
|
||||
{
|
||||
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
struct arch_info *arch_info = mhi_dev->arch_info;
|
||||
|
||||
MHI_LOG("Setting bus request to index %d\n", index);
|
||||
|
||||
if (arch_info->bus_client)
|
||||
return msm_bus_scale_client_update_request(
|
||||
arch_info->bus_client,
|
||||
index);
|
||||
|
||||
/* default return success */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mhi_bl_dl_cb(struct mhi_device *mhi_dev,
|
||||
struct mhi_result *mhi_result)
|
||||
{
|
||||
struct mhi_bl_info *mhi_bl_info = mhi_device_get_devdata(mhi_dev);
|
||||
char *buf = mhi_result->buf_addr;
|
||||
|
||||
/* force a null at last character */
|
||||
buf[mhi_result->bytes_xferd - 1] = 0;
|
||||
|
||||
ipc_log_string(mhi_bl_info->ipc_log, "%s %s", DLOG, buf);
|
||||
}
|
||||
|
||||
static void mhi_bl_dummy_cb(struct mhi_device *mhi_dev,
|
||||
struct mhi_result *mhi_result)
|
||||
{
|
||||
}
|
||||
|
||||
static void mhi_bl_remove(struct mhi_device *mhi_dev)
|
||||
{
|
||||
struct mhi_bl_info *mhi_bl_info = mhi_device_get_devdata(mhi_dev);
|
||||
|
||||
ipc_log_string(mhi_bl_info->ipc_log, HLOG "Received Remove notif.\n");
|
||||
|
||||
/* wait for boot monitor to exit */
|
||||
async_synchronize_cookie(mhi_bl_info->cookie + 1);
|
||||
}
|
||||
|
||||
static void mhi_bl_boot_monitor(void *data, async_cookie_t cookie)
|
||||
{
|
||||
struct mhi_bl_info *mhi_bl_info = data;
|
||||
struct mhi_device *mhi_device = mhi_bl_info->mhi_device;
|
||||
struct mhi_controller *mhi_cntrl = mhi_device->mhi_cntrl;
|
||||
/* 15 sec timeout for booting device */
|
||||
const u32 timeout = msecs_to_jiffies(15000);
|
||||
|
||||
/* wait for device to enter boot stage */
|
||||
wait_event_timeout(mhi_cntrl->state_event, mhi_cntrl->ee == MHI_EE_AMSS
|
||||
|| mhi_cntrl->ee == MHI_EE_DISABLE_TRANSITION,
|
||||
timeout);
|
||||
|
||||
if (mhi_cntrl->ee == MHI_EE_AMSS) {
|
||||
ipc_log_string(mhi_bl_info->ipc_log, HLOG
|
||||
"Device successfully booted to mission mode\n");
|
||||
|
||||
mhi_unprepare_from_transfer(mhi_device);
|
||||
} else {
|
||||
ipc_log_string(mhi_bl_info->ipc_log, HLOG
|
||||
"Device failed to boot to mission mode, ee = %s\n",
|
||||
TO_MHI_EXEC_STR(mhi_cntrl->ee));
|
||||
}
|
||||
}
|
||||
|
||||
static int mhi_bl_probe(struct mhi_device *mhi_dev,
|
||||
const struct mhi_device_id *id)
|
||||
{
|
||||
char node_name[32];
|
||||
struct mhi_bl_info *mhi_bl_info;
|
||||
|
||||
mhi_bl_info = devm_kzalloc(&mhi_dev->dev, sizeof(*mhi_bl_info),
|
||||
GFP_KERNEL);
|
||||
if (!mhi_bl_info)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(node_name, sizeof(node_name), "mhi_bl_%04x_%02u.%02u.%02u",
|
||||
mhi_dev->dev_id, mhi_dev->domain, mhi_dev->bus, mhi_dev->slot);
|
||||
|
||||
mhi_bl_info->ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES,
|
||||
node_name, 0);
|
||||
if (!mhi_bl_info->ipc_log)
|
||||
return -EINVAL;
|
||||
|
||||
mhi_bl_info->mhi_device = mhi_dev;
|
||||
mhi_device_set_devdata(mhi_dev, mhi_bl_info);
|
||||
|
||||
ipc_log_string(mhi_bl_info->ipc_log, HLOG
|
||||
"Entered SBL, Session ID:0x%x\n",
|
||||
mhi_dev->mhi_cntrl->session_id);
|
||||
|
||||
/* start a thread to monitor entering mission mode */
|
||||
mhi_bl_info->cookie = async_schedule(mhi_bl_boot_monitor, mhi_bl_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mhi_device_id mhi_bl_match_table[] = {
|
||||
{ .chan = "BL" },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct mhi_driver mhi_bl_driver = {
|
||||
.id_table = mhi_bl_match_table,
|
||||
.remove = mhi_bl_remove,
|
||||
.probe = mhi_bl_probe,
|
||||
.ul_xfer_cb = mhi_bl_dummy_cb,
|
||||
.dl_xfer_cb = mhi_bl_dl_cb,
|
||||
.driver = {
|
||||
.name = "MHI_BL",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
struct arch_info *arch_info = mhi_dev->arch_info;
|
||||
char node[32];
|
||||
|
||||
if (!arch_info) {
|
||||
arch_info = devm_kzalloc(&mhi_dev->pci_dev->dev,
|
||||
sizeof(*arch_info), GFP_KERNEL);
|
||||
if (!arch_info)
|
||||
return -ENOMEM;
|
||||
|
||||
mhi_dev->arch_info = arch_info;
|
||||
|
||||
snprintf(node, sizeof(node), "mhi_%04x_%02u.%02u.%02u",
|
||||
mhi_cntrl->dev_id, mhi_cntrl->domain, mhi_cntrl->bus,
|
||||
mhi_cntrl->slot);
|
||||
mhi_cntrl->log_buf = ipc_log_context_create(MHI_IPC_LOG_PAGES,
|
||||
node, 0);
|
||||
mhi_cntrl->log_lvl = mhi_ipc_log_lvl;
|
||||
|
||||
/* save reference state for pcie config space */
|
||||
arch_info->ref_pcie_state = pci_store_saved_state(
|
||||
mhi_dev->pci_dev);
|
||||
|
||||
mhi_driver_register(&mhi_bl_driver);
|
||||
}
|
||||
|
||||
return mhi_arch_set_bus_request(mhi_cntrl, 1);
|
||||
}
|
||||
|
||||
void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
mhi_arch_set_bus_request(mhi_cntrl, 0);
|
||||
}
|
||||
|
||||
int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, bool graceful)
|
||||
{
|
||||
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
struct arch_info *arch_info = mhi_dev->arch_info;
|
||||
struct pci_dev *pci_dev = mhi_dev->pci_dev;
|
||||
int ret;
|
||||
|
||||
MHI_LOG("Entered\n");
|
||||
|
||||
if (graceful) {
|
||||
pci_clear_master(pci_dev);
|
||||
ret = pci_save_state(mhi_dev->pci_dev);
|
||||
if (ret) {
|
||||
MHI_ERR("Failed with pci_save_state, ret:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
arch_info->pcie_state = pci_store_saved_state(pci_dev);
|
||||
pci_disable_device(pci_dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* We will always attempt to put link into D3hot, however
|
||||
* link down may have happened due to error fatal, so
|
||||
* ignoring the return code
|
||||
*/
|
||||
pci_set_power_state(pci_dev, PCI_D3hot);
|
||||
|
||||
/* release the resources */
|
||||
mhi_arch_set_bus_request(mhi_cntrl, 0);
|
||||
|
||||
MHI_LOG("Exited\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mhi_arch_link_on(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
struct arch_info *arch_info = mhi_dev->arch_info;
|
||||
struct pci_dev *pci_dev = mhi_dev->pci_dev;
|
||||
int ret;
|
||||
|
||||
MHI_LOG("Entered\n");
|
||||
|
||||
/* request resources and establish link trainning */
|
||||
ret = mhi_arch_set_bus_request(mhi_cntrl, 1);
|
||||
if (ret)
|
||||
MHI_LOG("Could not set bus frequency, ret:%d\n", ret);
|
||||
|
||||
ret = pci_set_power_state(pci_dev, PCI_D0);
|
||||
if (ret) {
|
||||
MHI_ERR("Failed to set PCI_D0 state, ret:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pci_enable_device(pci_dev);
|
||||
if (ret) {
|
||||
MHI_ERR("Failed to enable device, ret:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pci_load_and_free_saved_state(pci_dev, &arch_info->pcie_state);
|
||||
if (ret)
|
||||
MHI_LOG("Failed to load saved cfg state\n");
|
||||
|
||||
pci_restore_state(pci_dev);
|
||||
pci_set_master(pci_dev);
|
||||
|
||||
MHI_LOG("Exited\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
715
applications/quectel_MHI/src/controllers/mhi_qcom.c
Normal file
715
applications/quectel_MHI/src/controllers/mhi_qcom.c
Normal file
@@ -0,0 +1,715 @@
|
||||
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-direction.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/version.h>
|
||||
#include "../core/mhi.h"
|
||||
#include "mhi_qcom.h"
|
||||
|
||||
#if 1
|
||||
#ifndef PCI_IRQ_MSI
|
||||
#define PCI_IRQ_MSI (1 << 1) /* Allow MSI interrupts */
|
||||
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,53 ))
|
||||
int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
|
||||
{
|
||||
int nvec = maxvec;
|
||||
int rc;
|
||||
|
||||
if (maxvec < minvec)
|
||||
return -ERANGE;
|
||||
|
||||
do {
|
||||
rc = pci_enable_msi_block(dev, nvec);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
} else if (rc > 0) {
|
||||
if (rc < minvec)
|
||||
return -ENOSPC;
|
||||
nvec = rc;
|
||||
}
|
||||
} while (rc);
|
||||
|
||||
return nvec;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
|
||||
unsigned int max_vecs, unsigned int flags)
|
||||
{
|
||||
return pci_enable_msi_range(dev, min_vecs, max_vecs);
|
||||
}
|
||||
|
||||
static void pci_free_irq_vectors(struct pci_dev *dev)
|
||||
{
|
||||
pci_disable_msi(dev);
|
||||
}
|
||||
|
||||
static int pci_irq_vector(struct pci_dev *dev, unsigned int nr)
|
||||
{
|
||||
return dev->irq + nr;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static struct pci_device_id mhi_pcie_device_id[] = {
|
||||
{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0303)}, //SDX20
|
||||
{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0304)}, //SDX24
|
||||
{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0305)},
|
||||
{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0306)}, //SDX55
|
||||
{PCI_DEVICE(0x2C7C, 0x0512)},
|
||||
{PCI_DEVICE(MHI_PCIE_VENDOR_ID, MHI_PCIE_DEBUG_ID)},
|
||||
{0},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, mhi_pcie_device_id);
|
||||
|
||||
static struct pci_driver mhi_pcie_driver;
|
||||
|
||||
void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
struct pci_dev *pci_dev = mhi_dev->pci_dev;
|
||||
|
||||
pci_free_irq_vectors(pci_dev);
|
||||
iounmap(mhi_cntrl->regs);
|
||||
mhi_cntrl->regs = NULL;
|
||||
pci_clear_master(pci_dev);
|
||||
pci_release_region(pci_dev, mhi_dev->resn);
|
||||
pci_disable_device(pci_dev);
|
||||
}
|
||||
|
||||
static int mhi_init_pci_dev(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
struct pci_dev *pci_dev = mhi_dev->pci_dev;
|
||||
int ret;
|
||||
resource_size_t start, len;
|
||||
int i;
|
||||
|
||||
mhi_dev->resn = MHI_PCI_BAR_NUM;
|
||||
ret = pci_assign_resource(pci_dev, mhi_dev->resn);
|
||||
if (ret) {
|
||||
MHI_ERR("Error assign pci resources, ret:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pci_enable_device(pci_dev);
|
||||
if (ret) {
|
||||
MHI_ERR("Error enabling device, ret:%d\n", ret);
|
||||
goto error_enable_device;
|
||||
}
|
||||
|
||||
ret = pci_request_region(pci_dev, mhi_dev->resn, "mhi");
|
||||
if (ret) {
|
||||
MHI_ERR("Error pci_request_region, ret:%d\n", ret);
|
||||
goto error_request_region;
|
||||
}
|
||||
|
||||
pci_set_master(pci_dev);
|
||||
|
||||
start = pci_resource_start(pci_dev, mhi_dev->resn);
|
||||
len = pci_resource_len(pci_dev, mhi_dev->resn);
|
||||
mhi_cntrl->regs = ioremap_nocache(start, len);
|
||||
MHI_LOG("mhi_cntrl->regs = %p\n", mhi_cntrl->regs);
|
||||
if (!mhi_cntrl->regs) {
|
||||
MHI_ERR("Error ioremap region\n");
|
||||
goto error_ioremap;
|
||||
}
|
||||
|
||||
ret = pci_alloc_irq_vectors(pci_dev, 1, mhi_cntrl->msi_required, PCI_IRQ_MSI);
|
||||
if (IS_ERR_VALUE((ulong)ret) || ret < mhi_cntrl->msi_required) {
|
||||
if (ret == -ENOSPC) {
|
||||
/* imx_3.14.52_1.1.0_ga
|
||||
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
|
||||
index f06e8f0..6a9614f 100644
|
||||
--- a/drivers/pci/host/pcie-designware.c
|
||||
+++ b/drivers/pci/host/pcie-designware.c
|
||||
@@ -376,6 +376,13 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
|
||||
if (msgvec > 5)
|
||||
msgvec = 0;
|
||||
|
||||
+#if 1 //Add by Quectel 20190419
|
||||
+ if (msgvec > 0 && pdev->vendor == 0x17cb) {
|
||||
+ dev_info(&pdev->dev, "%s quectel fixup pos=%d, msg_ctr=%04x, msgvec=%d\n", __func__, desc->msi_attrib.pos, msg_ctr, msgvec);
|
||||
+ msgvec = 0;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
irq = assign_irq((1 << msgvec), desc, &pos);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
*/
|
||||
}
|
||||
//imx_4.1.15_2.0.0_ga & DELL_OPTIPLEX_7010 only alloc one msi interrupt for one pcie device
|
||||
if (ret != 1) {
|
||||
MHI_ERR("Failed to enable MSI, ret=%d, msi_required=%d\n", ret, mhi_cntrl->msi_required);
|
||||
goto error_req_msi;
|
||||
}
|
||||
}
|
||||
|
||||
mhi_cntrl->msi_allocated = ret;
|
||||
MHI_LOG("msi_required = %d, msi_allocated = %d, msi_irq = %u\n", mhi_cntrl->msi_required, mhi_cntrl->msi_allocated, pci_dev->irq);
|
||||
|
||||
for (i = 0; i < mhi_cntrl->msi_allocated; i++) {
|
||||
mhi_cntrl->irq[i] = pci_irq_vector(pci_dev, i);
|
||||
if (mhi_cntrl->irq[i] < 0) {
|
||||
ret = mhi_cntrl->irq[i];
|
||||
goto error_get_irq_vec;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* configure runtime pm */
|
||||
pm_runtime_set_autosuspend_delay(&pci_dev->dev, MHI_RPM_SUSPEND_TMR_MS);
|
||||
pm_runtime_dont_use_autosuspend(&pci_dev->dev);
|
||||
pm_suspend_ignore_children(&pci_dev->dev, true);
|
||||
|
||||
/*
|
||||
* pci framework will increment usage count (twice) before
|
||||
* calling local device driver probe function.
|
||||
* 1st pci.c pci_pm_init() calls pm_runtime_forbid
|
||||
* 2nd pci-driver.c local_pci_probe calls pm_runtime_get_sync
|
||||
* Framework expect pci device driver to call
|
||||
* pm_runtime_put_noidle to decrement usage count after
|
||||
* successful probe and and call pm_runtime_allow to enable
|
||||
* runtime suspend.
|
||||
*/
|
||||
pm_runtime_mark_last_busy(&pci_dev->dev);
|
||||
pm_runtime_put_noidle(&pci_dev->dev);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
error_get_irq_vec:
|
||||
pci_free_irq_vectors(pci_dev);
|
||||
|
||||
error_req_msi:
|
||||
iounmap(mhi_cntrl->regs);
|
||||
|
||||
error_ioremap:
|
||||
pci_clear_master(pci_dev);
|
||||
|
||||
error_request_region:
|
||||
pci_disable_device(pci_dev);
|
||||
|
||||
error_enable_device:
|
||||
pci_release_region(pci_dev, mhi_dev->resn);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mhi_runtime_idle(struct device *dev)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
|
||||
|
||||
MHI_LOG("Entered returning -EBUSY\n");
|
||||
|
||||
/*
|
||||
* RPM framework during runtime resume always calls
|
||||
* rpm_idle to see if device ready to suspend.
|
||||
* If dev.power usage_count count is 0, rpm fw will call
|
||||
* rpm_idle cb to see if device is ready to suspend.
|
||||
* if cb return 0, or cb not defined the framework will
|
||||
* assume device driver is ready to suspend;
|
||||
* therefore, fw will schedule runtime suspend.
|
||||
* In MHI power management, MHI host shall go to
|
||||
* runtime suspend only after entering MHI State M2, even if
|
||||
* usage count is 0. Return -EBUSY to disable automatic suspend.
|
||||
*/
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int mhi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
|
||||
|
||||
MHI_LOG("Enter\n");
|
||||
|
||||
mutex_lock(&mhi_cntrl->pm_mutex);
|
||||
|
||||
ret = mhi_pm_suspend(mhi_cntrl);
|
||||
if (ret) {
|
||||
MHI_LOG("Abort due to ret:%d\n", ret);
|
||||
goto exit_runtime_suspend;
|
||||
}
|
||||
|
||||
ret = mhi_arch_link_off(mhi_cntrl, true);
|
||||
if (ret)
|
||||
MHI_ERR("Failed to Turn off link ret:%d\n", ret);
|
||||
|
||||
exit_runtime_suspend:
|
||||
mutex_unlock(&mhi_cntrl->pm_mutex);
|
||||
MHI_LOG("Exited with ret:%d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mhi_runtime_resume(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
|
||||
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
|
||||
MHI_LOG("Enter\n");
|
||||
|
||||
mutex_lock(&mhi_cntrl->pm_mutex);
|
||||
|
||||
if (!mhi_dev->powered_on) {
|
||||
MHI_LOG("Not fully powered, return success\n");
|
||||
mutex_unlock(&mhi_cntrl->pm_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* turn on link */
|
||||
ret = mhi_arch_link_on(mhi_cntrl);
|
||||
if (ret)
|
||||
goto rpm_resume_exit;
|
||||
|
||||
/* enter M0 state */
|
||||
ret = mhi_pm_resume(mhi_cntrl);
|
||||
|
||||
rpm_resume_exit:
|
||||
mutex_unlock(&mhi_cntrl->pm_mutex);
|
||||
MHI_LOG("Exited with :%d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mhi_system_resume(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
|
||||
|
||||
ret = mhi_runtime_resume(dev);
|
||||
if (ret) {
|
||||
MHI_ERR("Failed to resume link\n");
|
||||
} else {
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mhi_system_suspend(struct device *dev)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
|
||||
|
||||
MHI_LOG("Entered\n");
|
||||
|
||||
/* if rpm status still active then force suspend */
|
||||
if (!pm_runtime_status_suspended(dev))
|
||||
return mhi_runtime_suspend(dev);
|
||||
|
||||
pm_runtime_set_suspended(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
MHI_LOG("Exit\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* checks if link is down */
|
||||
static int mhi_link_status(struct mhi_controller *mhi_cntrl, void *priv)
|
||||
{
|
||||
struct mhi_dev *mhi_dev = priv;
|
||||
u16 dev_id;
|
||||
int ret;
|
||||
|
||||
/* try reading device id, if dev id don't match, link is down */
|
||||
ret = pci_read_config_word(mhi_dev->pci_dev, PCI_DEVICE_ID, &dev_id);
|
||||
|
||||
return (ret || dev_id != mhi_cntrl->dev_id) ? -EIO : 0;
|
||||
}
|
||||
|
||||
static int mhi_runtime_get(struct mhi_controller *mhi_cntrl, void *priv)
|
||||
{
|
||||
struct mhi_dev *mhi_dev = priv;
|
||||
struct device *dev = &mhi_dev->pci_dev->dev;
|
||||
|
||||
return pm_runtime_get(dev);
|
||||
}
|
||||
|
||||
static void mhi_runtime_put(struct mhi_controller *mhi_cntrl, void *priv)
|
||||
{
|
||||
struct mhi_dev *mhi_dev = priv;
|
||||
struct device *dev = &mhi_dev->pci_dev->dev;
|
||||
|
||||
pm_runtime_put_noidle(dev);
|
||||
}
|
||||
|
||||
static void mhi_status_cb(struct mhi_controller *mhi_cntrl,
|
||||
void *priv,
|
||||
enum MHI_CB reason)
|
||||
{
|
||||
struct mhi_dev *mhi_dev = priv;
|
||||
struct device *dev = &mhi_dev->pci_dev->dev;
|
||||
|
||||
if (reason == MHI_CB_IDLE) {
|
||||
MHI_LOG("Schedule runtime suspend 1\n");
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_request_autosuspend(dev);
|
||||
}
|
||||
}
|
||||
|
||||
int mhi_debugfs_trigger_m0(void *data, u64 val)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = data;
|
||||
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
|
||||
MHI_LOG("Trigger M3 Exit\n");
|
||||
pm_runtime_get(&mhi_dev->pci_dev->dev);
|
||||
pm_runtime_put(&mhi_dev->pci_dev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mhi_debugfs_trigger_m3(void *data, u64 val)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = data;
|
||||
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
|
||||
MHI_LOG("Trigger M3 Entry\n");
|
||||
pm_runtime_mark_last_busy(&mhi_dev->pci_dev->dev);
|
||||
pm_request_autosuspend(&mhi_dev->pci_dev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m0_fops, NULL,
|
||||
mhi_debugfs_trigger_m0, "%llu\n");
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m3_fops, NULL,
|
||||
mhi_debugfs_trigger_m3, "%llu\n");
|
||||
|
||||
static int mhi_init_debugfs_trigger_go(void *data, u64 val)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = data;
|
||||
|
||||
MHI_LOG("Trigger power up sequence\n");
|
||||
|
||||
mhi_async_power_up(mhi_cntrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(mhi_init_debugfs_trigger_go_fops, NULL,
|
||||
mhi_init_debugfs_trigger_go, "%llu\n");
|
||||
|
||||
|
||||
int mhi_init_debugfs_debug_show(struct seq_file *m, void *d)
|
||||
{
|
||||
seq_puts(m, "Enable debug mode to debug external soc\n");
|
||||
seq_puts(m,
|
||||
"Usage: echo 'devid,timeout,domain,smmu_cfg' > debug_mode\n");
|
||||
seq_puts(m, "No spaces between parameters\n");
|
||||
seq_puts(m, "\t1. devid : 0 or pci device id to register\n");
|
||||
seq_puts(m, "\t2. timeout: mhi cmd/state transition timeout\n");
|
||||
seq_puts(m, "\t3. domain: Rootcomplex\n");
|
||||
seq_puts(m, "\t4. smmu_cfg: smmu configuration mask:\n");
|
||||
seq_puts(m, "\t\t- BIT0: ATTACH\n");
|
||||
seq_puts(m, "\t\t- BIT1: S1 BYPASS\n");
|
||||
seq_puts(m, "\t\t-BIT2: FAST_MAP\n");
|
||||
seq_puts(m, "\t\t-BIT3: ATOMIC\n");
|
||||
seq_puts(m, "\t\t-BIT4: FORCE_COHERENT\n");
|
||||
seq_puts(m, "\t\t-BIT5: GEOMETRY\n");
|
||||
seq_puts(m, "\tAll timeout are in ms, enter 0 to keep default\n");
|
||||
seq_puts(m, "Examples inputs: '0x307,10000'\n");
|
||||
seq_puts(m, "\techo '0,10000,1'\n");
|
||||
seq_puts(m, "\techo '0x307,10000,0,0x3d'\n");
|
||||
seq_puts(m, "firmware image name will be changed to debug.mbn\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mhi_init_debugfs_debug_open(struct inode *node, struct file *file)
|
||||
{
|
||||
return single_open(file, mhi_init_debugfs_debug_show, NULL);
|
||||
}
|
||||
|
||||
static ssize_t mhi_init_debugfs_debug_write(struct file *fp,
|
||||
const char __user *ubuf,
|
||||
size_t count,
|
||||
loff_t *pos)
|
||||
{
|
||||
char *buf = kmalloc(count + 1, GFP_KERNEL);
|
||||
/* #,devid,timeout,domain,smmu-cfg */
|
||||
int args[5] = {0};
|
||||
static char const *dbg_fw = "debug.mbn";
|
||||
int ret;
|
||||
struct mhi_controller *mhi_cntrl = fp->f_inode->i_private;
|
||||
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
struct pci_device_id *id;
|
||||
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = copy_from_user(buf, ubuf, count);
|
||||
if (ret)
|
||||
goto error_read;
|
||||
buf[count] = 0;
|
||||
get_options(buf, ARRAY_SIZE(args), args);
|
||||
kfree(buf);
|
||||
|
||||
/* override default parameters */
|
||||
mhi_cntrl->fw_image = dbg_fw;
|
||||
mhi_cntrl->edl_image = dbg_fw;
|
||||
|
||||
if (args[0] >= 2 && args[2])
|
||||
mhi_cntrl->timeout_ms = args[2];
|
||||
|
||||
if (args[0] >= 3 && args[3])
|
||||
mhi_cntrl->domain = args[3];
|
||||
|
||||
if (args[0] >= 4 && args[4])
|
||||
mhi_dev->smmu_cfg = args[4];
|
||||
|
||||
/* If it's a new device id register it */
|
||||
if (args[0] && args[1]) {
|
||||
/* find the debug_id and overwrite it */
|
||||
for (id = mhi_pcie_device_id; id->vendor; id++)
|
||||
if (id->device == MHI_PCIE_DEBUG_ID) {
|
||||
id->device = args[1];
|
||||
pci_unregister_driver(&mhi_pcie_driver);
|
||||
ret = pci_register_driver(&mhi_pcie_driver);
|
||||
}
|
||||
}
|
||||
|
||||
mhi_dev->debug_mode = true;
|
||||
debugfs_create_file("go", 0444, mhi_cntrl->parent, mhi_cntrl,
|
||||
&mhi_init_debugfs_trigger_go_fops);
|
||||
pr_info(
|
||||
"%s: ret:%d pcidev:0x%x smm_cfg:%u timeout:%u\n",
|
||||
__func__, ret, args[1], mhi_dev->smmu_cfg,
|
||||
mhi_cntrl->timeout_ms);
|
||||
return count;
|
||||
|
||||
error_read:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations debugfs_debug_ops = {
|
||||
.open = mhi_init_debugfs_debug_open,
|
||||
.release = single_release,
|
||||
.read = seq_read,
|
||||
.write = mhi_init_debugfs_debug_write,
|
||||
};
|
||||
|
||||
static struct mhi_controller * mhi_platform_probe(struct pci_dev *pci_dev)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl;
|
||||
struct mhi_dev *mhi_dev;
|
||||
u64 addr_win[2];
|
||||
int ret;
|
||||
|
||||
mhi_cntrl = mhi_alloc_controller(sizeof(*mhi_dev));
|
||||
if (!mhi_cntrl) {
|
||||
pr_err("mhi_alloc_controller fail\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
|
||||
mhi_cntrl->dev_id = pci_dev->device;
|
||||
mhi_cntrl->domain = pci_domain_nr(pci_dev->bus);
|
||||
mhi_cntrl->bus = pci_dev->bus->number;
|
||||
mhi_cntrl->slot = PCI_SLOT(pci_dev->devfn);
|
||||
mhi_dev->smmu_cfg = 0;
|
||||
#if 0 //def CONFIG_HAVE_MEMBLOCK
|
||||
addr_win[0] = memblock_start_of_DRAM();
|
||||
addr_win[1] = memblock_end_of_DRAM();
|
||||
#else
|
||||
#define MHI_MEM_BASE_DEFAULT 0x000000000
|
||||
#define MHI_MEM_SIZE_DEFAULT 0x2000000000
|
||||
addr_win[0] = MHI_MEM_BASE_DEFAULT;
|
||||
addr_win[1] = MHI_MEM_SIZE_DEFAULT;
|
||||
if (sizeof(dma_addr_t) == 4) {
|
||||
addr_win[1] = 0xFFFFFFFF;
|
||||
}
|
||||
#endif
|
||||
|
||||
mhi_cntrl->iova_start = addr_win[0];
|
||||
mhi_cntrl->iova_stop = addr_win[1];
|
||||
|
||||
mhi_dev->pci_dev = pci_dev;
|
||||
mhi_cntrl->pci_dev = pci_dev;
|
||||
|
||||
/* setup power management apis */
|
||||
mhi_cntrl->status_cb = mhi_status_cb;
|
||||
mhi_cntrl->runtime_get = mhi_runtime_get;
|
||||
mhi_cntrl->runtime_put = mhi_runtime_put;
|
||||
mhi_cntrl->link_status = mhi_link_status;
|
||||
|
||||
ret = mhi_arch_platform_init(mhi_dev);
|
||||
if (ret)
|
||||
goto error_probe;
|
||||
|
||||
ret = mhi_register_mhi_controller(mhi_cntrl);
|
||||
if (ret)
|
||||
goto error_register;
|
||||
|
||||
if (mhi_cntrl->parent)
|
||||
debugfs_create_file("debug_mode", 0444, mhi_cntrl->parent,
|
||||
mhi_cntrl, &debugfs_debug_ops);
|
||||
|
||||
return mhi_cntrl;
|
||||
|
||||
error_register:
|
||||
mhi_arch_platform_deinit(mhi_dev);
|
||||
|
||||
error_probe:
|
||||
mhi_free_controller(mhi_cntrl);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int mhi_pci_probe(struct pci_dev *pci_dev,
|
||||
const struct pci_device_id *device_id)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = NULL;
|
||||
u32 domain = pci_domain_nr(pci_dev->bus);
|
||||
u32 bus = pci_dev->bus->number;
|
||||
u32 slot = PCI_SLOT(pci_dev->devfn);
|
||||
struct mhi_dev *mhi_dev;
|
||||
int ret;
|
||||
|
||||
pr_info("%s pci_dev->name = %s, domain=%d, bus=%d, slot=%d, vendor=%04X, device=%04X\n",
|
||||
__func__, dev_name(&pci_dev->dev), domain, bus, slot, pci_dev->vendor, pci_dev->device);
|
||||
|
||||
mhi_cntrl = mhi_platform_probe(pci_dev);
|
||||
if (!mhi_cntrl) {
|
||||
pr_err("mhi_platform_probe fail\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
mhi_cntrl->dev_id = pci_dev->device;
|
||||
mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
mhi_dev->pci_dev = pci_dev;
|
||||
mhi_dev->powered_on = true;
|
||||
|
||||
ret = mhi_arch_pcie_init(mhi_cntrl);
|
||||
if (ret) {
|
||||
MHI_ERR("Error mhi_arch_pcie_init, ret:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mhi_arch_iommu_init(mhi_cntrl);
|
||||
if (ret) {
|
||||
MHI_ERR("Error mhi_arch_iommu_init, ret:%d\n", ret);
|
||||
goto error_iommu_init;
|
||||
}
|
||||
|
||||
ret = mhi_init_pci_dev(mhi_cntrl);
|
||||
if (ret) {
|
||||
MHI_ERR("Error mhi_init_pci_dev, ret:%d\n", ret);
|
||||
goto error_init_pci;
|
||||
}
|
||||
|
||||
/* start power up sequence if not in debug mode */
|
||||
if (!mhi_dev->debug_mode) {
|
||||
ret = mhi_async_power_up(mhi_cntrl);
|
||||
if (ret) {
|
||||
MHI_ERR("Error mhi_async_power_up, ret:%d\n", ret);
|
||||
goto error_power_up;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
pm_runtime_mark_last_busy(&pci_dev->dev);
|
||||
pm_runtime_allow(&pci_dev->dev);
|
||||
pm_runtime_disable(&pci_dev->dev);
|
||||
#endif
|
||||
|
||||
if (mhi_cntrl->dentry) {
|
||||
debugfs_create_file("m0", 0444, mhi_cntrl->dentry, mhi_cntrl,
|
||||
&debugfs_trigger_m0_fops);
|
||||
debugfs_create_file("m3", 0444, mhi_cntrl->dentry, mhi_cntrl,
|
||||
&debugfs_trigger_m3_fops);
|
||||
}
|
||||
|
||||
dev_set_drvdata(&pci_dev->dev, mhi_cntrl);
|
||||
MHI_LOG("Return successful\n");
|
||||
|
||||
return 0;
|
||||
|
||||
error_power_up:
|
||||
mhi_deinit_pci_dev(mhi_cntrl);
|
||||
|
||||
error_init_pci:
|
||||
mhi_arch_iommu_deinit(mhi_cntrl);
|
||||
|
||||
error_iommu_init:
|
||||
mhi_arch_pcie_deinit(mhi_cntrl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mhi_pci_remove(struct pci_dev *pci_dev)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = (struct mhi_controller *)dev_get_drvdata(&pci_dev->dev);
|
||||
|
||||
if (mhi_cntrl && mhi_cntrl->pci_dev == pci_dev) {
|
||||
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
MHI_LOG("%s\n", dev_name(&pci_dev->dev));
|
||||
if (!mhi_dev->debug_mode) {
|
||||
mhi_power_down(mhi_cntrl, 1);
|
||||
}
|
||||
mhi_deinit_pci_dev(mhi_cntrl);
|
||||
mhi_arch_iommu_deinit(mhi_cntrl);
|
||||
mhi_arch_pcie_deinit(mhi_cntrl);
|
||||
mhi_unregister_mhi_controller(mhi_cntrl);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(mhi_runtime_suspend,
|
||||
mhi_runtime_resume,
|
||||
mhi_runtime_idle)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mhi_system_suspend, mhi_system_resume)
|
||||
};
|
||||
|
||||
static struct pci_driver mhi_pcie_driver = {
|
||||
.name = "mhi",
|
||||
.id_table = mhi_pcie_device_id,
|
||||
.probe = mhi_pci_probe,
|
||||
.remove = mhi_pci_remove,
|
||||
.driver = {
|
||||
.pm = &pm_ops
|
||||
}
|
||||
};
|
||||
|
||||
int __init mhi_controller_qcom_init(void)
|
||||
{
|
||||
return pci_register_driver(&mhi_pcie_driver);
|
||||
};
|
||||
|
||||
void mhi_controller_qcom_exit(void)
|
||||
{
|
||||
pr_info("%s enter\n", __func__);
|
||||
pci_unregister_driver(&mhi_pcie_driver);
|
||||
pr_info("%s exit\n", __func__);
|
||||
}
|
||||
92
applications/quectel_MHI/src/controllers/mhi_qcom.h
Normal file
92
applications/quectel_MHI/src/controllers/mhi_qcom.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _MHI_QCOM_
|
||||
#define _MHI_QCOM_
|
||||
|
||||
/* iova cfg bitmask */
|
||||
#define MHI_SMMU_ATTACH BIT(0)
|
||||
#define MHI_SMMU_S1_BYPASS BIT(1)
|
||||
#define MHI_SMMU_FAST BIT(2)
|
||||
#define MHI_SMMU_ATOMIC BIT(3)
|
||||
#define MHI_SMMU_FORCE_COHERENT BIT(4)
|
||||
|
||||
#define MHI_PCIE_VENDOR_ID (0x17cb)
|
||||
#define MHI_PCIE_DEBUG_ID (0xffff)
|
||||
#define MHI_RPM_SUSPEND_TMR_MS (3000)
|
||||
#define MHI_PCI_BAR_NUM (0)
|
||||
|
||||
struct mhi_dev {
|
||||
struct pci_dev *pci_dev;
|
||||
u32 smmu_cfg;
|
||||
int resn;
|
||||
void *arch_info;
|
||||
bool powered_on;
|
||||
bool debug_mode;
|
||||
};
|
||||
|
||||
void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl);
|
||||
int mhi_pci_probe(struct pci_dev *pci_dev,
|
||||
const struct pci_device_id *device_id);
|
||||
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,65 ))
|
||||
static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask)
|
||||
{
|
||||
int rc = dma_set_mask(dev, mask);
|
||||
if (rc == 0)
|
||||
dma_set_coherent_mask(dev, mask);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
|
||||
mhi_cntrl->dev = &mhi_dev->pci_dev->dev;
|
||||
|
||||
return dma_set_mask_and_coherent(mhi_cntrl->dev, DMA_BIT_MASK(64));
|
||||
}
|
||||
|
||||
static inline void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mhi_arch_platform_init(struct mhi_dev *mhi_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mhi_arch_platform_deinit(struct mhi_dev *mhi_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mhi_arch_link_off(struct mhi_controller *mhi_cntrl,
|
||||
bool graceful)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mhi_arch_link_on(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _MHI_QCOM_ */
|
||||
1053
applications/quectel_MHI/src/controllers/mhi_qti.c
Normal file
1053
applications/quectel_MHI/src/controllers/mhi_qti.c
Normal file
File diff suppressed because it is too large
Load Diff
44
applications/quectel_MHI/src/controllers/mhi_qti.h
Normal file
44
applications/quectel_MHI/src/controllers/mhi_qti.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/
|
||||
|
||||
#ifndef _MHI_QTI_
|
||||
#define _MHI_QTI_
|
||||
|
||||
/* iova cfg bitmask */
|
||||
#define MHI_SMMU_ATTACH BIT(0)
|
||||
#define MHI_SMMU_S1_BYPASS BIT(1)
|
||||
#define MHI_SMMU_FAST BIT(2)
|
||||
#define MHI_SMMU_ATOMIC BIT(3)
|
||||
#define MHI_SMMU_FORCE_COHERENT BIT(4)
|
||||
|
||||
#define MHI_PCIE_VENDOR_ID (0x17cb)
|
||||
#define MHI_PCIE_DEBUG_ID (0xffff)
|
||||
|
||||
/* runtime suspend timer */
|
||||
#define MHI_RPM_SUSPEND_TMR_MS (2000)
|
||||
#define MHI_PCI_BAR_NUM (0)
|
||||
|
||||
struct mhi_dev {
|
||||
struct pci_dev *pci_dev;
|
||||
u32 smmu_cfg;
|
||||
int resn;
|
||||
void *arch_info;
|
||||
bool powered_on;
|
||||
dma_addr_t iova_start;
|
||||
dma_addr_t iova_stop;
|
||||
bool lpm_disabled;
|
||||
};
|
||||
|
||||
void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl);
|
||||
int mhi_pci_probe(struct pci_dev *pci_dev,
|
||||
const struct pci_device_id *device_id);
|
||||
|
||||
void mhi_pci_device_removed(struct pci_dev *pci_dev);
|
||||
int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl);
|
||||
void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl);
|
||||
int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl);
|
||||
void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl);
|
||||
int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, bool graceful);
|
||||
int mhi_arch_link_on(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
#endif /* _MHI_QTI_ */
|
||||
1
applications/quectel_MHI/src/core/Makefile
Normal file
1
applications/quectel_MHI/src/core/Makefile
Normal file
@@ -0,0 +1 @@
|
||||
obj-$(CONFIG_MHI_BUS) +=mhi_init.o mhi_main.o mhi_pm.o mhi_boot.o mhi_dtr.o
|
||||
890
applications/quectel_MHI/src/core/mhi.h
Normal file
890
applications/quectel_MHI/src/core/mhi.h
Normal file
@@ -0,0 +1,890 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */
|
||||
|
||||
#ifndef _MHI_H_
|
||||
#define _MHI_H_
|
||||
|
||||
#define PCIE_MHI_DRIVER_VERSION "V1.3.4"
|
||||
#define ENABLE_MHI_MON
|
||||
//#define ENABLE_IP_SW0
|
||||
|
||||
#include <linux/miscdevice.h>
|
||||
typedef enum
|
||||
{
|
||||
MHI_CLIENT_LOOPBACK_OUT = 0,
|
||||
MHI_CLIENT_LOOPBACK_IN = 1,
|
||||
MHI_CLIENT_SAHARA_OUT = 2,
|
||||
MHI_CLIENT_SAHARA_IN = 3,
|
||||
MHI_CLIENT_DIAG_OUT = 4,
|
||||
MHI_CLIENT_DIAG_IN = 5,
|
||||
MHI_CLIENT_SSR_OUT = 6,
|
||||
MHI_CLIENT_SSR_IN = 7,
|
||||
MHI_CLIENT_QDSS_OUT = 8,
|
||||
MHI_CLIENT_QDSS_IN = 9,
|
||||
MHI_CLIENT_EFS_OUT = 10,
|
||||
MHI_CLIENT_EFS_IN = 11,
|
||||
MHI_CLIENT_MBIM_OUT = 12,
|
||||
MHI_CLIENT_MBIM_IN = 13,
|
||||
MHI_CLIENT_QMI_OUT = 14,
|
||||
MHI_CLIENT_QMI_IN = 15,
|
||||
MHI_CLIENT_QMI_2_OUT = 16,
|
||||
MHI_CLIENT_QMI_2_IN = 17,
|
||||
MHI_CLIENT_IP_CTRL_1_OUT = 18,
|
||||
MHI_CLIENT_IP_CTRL_1_IN = 19,
|
||||
MHI_CLIENT_IPCR_OUT = 20,
|
||||
MHI_CLIENT_IPCR_IN = 21,
|
||||
MHI_CLIENT_TEST_FW_OUT = 22,
|
||||
MHI_CLIENT_TEST_FW_IN = 23,
|
||||
MHI_CLIENT_RESERVED_0 = 24,
|
||||
MHI_CLIENT_BOOT_LOG_IN = 25,
|
||||
MHI_CLIENT_DCI_OUT = 26,
|
||||
MHI_CLIENT_DCI_IN = 27,
|
||||
MHI_CLIENT_QBI_OUT = 28,
|
||||
MHI_CLIENT_QBI_IN = 29,
|
||||
MHI_CLIENT_RESERVED_1_LOWER = 30,
|
||||
MHI_CLIENT_RESERVED_1_UPPER = 31,
|
||||
MHI_CLIENT_DUN_OUT = 32,
|
||||
MHI_CLIENT_DUN_IN = 33,
|
||||
MHI_CLIENT_EDL_OUT = 34,
|
||||
MHI_CLIENT_EDL_IN = 35,
|
||||
MHI_CLIENT_ADB_FB_OUT = 36,
|
||||
MHI_CLIENT_ADB_FB_IN = 37,
|
||||
MHI_CLIENT_RESERVED_2_LOWER = 38,
|
||||
MHI_CLIENT_RESERVED_2_UPPER = 41,
|
||||
MHI_CLIENT_CSVT_OUT = 42,
|
||||
MHI_CLIENT_CSVT_IN = 43,
|
||||
MHI_CLIENT_SMCT_OUT = 44,
|
||||
MHI_CLIENT_SMCT_IN = 45,
|
||||
MHI_CLIENT_IP_SW_0_OUT = 46,
|
||||
MHI_CLIENT_IP_SW_0_IN = 47,
|
||||
MHI_CLIENT_IP_SW_1_OUT = 48,
|
||||
MHI_CLIENT_IP_SW_1_IN = 49,
|
||||
MHI_CLIENT_RESERVED_3_LOWER = 50,
|
||||
MHI_CLIENT_RESERVED_3_UPPER = 59,
|
||||
MHI_CLIENT_TEST_0_OUT = 60,
|
||||
MHI_CLIENT_TEST_0_IN = 61,
|
||||
MHI_CLIENT_TEST_1_OUT = 62,
|
||||
MHI_CLIENT_TEST_1_IN = 63,
|
||||
MHI_CLIENT_TEST_2_OUT = 64,
|
||||
MHI_CLIENT_TEST_2_IN = 65,
|
||||
MHI_CLIENT_TEST_3_OUT = 66,
|
||||
MHI_CLIENT_TEST_3_IN = 67,
|
||||
MHI_CLIENT_RESERVED_4_LOWER = 68,
|
||||
MHI_CLIENT_RESERVED_4_UPPER = 91,
|
||||
MHI_CLIENT_OEM_0_OUT = 92,
|
||||
MHI_CLIENT_OEM_0_IN = 93,
|
||||
MHI_CLIENT_OEM_1_OUT = 94,
|
||||
MHI_CLIENT_OEM_1_IN = 95,
|
||||
MHI_CLIENT_OEM_2_OUT = 96,
|
||||
MHI_CLIENT_OEM_2_IN = 97,
|
||||
MHI_CLIENT_OEM_3_OUT = 98,
|
||||
MHI_CLIENT_OEM_3_IN = 99,
|
||||
MHI_CLIENT_IP_HW_0_OUT = 100,
|
||||
MHI_CLIENT_IP_HW_0_IN = 101,
|
||||
MHI_CLIENT_ADPL = 102,
|
||||
MHI_CLIENT_RESERVED_5_LOWER = 103,
|
||||
MHI_CLIENT_RESERVED_5_UPPER = 127,
|
||||
MHI_MAX_CHANNELS = 128
|
||||
}MHI_CLIENT_CHANNEL_TYPE;
|
||||
|
||||
/* Event Ring Index */
|
||||
typedef enum
|
||||
{
|
||||
SW_EVT_RING = 0,
|
||||
PRIMARY_EVENT_RING = SW_EVT_RING,
|
||||
#ifdef ENABLE_IP_SW0
|
||||
SW_0_OUT_EVT_RING,
|
||||
SW_0_IN_EVT_RING,
|
||||
#endif
|
||||
IPA_OUT_EVENT_RING,
|
||||
IPA_IN_EVENT_RING,
|
||||
ADPL_EVT_RING,
|
||||
|
||||
MAX_EVT_RING_IDX
|
||||
}MHI_EVT_RING_IDX;
|
||||
|
||||
#define MHI_VERSION 0x01000000
|
||||
#define MHIREGLEN_VALUE 0x100 /* **** WRONG VALUE *** */
|
||||
#define MHI_MSI_INDEX 1
|
||||
#define MAX_NUM_MHI_DEVICES 1
|
||||
#define NUM_MHI_XFER_RINGS 128
|
||||
#define NUM_MHI_EVT_RINGS MAX_EVT_RING_IDX
|
||||
#define NUM_MHI_HW_EVT_RINGS 3
|
||||
#define NUM_MHI_XFER_RING_ELEMENTS 16
|
||||
#define NUM_MHI_EVT_RING_ELEMENTS (NUM_MHI_IPA_IN_RING_ELEMENTS*2) //must *2, event ring full will make x55 dump
|
||||
#define NUM_MHI_IPA_IN_RING_ELEMENTS 512
|
||||
#define NUM_MHI_IPA_OUT_RING_ELEMENTS 512 //donot use ul agg, so increase
|
||||
#define NUM_MHI_DIAG_IN_RING_ELEMENTS 128
|
||||
#define NUM_MHI_SW_IP_RING_ELEMENTS 512
|
||||
|
||||
/*
|
||||
* for if set Interrupt moderation time as 1ms,
|
||||
and transfer more than NUM_MHI_CHAN_RING_ELEMENTS data are sent to the modem in 1ms.
|
||||
e.g. firehose upgrade.
|
||||
modem will not trigger irq for these transfer.
|
||||
*/
|
||||
#define NUM_MHI_CHAN_RING_ELEMENTS 32 //8
|
||||
#define MHI_EVT_CMD_QUEUE_SIZE 160
|
||||
#define MHI_EVT_STATE_QUEUE_SIZE 128
|
||||
#define MHI_EVT_XFER_QUEUE_SIZE 1024
|
||||
|
||||
#define CHAN_INBOUND(_x) ((_x)%2)
|
||||
|
||||
#define CHAN_SBL(_x) (((_x) == MHI_CLIENT_SAHARA_OUT) || \
|
||||
((_x) == MHI_CLIENT_SAHARA_IN) || \
|
||||
((_x) == MHI_CLIENT_BOOT_LOG_IN))
|
||||
|
||||
#define CHAN_EDL(_x) (((_x) == MHI_CLIENT_EDL_OUT) || \
|
||||
((_x) == MHI_CLIENT_EDL_IN))
|
||||
|
||||
struct mhi_chan;
|
||||
struct mhi_event;
|
||||
struct mhi_ctxt;
|
||||
struct mhi_cmd;
|
||||
struct image_info;
|
||||
struct bhi_vec_entry;
|
||||
struct mhi_timesync;
|
||||
struct mhi_buf_info;
|
||||
|
||||
/**
|
||||
* enum MHI_CB - MHI callback
|
||||
* @MHI_CB_IDLE: MHI entered idle state
|
||||
* @MHI_CB_PENDING_DATA: New data available for client to process
|
||||
* @MHI_CB_LPM_ENTER: MHI host entered low power mode
|
||||
* @MHI_CB_LPM_EXIT: MHI host about to exit low power mode
|
||||
* @MHI_CB_EE_RDDM: MHI device entered RDDM execution enviornment
|
||||
* @MHI_CB_EE_MISSION_MODE: MHI device entered Mission Mode exec env
|
||||
* @MHI_CB_SYS_ERROR: MHI device enter error state (may recover)
|
||||
* @MHI_CB_FATAL_ERROR: MHI device entered fatal error
|
||||
*/
|
||||
enum MHI_CB {
|
||||
MHI_CB_IDLE,
|
||||
MHI_CB_PENDING_DATA,
|
||||
MHI_CB_LPM_ENTER,
|
||||
MHI_CB_LPM_EXIT,
|
||||
MHI_CB_EE_RDDM,
|
||||
MHI_CB_EE_MISSION_MODE,
|
||||
MHI_CB_SYS_ERROR,
|
||||
MHI_CB_FATAL_ERROR,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum MHI_DEBUG_LEVL - various debugging level
|
||||
*/
|
||||
enum MHI_DEBUG_LEVEL {
|
||||
MHI_MSG_LVL_VERBOSE,
|
||||
MHI_MSG_LVL_INFO,
|
||||
MHI_MSG_LVL_ERROR,
|
||||
MHI_MSG_LVL_CRITICAL,
|
||||
MHI_MSG_LVL_MASK_ALL,
|
||||
};
|
||||
|
||||
/*
|
||||
GSI_XFER_FLAG_BEI: Block event interrupt
|
||||
1: Event generated by this ring element must not assert an interrupt to the host
|
||||
0: Event generated by this ring element must assert an interrupt to the host
|
||||
|
||||
GSI_XFER_FLAG_EOT: Interrupt on end of transfer
|
||||
1: If an EOT condition is encountered when processing this ring element, an event is generated by the device with its completion code set to EOT.
|
||||
0: If an EOT condition is encountered for this ring element, a completion event is not be generated by the device, unless IEOB is 1
|
||||
|
||||
GSI_XFER_FLAG_EOB: Interrupt on end of block
|
||||
1: Device notifies host after processing this ring element by sending a completion event
|
||||
0: Completion event is not required after processing this ring element
|
||||
|
||||
GSI_XFER_FLAG_CHAIN: Chain bit that identifies the ring elements in a TD
|
||||
*/
|
||||
|
||||
/**
|
||||
* enum MHI_FLAGS - Transfer flags
|
||||
* @MHI_EOB: End of buffer for bulk transfer
|
||||
* @MHI_EOT: End of transfer
|
||||
* @MHI_CHAIN: Linked transfer
|
||||
*/
|
||||
enum MHI_FLAGS {
|
||||
MHI_EOB,
|
||||
MHI_EOT,
|
||||
MHI_CHAIN,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum mhi_device_type - Device types
|
||||
* @MHI_XFER_TYPE: Handles data transfer
|
||||
* @MHI_TIMESYNC_TYPE: Use for timesync feature
|
||||
* @MHI_CONTROLLER_TYPE: Control device
|
||||
*/
|
||||
enum mhi_device_type {
|
||||
MHI_XFER_TYPE,
|
||||
MHI_TIMESYNC_TYPE,
|
||||
MHI_CONTROLLER_TYPE,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum mhi_ee - device current execution enviornment
|
||||
* @MHI_EE_PBL - device in PBL
|
||||
* @MHI_EE_SBL - device in SBL
|
||||
* @MHI_EE_AMSS - device in mission mode (firmware fully loaded)
|
||||
* @MHI_EE_RDDM - device in ram dump collection mode
|
||||
* @MHI_EE_WFW - device in WLAN firmware mode
|
||||
* @MHI_EE_PTHRU - device in PBL but configured in pass thru mode
|
||||
* @MHI_EE_EDL - device in emergency download mode
|
||||
*/
|
||||
enum mhi_ee {
|
||||
MHI_EE_PBL = 0x0,
|
||||
MHI_EE_SBL = 0x1,
|
||||
MHI_EE_AMSS = 0x2,
|
||||
MHI_EE_RDDM = 0x3,
|
||||
MHI_EE_WFW = 0x4,
|
||||
MHI_EE_PTHRU = 0x5,
|
||||
MHI_EE_EDL = 0x6,
|
||||
MHI_EE_FP = 0x7, /* FlashProg, Flash Programmer Environment */
|
||||
MHI_EE_MAX_SUPPORTED = MHI_EE_FP,
|
||||
MHI_EE_DISABLE_TRANSITION, /* local EE, not related to mhi spec */
|
||||
MHI_EE_MAX,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum mhi_dev_state - device current MHI state
|
||||
*/
|
||||
enum mhi_dev_state {
|
||||
MHI_STATE_RESET = 0x0,
|
||||
MHI_STATE_READY = 0x1,
|
||||
MHI_STATE_M0 = 0x2,
|
||||
MHI_STATE_M1 = 0x3,
|
||||
MHI_STATE_M2 = 0x4,
|
||||
MHI_STATE_M3 = 0x5,
|
||||
MHI_STATE_BHI = 0x7,
|
||||
MHI_STATE_SYS_ERR = 0xFF,
|
||||
MHI_STATE_MAX,
|
||||
};
|
||||
|
||||
extern const char * const mhi_ee_str[MHI_EE_MAX];
|
||||
#define TO_MHI_EXEC_STR(ee) (((ee) >= MHI_EE_MAX) ? \
|
||||
"INVALID_EE" : mhi_ee_str[ee])
|
||||
|
||||
/**
|
||||
* struct image_info - firmware and rddm table table
|
||||
* @mhi_buf - Contain device firmware and rddm table
|
||||
* @entries - # of entries in table
|
||||
*/
|
||||
struct image_info {
|
||||
struct mhi_buf *mhi_buf;
|
||||
struct bhi_vec_entry *bhi_vec;
|
||||
u32 entries;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mhi_controller - Master controller structure for external modem
|
||||
* @dev: Device associated with this controller
|
||||
* @of_node: DT that has MHI configuration information
|
||||
* @regs: Points to base of MHI MMIO register space
|
||||
* @bhi: Points to base of MHI BHI register space
|
||||
* @bhie: Points to base of MHI BHIe register space
|
||||
* @wake_db: MHI WAKE doorbell register address
|
||||
* @dev_id: PCIe device id of the external device
|
||||
* @domain: PCIe domain the device connected to
|
||||
* @bus: PCIe bus the device assigned to
|
||||
* @slot: PCIe slot for the modem
|
||||
* @iova_start: IOMMU starting address for data
|
||||
* @iova_stop: IOMMU stop address for data
|
||||
* @fw_image: Firmware image name for normal booting
|
||||
* @edl_image: Firmware image name for emergency download mode
|
||||
* @fbc_download: MHI host needs to do complete image transfer
|
||||
* @rddm_size: RAM dump size that host should allocate for debugging purpose
|
||||
* @sbl_size: SBL image size
|
||||
* @seg_len: BHIe vector size
|
||||
* @fbc_image: Points to firmware image buffer
|
||||
* @rddm_image: Points to RAM dump buffer
|
||||
* @max_chan: Maximum number of channels controller support
|
||||
* @mhi_chan: Points to channel configuration table
|
||||
* @lpm_chans: List of channels that require LPM notifications
|
||||
* @total_ev_rings: Total # of event rings allocated
|
||||
* @hw_ev_rings: Number of hardware event rings
|
||||
* @sw_ev_rings: Number of software event rings
|
||||
* @msi_required: Number of msi required to operate
|
||||
* @msi_allocated: Number of msi allocated by bus master
|
||||
* @irq: base irq # to request
|
||||
* @mhi_event: MHI event ring configurations table
|
||||
* @mhi_cmd: MHI command ring configurations table
|
||||
* @mhi_ctxt: MHI device context, shared memory between host and device
|
||||
* @timeout_ms: Timeout in ms for state transitions
|
||||
* @pm_state: Power management state
|
||||
* @ee: MHI device execution environment
|
||||
* @dev_state: MHI STATE
|
||||
* @status_cb: CB function to notify various power states to but master
|
||||
* @link_status: Query link status in case of abnormal value read from device
|
||||
* @runtime_get: Async runtime resume function
|
||||
* @runtimet_put: Release votes
|
||||
* @time_get: Return host time in us
|
||||
* @lpm_disable: Request controller to disable link level low power modes
|
||||
* @lpm_enable: Controller may enable link level low power modes again
|
||||
* @priv_data: Points to bus master's private data
|
||||
*/
|
||||
struct mhi_controller {
|
||||
struct list_head node;
|
||||
struct mhi_device *mhi_dev;
|
||||
|
||||
/* device node for iommu ops */
|
||||
struct device *dev;
|
||||
struct device_node *of_node;
|
||||
|
||||
/* mmio base */
|
||||
phys_addr_t base_addr;
|
||||
void __iomem *regs;
|
||||
void __iomem *bhi;
|
||||
void __iomem *bhie;
|
||||
void __iomem *wake_db;
|
||||
|
||||
/* device topology */
|
||||
u32 vendor;
|
||||
u32 dev_id;
|
||||
u32 domain;
|
||||
u32 bus;
|
||||
u32 slot;
|
||||
u32 cntrl_idx;
|
||||
struct device *cntrl_dev;
|
||||
|
||||
/* addressing window */
|
||||
dma_addr_t iova_start;
|
||||
dma_addr_t iova_stop;
|
||||
|
||||
/* fw images */
|
||||
const char *fw_image;
|
||||
const char *edl_image;
|
||||
|
||||
/* mhi host manages downloading entire fbc images */
|
||||
bool fbc_download;
|
||||
size_t rddm_size;
|
||||
size_t sbl_size;
|
||||
size_t seg_len;
|
||||
u32 session_id;
|
||||
u32 sequence_id;
|
||||
struct image_info *fbc_image;
|
||||
struct image_info *rddm_image;
|
||||
|
||||
/* physical channel config data */
|
||||
u32 max_chan;
|
||||
struct mhi_chan *mhi_chan;
|
||||
struct list_head lpm_chans; /* these chan require lpm notification */
|
||||
|
||||
/* physical event config data */
|
||||
u32 total_ev_rings;
|
||||
u32 hw_ev_rings;
|
||||
u32 sw_ev_rings;
|
||||
u32 msi_required;
|
||||
u32 msi_allocated;
|
||||
u32 msi_irq_base;
|
||||
int *irq; /* interrupt table */
|
||||
struct mhi_event *mhi_event;
|
||||
|
||||
/* cmd rings */
|
||||
struct mhi_cmd *mhi_cmd;
|
||||
|
||||
/* mhi context (shared with device) */
|
||||
struct mhi_ctxt *mhi_ctxt;
|
||||
|
||||
u32 timeout_ms;
|
||||
|
||||
/* caller should grab pm_mutex for suspend/resume operations */
|
||||
struct mutex pm_mutex;
|
||||
bool pre_init;
|
||||
rwlock_t pm_lock;
|
||||
u32 pm_state;
|
||||
enum mhi_ee ee;
|
||||
enum mhi_dev_state dev_state;
|
||||
bool wake_set;
|
||||
atomic_t dev_wake;
|
||||
atomic_t alloc_size;
|
||||
atomic_t pending_pkts;
|
||||
struct list_head transition_list;
|
||||
spinlock_t transition_lock;
|
||||
spinlock_t wlock;
|
||||
|
||||
/* debug counters */
|
||||
u32 M0, M2, M3;
|
||||
|
||||
/* worker for different state transitions */
|
||||
struct work_struct st_worker;
|
||||
struct work_struct fw_worker;
|
||||
struct work_struct syserr_worker;
|
||||
struct delayed_work ready_worker;
|
||||
wait_queue_head_t state_event;
|
||||
|
||||
/* shadow functions */
|
||||
void (*status_cb)(struct mhi_controller *mhi_cntrl, void *priv,
|
||||
enum MHI_CB reason);
|
||||
int (*link_status)(struct mhi_controller *mhi_cntrl, void *priv);
|
||||
void (*wake_get)(struct mhi_controller *mhi_cntrl, bool override);
|
||||
void (*wake_put)(struct mhi_controller *mhi_cntrl, bool override);
|
||||
int (*runtime_get)(struct mhi_controller *mhi_cntrl, void *priv);
|
||||
void (*runtime_put)(struct mhi_controller *mhi_cntrl, void *priv);
|
||||
void (*runtime_mark_last_busy)(struct mhi_controller *mhi_cntrl, void *priv);
|
||||
u64 (*time_get)(struct mhi_controller *mhi_cntrl, void *priv);
|
||||
int (*lpm_disable)(struct mhi_controller *mhi_cntrl, void *priv);
|
||||
int (*lpm_enable)(struct mhi_controller *mhi_cntrl, void *priv);
|
||||
int (*map_single)(struct mhi_controller *mhi_cntrl,
|
||||
struct mhi_buf_info *buf);
|
||||
void (*unmap_single)(struct mhi_controller *mhi_cntrl,
|
||||
struct mhi_buf_info *buf);
|
||||
|
||||
/* channel to control DTR messaging */
|
||||
struct mhi_device *dtr_dev;
|
||||
|
||||
/* bounce buffer settings */
|
||||
bool bounce_buf;
|
||||
size_t buffer_len;
|
||||
|
||||
/* supports time sync feature */
|
||||
struct mhi_timesync *mhi_tsync;
|
||||
struct mhi_device *tsync_dev;
|
||||
|
||||
/* kernel log level */
|
||||
enum MHI_DEBUG_LEVEL klog_lvl;
|
||||
int klog_slient;
|
||||
|
||||
/* private log level controller driver to set */
|
||||
enum MHI_DEBUG_LEVEL log_lvl;
|
||||
|
||||
/* controller specific data */
|
||||
void *priv_data;
|
||||
void *log_buf;
|
||||
struct dentry *dentry;
|
||||
struct dentry *parent;
|
||||
|
||||
struct miscdevice miscdev;
|
||||
|
||||
#ifdef ENABLE_MHI_MON
|
||||
spinlock_t lock;
|
||||
|
||||
/* Ref */
|
||||
int nreaders; /* Under mon_lock AND mbus->lock */
|
||||
struct list_head r_list; /* Chain of readers (usually one) */
|
||||
struct kref ref; /* Under mon_lock */
|
||||
|
||||
/* Stats */
|
||||
unsigned int cnt_events;
|
||||
unsigned int cnt_text_lost;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef ENABLE_MHI_MON
|
||||
struct mhi_tre;
|
||||
struct mon_reader {
|
||||
struct list_head r_link;
|
||||
struct mhi_controller *m_bus;
|
||||
void *r_data; /* Use container_of instead? */
|
||||
|
||||
void (*rnf_submit)(void *data, u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre, void *buf, size_t len);
|
||||
void (*rnf_receive)(void *data, u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre, void *buf, size_t len);
|
||||
void (*rnf_complete)(void *data, u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre);
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct mhi_device - mhi device structure associated bind to channel
|
||||
* @dev: Device associated with the channels
|
||||
* @mtu: Maximum # of bytes controller support
|
||||
* @ul_chan_id: MHI channel id for UL transfer
|
||||
* @dl_chan_id: MHI channel id for DL transfer
|
||||
* @tiocm: Device current terminal settings
|
||||
* @priv: Driver private data
|
||||
*/
|
||||
struct mhi_device {
|
||||
struct device dev;
|
||||
u32 vendor;
|
||||
u32 dev_id;
|
||||
u32 domain;
|
||||
u32 bus;
|
||||
u32 slot;
|
||||
size_t mtu;
|
||||
int ul_chan_id;
|
||||
int dl_chan_id;
|
||||
int ul_event_id;
|
||||
int dl_event_id;
|
||||
u32 tiocm;
|
||||
const struct mhi_device_id *id;
|
||||
const char *chan_name;
|
||||
struct mhi_controller *mhi_cntrl;
|
||||
struct mhi_chan *ul_chan;
|
||||
struct mhi_chan *dl_chan;
|
||||
atomic_t dev_wake;
|
||||
enum mhi_device_type dev_type;
|
||||
void *priv_data;
|
||||
int (*ul_xfer)(struct mhi_device *mhi_dev, struct mhi_chan *mhi_chan,
|
||||
void *buf, size_t len, enum MHI_FLAGS flags);
|
||||
int (*dl_xfer)(struct mhi_device *mhi_dev, struct mhi_chan *mhi_chan,
|
||||
void *buf, size_t size, enum MHI_FLAGS flags);
|
||||
void (*status_cb)(struct mhi_device *mhi_dev, enum MHI_CB reason);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mhi_result - Completed buffer information
|
||||
* @buf_addr: Address of data buffer
|
||||
* @dir: Channel direction
|
||||
* @bytes_xfer: # of bytes transferred
|
||||
* @transaction_status: Status of last trasnferred
|
||||
*/
|
||||
struct mhi_result {
|
||||
void *buf_addr;
|
||||
enum dma_data_direction dir;
|
||||
size_t bytes_xferd;
|
||||
int transaction_status;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mhi_buf - Describes the buffer
|
||||
* @page: buffer as a page
|
||||
* @buf: cpu address for the buffer
|
||||
* @phys_addr: physical address of the buffer
|
||||
* @dma_addr: iommu address for the buffer
|
||||
* @skb: skb of ip packet
|
||||
* @len: # of bytes
|
||||
* @name: Buffer label, for offload channel configurations name must be:
|
||||
* ECA - Event context array data
|
||||
* CCA - Channel context array data
|
||||
*/
|
||||
struct mhi_buf {
|
||||
struct list_head node;
|
||||
struct page *page;
|
||||
void *buf;
|
||||
phys_addr_t phys_addr;
|
||||
dma_addr_t dma_addr;
|
||||
struct sk_buff *skb;
|
||||
size_t len;
|
||||
const char *name; /* ECA, CCA */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mhi_driver - mhi driver information
|
||||
* @id_table: NULL terminated channel ID names
|
||||
* @ul_xfer_cb: UL data transfer callback
|
||||
* @dl_xfer_cb: DL data transfer callback
|
||||
* @status_cb: Asynchronous status callback
|
||||
*/
|
||||
struct mhi_driver {
|
||||
const struct mhi_device_id *id_table;
|
||||
int (*probe)(struct mhi_device *mhi_dev,
|
||||
const struct mhi_device_id *id);
|
||||
void (*remove)(struct mhi_device *mhi_dev);
|
||||
void (*ul_xfer_cb)(struct mhi_device *mhi_dev, struct mhi_result *res);
|
||||
void (*dl_xfer_cb)(struct mhi_device *mhi_dev, struct mhi_result *res);
|
||||
void (*status_cb)(struct mhi_device *mhi_dev, enum MHI_CB mhi_cb);
|
||||
struct device_driver driver;
|
||||
};
|
||||
|
||||
#define to_mhi_driver(drv) container_of(drv, struct mhi_driver, driver)
|
||||
#define to_mhi_device(dev) container_of(dev, struct mhi_device, dev)
|
||||
|
||||
static inline void mhi_device_set_devdata(struct mhi_device *mhi_dev,
|
||||
void *priv)
|
||||
{
|
||||
mhi_dev->priv_data = priv;
|
||||
}
|
||||
|
||||
static inline void *mhi_device_get_devdata(struct mhi_device *mhi_dev)
|
||||
{
|
||||
return mhi_dev->priv_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* mhi_queue_transfer - Queue a buffer to hardware
|
||||
* All transfers are asyncronous transfers
|
||||
* @mhi_dev: Device associated with the channels
|
||||
* @dir: Data direction
|
||||
* @buf: Data buffer (skb for hardware channels)
|
||||
* @len: Size in bytes
|
||||
* @mflags: Interrupt flags for the device
|
||||
*/
|
||||
static inline int mhi_queue_transfer(struct mhi_device *mhi_dev,
|
||||
enum dma_data_direction dir,
|
||||
void *buf,
|
||||
size_t len,
|
||||
enum MHI_FLAGS mflags)
|
||||
{
|
||||
if (dir == DMA_TO_DEVICE)
|
||||
return mhi_dev->ul_xfer(mhi_dev, mhi_dev->ul_chan, buf, len,
|
||||
mflags);
|
||||
else
|
||||
return mhi_dev->dl_xfer(mhi_dev, mhi_dev->dl_chan, buf, len,
|
||||
mflags);
|
||||
}
|
||||
|
||||
static inline void *mhi_controller_get_devdata(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
return mhi_cntrl->priv_data;
|
||||
}
|
||||
|
||||
static inline void mhi_free_controller(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
kfree(mhi_cntrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* mhi_driver_register - Register driver with MHI framework
|
||||
* @mhi_drv: mhi_driver structure
|
||||
*/
|
||||
int mhi_driver_register(struct mhi_driver *mhi_drv);
|
||||
|
||||
/**
|
||||
* mhi_driver_unregister - Unregister a driver for mhi_devices
|
||||
* @mhi_drv: mhi_driver structure
|
||||
*/
|
||||
void mhi_driver_unregister(struct mhi_driver *mhi_drv);
|
||||
|
||||
/**
|
||||
* mhi_device_configure - configure ECA or CCA context
|
||||
* For offload channels that client manage, call this
|
||||
* function to configure channel context or event context
|
||||
* array associated with the channel
|
||||
* @mhi_div: Device associated with the channels
|
||||
* @dir: Direction of the channel
|
||||
* @mhi_buf: Configuration data
|
||||
* @elements: # of configuration elements
|
||||
*/
|
||||
int mhi_device_configure(struct mhi_device *mhi_div,
|
||||
enum dma_data_direction dir,
|
||||
struct mhi_buf *mhi_buf,
|
||||
int elements);
|
||||
|
||||
/**
|
||||
* mhi_device_get - disable all low power modes
|
||||
* Only disables lpm, does not immediately exit low power mode
|
||||
* if controller already in a low power mode
|
||||
* @mhi_dev: Device associated with the channels
|
||||
*/
|
||||
void mhi_device_get(struct mhi_device *mhi_dev);
|
||||
|
||||
/**
|
||||
* mhi_device_get_sync - disable all low power modes
|
||||
* Synchronously disable all low power, exit low power mode if
|
||||
* controller already in a low power state
|
||||
* @mhi_dev: Device associated with the channels
|
||||
*/
|
||||
int mhi_device_get_sync(struct mhi_device *mhi_dev);
|
||||
|
||||
/**
|
||||
* mhi_device_put - re-enable low power modes
|
||||
* @mhi_dev: Device associated with the channels
|
||||
*/
|
||||
void mhi_device_put(struct mhi_device *mhi_dev);
|
||||
|
||||
/**
|
||||
* mhi_prepare_for_transfer - setup channel for data transfer
|
||||
* Moves both UL and DL channel from RESET to START state
|
||||
* @mhi_dev: Device associated with the channels
|
||||
*/
|
||||
int mhi_prepare_for_transfer(struct mhi_device *mhi_dev);
|
||||
|
||||
/**
|
||||
* mhi_unprepare_from_transfer -unprepare the channels
|
||||
* Moves both UL and DL channels to RESET state
|
||||
* @mhi_dev: Device associated with the channels
|
||||
*/
|
||||
void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev);
|
||||
|
||||
/**
|
||||
* mhi_get_no_free_descriptors - Get transfer ring length
|
||||
* Get # of TD available to queue buffers
|
||||
* @mhi_dev: Device associated with the channels
|
||||
* @dir: Direction of the channel
|
||||
*/
|
||||
int mhi_get_no_free_descriptors(struct mhi_device *mhi_dev,
|
||||
enum dma_data_direction dir);
|
||||
|
||||
/**
|
||||
* mhi_poll - poll for any available data to consume
|
||||
* This is only applicable for DL direction
|
||||
* @mhi_dev: Device associated with the channels
|
||||
* @budget: In descriptors to service before returning
|
||||
*/
|
||||
int mhi_poll(struct mhi_device *mhi_dev, u32 budget);
|
||||
|
||||
/**
|
||||
* mhi_ioctl - user space IOCTL support for MHI channels
|
||||
* Native support for setting TIOCM
|
||||
* @mhi_dev: Device associated with the channels
|
||||
* @cmd: IOCTL cmd
|
||||
* @arg: Optional parameter, iotcl cmd specific
|
||||
*/
|
||||
long mhi_ioctl(struct mhi_device *mhi_dev, unsigned int cmd, unsigned long arg);
|
||||
|
||||
/**
|
||||
* mhi_alloc_controller - Allocate mhi_controller structure
|
||||
* Allocate controller structure and additional data for controller
|
||||
* private data. You may get the private data pointer by calling
|
||||
* mhi_controller_get_devdata
|
||||
* @size: # of additional bytes to allocate
|
||||
*/
|
||||
struct mhi_controller *mhi_alloc_controller(size_t size);
|
||||
|
||||
/**
|
||||
* of_register_mhi_controller - Register MHI controller
|
||||
* Registers MHI controller with MHI bus framework. DT must be supported
|
||||
* @mhi_cntrl: MHI controller to register
|
||||
*/
|
||||
int of_register_mhi_controller(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
void mhi_unregister_mhi_controller(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
/**
|
||||
* mhi_bdf_to_controller - Look up a registered controller
|
||||
* Search for controller based on device identification
|
||||
* @domain: RC domain of the device
|
||||
* @bus: Bus device connected to
|
||||
* @slot: Slot device assigned to
|
||||
* @dev_id: Device Identification
|
||||
*/
|
||||
struct mhi_controller *mhi_bdf_to_controller(u32 domain, u32 bus, u32 slot,
|
||||
u32 dev_id);
|
||||
|
||||
/**
|
||||
* mhi_prepare_for_power_up - Do pre-initialization before power up
|
||||
* This is optional, call this before power up if controller do not
|
||||
* want bus framework to automatically free any allocated memory during shutdown
|
||||
* process.
|
||||
* @mhi_cntrl: MHI controller
|
||||
*/
|
||||
int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
/**
|
||||
* mhi_async_power_up - Starts MHI power up sequence
|
||||
* @mhi_cntrl: MHI controller
|
||||
*/
|
||||
int mhi_async_power_up(struct mhi_controller *mhi_cntrl);
|
||||
int mhi_sync_power_up(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
/**
|
||||
* mhi_power_down - Start MHI power down sequence
|
||||
* @mhi_cntrl: MHI controller
|
||||
* @graceful: link is still accessible, do a graceful shutdown process otherwise
|
||||
* we will shutdown host w/o putting device into RESET state
|
||||
*/
|
||||
void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful);
|
||||
|
||||
/**
|
||||
* mhi_unprepare_after_powre_down - free any allocated memory for power up
|
||||
* @mhi_cntrl: MHI controller
|
||||
*/
|
||||
void mhi_unprepare_after_power_down(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
/**
|
||||
* mhi_pm_suspend - Move MHI into a suspended state
|
||||
* Transition to MHI state M3 state from M0||M1||M2 state
|
||||
* @mhi_cntrl: MHI controller
|
||||
*/
|
||||
int mhi_pm_suspend(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
/**
|
||||
* mhi_pm_resume - Resume MHI from suspended state
|
||||
* Transition to MHI state M0 state from M3 state
|
||||
* @mhi_cntrl: MHI controller
|
||||
*/
|
||||
int mhi_pm_resume(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
/**
|
||||
* mhi_download_rddm_img - Download ramdump image from device for
|
||||
* debugging purpose.
|
||||
* @mhi_cntrl: MHI controller
|
||||
* @in_panic: If we trying to capture image while in kernel panic
|
||||
*/
|
||||
int mhi_download_rddm_img(struct mhi_controller *mhi_cntrl, bool in_panic);
|
||||
|
||||
/**
|
||||
* mhi_force_rddm_mode - Force external device into rddm mode
|
||||
* to collect device ramdump. This is useful if host driver assert
|
||||
* and we need to see device state as well.
|
||||
* @mhi_cntrl: MHI controller
|
||||
*/
|
||||
int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
/**
|
||||
* mhi_get_remote_time_sync - Get external soc time relative to local soc time
|
||||
* using MMIO method.
|
||||
* @mhi_dev: Device associated with the channels
|
||||
* @t_host: Pointer to output local soc time
|
||||
* @t_dev: Pointer to output remote soc time
|
||||
*/
|
||||
int mhi_get_remote_time_sync(struct mhi_device *mhi_dev,
|
||||
u64 *t_host,
|
||||
u64 *t_dev);
|
||||
|
||||
/**
|
||||
* mhi_get_mhi_state - Return MHI state of device
|
||||
* @mhi_cntrl: MHI controller
|
||||
*/
|
||||
enum mhi_dev_state mhi_get_mhi_state(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
/**
|
||||
* mhi_set_mhi_state - Set device state
|
||||
* @mhi_cntrl: MHI controller
|
||||
* @state: state to set
|
||||
*/
|
||||
void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl,
|
||||
enum mhi_dev_state state);
|
||||
|
||||
|
||||
/**
|
||||
* mhi_is_active - helper function to determine if MHI in active state
|
||||
* @mhi_dev: client device
|
||||
*/
|
||||
static inline bool mhi_is_active(struct mhi_device *mhi_dev)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
|
||||
return (mhi_cntrl->dev_state >= MHI_STATE_M0 &&
|
||||
mhi_cntrl->dev_state <= MHI_STATE_M3);
|
||||
}
|
||||
|
||||
/**
|
||||
* mhi_debug_reg_dump - dump MHI registers for debug purpose
|
||||
* @mhi_cntrl: MHI controller
|
||||
*/
|
||||
void mhi_debug_reg_dump(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
#ifdef CONFIG_MHI_DEBUG
|
||||
|
||||
#define MHI_VERB(fmt, ...) do { \
|
||||
if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_VERBOSE) \
|
||||
pr_debug("[D][mhi%d][%s] " fmt, mhi_cntrl->cntrl_idx, __func__, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
||||
#define MHI_VERB(fmt, ...)
|
||||
|
||||
#endif
|
||||
|
||||
#define MHI_LOG(fmt, ...) do { \
|
||||
if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_INFO) \
|
||||
pr_info("[I][mhi%d][%s] " fmt, mhi_cntrl->cntrl_idx, __func__, ##__VA_ARGS__);\
|
||||
else if (!mhi_cntrl->klog_slient) \
|
||||
printk(KERN_DEBUG "[I][mhi%d][%s] " fmt, mhi_cntrl->cntrl_idx, __func__, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define MHI_ERR(fmt, ...) do { \
|
||||
if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_ERROR) \
|
||||
pr_err("[E][mhi%d][%s] " fmt, mhi_cntrl->cntrl_idx, __func__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define MHI_CRITICAL(fmt, ...) do { \
|
||||
if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_CRITICAL) \
|
||||
pr_alert("[C][mhi%d][%s] " fmt, mhi_cntrl->cntrl_idx, __func__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
int mhi_register_mhi_controller(struct mhi_controller *mhi_cntrl);
|
||||
void mhi_unregister_mhi_controller(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
#ifndef MHI_NAME_SIZE
|
||||
#define MHI_NAME_SIZE 32
|
||||
/**
|
||||
* * struct mhi_device_id - MHI device identification
|
||||
* * @chan: MHI channel name
|
||||
* * @driver_data: driver data;
|
||||
* */
|
||||
struct mhi_device_id {
|
||||
const char chan[MHI_NAME_SIZE];
|
||||
unsigned long driver_data;
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* _MHI_H_ */
|
||||
856
applications/quectel_MHI/src/core/mhi_boot.c
Normal file
856
applications/quectel_MHI/src/core/mhi_boot.c
Normal file
@@ -0,0 +1,856 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-direction.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include "mhi.h"
|
||||
#include "mhi_internal.h"
|
||||
|
||||
/* Software defines */
|
||||
/* BHI Version */
|
||||
#define BHI_MAJOR_VERSION 0x1
|
||||
#define BHI_MINOR_VERSION 0x1
|
||||
|
||||
#define MSMHWID_NUMDWORDS 6 /* Number of dwords that make the MSMHWID */
|
||||
#define OEMPKHASH_NUMDWORDS 48 /* Number of dwords that make the OEM PK HASH */
|
||||
|
||||
#define IsPBLExecEnv(ExecEnv) ((ExecEnv == MHI_EE_PBL) || (ExecEnv == MHI_EE_EDL) )
|
||||
|
||||
typedef u32 ULONG;
|
||||
|
||||
typedef struct _bhi_info_type
|
||||
{
|
||||
ULONG bhi_ver_minor;
|
||||
ULONG bhi_ver_major;
|
||||
ULONG bhi_image_address_low;
|
||||
ULONG bhi_image_address_high;
|
||||
ULONG bhi_image_size;
|
||||
ULONG bhi_rsvd1;
|
||||
ULONG bhi_imgtxdb;
|
||||
ULONG bhi_rsvd2;
|
||||
ULONG bhi_msivec;
|
||||
ULONG bhi_rsvd3;
|
||||
ULONG bhi_ee;
|
||||
ULONG bhi_status;
|
||||
ULONG bhi_errorcode;
|
||||
ULONG bhi_errdbg1;
|
||||
ULONG bhi_errdbg2;
|
||||
ULONG bhi_errdbg3;
|
||||
ULONG bhi_sernum;
|
||||
ULONG bhi_sblantirollbackver;
|
||||
ULONG bhi_numsegs;
|
||||
ULONG bhi_msmhwid[6];
|
||||
ULONG bhi_oempkhash[48];
|
||||
ULONG bhi_rsvd5;
|
||||
}BHI_INFO_TYPE, *PBHI_INFO_TYPE;
|
||||
|
||||
static void PrintBhiInfo(struct mhi_controller *mhi_cntrl, BHI_INFO_TYPE *bhi_info)
|
||||
{
|
||||
ULONG index;
|
||||
char str[128];
|
||||
|
||||
MHI_LOG("BHI Device Info...\n");
|
||||
MHI_LOG("BHI Version = { Major = 0x%X Minor = 0x%X}\n", bhi_info->bhi_ver_major, bhi_info->bhi_ver_minor);
|
||||
MHI_LOG("BHI Execution Environment = 0x%X\n", bhi_info->bhi_ee);
|
||||
MHI_LOG("BHI Status = 0x%X\n", bhi_info->bhi_status);
|
||||
MHI_LOG("BHI Error code = 0x%X { Dbg1 = 0x%X Dbg2 = 0x%X Dbg3 = 0x%X }\n", bhi_info->bhi_errorcode, bhi_info->bhi_errdbg1, bhi_info->bhi_errdbg2, bhi_info->bhi_errdbg3);
|
||||
MHI_LOG("BHI Serial Number = 0x%X\n", bhi_info->bhi_sernum);
|
||||
MHI_LOG("BHI SBL Anti-Rollback Ver = 0x%X\n", bhi_info->bhi_sblantirollbackver);
|
||||
MHI_LOG("BHI Number of Segments = 0x%X\n", bhi_info->bhi_numsegs);
|
||||
for (index = 0; index < 6; index++)
|
||||
{
|
||||
snprintf(str+3*index, sizeof(str)-3*index, "%02x ", bhi_info->bhi_msmhwid[index]);
|
||||
}
|
||||
MHI_LOG("BHI MSM HW-Id = %s\n", str);
|
||||
|
||||
for (index = 0; index < 24; index++)
|
||||
{
|
||||
snprintf(str+3*index, sizeof(str)-3*index, "%02x ", bhi_info->bhi_oempkhash[index]);
|
||||
}
|
||||
MHI_LOG("BHI OEM PK Hash = %s\n", str);
|
||||
}
|
||||
|
||||
static u32 bhi_read_reg(struct mhi_controller *mhi_cntrl, u32 offset)
|
||||
{
|
||||
u32 out = 0;
|
||||
int ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_EXECENV, &out);
|
||||
|
||||
return (ret) ? 0 : out;
|
||||
}
|
||||
|
||||
static int BhiRead(struct mhi_controller *mhi_cntrl, BHI_INFO_TYPE *bhi_info)
|
||||
{
|
||||
ULONG index;
|
||||
|
||||
memset(bhi_info, 0x00, sizeof(BHI_INFO_TYPE));
|
||||
|
||||
/* bhi_ver */
|
||||
bhi_info->bhi_ver_minor = bhi_read_reg(mhi_cntrl, BHI_BHIVERSION_MINOR);
|
||||
bhi_info->bhi_ver_major = bhi_read_reg(mhi_cntrl, BHI_BHIVERSION_MINOR);
|
||||
bhi_info->bhi_image_address_low = bhi_read_reg(mhi_cntrl, BHI_IMGADDR_LOW);
|
||||
bhi_info->bhi_image_address_high = bhi_read_reg(mhi_cntrl, BHI_IMGADDR_HIGH);
|
||||
bhi_info->bhi_image_size = bhi_read_reg(mhi_cntrl, BHI_IMGSIZE);
|
||||
bhi_info->bhi_rsvd1 = bhi_read_reg(mhi_cntrl, BHI_RSVD1);
|
||||
bhi_info->bhi_imgtxdb = bhi_read_reg(mhi_cntrl, BHI_IMGTXDB);
|
||||
bhi_info->bhi_rsvd2 = bhi_read_reg(mhi_cntrl, BHI_RSVD2);
|
||||
bhi_info->bhi_msivec = bhi_read_reg(mhi_cntrl, BHI_INTVEC);
|
||||
bhi_info->bhi_rsvd3 = bhi_read_reg(mhi_cntrl, BHI_RSVD3);
|
||||
bhi_info->bhi_ee = bhi_read_reg(mhi_cntrl, BHI_EXECENV);
|
||||
bhi_info->bhi_status = bhi_read_reg(mhi_cntrl, BHI_STATUS);
|
||||
bhi_info->bhi_errorcode = bhi_read_reg(mhi_cntrl, BHI_ERRCODE);
|
||||
bhi_info->bhi_errdbg1 = bhi_read_reg(mhi_cntrl, BHI_ERRDBG1);
|
||||
bhi_info->bhi_errdbg2 = bhi_read_reg(mhi_cntrl, BHI_ERRDBG2);
|
||||
bhi_info->bhi_errdbg3 = bhi_read_reg(mhi_cntrl, BHI_ERRDBG3);
|
||||
bhi_info->bhi_sernum = bhi_read_reg(mhi_cntrl, BHI_SERIALNU);
|
||||
bhi_info->bhi_sblantirollbackver = bhi_read_reg(mhi_cntrl, BHI_SBLANTIROLLVER);
|
||||
bhi_info->bhi_numsegs = bhi_read_reg(mhi_cntrl, BHI_NUMSEG);
|
||||
for (index = 0; index < MSMHWID_NUMDWORDS; index++)
|
||||
{
|
||||
bhi_info->bhi_msmhwid[index] = bhi_read_reg(mhi_cntrl, BHI_MSMHWID(index));
|
||||
}
|
||||
for (index = 0; index < OEMPKHASH_NUMDWORDS; index++)
|
||||
{
|
||||
bhi_info->bhi_oempkhash[index] = bhi_read_reg(mhi_cntrl, BHI_OEMPKHASH(index));
|
||||
}
|
||||
bhi_info->bhi_rsvd5 = bhi_read_reg(mhi_cntrl, BHI_RSVD5);
|
||||
PrintBhiInfo(mhi_cntrl, bhi_info);
|
||||
/* Check the Execution Environment */
|
||||
if (!IsPBLExecEnv(bhi_info->bhi_ee))
|
||||
{
|
||||
MHI_LOG("E - EE: 0x%X Expected PBL/EDL\n", bhi_info->bhi_ee);
|
||||
}
|
||||
|
||||
/* Return the number of bytes read */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* setup rddm vector table for rddm transfer */
|
||||
static void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
|
||||
struct image_info *img_info)
|
||||
{
|
||||
struct mhi_buf *mhi_buf = img_info->mhi_buf;
|
||||
struct bhi_vec_entry *bhi_vec = img_info->bhi_vec;
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < img_info->entries - 1; i++, mhi_buf++, bhi_vec++) {
|
||||
MHI_VERB("Setting vector:%pad size:%zu\n",
|
||||
&mhi_buf->dma_addr, mhi_buf->len);
|
||||
bhi_vec->dma_addr = mhi_buf->dma_addr;
|
||||
bhi_vec->size = mhi_buf->len;
|
||||
}
|
||||
}
|
||||
|
||||
/* collect rddm during kernel panic */
|
||||
static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
int ret;
|
||||
struct mhi_buf *mhi_buf;
|
||||
u32 sequence_id;
|
||||
u32 rx_status;
|
||||
enum mhi_ee ee;
|
||||
struct image_info *rddm_image = mhi_cntrl->rddm_image;
|
||||
const u32 delayus = 2000;
|
||||
u32 retry = (mhi_cntrl->timeout_ms * 1000) / delayus;
|
||||
const u32 rddm_timeout_us = 200000;
|
||||
int rddm_retry = rddm_timeout_us / delayus; /* time to enter rddm */
|
||||
void __iomem *base = mhi_cntrl->bhie;
|
||||
|
||||
MHI_LOG("Entered with pm_state:%s dev_state:%s ee:%s\n",
|
||||
to_mhi_pm_state_str(mhi_cntrl->pm_state),
|
||||
TO_MHI_STATE_STR(mhi_cntrl->dev_state),
|
||||
TO_MHI_EXEC_STR(mhi_cntrl->ee));
|
||||
|
||||
/*
|
||||
* This should only be executing during a kernel panic, we expect all
|
||||
* other cores to shutdown while we're collecting rddm buffer. After
|
||||
* returning from this function, we expect device to reset.
|
||||
*
|
||||
* Normaly, we would read/write pm_state only after grabbing
|
||||
* pm_lock, since we're in a panic, skipping it. Also there is no
|
||||
* gurantee this state change would take effect since
|
||||
* we're setting it w/o grabbing pmlock, it's best effort
|
||||
*/
|
||||
mhi_cntrl->pm_state = MHI_PM_LD_ERR_FATAL_DETECT;
|
||||
/* update should take the effect immediately */
|
||||
smp_wmb();
|
||||
|
||||
/* setup the RX vector table */
|
||||
mhi_rddm_prepare(mhi_cntrl, rddm_image);
|
||||
mhi_buf = &rddm_image->mhi_buf[rddm_image->entries - 1];
|
||||
|
||||
MHI_LOG("Starting BHIe programming for RDDM\n");
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_HIGH_OFFS,
|
||||
upper_32_bits(mhi_buf->dma_addr));
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_LOW_OFFS,
|
||||
lower_32_bits(mhi_buf->dma_addr));
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECSIZE_OFFS, mhi_buf->len);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||
sequence_id = get_random_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK;
|
||||
#else
|
||||
sequence_id = prandom_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK;
|
||||
#endif
|
||||
if (unlikely(!sequence_id))
|
||||
sequence_id = 1;
|
||||
|
||||
|
||||
mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS,
|
||||
BHIE_RXVECDB_SEQNUM_BMSK, BHIE_RXVECDB_SEQNUM_SHFT,
|
||||
sequence_id);
|
||||
|
||||
MHI_LOG("Trigger device into RDDM mode\n");
|
||||
mhi_set_mhi_state(mhi_cntrl, MHI_STATE_SYS_ERR);
|
||||
|
||||
MHI_LOG("Waiting for device to enter RDDM\n");
|
||||
while (rddm_retry--) {
|
||||
ee = mhi_get_exec_env(mhi_cntrl);
|
||||
if (ee == MHI_EE_RDDM)
|
||||
break;
|
||||
|
||||
udelay(delayus);
|
||||
}
|
||||
|
||||
if (rddm_retry <= 0) {
|
||||
/* This is a hardware reset, will force device to enter rddm */
|
||||
MHI_LOG(
|
||||
"Did not enter RDDM triggering host req. reset to force rddm\n");
|
||||
mhi_write_reg(mhi_cntrl, mhi_cntrl->regs,
|
||||
MHI_SOC_RESET_REQ_OFFSET, MHI_SOC_RESET_REQ);
|
||||
udelay(delayus);
|
||||
}
|
||||
|
||||
ee = mhi_get_exec_env(mhi_cntrl);
|
||||
MHI_LOG("Waiting for image download completion, current EE:%s\n",
|
||||
TO_MHI_EXEC_STR(ee));
|
||||
while (retry--) {
|
||||
ret = mhi_read_reg_field(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS,
|
||||
BHIE_RXVECSTATUS_STATUS_BMSK,
|
||||
BHIE_RXVECSTATUS_STATUS_SHFT,
|
||||
&rx_status);
|
||||
if (ret)
|
||||
return -EIO;
|
||||
|
||||
if (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL) {
|
||||
MHI_LOG("RDDM successfully collected\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
udelay(delayus);
|
||||
}
|
||||
|
||||
ee = mhi_get_exec_env(mhi_cntrl);
|
||||
ret = mhi_read_reg(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS, &rx_status);
|
||||
|
||||
MHI_ERR("Did not complete RDDM transfer\n");
|
||||
MHI_ERR("Current EE:%s\n", TO_MHI_EXEC_STR(ee));
|
||||
MHI_ERR("RXVEC_STATUS:0x%x, ret:%d\n", rx_status, ret);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* download ramdump image from device */
|
||||
int mhi_download_rddm_img(struct mhi_controller *mhi_cntrl, bool in_panic)
|
||||
{
|
||||
void __iomem *base = mhi_cntrl->bhie;
|
||||
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
|
||||
struct image_info *rddm_image = mhi_cntrl->rddm_image;
|
||||
struct mhi_buf *mhi_buf;
|
||||
int ret;
|
||||
u32 rx_status;
|
||||
u32 sequence_id;
|
||||
|
||||
if (!rddm_image)
|
||||
return -ENOMEM;
|
||||
|
||||
if (in_panic)
|
||||
return __mhi_download_rddm_in_panic(mhi_cntrl);
|
||||
|
||||
MHI_LOG("Waiting for device to enter RDDM state from EE:%s\n",
|
||||
TO_MHI_EXEC_STR(mhi_cntrl->ee));
|
||||
|
||||
ret = wait_event_timeout(mhi_cntrl->state_event,
|
||||
mhi_cntrl->ee == MHI_EE_RDDM ||
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
|
||||
if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
|
||||
MHI_ERR("MHI is not in valid state, pm_state:%s ee:%s\n",
|
||||
to_mhi_pm_state_str(mhi_cntrl->pm_state),
|
||||
TO_MHI_EXEC_STR(mhi_cntrl->ee));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
mhi_rddm_prepare(mhi_cntrl, mhi_cntrl->rddm_image);
|
||||
|
||||
/* vector table is the last entry */
|
||||
mhi_buf = &rddm_image->mhi_buf[rddm_image->entries - 1];
|
||||
|
||||
read_lock_bh(pm_lock);
|
||||
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
|
||||
read_unlock_bh(pm_lock);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
MHI_LOG("Starting BHIe Programming for RDDM\n");
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_HIGH_OFFS,
|
||||
upper_32_bits(mhi_buf->dma_addr));
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_LOW_OFFS,
|
||||
lower_32_bits(mhi_buf->dma_addr));
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECSIZE_OFFS, mhi_buf->len);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||
sequence_id = get_random_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK;
|
||||
#else
|
||||
sequence_id = prandom_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK;
|
||||
#endif
|
||||
mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS,
|
||||
BHIE_RXVECDB_SEQNUM_BMSK, BHIE_RXVECDB_SEQNUM_SHFT,
|
||||
sequence_id);
|
||||
read_unlock_bh(pm_lock);
|
||||
|
||||
MHI_LOG("Upper:0x%x Lower:0x%x len:0x%zx sequence:%u\n",
|
||||
upper_32_bits(mhi_buf->dma_addr),
|
||||
lower_32_bits(mhi_buf->dma_addr),
|
||||
mhi_buf->len, sequence_id);
|
||||
MHI_LOG("Waiting for image download completion\n");
|
||||
|
||||
/* waiting for image download completion */
|
||||
wait_event_timeout(mhi_cntrl->state_event,
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
|
||||
mhi_read_reg_field(mhi_cntrl, base,
|
||||
BHIE_RXVECSTATUS_OFFS,
|
||||
BHIE_RXVECSTATUS_STATUS_BMSK,
|
||||
BHIE_RXVECSTATUS_STATUS_SHFT,
|
||||
&rx_status) || rx_status,
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
|
||||
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
|
||||
return -EIO;
|
||||
|
||||
return (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL) ? 0 : -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL(mhi_download_rddm_img);
|
||||
|
||||
static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
|
||||
const struct mhi_buf *mhi_buf)
|
||||
{
|
||||
void __iomem *base = mhi_cntrl->bhie;
|
||||
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
|
||||
u32 tx_status;
|
||||
|
||||
read_lock_bh(pm_lock);
|
||||
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
|
||||
read_unlock_bh(pm_lock);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
MHI_LOG("Starting BHIe Programming\n");
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_HIGH_OFFS,
|
||||
upper_32_bits(mhi_buf->dma_addr));
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_LOW_OFFS,
|
||||
lower_32_bits(mhi_buf->dma_addr));
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECSIZE_OFFS, mhi_buf->len);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||
mhi_cntrl->sequence_id = get_random_u32() & BHIE_TXVECSTATUS_SEQNUM_BMSK;
|
||||
#else
|
||||
mhi_cntrl->sequence_id = prandom_u32() & BHIE_TXVECSTATUS_SEQNUM_BMSK;
|
||||
#endif
|
||||
|
||||
mhi_write_reg_field(mhi_cntrl, base, BHIE_TXVECDB_OFFS,
|
||||
BHIE_TXVECDB_SEQNUM_BMSK, BHIE_TXVECDB_SEQNUM_SHFT,
|
||||
mhi_cntrl->sequence_id);
|
||||
read_unlock_bh(pm_lock);
|
||||
|
||||
MHI_LOG("Upper:0x%x Lower:0x%x len:0x%zx sequence:%u\n",
|
||||
upper_32_bits(mhi_buf->dma_addr),
|
||||
lower_32_bits(mhi_buf->dma_addr),
|
||||
mhi_buf->len, mhi_cntrl->sequence_id);
|
||||
MHI_LOG("Waiting for image transfer completion\n");
|
||||
|
||||
/* waiting for image download completion */
|
||||
wait_event_timeout(mhi_cntrl->state_event,
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
|
||||
mhi_read_reg_field(mhi_cntrl, base,
|
||||
BHIE_TXVECSTATUS_OFFS,
|
||||
BHIE_TXVECSTATUS_STATUS_BMSK,
|
||||
BHIE_TXVECSTATUS_STATUS_SHFT,
|
||||
&tx_status) || tx_status,
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
|
||||
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
|
||||
return -EIO;
|
||||
|
||||
return (tx_status == BHIE_TXVECSTATUS_STATUS_XFER_COMPL) ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static int mhi_fw_load_sbl(struct mhi_controller *mhi_cntrl,
|
||||
dma_addr_t dma_addr,
|
||||
size_t size)
|
||||
{
|
||||
u32 tx_status, val;
|
||||
u32 ImgTxDb = 0x1;
|
||||
int i, ret;
|
||||
void __iomem *base = mhi_cntrl->bhi;
|
||||
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
|
||||
struct {
|
||||
char *name;
|
||||
u32 offset;
|
||||
} error_reg[] = {
|
||||
{ "ERROR_CODE", BHI_ERRCODE },
|
||||
{ "ERROR_DBG1", BHI_ERRDBG1 },
|
||||
{ "ERROR_DBG2", BHI_ERRDBG2 },
|
||||
{ "ERROR_DBG3", BHI_ERRDBG3 },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
MHI_LOG("Starting BHI programming\n");
|
||||
|
||||
/* program start sbl download via bhi protocol */
|
||||
read_lock_bh(pm_lock);
|
||||
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
|
||||
read_unlock_bh(pm_lock);
|
||||
goto invalid_pm_state;
|
||||
}
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHI_STATUS, 0);
|
||||
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH,
|
||||
upper_32_bits(dma_addr));
|
||||
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_LOW,
|
||||
lower_32_bits(dma_addr));
|
||||
mhi_write_reg(mhi_cntrl, base, BHI_IMGSIZE, size);
|
||||
mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICFG, MHICFG_NER_MASK, MHICFG_NER_SHIFT, NUM_MHI_EVT_RINGS);
|
||||
mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICFG, MHICFG_NHWER_MASK, MHICFG_NHWER_SHIFT, NUM_MHI_HW_EVT_RINGS);
|
||||
mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, mhi_cntrl->msi_irq_base);
|
||||
mhi_write_reg(mhi_cntrl, base, BHI_IMGTXDB, ImgTxDb);
|
||||
read_unlock_bh(pm_lock);
|
||||
|
||||
MHI_LOG("Waiting for image transfer completion\n");
|
||||
|
||||
/* waiting for image download completion */
|
||||
ret = wait_event_timeout(mhi_cntrl->state_event,
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
|
||||
mhi_read_reg_field(mhi_cntrl, base, BHI_STATUS,
|
||||
BHI_STATUS_MASK, BHI_STATUS_SHIFT,
|
||||
&tx_status) || tx_status,
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
|
||||
goto invalid_pm_state;
|
||||
|
||||
if (tx_status == BHI_STATUS_ERROR) {
|
||||
MHI_ERR("Image transfer failed\n");
|
||||
read_lock_bh(pm_lock);
|
||||
if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
|
||||
for (i = 0; error_reg[i].name; i++) {
|
||||
ret = mhi_read_reg(mhi_cntrl, base,
|
||||
error_reg[i].offset, &val);
|
||||
if (ret)
|
||||
break;
|
||||
MHI_ERR("reg:%s value:0x%x\n",
|
||||
error_reg[i].name, val);
|
||||
}
|
||||
}
|
||||
read_unlock_bh(pm_lock);
|
||||
goto invalid_pm_state;
|
||||
}
|
||||
|
||||
return (tx_status == BHI_STATUS_SUCCESS) ? 0 : -ETIMEDOUT;
|
||||
|
||||
invalid_pm_state:
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
|
||||
struct image_info *image_info)
|
||||
{
|
||||
int i;
|
||||
struct mhi_buf *mhi_buf = image_info->mhi_buf;
|
||||
|
||||
for (i = 0; i < image_info->entries; i++, mhi_buf++)
|
||||
mhi_free_coherent(mhi_cntrl, mhi_buf->len, mhi_buf->buf,
|
||||
mhi_buf->dma_addr);
|
||||
|
||||
kfree(image_info->mhi_buf);
|
||||
kfree(image_info);
|
||||
}
|
||||
|
||||
int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl,
|
||||
struct image_info **image_info,
|
||||
size_t alloc_size)
|
||||
{
|
||||
size_t seg_size = mhi_cntrl->seg_len;
|
||||
/* requier additional entry for vec table */
|
||||
int segments = DIV_ROUND_UP(alloc_size, seg_size) + 1;
|
||||
int i;
|
||||
struct image_info *img_info;
|
||||
struct mhi_buf *mhi_buf;
|
||||
|
||||
MHI_LOG("Allocating bytes:%zu seg_size:%zu total_seg:%u\n",
|
||||
alloc_size, seg_size, segments);
|
||||
|
||||
img_info = kzalloc(sizeof(*img_info), GFP_KERNEL);
|
||||
if (!img_info)
|
||||
return -ENOMEM;
|
||||
|
||||
/* allocate memory for entries */
|
||||
img_info->mhi_buf = kcalloc(segments, sizeof(*img_info->mhi_buf),
|
||||
GFP_KERNEL);
|
||||
if (!img_info->mhi_buf)
|
||||
goto error_alloc_mhi_buf;
|
||||
|
||||
/* allocate and populate vector table */
|
||||
mhi_buf = img_info->mhi_buf;
|
||||
for (i = 0; i < segments; i++, mhi_buf++) {
|
||||
size_t vec_size = seg_size;
|
||||
|
||||
/* last entry is for vector table */
|
||||
if (i == segments - 1)
|
||||
vec_size = sizeof(struct bhi_vec_entry) * i;
|
||||
|
||||
mhi_buf->len = vec_size;
|
||||
mhi_buf->buf = mhi_alloc_coherent(mhi_cntrl, vec_size,
|
||||
&mhi_buf->dma_addr, GFP_KERNEL);
|
||||
if (!mhi_buf->buf)
|
||||
goto error_alloc_segment;
|
||||
|
||||
MHI_LOG("Entry:%d Address:0x%llx size:%zu\n", i,
|
||||
(unsigned long long)mhi_buf->dma_addr,
|
||||
mhi_buf->len);
|
||||
}
|
||||
|
||||
img_info->bhi_vec = img_info->mhi_buf[segments - 1].buf;
|
||||
img_info->entries = segments;
|
||||
*image_info = img_info;
|
||||
|
||||
MHI_LOG("Successfully allocated bhi vec table\n");
|
||||
|
||||
return 0;
|
||||
|
||||
error_alloc_segment:
|
||||
for (--i, --mhi_buf; i >= 0; i--, mhi_buf--)
|
||||
mhi_free_coherent(mhi_cntrl, mhi_buf->len, mhi_buf->buf,
|
||||
mhi_buf->dma_addr);
|
||||
|
||||
error_alloc_mhi_buf:
|
||||
kfree(img_info);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void mhi_firmware_copy(struct mhi_controller *mhi_cntrl,
|
||||
const struct firmware *firmware,
|
||||
struct image_info *img_info)
|
||||
{
|
||||
size_t remainder = firmware->size;
|
||||
size_t to_cpy;
|
||||
const u8 *buf = firmware->data;
|
||||
int i = 0;
|
||||
struct mhi_buf *mhi_buf = img_info->mhi_buf;
|
||||
struct bhi_vec_entry *bhi_vec = img_info->bhi_vec;
|
||||
|
||||
while (remainder) {
|
||||
MHI_ASSERT(i >= img_info->entries, "malformed vector table");
|
||||
|
||||
to_cpy = min(remainder, mhi_buf->len);
|
||||
memcpy(mhi_buf->buf, buf, to_cpy);
|
||||
bhi_vec->dma_addr = mhi_buf->dma_addr;
|
||||
bhi_vec->size = to_cpy;
|
||||
|
||||
MHI_VERB("Setting Vector:0x%llx size: %llu\n",
|
||||
bhi_vec->dma_addr, bhi_vec->size);
|
||||
buf += to_cpy;
|
||||
remainder -= to_cpy;
|
||||
i++;
|
||||
bhi_vec++;
|
||||
mhi_buf++;
|
||||
}
|
||||
}
|
||||
|
||||
void mhi_fw_load_worker(struct work_struct *work)
|
||||
{
|
||||
int ret;
|
||||
struct mhi_controller *mhi_cntrl;
|
||||
const char *fw_name;
|
||||
const struct firmware *firmware;
|
||||
struct image_info *image_info;
|
||||
void *buf;
|
||||
dma_addr_t dma_addr;
|
||||
size_t size;
|
||||
|
||||
mhi_cntrl = container_of(work, struct mhi_controller, fw_worker);
|
||||
|
||||
MHI_LOG("Waiting for device to enter PBL from EE:%s\n",
|
||||
TO_MHI_EXEC_STR(mhi_cntrl->ee));
|
||||
|
||||
ret = wait_event_timeout(mhi_cntrl->state_event,
|
||||
MHI_IN_PBL(mhi_cntrl->ee) ||
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
|
||||
if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
|
||||
MHI_ERR("MHI is not in valid state\n");
|
||||
return;
|
||||
}
|
||||
|
||||
MHI_LOG("Device current EE:%s\n", TO_MHI_EXEC_STR(mhi_cntrl->ee));
|
||||
|
||||
/* if device in pthru, we do not have to load firmware */
|
||||
if (mhi_cntrl->ee == MHI_EE_PTHRU)
|
||||
return;
|
||||
|
||||
fw_name = (mhi_cntrl->ee == MHI_EE_EDL) ?
|
||||
mhi_cntrl->edl_image : mhi_cntrl->fw_image;
|
||||
|
||||
if (!fw_name || (mhi_cntrl->fbc_download && (!mhi_cntrl->sbl_size ||
|
||||
!mhi_cntrl->seg_len))) {
|
||||
MHI_ERR("No firmware image defined or !sbl_size || !seg_len\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = request_firmware(&firmware, fw_name, mhi_cntrl->dev);
|
||||
if (ret) {
|
||||
MHI_ERR("Error loading firmware, ret:%d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
size = (mhi_cntrl->fbc_download) ? mhi_cntrl->sbl_size : firmware->size;
|
||||
|
||||
/* the sbl size provided is maximum size, not necessarily image size */
|
||||
if (size > firmware->size)
|
||||
size = firmware->size;
|
||||
|
||||
buf = mhi_alloc_coherent(mhi_cntrl, size, &dma_addr, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
MHI_ERR("Could not allocate memory for image\n");
|
||||
release_firmware(firmware);
|
||||
return;
|
||||
}
|
||||
|
||||
/* load sbl image */
|
||||
memcpy(buf, firmware->data, size);
|
||||
ret = mhi_fw_load_sbl(mhi_cntrl, dma_addr, size);
|
||||
mhi_free_coherent(mhi_cntrl, size, buf, dma_addr);
|
||||
|
||||
if (!mhi_cntrl->fbc_download || ret || mhi_cntrl->ee == MHI_EE_EDL)
|
||||
release_firmware(firmware);
|
||||
|
||||
/* error or in edl, we're done */
|
||||
if (ret || mhi_cntrl->ee == MHI_EE_EDL)
|
||||
return;
|
||||
|
||||
write_lock_irq(&mhi_cntrl->pm_lock);
|
||||
mhi_cntrl->dev_state = MHI_STATE_RESET;
|
||||
write_unlock_irq(&mhi_cntrl->pm_lock);
|
||||
|
||||
/*
|
||||
* if we're doing fbc, populate vector tables while
|
||||
* device transitioning into MHI READY state
|
||||
*/
|
||||
if (mhi_cntrl->fbc_download) {
|
||||
ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image,
|
||||
firmware->size);
|
||||
if (ret) {
|
||||
MHI_ERR("Error alloc size of %zu\n", firmware->size);
|
||||
goto error_alloc_fw_table;
|
||||
}
|
||||
|
||||
MHI_LOG("Copying firmware image into vector table\n");
|
||||
|
||||
/* load the firmware into BHIE vec table */
|
||||
mhi_firmware_copy(mhi_cntrl, firmware, mhi_cntrl->fbc_image);
|
||||
}
|
||||
|
||||
/* transitioning into MHI RESET->READY state */
|
||||
ret = mhi_ready_state_transition(mhi_cntrl);
|
||||
|
||||
MHI_LOG("To Reset->Ready PM_STATE:%s MHI_STATE:%s EE:%s, ret:%d\n",
|
||||
to_mhi_pm_state_str(mhi_cntrl->pm_state),
|
||||
TO_MHI_STATE_STR(mhi_cntrl->dev_state),
|
||||
TO_MHI_EXEC_STR(mhi_cntrl->ee), ret);
|
||||
|
||||
if (!mhi_cntrl->fbc_download)
|
||||
return;
|
||||
|
||||
if (ret) {
|
||||
MHI_ERR("Did not transition to READY state\n");
|
||||
goto error_read;
|
||||
}
|
||||
|
||||
/* wait for SBL event */
|
||||
ret = wait_event_timeout(mhi_cntrl->state_event,
|
||||
mhi_cntrl->ee == MHI_EE_SBL ||
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
|
||||
if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
|
||||
MHI_ERR("MHI did not enter BHIE\n");
|
||||
goto error_read;
|
||||
}
|
||||
|
||||
/* start full firmware image download */
|
||||
image_info = mhi_cntrl->fbc_image;
|
||||
ret = mhi_fw_load_amss(mhi_cntrl,
|
||||
/* last entry is vec table */
|
||||
&image_info->mhi_buf[image_info->entries - 1]);
|
||||
|
||||
MHI_LOG("amss fw_load, ret:%d\n", ret);
|
||||
|
||||
release_firmware(firmware);
|
||||
|
||||
return;
|
||||
|
||||
error_read:
|
||||
mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image);
|
||||
mhi_cntrl->fbc_image = NULL;
|
||||
|
||||
error_alloc_fw_table:
|
||||
release_firmware(firmware);
|
||||
}
|
||||
|
||||
int BhiWrite(struct mhi_controller *mhi_cntrl, void __user *ubuf, size_t size)
|
||||
{
|
||||
int ret;
|
||||
dma_addr_t dma_addr;
|
||||
void *dma_buf;
|
||||
|
||||
MHI_LOG("Device current EE:%s, M:%s, PM:%s\n",
|
||||
TO_MHI_EXEC_STR(mhi_get_exec_env(mhi_cntrl)),
|
||||
TO_MHI_STATE_STR(mhi_get_mhi_state(mhi_cntrl)),
|
||||
to_mhi_pm_state_str(mhi_cntrl->pm_state));
|
||||
|
||||
#if 0
|
||||
if (mhi_get_exec_env(mhi_cntrl) == MHI_EE_EDL && mhi_cntrl->ee != MHI_EE_EDL) {
|
||||
mhi_cntrl->ee = MHI_EE_EDL;
|
||||
wait_event_timeout(mhi_cntrl->state_event,
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms + 500));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
if (!MHI_IN_PBL(mhi_cntrl->ee) || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
|
||||
MHI_ERR("MHI is not in valid BHI state\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mhi_cntrl->ee != MHI_EE_EDL) {
|
||||
MHI_ERR("MHI is not in EDL state\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dma_buf = mhi_alloc_coherent(mhi_cntrl, size, &dma_addr, GFP_KERNEL);
|
||||
if (!dma_buf) {
|
||||
MHI_ERR("Could not allocate memory for image\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = copy_from_user(dma_buf, ubuf, size);
|
||||
if (ret) {
|
||||
MHI_ERR("IOCTL_BHI_WRITEIMAGE copy buf error, ret = %d\n", ret);
|
||||
mhi_free_coherent(mhi_cntrl, size, dma_buf, dma_addr);;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mhi_fw_load_sbl(mhi_cntrl, dma_addr, size);
|
||||
mhi_free_coherent(mhi_cntrl, size, dma_buf, dma_addr);
|
||||
|
||||
if (ret) {
|
||||
MHI_ERR("ret = %d, ee=%d\n", ret, mhi_cntrl->ee);
|
||||
goto error_state;
|
||||
}
|
||||
|
||||
write_lock_irq(&mhi_cntrl->pm_lock);
|
||||
mhi_cntrl->dev_state = MHI_STATE_RESET;
|
||||
write_unlock_irq(&mhi_cntrl->pm_lock);
|
||||
|
||||
/* transitioning into MHI RESET->READY state */
|
||||
ret = mhi_ready_state_transition(mhi_cntrl);
|
||||
if (ret) {
|
||||
MHI_ERR("Did not transition to READY state\n");
|
||||
goto error_state;
|
||||
}
|
||||
|
||||
MHI_LOG("To Reset->Ready PM_STATE:%s MHI_STATE:%s EE:%s, ret:%d\n",
|
||||
to_mhi_pm_state_str(mhi_cntrl->pm_state),
|
||||
TO_MHI_STATE_STR(mhi_cntrl->dev_state),
|
||||
TO_MHI_EXEC_STR(mhi_cntrl->ee), ret);
|
||||
|
||||
/* wait for BHIE event */
|
||||
ret = wait_event_timeout(mhi_cntrl->state_event,
|
||||
mhi_cntrl->ee == MHI_EE_FP ||
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
|
||||
MHI_ERR("MHI did not enter Flash Programmer Environment\n");
|
||||
goto error_state;
|
||||
}
|
||||
|
||||
MHI_LOG("MHI enter Flash Programmer Environment\n");
|
||||
return 0;
|
||||
|
||||
error_state:
|
||||
MHI_LOG("Device current EE:%s, M:%s\n",
|
||||
TO_MHI_EXEC_STR(mhi_get_exec_env(mhi_cntrl)),
|
||||
TO_MHI_STATE_STR(mhi_get_mhi_state(mhi_cntrl)));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
long bhi_get_dev_info(struct mhi_controller *mhi_cntrl, void __user *ubuf)
|
||||
{
|
||||
long ret = -EINVAL;
|
||||
BHI_INFO_TYPE bhi_info;
|
||||
|
||||
ret = BhiRead(mhi_cntrl, &bhi_info);
|
||||
if (ret) {
|
||||
MHI_ERR("IOCTL_BHI_GETDEVINFO BhiRead error, ret = %ld\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = copy_to_user(ubuf, &bhi_info, sizeof(bhi_info));
|
||||
if (ret) {
|
||||
MHI_ERR("IOCTL_BHI_GETDEVINFO copy error, ret = %ld\n", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
long bhi_write_image(struct mhi_controller *mhi_cntrl, void __user *ubuf)
|
||||
{
|
||||
long ret = -EINVAL;
|
||||
size_t size;
|
||||
|
||||
ret = copy_from_user(&size, ubuf, sizeof(size));
|
||||
if (ret) {
|
||||
MHI_ERR("IOCTL_BHI_WRITEIMAGE copy size error, ret = %ld\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = BhiWrite(mhi_cntrl, ubuf+sizeof(size), size);
|
||||
if (ret) {
|
||||
MHI_ERR("IOCTL_BHI_WRITEIMAGE BhiWrite error, ret = %ld\n", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
274
applications/quectel_MHI/src/core/mhi_dtr.c
Normal file
274
applications/quectel_MHI/src/core/mhi_dtr.c
Normal file
@@ -0,0 +1,274 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-direction.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/termios.h>
|
||||
#include <linux/wait.h>
|
||||
#include "mhi.h"
|
||||
#include "mhi_internal.h"
|
||||
|
||||
struct __packed dtr_ctrl_msg {
|
||||
u32 preamble;
|
||||
u32 msg_id;
|
||||
u32 dest_id;
|
||||
u32 size;
|
||||
u32 msg;
|
||||
};
|
||||
|
||||
#define CTRL_MAGIC (0x4C525443)
|
||||
#define CTRL_MSG_DTR BIT(0)
|
||||
#define CTRL_MSG_RTS BIT(1)
|
||||
#define CTRL_MSG_DCD BIT(0)
|
||||
#define CTRL_MSG_DSR BIT(1)
|
||||
#define CTRL_MSG_RI BIT(3)
|
||||
#define CTRL_HOST_STATE (0x10)
|
||||
#define CTRL_DEVICE_STATE (0x11)
|
||||
#define CTRL_GET_CHID(dtr) (dtr->dest_id & 0xFF)
|
||||
|
||||
static int mhi_dtr_tiocmset(struct mhi_controller *mhi_cntrl,
|
||||
struct mhi_device *mhi_dev,
|
||||
u32 tiocm)
|
||||
{
|
||||
struct dtr_ctrl_msg *dtr_msg = NULL;
|
||||
struct mhi_chan *dtr_chan = mhi_cntrl->dtr_dev->ul_chan;
|
||||
spinlock_t *res_lock = &mhi_dev->dev.devres_lock;
|
||||
u32 cur_tiocm;
|
||||
int ret = 0;
|
||||
|
||||
cur_tiocm = mhi_dev->tiocm & ~(TIOCM_CD | TIOCM_DSR | TIOCM_RI);
|
||||
|
||||
tiocm &= (TIOCM_DTR | TIOCM_RTS);
|
||||
|
||||
/* state did not changed */
|
||||
if (cur_tiocm == tiocm)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&dtr_chan->mutex);
|
||||
|
||||
dtr_msg = kzalloc(sizeof(*dtr_msg), GFP_KERNEL);
|
||||
if (!dtr_msg) {
|
||||
ret = -ENOMEM;
|
||||
goto tiocm_exit;
|
||||
}
|
||||
|
||||
dtr_msg->preamble = CTRL_MAGIC;
|
||||
dtr_msg->msg_id = CTRL_HOST_STATE;
|
||||
dtr_msg->dest_id = mhi_dev->ul_chan_id;
|
||||
dtr_msg->size = sizeof(u32);
|
||||
if (tiocm & TIOCM_DTR)
|
||||
dtr_msg->msg |= CTRL_MSG_DTR;
|
||||
if (tiocm & TIOCM_RTS)
|
||||
dtr_msg->msg |= CTRL_MSG_RTS;
|
||||
|
||||
/*
|
||||
* 'minicom -D /dev/mhi_DUN' will send RTS:1 when open, and RTS:0 when exit.
|
||||
* RTS:0 will prevent modem output AT response.
|
||||
* But 'busybox microcom' do not send any RTS to modem.
|
||||
* [75094.969783] mhi_uci_q 0306_00.03.00_DUN: mhi_dtr_tiocmset DTR:0 RTS:1
|
||||
* [75100.210994] mhi_uci_q 0306_00.03.00_DUN: mhi_dtr_tiocmset DTR:0 RTS:0
|
||||
*/
|
||||
dev_dbg(&mhi_dev->dev, "%s DTR:%d RTS:%d\n", __func__,
|
||||
!!(tiocm & TIOCM_DTR), !!(tiocm & TIOCM_RTS));
|
||||
|
||||
reinit_completion(&dtr_chan->completion);
|
||||
ret = mhi_queue_transfer(mhi_cntrl->dtr_dev, DMA_TO_DEVICE, dtr_msg,
|
||||
sizeof(*dtr_msg), MHI_EOT);
|
||||
if (ret)
|
||||
goto tiocm_exit;
|
||||
|
||||
ret = wait_for_completion_timeout(&dtr_chan->completion,
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
if (!ret) {
|
||||
MHI_ERR("Failed to receive transfer callback\n");
|
||||
ret = -EIO;
|
||||
goto tiocm_exit;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
spin_lock_irq(res_lock);
|
||||
mhi_dev->tiocm &= ~(TIOCM_DTR | TIOCM_RTS);
|
||||
mhi_dev->tiocm |= tiocm;
|
||||
spin_unlock_irq(res_lock);
|
||||
|
||||
tiocm_exit:
|
||||
kfree(dtr_msg);
|
||||
mutex_unlock(&dtr_chan->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
long mhi_ioctl(struct mhi_device *mhi_dev, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
int ret;
|
||||
|
||||
/* ioctl not supported by this controller */
|
||||
if (!mhi_cntrl->dtr_dev)
|
||||
return -EIO;
|
||||
|
||||
switch (cmd) {
|
||||
case TIOCMGET:
|
||||
return mhi_dev->tiocm;
|
||||
case TIOCMSET:
|
||||
{
|
||||
u32 tiocm;
|
||||
|
||||
ret = get_user(tiocm, (u32 *)arg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return mhi_dtr_tiocmset(mhi_cntrl, mhi_dev, tiocm);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(mhi_ioctl);
|
||||
|
||||
static int mhi_dtr_queue_inbound(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
struct mhi_device *mhi_dev = mhi_cntrl->dtr_dev;
|
||||
int nr_trbs = mhi_get_no_free_descriptors(mhi_dev, DMA_FROM_DEVICE);
|
||||
size_t mtu = mhi_dev->mtu;
|
||||
void *buf;
|
||||
int ret = -EIO, i;
|
||||
|
||||
for (i = 0; i < nr_trbs; i++) {
|
||||
buf = kmalloc(mtu, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, buf, mtu,
|
||||
MHI_EOT);
|
||||
if (ret) {
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mhi_dtr_dl_xfer_cb(struct mhi_device *mhi_dev,
|
||||
struct mhi_result *mhi_result)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
struct dtr_ctrl_msg *dtr_msg = mhi_result->buf_addr;
|
||||
u32 chan;
|
||||
spinlock_t *res_lock;
|
||||
|
||||
if (mhi_result->transaction_status == -ENOTCONN) {
|
||||
kfree(mhi_result->buf_addr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mhi_result->bytes_xferd != sizeof(*dtr_msg)) {
|
||||
MHI_ERR("Unexpected length %zu received\n",
|
||||
mhi_result->bytes_xferd);
|
||||
return;
|
||||
}
|
||||
|
||||
MHI_LOG("preamble:0x%x msg_id:%u dest_id:%u msg:0x%x\n",
|
||||
dtr_msg->preamble, dtr_msg->msg_id, dtr_msg->dest_id,
|
||||
dtr_msg->msg);
|
||||
|
||||
chan = CTRL_GET_CHID(dtr_msg);
|
||||
if (chan >= mhi_cntrl->max_chan)
|
||||
goto auto_queue;
|
||||
|
||||
mhi_dev = mhi_cntrl->mhi_chan[chan].mhi_dev;
|
||||
if (!mhi_dev)
|
||||
goto auto_queue;
|
||||
|
||||
res_lock = &mhi_dev->dev.devres_lock;
|
||||
spin_lock_irq(res_lock);
|
||||
mhi_dev->tiocm &= ~(TIOCM_CD | TIOCM_DSR | TIOCM_RI);
|
||||
|
||||
if (dtr_msg->msg & CTRL_MSG_DCD)
|
||||
mhi_dev->tiocm |= TIOCM_CD;
|
||||
|
||||
if (dtr_msg->msg & CTRL_MSG_DSR)
|
||||
mhi_dev->tiocm |= TIOCM_DSR;
|
||||
|
||||
if (dtr_msg->msg & CTRL_MSG_RI)
|
||||
mhi_dev->tiocm |= TIOCM_RI;
|
||||
spin_unlock_irq(res_lock);
|
||||
|
||||
auto_queue:
|
||||
mhi_queue_transfer(mhi_cntrl->dtr_dev, DMA_FROM_DEVICE, mhi_result->buf_addr,
|
||||
mhi_cntrl->dtr_dev->mtu, MHI_EOT);
|
||||
}
|
||||
|
||||
static void mhi_dtr_ul_xfer_cb(struct mhi_device *mhi_dev,
|
||||
struct mhi_result *mhi_result)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
struct mhi_chan *dtr_chan = mhi_cntrl->dtr_dev->ul_chan;
|
||||
|
||||
MHI_VERB("Received with status:%d\n", mhi_result->transaction_status);
|
||||
if (!mhi_result->transaction_status)
|
||||
complete(&dtr_chan->completion);
|
||||
}
|
||||
|
||||
static void mhi_dtr_remove(struct mhi_device *mhi_dev)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
|
||||
mhi_cntrl->dtr_dev = NULL;
|
||||
}
|
||||
|
||||
static int mhi_dtr_probe(struct mhi_device *mhi_dev,
|
||||
const struct mhi_device_id *id)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
int ret;
|
||||
|
||||
MHI_LOG("Enter for DTR control channel\n");
|
||||
|
||||
mhi_dev->mtu = min_t(size_t, id->driver_data, mhi_dev->mtu);
|
||||
ret = mhi_prepare_for_transfer(mhi_dev);
|
||||
if (!ret)
|
||||
mhi_cntrl->dtr_dev = mhi_dev;
|
||||
|
||||
if (!ret)
|
||||
ret = mhi_dtr_queue_inbound(mhi_cntrl);
|
||||
|
||||
MHI_LOG("Exit with ret:%d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct mhi_device_id mhi_dtr_table[] = {
|
||||
{ .chan = "IP_CTRL", .driver_data = sizeof(struct dtr_ctrl_msg) },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct mhi_driver mhi_dtr_driver = {
|
||||
.id_table = mhi_dtr_table,
|
||||
.remove = mhi_dtr_remove,
|
||||
.probe = mhi_dtr_probe,
|
||||
.ul_xfer_cb = mhi_dtr_ul_xfer_cb,
|
||||
.dl_xfer_cb = mhi_dtr_dl_xfer_cb,
|
||||
.driver = {
|
||||
.name = "MHI_DTR",
|
||||
.owner = THIS_MODULE,
|
||||
}
|
||||
};
|
||||
|
||||
int __init mhi_dtr_init(void)
|
||||
{
|
||||
return mhi_driver_register(&mhi_dtr_driver);
|
||||
}
|
||||
void mhi_dtr_exit(void) {
|
||||
mhi_driver_unregister(&mhi_dtr_driver);
|
||||
}
|
||||
2645
applications/quectel_MHI/src/core/mhi_init.c
Normal file
2645
applications/quectel_MHI/src/core/mhi_init.c
Normal file
File diff suppressed because it is too large
Load Diff
1181
applications/quectel_MHI/src/core/mhi_internal.h
Normal file
1181
applications/quectel_MHI/src/core/mhi_internal.h
Normal file
File diff suppressed because it is too large
Load Diff
2722
applications/quectel_MHI/src/core/mhi_main.c
Normal file
2722
applications/quectel_MHI/src/core/mhi_main.c
Normal file
File diff suppressed because it is too large
Load Diff
1253
applications/quectel_MHI/src/core/mhi_pm.c
Normal file
1253
applications/quectel_MHI/src/core/mhi_pm.c
Normal file
File diff suppressed because it is too large
Load Diff
362
applications/quectel_MHI/src/core/mhi_sdx20.h
Normal file
362
applications/quectel_MHI/src/core/mhi_sdx20.h
Normal file
@@ -0,0 +1,362 @@
|
||||
#ifndef __SDX20_MHI_H
|
||||
#define __SDX20_MHI_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* MHI control data structures alloted by the host, including
|
||||
* channel context array, event context array, command context and rings */
|
||||
|
||||
/* Channel context state */
|
||||
enum mhi_dev_ch_ctx_state {
|
||||
MHI_DEV_CH_STATE_DISABLED,
|
||||
MHI_DEV_CH_STATE_ENABLED,
|
||||
MHI_DEV_CH_STATE_RUNNING,
|
||||
MHI_DEV_CH_STATE_SUSPENDED,
|
||||
MHI_DEV_CH_STATE_STOP,
|
||||
MHI_DEV_CH_STATE_ERROR,
|
||||
MHI_DEV_CH_STATE_RESERVED,
|
||||
MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF
|
||||
};
|
||||
|
||||
/* Channel type */
|
||||
enum mhi_dev_ch_ctx_type {
|
||||
MHI_DEV_CH_TYPE_NONE,
|
||||
MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL,
|
||||
MHI_DEV_CH_TYPE_INBOUND_CHANNEL,
|
||||
MHI_DEV_CH_RESERVED
|
||||
};
|
||||
|
||||
/* Channel context type */
|
||||
struct mhi_dev_ch_ctx {
|
||||
enum mhi_dev_ch_ctx_state ch_state;
|
||||
enum mhi_dev_ch_ctx_type ch_type;
|
||||
uint32_t err_indx;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_ring_element_type_id {
|
||||
MHI_DEV_RING_EL_INVALID = 0,
|
||||
MHI_DEV_RING_EL_NOOP = 1,
|
||||
MHI_DEV_RING_EL_TRANSFER = 2,
|
||||
MHI_DEV_RING_EL_RESET = 16,
|
||||
MHI_DEV_RING_EL_STOP = 17,
|
||||
MHI_DEV_RING_EL_START = 18,
|
||||
MHI_DEV_RING_EL_MHI_STATE_CHG = 32,
|
||||
MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33,
|
||||
MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34,
|
||||
MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64,
|
||||
MHI_DEV_RING_EL_UNDEF
|
||||
};
|
||||
|
||||
enum mhi_dev_ring_state {
|
||||
RING_STATE_UINT = 0,
|
||||
RING_STATE_IDLE,
|
||||
RING_STATE_PENDING,
|
||||
};
|
||||
|
||||
enum mhi_dev_ring_type {
|
||||
RING_TYPE_CMD = 0,
|
||||
RING_TYPE_ER,
|
||||
RING_TYPE_CH,
|
||||
RING_TYPE_INVAL
|
||||
};
|
||||
|
||||
/* Event context interrupt moderation */
|
||||
enum mhi_dev_evt_ctx_int_mod_timer {
|
||||
MHI_DEV_EVT_INT_MODERATION_DISABLED
|
||||
};
|
||||
|
||||
/* Event ring type */
|
||||
enum mhi_dev_evt_ctx_event_ring_type {
|
||||
MHI_DEV_EVT_TYPE_DEFAULT,
|
||||
MHI_DEV_EVT_TYPE_VALID,
|
||||
MHI_DEV_EVT_RESERVED
|
||||
};
|
||||
|
||||
/* Event ring context type */
|
||||
struct mhi_dev_ev_ctx {
|
||||
uint32_t res1:16;
|
||||
enum mhi_dev_evt_ctx_int_mod_timer intmodt:16;
|
||||
enum mhi_dev_evt_ctx_event_ring_type ertype;
|
||||
uint32_t msivec;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* Command context */
|
||||
struct mhi_dev_cmd_ctx {
|
||||
uint32_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* generic context */
|
||||
struct mhi_dev_gen_ctx {
|
||||
uint32_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* Transfer ring element */
|
||||
struct mhi_dev_transfer_ring_element {
|
||||
uint64_t data_buf_ptr;
|
||||
uint32_t len:16;
|
||||
uint32_t res1:16;
|
||||
uint32_t chain:1;
|
||||
uint32_t res2:7;
|
||||
uint32_t ieob:1;
|
||||
uint32_t ieot:1;
|
||||
uint32_t bei:1;
|
||||
uint32_t res3:5;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res4:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring element */
|
||||
/* Command ring No op command */
|
||||
struct mhi_dev_cmd_ring_op {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring reset channel command */
|
||||
struct mhi_dev_cmd_ring_reset_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring stop channel command */
|
||||
struct mhi_dev_cmd_ring_stop_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring start channel command */
|
||||
struct mhi_dev_cmd_ring_start_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t seqnum;
|
||||
uint32_t reliable:1;
|
||||
uint32_t res2:15;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_cmd_completion_code {
|
||||
MHI_CMD_COMPL_CODE_INVALID = 0,
|
||||
MHI_CMD_COMPL_CODE_SUCCESS = 1,
|
||||
MHI_CMD_COMPL_CODE_EOT = 2,
|
||||
MHI_CMD_COMPL_CODE_OVERFLOW = 3,
|
||||
MHI_CMD_COMPL_CODE_EOB = 4,
|
||||
MHI_CMD_COMPL_CODE_UNDEFINED = 16,
|
||||
MHI_CMD_COMPL_CODE_RING_EL = 17,
|
||||
MHI_CMD_COMPL_CODE_RES
|
||||
};
|
||||
|
||||
/* Event ring elements */
|
||||
/* Transfer completion event */
|
||||
struct mhi_dev_event_ring_transfer_completion {
|
||||
uint64_t ptr;
|
||||
uint32_t len:16;
|
||||
uint32_t res1:8;
|
||||
enum mhi_dev_cmd_completion_code code:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command completion event */
|
||||
struct mhi_dev_event_ring_cmd_completion {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_cmd_completion_code code:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_state {
|
||||
MHI_DEV_RESET_STATE = 0,
|
||||
MHI_DEV_READY_STATE,
|
||||
MHI_DEV_M0_STATE,
|
||||
MHI_DEV_M1_STATE,
|
||||
MHI_DEV_M2_STATE,
|
||||
MHI_DEV_M3_STATE,
|
||||
MHI_DEV_MAX_STATE,
|
||||
MHI_DEV_SYSERR_STATE = 0xff
|
||||
};
|
||||
|
||||
/* MHI state change event */
|
||||
struct mhi_dev_event_ring_state_change {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_state mhistate:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_execenv {
|
||||
MHI_DEV_SBL_EE = 1,
|
||||
MHI_DEV_AMSS_EE = 2,
|
||||
MHI_DEV_UNRESERVED
|
||||
};
|
||||
|
||||
/* EE state change event */
|
||||
struct mhi_dev_event_ring_ee_state_change {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_execenv execenv:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
/* Generic cmd to parse common details like type and channel id */
|
||||
struct mhi_dev_ring_generic {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_state mhistate:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
struct mhi_config {
|
||||
uint32_t mhi_reg_len;
|
||||
uint32_t version;
|
||||
uint32_t event_rings;
|
||||
uint32_t channels;
|
||||
uint32_t chdb_offset;
|
||||
uint32_t erdb_offset;
|
||||
};
|
||||
|
||||
#define NUM_CHANNELS 128
|
||||
#define HW_CHANNEL_BASE 100
|
||||
#define HW_CHANNEL_END 107
|
||||
#define MHI_ENV_VALUE 2
|
||||
#define MHI_MASK_ROWS_CH_EV_DB 4
|
||||
#define TRB_MAX_DATA_SIZE 8192
|
||||
#define MHI_CTRL_STATE 25
|
||||
#define IPA_DMA_SYNC 1
|
||||
#define IPA_DMA_ASYNC 0
|
||||
|
||||
/*maximum trasnfer completion events buffer*/
|
||||
#define MAX_TR_EVENTS 50
|
||||
/*maximum event requests */
|
||||
#define MHI_MAX_EVT_REQ 50
|
||||
|
||||
/* Possible ring element types */
|
||||
union mhi_dev_ring_element_type {
|
||||
struct mhi_dev_cmd_ring_op cmd_no_op;
|
||||
struct mhi_dev_cmd_ring_reset_channel_cmd cmd_reset;
|
||||
struct mhi_dev_cmd_ring_stop_channel_cmd cmd_stop;
|
||||
struct mhi_dev_cmd_ring_start_channel_cmd cmd_start;
|
||||
struct mhi_dev_transfer_ring_element cmd_transfer;
|
||||
struct mhi_dev_event_ring_transfer_completion evt_tr_comp;
|
||||
struct mhi_dev_event_ring_cmd_completion evt_cmd_comp;
|
||||
struct mhi_dev_event_ring_state_change evt_state_change;
|
||||
struct mhi_dev_event_ring_ee_state_change evt_ee_state;
|
||||
struct mhi_dev_ring_generic generic;
|
||||
};
|
||||
|
||||
/* Transfer ring element type */
|
||||
union mhi_dev_ring_ctx {
|
||||
struct mhi_dev_cmd_ctx cmd;
|
||||
struct mhi_dev_ev_ctx ev;
|
||||
struct mhi_dev_ch_ctx ch;
|
||||
struct mhi_dev_gen_ctx generic;
|
||||
};
|
||||
|
||||
/* MHI host Control and data address region */
|
||||
struct mhi_host_addr {
|
||||
uint32_t ctrl_base_lsb;
|
||||
uint32_t ctrl_base_msb;
|
||||
uint32_t ctrl_limit_lsb;
|
||||
uint32_t ctrl_limit_msb;
|
||||
uint32_t data_base_lsb;
|
||||
uint32_t data_base_msb;
|
||||
uint32_t data_limit_lsb;
|
||||
uint32_t data_limit_msb;
|
||||
};
|
||||
|
||||
/* MHI physical and virtual address region */
|
||||
struct mhi_meminfo {
|
||||
struct device *dev;
|
||||
uintptr_t pa_aligned;
|
||||
uintptr_t pa_unaligned;
|
||||
uintptr_t va_aligned;
|
||||
uintptr_t va_unaligned;
|
||||
uintptr_t size;
|
||||
};
|
||||
|
||||
struct mhi_addr {
|
||||
uint64_t host_pa;
|
||||
uintptr_t device_pa;
|
||||
uintptr_t device_va;
|
||||
size_t size;
|
||||
dma_addr_t phy_addr;
|
||||
void *virt_addr;
|
||||
bool use_ipa_dma;
|
||||
};
|
||||
|
||||
struct mhi_interrupt_state {
|
||||
uint32_t mask;
|
||||
uint32_t status;
|
||||
};
|
||||
|
||||
enum mhi_dev_channel_state {
|
||||
MHI_DEV_CH_UNINT,
|
||||
MHI_DEV_CH_STARTED,
|
||||
MHI_DEV_CH_PENDING_START,
|
||||
MHI_DEV_CH_PENDING_STOP,
|
||||
MHI_DEV_CH_STOPPED,
|
||||
MHI_DEV_CH_CLOSED,
|
||||
};
|
||||
|
||||
enum mhi_dev_ch_operation {
|
||||
MHI_DEV_OPEN_CH,
|
||||
MHI_DEV_CLOSE_CH,
|
||||
MHI_DEV_READ_CH,
|
||||
MHI_DEV_READ_WR,
|
||||
MHI_DEV_POLL,
|
||||
};
|
||||
|
||||
enum mhi_ctrl_info {
|
||||
MHI_STATE_CONFIGURED = 0,
|
||||
MHI_STATE_CONNECTED = 1,
|
||||
MHI_STATE_DISCONNECTED = 2,
|
||||
MHI_STATE_INVAL,
|
||||
};
|
||||
|
||||
enum mhi_dev_tr_compl_evt_type {
|
||||
SEND_EVENT_BUFFER,
|
||||
SEND_EVENT_RD_OFFSET,
|
||||
};
|
||||
|
||||
enum mhi_dev_transfer_type {
|
||||
MHI_DEV_DMA_SYNC,
|
||||
MHI_DEV_DMA_ASYNC,
|
||||
};
|
||||
#endif /* _SDX20_MHI_H_ */
|
||||
426
applications/quectel_MHI/src/core/sdx20_mhi.h
Normal file
426
applications/quectel_MHI/src/core/sdx20_mhi.h
Normal file
@@ -0,0 +1,426 @@
|
||||
#ifndef __SDX20_MHI_H
|
||||
#define __SDX20_MHI_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* MHI control data structures alloted by the host, including
|
||||
* channel context array, event context array, command context and rings */
|
||||
|
||||
/* Channel context state */
|
||||
enum mhi_dev_ch_ctx_state {
|
||||
MHI_DEV_CH_STATE_DISABLED,
|
||||
MHI_DEV_CH_STATE_ENABLED,
|
||||
MHI_DEV_CH_STATE_RUNNING,
|
||||
MHI_DEV_CH_STATE_SUSPENDED,
|
||||
MHI_DEV_CH_STATE_STOP,
|
||||
MHI_DEV_CH_STATE_ERROR,
|
||||
MHI_DEV_CH_STATE_RESERVED,
|
||||
MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF
|
||||
};
|
||||
|
||||
/* Channel type */
|
||||
enum mhi_dev_ch_ctx_type {
|
||||
MHI_DEV_CH_TYPE_NONE,
|
||||
MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL,
|
||||
MHI_DEV_CH_TYPE_INBOUND_CHANNEL,
|
||||
MHI_DEV_CH_RESERVED
|
||||
};
|
||||
|
||||
/* Channel context type */
|
||||
struct mhi_dev_ch_ctx {
|
||||
enum mhi_dev_ch_ctx_state ch_state;
|
||||
enum mhi_dev_ch_ctx_type ch_type;
|
||||
uint32_t err_indx;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_ring_element_type_id {
|
||||
MHI_DEV_RING_EL_INVALID = 0,
|
||||
MHI_DEV_RING_EL_NOOP = 1,
|
||||
MHI_DEV_RING_EL_TRANSFER = 2,
|
||||
MHI_DEV_RING_EL_RESET = 16,
|
||||
MHI_DEV_RING_EL_STOP = 17,
|
||||
MHI_DEV_RING_EL_START = 18,
|
||||
MHI_DEV_RING_EL_MHI_STATE_CHG = 32,
|
||||
MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33,
|
||||
MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34,
|
||||
MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64,
|
||||
MHI_DEV_RING_EL_UNDEF
|
||||
};
|
||||
|
||||
enum mhi_dev_ring_state {
|
||||
RING_STATE_UINT = 0,
|
||||
RING_STATE_IDLE,
|
||||
RING_STATE_PENDING,
|
||||
};
|
||||
|
||||
enum mhi_dev_ring_type {
|
||||
RING_TYPE_CMD = 0,
|
||||
RING_TYPE_ER,
|
||||
RING_TYPE_CH,
|
||||
RING_TYPE_INVAL
|
||||
};
|
||||
|
||||
/* Event context interrupt moderation */
|
||||
enum mhi_dev_evt_ctx_int_mod_timer {
|
||||
MHI_DEV_EVT_INT_MODERATION_DISABLED
|
||||
};
|
||||
|
||||
/* Event ring type */
|
||||
enum mhi_dev_evt_ctx_event_ring_type {
|
||||
MHI_DEV_EVT_TYPE_DEFAULT,
|
||||
MHI_DEV_EVT_TYPE_VALID,
|
||||
MHI_DEV_EVT_RESERVED
|
||||
};
|
||||
|
||||
/* Event ring context type */
|
||||
struct mhi_dev_ev_ctx {
|
||||
uint32_t res1:16;
|
||||
enum mhi_dev_evt_ctx_int_mod_timer intmodt:16;
|
||||
enum mhi_dev_evt_ctx_event_ring_type ertype;
|
||||
uint32_t msivec;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* Command context */
|
||||
struct mhi_dev_cmd_ctx {
|
||||
uint32_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* generic context */
|
||||
struct mhi_dev_gen_ctx {
|
||||
uint32_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* Transfer ring element */
|
||||
struct mhi_dev_transfer_ring_element {
|
||||
uint64_t data_buf_ptr;
|
||||
uint32_t len:16;
|
||||
uint32_t res1:16;
|
||||
uint32_t chain:1;
|
||||
uint32_t res2:7;
|
||||
uint32_t ieob:1;
|
||||
uint32_t ieot:1;
|
||||
uint32_t bei:1;
|
||||
uint32_t res3:5;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res4:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring element */
|
||||
/* Command ring No op command */
|
||||
struct mhi_dev_cmd_ring_op {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring reset channel command */
|
||||
struct mhi_dev_cmd_ring_reset_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring stop channel command */
|
||||
struct mhi_dev_cmd_ring_stop_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring start channel command */
|
||||
struct mhi_dev_cmd_ring_start_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t seqnum;
|
||||
uint32_t reliable:1;
|
||||
uint32_t res2:15;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_cmd_completion_code {
|
||||
MHI_CMD_COMPL_CODE_INVALID = 0,
|
||||
MHI_CMD_COMPL_CODE_SUCCESS = 1,
|
||||
MHI_CMD_COMPL_CODE_EOT = 2,
|
||||
MHI_CMD_COMPL_CODE_OVERFLOW = 3,
|
||||
MHI_CMD_COMPL_CODE_EOB = 4,
|
||||
MHI_CMD_COMPL_CODE_UNDEFINED = 16,
|
||||
MHI_CMD_COMPL_CODE_RING_EL = 17,
|
||||
MHI_CMD_COMPL_CODE_RES
|
||||
};
|
||||
|
||||
/* Event ring elements */
|
||||
/* Transfer completion event */
|
||||
struct mhi_dev_event_ring_transfer_completion {
|
||||
uint64_t ptr;
|
||||
uint32_t len:16;
|
||||
uint32_t res1:8;
|
||||
enum mhi_dev_cmd_completion_code code:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command completion event */
|
||||
struct mhi_dev_event_ring_cmd_completion {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_cmd_completion_code code:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_state {
|
||||
MHI_DEV_RESET_STATE = 0,
|
||||
MHI_DEV_READY_STATE,
|
||||
MHI_DEV_M0_STATE,
|
||||
MHI_DEV_M1_STATE,
|
||||
MHI_DEV_M2_STATE,
|
||||
MHI_DEV_M3_STATE,
|
||||
MHI_DEV_MAX_STATE,
|
||||
MHI_DEV_SYSERR_STATE = 0xff
|
||||
};
|
||||
|
||||
/* MHI state change event */
|
||||
struct mhi_dev_event_ring_state_change {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_state mhistate:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_execenv {
|
||||
MHI_DEV_SBL_EE = 1,
|
||||
MHI_DEV_AMSS_EE = 2,
|
||||
MHI_DEV_UNRESERVED
|
||||
};
|
||||
|
||||
/* EE state change event */
|
||||
struct mhi_dev_event_ring_ee_state_change {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_execenv execenv:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
/* Generic cmd to parse common details like type and channel id */
|
||||
struct mhi_dev_ring_generic {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_state mhistate:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
struct mhi_config {
|
||||
uint32_t mhi_reg_len;
|
||||
uint32_t version;
|
||||
uint32_t event_rings;
|
||||
uint32_t channels;
|
||||
uint32_t chdb_offset;
|
||||
uint32_t erdb_offset;
|
||||
};
|
||||
|
||||
#define NUM_CHANNELS 128
|
||||
#define HW_CHANNEL_BASE 100
|
||||
#define HW_CHANNEL_END 107
|
||||
#define MHI_ENV_VALUE 2
|
||||
#define MHI_MASK_ROWS_CH_EV_DB 4
|
||||
#define TRB_MAX_DATA_SIZE 8192
|
||||
#define MHI_CTRL_STATE 25
|
||||
#define IPA_DMA_SYNC 1
|
||||
#define IPA_DMA_ASYNC 0
|
||||
|
||||
/*maximum trasnfer completion events buffer*/
|
||||
#define MAX_TR_EVENTS 50
|
||||
/*maximum event requests */
|
||||
#define MHI_MAX_EVT_REQ 50
|
||||
|
||||
/* Possible ring element types */
|
||||
union mhi_dev_ring_element_type {
|
||||
struct mhi_dev_cmd_ring_op cmd_no_op;
|
||||
struct mhi_dev_cmd_ring_reset_channel_cmd cmd_reset;
|
||||
struct mhi_dev_cmd_ring_stop_channel_cmd cmd_stop;
|
||||
struct mhi_dev_cmd_ring_start_channel_cmd cmd_start;
|
||||
struct mhi_dev_transfer_ring_element tre;
|
||||
struct mhi_dev_event_ring_transfer_completion evt_tr_comp;
|
||||
struct mhi_dev_event_ring_cmd_completion evt_cmd_comp;
|
||||
struct mhi_dev_event_ring_state_change evt_state_change;
|
||||
struct mhi_dev_event_ring_ee_state_change evt_ee_state;
|
||||
struct mhi_dev_ring_generic generic;
|
||||
};
|
||||
|
||||
/* Transfer ring element type */
|
||||
union mhi_dev_ring_ctx {
|
||||
struct mhi_dev_cmd_ctx cmd;
|
||||
struct mhi_dev_ev_ctx ev;
|
||||
struct mhi_dev_ch_ctx ch;
|
||||
struct mhi_dev_gen_ctx generic;
|
||||
};
|
||||
|
||||
/* MHI host Control and data address region */
|
||||
struct mhi_host_addr {
|
||||
uint32_t ctrl_base_lsb;
|
||||
uint32_t ctrl_base_msb;
|
||||
uint32_t ctrl_limit_lsb;
|
||||
uint32_t ctrl_limit_msb;
|
||||
uint32_t data_base_lsb;
|
||||
uint32_t data_base_msb;
|
||||
uint32_t data_limit_lsb;
|
||||
uint32_t data_limit_msb;
|
||||
};
|
||||
|
||||
/* MHI physical and virtual address region */
|
||||
struct mhi_meminfo {
|
||||
struct device *dev;
|
||||
uintptr_t pa_aligned;
|
||||
uintptr_t pa_unaligned;
|
||||
uintptr_t va_aligned;
|
||||
uintptr_t va_unaligned;
|
||||
uintptr_t size;
|
||||
};
|
||||
|
||||
struct mhi_addr {
|
||||
uint64_t host_pa;
|
||||
uintptr_t device_pa;
|
||||
uintptr_t device_va;
|
||||
size_t size;
|
||||
dma_addr_t phy_addr;
|
||||
void *virt_addr;
|
||||
bool use_ipa_dma;
|
||||
};
|
||||
|
||||
struct mhi_interrupt_state {
|
||||
uint32_t mask;
|
||||
uint32_t status;
|
||||
};
|
||||
|
||||
enum mhi_dev_channel_state {
|
||||
MHI_DEV_CH_UNINT,
|
||||
MHI_DEV_CH_STARTED,
|
||||
MHI_DEV_CH_PENDING_START,
|
||||
MHI_DEV_CH_PENDING_STOP,
|
||||
MHI_DEV_CH_STOPPED,
|
||||
MHI_DEV_CH_CLOSED,
|
||||
};
|
||||
|
||||
enum mhi_dev_ch_operation {
|
||||
MHI_DEV_OPEN_CH,
|
||||
MHI_DEV_CLOSE_CH,
|
||||
MHI_DEV_READ_CH,
|
||||
MHI_DEV_READ_WR,
|
||||
MHI_DEV_POLL,
|
||||
};
|
||||
|
||||
enum mhi_ctrl_info {
|
||||
MHI_STATE_CONFIGURED = 0,
|
||||
MHI_STATE_CONNECTED = 1,
|
||||
MHI_STATE_DISCONNECTED = 2,
|
||||
MHI_STATE_INVAL,
|
||||
};
|
||||
|
||||
enum mhi_dev_tr_compl_evt_type {
|
||||
SEND_EVENT_BUFFER,
|
||||
SEND_EVENT_RD_OFFSET,
|
||||
};
|
||||
|
||||
enum mhi_dev_transfer_type {
|
||||
MHI_DEV_DMA_SYNC,
|
||||
MHI_DEV_DMA_ASYNC,
|
||||
};
|
||||
|
||||
#if 0
|
||||
/* SW channel client list */
|
||||
enum mhi_client_channel {
|
||||
MHI_CLIENT_LOOPBACK_OUT = 0,
|
||||
MHI_CLIENT_LOOPBACK_IN = 1,
|
||||
MHI_CLIENT_SAHARA_OUT = 2,
|
||||
MHI_CLIENT_SAHARA_IN = 3,
|
||||
MHI_CLIENT_DIAG_OUT = 4,
|
||||
MHI_CLIENT_DIAG_IN = 5,
|
||||
MHI_CLIENT_SSR_OUT = 6,
|
||||
MHI_CLIENT_SSR_IN = 7,
|
||||
MHI_CLIENT_QDSS_OUT = 8,
|
||||
MHI_CLIENT_QDSS_IN = 9,
|
||||
MHI_CLIENT_EFS_OUT = 10,
|
||||
MHI_CLIENT_EFS_IN = 11,
|
||||
MHI_CLIENT_MBIM_OUT = 12,
|
||||
MHI_CLIENT_MBIM_IN = 13,
|
||||
MHI_CLIENT_QMI_OUT = 14,
|
||||
MHI_CLIENT_QMI_IN = 15,
|
||||
MHI_CLIENT_IP_CTRL_0_OUT = 16,
|
||||
MHI_CLIENT_IP_CTRL_0_IN = 17,
|
||||
MHI_CLIENT_IP_CTRL_1_OUT = 18,
|
||||
MHI_CLIENT_IP_CTRL_1_IN = 19,
|
||||
MHI_CLIENT_DCI_OUT = 20,
|
||||
MHI_CLIENT_DCI_IN = 21,
|
||||
MHI_CLIENT_IP_CTRL_3_OUT = 22,
|
||||
MHI_CLIENT_IP_CTRL_3_IN = 23,
|
||||
MHI_CLIENT_IP_CTRL_4_OUT = 24,
|
||||
MHI_CLIENT_IP_CTRL_4_IN = 25,
|
||||
MHI_CLIENT_IP_CTRL_5_OUT = 26,
|
||||
MHI_CLIENT_IP_CTRL_5_IN = 27,
|
||||
MHI_CLIENT_IP_CTRL_6_OUT = 28,
|
||||
MHI_CLIENT_IP_CTRL_6_IN = 29,
|
||||
MHI_CLIENT_IP_CTRL_7_OUT = 30,
|
||||
MHI_CLIENT_IP_CTRL_7_IN = 31,
|
||||
MHI_CLIENT_DUN_OUT = 32,
|
||||
MHI_CLIENT_DUN_IN = 33,
|
||||
MHI_CLIENT_IP_SW_0_OUT = 34,
|
||||
MHI_CLIENT_IP_SW_0_IN = 35,
|
||||
MHI_CLIENT_IP_SW_1_OUT = 36,
|
||||
MHI_CLIENT_IP_SW_1_IN = 37,
|
||||
MHI_CLIENT_IP_SW_2_OUT = 38,
|
||||
MHI_CLIENT_IP_SW_2_IN = 39,
|
||||
MHI_CLIENT_IP_SW_3_OUT = 40,
|
||||
MHI_CLIENT_IP_SW_3_IN = 41,
|
||||
MHI_CLIENT_CSVT_OUT = 42,
|
||||
MHI_CLIENT_CSVT_IN = 43,
|
||||
MHI_CLIENT_SMCT_OUT = 44,
|
||||
MHI_CLIENT_SMCT_IN = 45,
|
||||
MHI_CLIENT_IP_SW_4_OUT = 46,
|
||||
MHI_CLIENT_IP_SW_4_IN = 47,
|
||||
MHI_MAX_SOFTWARE_CHANNELS = 48,
|
||||
MHI_CLIENT_TEST_OUT = 60,
|
||||
MHI_CLIENT_TEST_IN = 61,
|
||||
MHI_CLIENT_RESERVED_1_LOWER = 62,
|
||||
MHI_CLIENT_RESERVED_1_UPPER = 99,
|
||||
MHI_CLIENT_IP_HW_0_OUT = 100,
|
||||
MHI_CLIENT_IP_HW_0_IN = 101,
|
||||
MHI_CLIENT_RESERVED_2_LOWER = 102,
|
||||
MHI_CLIENT_RESERVED_2_UPPER = 127,
|
||||
MHI_MAX_CHANNELS = 102,
|
||||
};
|
||||
#endif
|
||||
#endif /* _SDX20_MHI_H_ */
|
||||
33
applications/quectel_MHI/src/devices/Kconfig
Normal file
33
applications/quectel_MHI/src/devices/Kconfig
Normal file
@@ -0,0 +1,33 @@
|
||||
menu "MHI device support"
|
||||
|
||||
config MHI_NETDEV
|
||||
tristate "MHI NETDEV"
|
||||
depends on MHI_BUS
|
||||
help
|
||||
MHI based net device driver for transferring IP traffic
|
||||
between host and modem. By enabling this driver, clients
|
||||
can transfer data using standard network interface. Over
|
||||
the air traffic goes thru mhi netdev interface.
|
||||
|
||||
config MHI_UCI
|
||||
tristate "MHI UCI"
|
||||
depends on MHI_BUS
|
||||
help
|
||||
MHI based uci driver is for transferring data between host and
|
||||
modem using standard file operations from user space. Open, read,
|
||||
write, ioctl, and close operations are supported by this driver.
|
||||
Please check mhi_uci_match_table for all supported channels that
|
||||
are exposed to userspace.
|
||||
|
||||
config MHI_SATELLITE
|
||||
tristate "MHI SATELLITE"
|
||||
depends on MHI_BUS
|
||||
help
|
||||
MHI proxy satellite device driver enables NON-HLOS MHI satellite
|
||||
drivers to communicate with device over PCIe link without host
|
||||
involvement. Host facilitates propagation of events from device
|
||||
to NON-HLOS MHI satellite drivers, channel states, and power
|
||||
management over IPC communication. It helps in HLOS power
|
||||
savings.
|
||||
|
||||
endmenu
|
||||
3
applications/quectel_MHI/src/devices/Makefile
Normal file
3
applications/quectel_MHI/src/devices/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
obj-$(CONFIG_MHI_NETDEV) +=mhi_netdev.o
|
||||
obj-$(CONFIG_MHI_UCI) +=mhi_uci.o
|
||||
obj-$(CONFIG_MHI_SATELLITE) +=mhi_satellite.o
|
||||
1063
applications/quectel_MHI/src/devices/mhi_netdev.c
Normal file
1063
applications/quectel_MHI/src/devices/mhi_netdev.c
Normal file
File diff suppressed because it is too large
Load Diff
2908
applications/quectel_MHI/src/devices/mhi_netdev_quectel.c
Normal file
2908
applications/quectel_MHI/src/devices/mhi_netdev_quectel.c
Normal file
File diff suppressed because it is too large
Load Diff
1153
applications/quectel_MHI/src/devices/mhi_satellite.c
Normal file
1153
applications/quectel_MHI/src/devices/mhi_satellite.c
Normal file
File diff suppressed because it is too large
Load Diff
937
applications/quectel_MHI/src/devices/mhi_uci.c
Normal file
937
applications/quectel_MHI/src/devices/mhi_uci.c
Normal file
@@ -0,0 +1,937 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-direction.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#if 1
|
||||
static inline void *ipc_log_context_create(int max_num_pages,
|
||||
const char *modname, uint16_t user_version)
|
||||
{ return NULL; }
|
||||
static inline int ipc_log_string(void *ilctxt, const char *fmt, ...)
|
||||
{ return -EINVAL; }
|
||||
#endif
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/tty.h>
|
||||
#include "../core/mhi.h"
|
||||
|
||||
#define DEVICE_NAME "mhi"
|
||||
#define MHI_UCI_DRIVER_NAME "mhi_uci_q"
|
||||
|
||||
struct uci_chan {
|
||||
wait_queue_head_t wq;
|
||||
spinlock_t lock;
|
||||
struct list_head pending; /* user space waiting to read */
|
||||
struct uci_buf *cur_buf; /* current buffer user space reading */
|
||||
size_t rx_size;
|
||||
};
|
||||
|
||||
struct uci_buf {
|
||||
struct page *page;
|
||||
void *data;
|
||||
size_t len;
|
||||
unsigned nr_trb;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct uci_dev {
|
||||
struct list_head node;
|
||||
dev_t devt;
|
||||
struct device *dev;
|
||||
struct mhi_device *mhi_dev;
|
||||
const char *chan;
|
||||
struct mutex mutex; /* sync open and close */
|
||||
struct mutex r_mutex;
|
||||
struct mutex w_mutex;
|
||||
struct uci_chan ul_chan;
|
||||
struct uci_chan dl_chan;
|
||||
size_t mtu;
|
||||
int ref_count;
|
||||
bool enabled;
|
||||
unsigned rx_error;
|
||||
unsigned nr_trb;
|
||||
unsigned nr_trbs;
|
||||
struct uci_buf *uci_buf;
|
||||
struct ktermios termios;
|
||||
size_t bytes_xferd;
|
||||
};
|
||||
|
||||
struct mhi_uci_drv {
|
||||
struct list_head head;
|
||||
struct mutex lock;
|
||||
struct class *class;
|
||||
int major;
|
||||
dev_t dev_t;
|
||||
};
|
||||
|
||||
static int uci_msg_lvl = MHI_MSG_LVL_ERROR;
|
||||
module_param( uci_msg_lvl, uint, S_IRUGO | S_IWUSR);
|
||||
|
||||
#define MSG_VERB(fmt, ...) do { \
|
||||
if (uci_msg_lvl <= MHI_MSG_LVL_VERBOSE) \
|
||||
pr_err("[D][%s] " fmt, __func__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define MSG_LOG(fmt, ...) do { \
|
||||
if (uci_msg_lvl <= MHI_MSG_LVL_INFO) \
|
||||
pr_err("[I][%s] " fmt, __func__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define MSG_ERR(fmt, ...) do { \
|
||||
if (uci_msg_lvl <= MHI_MSG_LVL_ERROR) \
|
||||
pr_err("[E][%s] " fmt, __func__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define MAX_UCI_DEVICES (64)
|
||||
#define QUEC_MHI_UCI_ALWAYS_OPEN //by now, sdx20 can not handle "start-reset-start" operation, so the simply solution is keep start state
|
||||
|
||||
static DECLARE_BITMAP(uci_minors, MAX_UCI_DEVICES);
|
||||
static struct mhi_uci_drv mhi_uci_drv;
|
||||
|
||||
static int mhi_queue_inbound(struct uci_dev *uci_dev)
|
||||
{
|
||||
struct mhi_device *mhi_dev = uci_dev->mhi_dev;
|
||||
int nr_trbs = mhi_get_no_free_descriptors(mhi_dev, DMA_FROM_DEVICE);
|
||||
size_t mtu = uci_dev->mtu;
|
||||
void *buf;
|
||||
struct uci_buf *uci_buf;
|
||||
int ret = -EIO, i;
|
||||
|
||||
if (uci_dev->uci_buf == NULL) {
|
||||
uci_dev->nr_trb = 0;
|
||||
uci_dev->nr_trbs = (nr_trbs + 1);
|
||||
uci_dev->uci_buf = kmalloc_array(uci_dev->nr_trbs, sizeof(*uci_buf), GFP_KERNEL);
|
||||
if (!uci_dev->uci_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
uci_buf = uci_dev->uci_buf;
|
||||
for (i = 0; i < uci_dev->nr_trbs; i++, uci_buf++) {
|
||||
uci_buf->page = alloc_pages(GFP_KERNEL, get_order(mtu));
|
||||
if (!uci_buf->page)
|
||||
return -ENOMEM;
|
||||
uci_buf->data = page_address(uci_buf->page);
|
||||
uci_buf->len = 0;
|
||||
uci_buf->nr_trb = i;
|
||||
if (mhi_dev->dl_chan_id == MHI_CLIENT_DUN_IN) {
|
||||
//MSG_ERR("[%d] = %p\n", i, uci_buf->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_trbs; i++) {
|
||||
#if 0
|
||||
buf = kmalloc(mtu + sizeof(*uci_buf), GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
uci_buf = buf + mtu;
|
||||
uci_buf->data = buf;
|
||||
#else
|
||||
uci_buf = &uci_dev->uci_buf[i];
|
||||
buf = uci_buf->data;
|
||||
#endif
|
||||
|
||||
MSG_VERB("Allocated buf %d of %d size %zu\n", i, nr_trbs, mtu);
|
||||
|
||||
ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, buf, mtu,
|
||||
MHI_EOT);
|
||||
if (ret) {
|
||||
#if 0
|
||||
kfree(buf);
|
||||
#endif
|
||||
MSG_ERR("Failed to queue buffer %d\n", i);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||
#ifdef TCGETS2
|
||||
static int kernel_termios_to_user_termios_1(struct termios __user *u,
|
||||
struct ktermios *k)
|
||||
{
|
||||
return copy_to_user(u, k, sizeof(struct termios));
|
||||
}
|
||||
static int user_termios_to_kernel_termios_1(struct ktermios *k,
|
||||
struct termios __user *u)
|
||||
{
|
||||
return copy_from_user(k, u, sizeof(struct termios));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
static long mhi_uci_ioctl(struct file *file,
|
||||
unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct uci_dev *uci_dev = file->private_data;
|
||||
struct mhi_device *mhi_dev = uci_dev->mhi_dev;
|
||||
long ret = -ERESTARTSYS;
|
||||
|
||||
mutex_lock(&uci_dev->mutex);
|
||||
if (uci_dev->enabled)
|
||||
ret = mhi_ioctl(mhi_dev, cmd, arg);
|
||||
|
||||
if (uci_dev->enabled) {
|
||||
switch (cmd) {
|
||||
case TCGETS:
|
||||
#ifndef TCGETS2
|
||||
ret = kernel_termios_to_user_termios((struct termios __user *)arg, &uci_dev->termios);
|
||||
#else
|
||||
ret = kernel_termios_to_user_termios_1((struct termios __user *)arg, &uci_dev->termios);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case TCSETSF:
|
||||
case TCSETS:
|
||||
#ifndef TCGETS2
|
||||
ret = user_termios_to_kernel_termios(&uci_dev->termios, (struct termios __user *)arg);
|
||||
#else
|
||||
ret = user_termios_to_kernel_termios_1(&uci_dev->termios, (struct termios __user *)arg);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case TCFLSH:
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&uci_dev->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mhi_uci_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct uci_dev *uci_dev = file->private_data;
|
||||
|
||||
mutex_lock(&uci_dev->mutex);
|
||||
uci_dev->ref_count--;
|
||||
if (!uci_dev->ref_count) {
|
||||
struct uci_chan *uci_chan;
|
||||
|
||||
MSG_LOG("Last client left, closing node\n");
|
||||
|
||||
if (uci_dev->enabled)
|
||||
mhi_unprepare_from_transfer(uci_dev->mhi_dev);
|
||||
|
||||
/* clean inbound channel */
|
||||
uci_chan = &uci_dev->dl_chan;
|
||||
if (uci_dev->uci_buf) {
|
||||
unsigned nr_trb = 0;
|
||||
|
||||
for (nr_trb = 0; nr_trb < uci_dev->nr_trbs; nr_trb++) {
|
||||
if (uci_dev->uci_buf[nr_trb].page)
|
||||
__free_pages(uci_dev->uci_buf[nr_trb].page, get_order(uci_dev->mtu));
|
||||
}
|
||||
kfree(uci_dev->uci_buf);
|
||||
}
|
||||
|
||||
uci_chan->cur_buf = NULL;
|
||||
|
||||
if (!uci_dev->enabled) {
|
||||
MSG_LOG("Node is deleted, freeing dev node\n");
|
||||
mutex_unlock(&uci_dev->mutex);
|
||||
mutex_destroy(&uci_dev->mutex);
|
||||
clear_bit(MINOR(uci_dev->devt), uci_minors);
|
||||
kfree(uci_dev);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
MSG_LOG("exit: ref_count:%d\n", uci_dev->ref_count);
|
||||
|
||||
mutex_unlock(&uci_dev->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int mhi_uci_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct uci_dev *uci_dev = file->private_data;
|
||||
struct mhi_device *mhi_dev = uci_dev->mhi_dev;
|
||||
struct uci_chan *uci_chan;
|
||||
unsigned int mask = 0;
|
||||
|
||||
poll_wait(file, &uci_dev->dl_chan.wq, wait);
|
||||
poll_wait(file, &uci_dev->ul_chan.wq, wait);
|
||||
|
||||
uci_chan = &uci_dev->dl_chan;
|
||||
spin_lock_bh(&uci_chan->lock);
|
||||
if (!uci_dev->enabled) {
|
||||
mask = POLLERR;
|
||||
} else if (!list_empty(&uci_chan->pending) || uci_chan->cur_buf) {
|
||||
MSG_VERB("Client can read from node\n");
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
}
|
||||
spin_unlock_bh(&uci_chan->lock);
|
||||
|
||||
uci_chan = &uci_dev->ul_chan;
|
||||
spin_lock_bh(&uci_chan->lock);
|
||||
if (!uci_dev->enabled) {
|
||||
mask |= POLLERR;
|
||||
} else if (mhi_get_no_free_descriptors(mhi_dev, DMA_TO_DEVICE) > 0) {
|
||||
MSG_VERB("Client can write to node\n");
|
||||
mask |= POLLOUT | POLLWRNORM;
|
||||
}
|
||||
|
||||
if (!uci_dev->enabled)
|
||||
mask |= POLLHUP;
|
||||
if (uci_dev->rx_error)
|
||||
mask |= POLLERR;
|
||||
|
||||
spin_unlock_bh(&uci_chan->lock);
|
||||
|
||||
MSG_LOG("Client attempted to poll, returning mask 0x%x\n", mask);
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static ssize_t mhi_uci_write(struct file *file,
|
||||
const char __user *buf,
|
||||
size_t count,
|
||||
loff_t *offp)
|
||||
{
|
||||
struct uci_dev *uci_dev = file->private_data;
|
||||
struct mhi_device *mhi_dev = uci_dev->mhi_dev;
|
||||
struct uci_chan *uci_chan = &uci_dev->ul_chan;
|
||||
size_t bytes_xfered = 0;
|
||||
int ret, nr_avail;
|
||||
|
||||
if (!buf || !count || uci_dev->rx_error)
|
||||
return -EINVAL;
|
||||
|
||||
/* confirm channel is active */
|
||||
spin_lock_bh(&uci_chan->lock);
|
||||
if (!uci_dev->enabled) {
|
||||
spin_unlock_bh(&uci_chan->lock);
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
MSG_VERB("Enter: to xfer:%zu bytes\n", count);
|
||||
|
||||
while (count) {
|
||||
size_t xfer_size;
|
||||
void *kbuf;
|
||||
enum MHI_FLAGS flags;
|
||||
|
||||
spin_unlock_bh(&uci_chan->lock);
|
||||
|
||||
nr_avail = mhi_get_no_free_descriptors(mhi_dev, DMA_TO_DEVICE);
|
||||
if ((nr_avail == 0) && (file->f_flags & O_NONBLOCK))
|
||||
return -EAGAIN;
|
||||
|
||||
/* wait for free descriptors */
|
||||
ret = wait_event_interruptible(uci_chan->wq,
|
||||
(!uci_dev->enabled) ||
|
||||
(nr_avail = mhi_get_no_free_descriptors(mhi_dev,
|
||||
DMA_TO_DEVICE)) > 0);
|
||||
|
||||
if (ret == -ERESTARTSYS || !uci_dev->enabled) {
|
||||
MSG_LOG("Exit signal caught for node or not enabled\n");
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
xfer_size = min_t(size_t, count, uci_dev->mtu);
|
||||
kbuf = kmalloc(xfer_size, GFP_KERNEL);
|
||||
if (!kbuf) {
|
||||
MSG_ERR("Failed to allocate memory %zu\n", xfer_size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = copy_from_user(kbuf, buf, xfer_size);
|
||||
if (unlikely(ret)) {
|
||||
kfree(kbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
spin_lock_bh(&uci_chan->lock);
|
||||
|
||||
/* if ring is full after this force EOT */
|
||||
if (nr_avail > 1 && (count - xfer_size))
|
||||
flags = MHI_CHAIN;
|
||||
else
|
||||
flags = MHI_EOT;
|
||||
|
||||
if (uci_dev->enabled)
|
||||
ret = mhi_queue_transfer(mhi_dev, DMA_TO_DEVICE, kbuf,
|
||||
xfer_size, flags);
|
||||
else
|
||||
ret = -ERESTARTSYS;
|
||||
|
||||
if (ret) {
|
||||
kfree(kbuf);
|
||||
goto sys_interrupt;
|
||||
}
|
||||
|
||||
bytes_xfered += xfer_size;
|
||||
count -= xfer_size;
|
||||
buf += xfer_size;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&uci_chan->lock);
|
||||
MSG_VERB("Exit: Number of bytes xferred:%zu\n", bytes_xfered);
|
||||
|
||||
return bytes_xfered;
|
||||
|
||||
sys_interrupt:
|
||||
spin_unlock_bh(&uci_chan->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t mhi_uci_read(struct file *file,
|
||||
char __user *buf,
|
||||
size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct uci_dev *uci_dev = file->private_data;
|
||||
struct mhi_device *mhi_dev = uci_dev->mhi_dev;
|
||||
struct uci_chan *uci_chan = &uci_dev->dl_chan;
|
||||
struct uci_buf *uci_buf;
|
||||
char *ptr;
|
||||
size_t to_copy;
|
||||
int ret = 0;
|
||||
|
||||
if (!buf || uci_dev->rx_error)
|
||||
return -EINVAL;
|
||||
|
||||
MSG_VERB("Client provided buf len:%zu\n", count);
|
||||
|
||||
/* confirm channel is active */
|
||||
spin_lock_bh(&uci_chan->lock);
|
||||
if (!uci_dev->enabled) {
|
||||
spin_unlock_bh(&uci_chan->lock);
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
/* No data available to read, wait */
|
||||
if (!uci_chan->cur_buf && list_empty(&uci_chan->pending)) {
|
||||
MSG_VERB("No data available to read waiting\n");
|
||||
|
||||
spin_unlock_bh(&uci_chan->lock);
|
||||
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = wait_event_interruptible(uci_chan->wq,
|
||||
(!uci_dev->enabled ||
|
||||
!list_empty(&uci_chan->pending)));
|
||||
if (ret == -ERESTARTSYS) {
|
||||
MSG_LOG("Exit signal caught for node\n");
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
spin_lock_bh(&uci_chan->lock);
|
||||
if (!uci_dev->enabled) {
|
||||
MSG_LOG("node is disabled\n");
|
||||
ret = -ERESTARTSYS;
|
||||
goto read_error;
|
||||
}
|
||||
}
|
||||
|
||||
/* new read, get the next descriptor from the list */
|
||||
if (!uci_chan->cur_buf) {
|
||||
uci_buf = list_first_entry_or_null(&uci_chan->pending,
|
||||
struct uci_buf, node);
|
||||
if (unlikely(!uci_buf)) {
|
||||
ret = -EIO;
|
||||
goto read_error;
|
||||
}
|
||||
|
||||
if (uci_buf->node.next == LIST_POISON1 || uci_buf->node.prev == LIST_POISON1) {
|
||||
dump_stack();
|
||||
ret = -EIO;
|
||||
MSG_ERR("chan[%d] data=%p, len=%zd, nr_trb=%d\n",
|
||||
mhi_dev->dl_chan_id, uci_buf->data, uci_buf->len, uci_buf->nr_trb);
|
||||
goto read_error;
|
||||
}
|
||||
|
||||
list_del(&uci_buf->node);
|
||||
uci_chan->cur_buf = uci_buf;
|
||||
uci_chan->rx_size = uci_buf->len;
|
||||
MSG_VERB("Got pkt of size:%zu\n", uci_chan->rx_size);
|
||||
}
|
||||
|
||||
uci_buf = uci_chan->cur_buf;
|
||||
spin_unlock_bh(&uci_chan->lock);
|
||||
|
||||
/* Copy the buffer to user space */
|
||||
to_copy = min_t(size_t, count, uci_chan->rx_size);
|
||||
ptr = uci_buf->data + (uci_buf->len - uci_chan->rx_size);
|
||||
ret = copy_to_user(buf, ptr, to_copy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
MSG_VERB("Copied %zu of %zu bytes\n", to_copy, uci_chan->rx_size);
|
||||
uci_chan->rx_size -= to_copy;
|
||||
|
||||
/* we finished with this buffer, queue it back to hardware */
|
||||
if (!uci_chan->rx_size) {
|
||||
spin_lock_bh(&uci_chan->lock);
|
||||
uci_chan->cur_buf = NULL;
|
||||
|
||||
if (uci_dev->enabled)
|
||||
#if 1 //this can make the address in ring do not change
|
||||
{
|
||||
if (uci_buf->page) {
|
||||
unsigned nr_trb = uci_buf->nr_trb ? (uci_buf->nr_trb - 1) : (uci_dev->nr_trbs - 1);
|
||||
|
||||
uci_buf = &uci_dev->uci_buf[nr_trb];
|
||||
ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE,
|
||||
uci_buf->data, uci_dev->mtu,
|
||||
MHI_EOT);
|
||||
} else {
|
||||
kfree(uci_buf);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else
|
||||
ret = -ERESTARTSYS;
|
||||
|
||||
if (ret) {
|
||||
MSG_ERR("Failed to recycle element for chan:%d , ret=%d\n", mhi_dev->ul_chan_id, ret);
|
||||
#if 0
|
||||
kfree(uci_buf->data);
|
||||
#endif
|
||||
goto read_error;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&uci_chan->lock);
|
||||
}
|
||||
|
||||
MSG_VERB("Returning %zu bytes\n", to_copy);
|
||||
|
||||
return to_copy;
|
||||
|
||||
read_error:
|
||||
spin_unlock_bh(&uci_chan->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t mhi_uci_write_mutex(struct file *file,
|
||||
const char __user *buf,
|
||||
size_t count,
|
||||
loff_t *offp)
|
||||
{
|
||||
struct uci_dev *uci_dev = file->private_data;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&uci_dev->w_mutex); /*concurrent writes */
|
||||
if (ret < 0)
|
||||
return -ERESTARTSYS;
|
||||
|
||||
ret = mhi_uci_write(file, buf, count, offp);
|
||||
mutex_unlock(&uci_dev->w_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t mhi_uci_read_mutex(struct file *file,
|
||||
char __user *buf,
|
||||
size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct uci_dev *uci_dev = file->private_data;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&uci_dev->r_mutex); /*concurrent reads */
|
||||
if (ret < 0)
|
||||
return -ERESTARTSYS;
|
||||
|
||||
ret = mhi_uci_read(file, buf, count, ppos);
|
||||
mutex_unlock(&uci_dev->r_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mhi_uci_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct uci_dev *uci_dev = NULL, *tmp_dev;
|
||||
int ret = -EIO;
|
||||
struct uci_chan *dl_chan;
|
||||
|
||||
mutex_lock(&mhi_uci_drv.lock);
|
||||
list_for_each_entry(tmp_dev, &mhi_uci_drv.head, node) {
|
||||
if (tmp_dev->devt == inode->i_rdev) {
|
||||
uci_dev = tmp_dev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* could not find a minor node */
|
||||
if (!uci_dev)
|
||||
goto error_exit;
|
||||
|
||||
mutex_lock(&uci_dev->mutex);
|
||||
if (!uci_dev->enabled) {
|
||||
MSG_ERR("Node exist, but not in active state!\n");
|
||||
goto error_open_chan;
|
||||
}
|
||||
|
||||
uci_dev->ref_count++;
|
||||
|
||||
MSG_LOG("Node open, ref counts %u\n", uci_dev->ref_count);
|
||||
|
||||
if (uci_dev->ref_count == 1) {
|
||||
MSG_LOG("Starting channel\n");
|
||||
ret = mhi_prepare_for_transfer(uci_dev->mhi_dev);
|
||||
if (ret) {
|
||||
MSG_ERR("Error starting transfer channels\n");
|
||||
uci_dev->ref_count--;
|
||||
goto error_open_chan;
|
||||
}
|
||||
|
||||
ret = mhi_queue_inbound(uci_dev);
|
||||
if (ret)
|
||||
goto error_rx_queue;
|
||||
|
||||
#ifdef QUEC_MHI_UCI_ALWAYS_OPEN
|
||||
uci_dev->ref_count++;
|
||||
#endif
|
||||
}
|
||||
|
||||
filp->private_data = uci_dev;
|
||||
mutex_unlock(&uci_dev->mutex);
|
||||
mutex_unlock(&mhi_uci_drv.lock);
|
||||
|
||||
return 0;
|
||||
|
||||
error_rx_queue:
|
||||
dl_chan = &uci_dev->dl_chan;
|
||||
mhi_unprepare_from_transfer(uci_dev->mhi_dev);
|
||||
if (uci_dev->uci_buf) {
|
||||
unsigned nr_trb = 0;
|
||||
|
||||
for (nr_trb = 0; nr_trb < uci_dev->nr_trbs; nr_trb++) {
|
||||
if (uci_dev->uci_buf[nr_trb].page)
|
||||
__free_pages(uci_dev->uci_buf[nr_trb].page, get_order(uci_dev->mtu));
|
||||
}
|
||||
kfree(uci_dev->uci_buf);
|
||||
}
|
||||
|
||||
error_open_chan:
|
||||
mutex_unlock(&uci_dev->mutex);
|
||||
|
||||
error_exit:
|
||||
mutex_unlock(&mhi_uci_drv.lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations mhidev_fops = {
|
||||
.open = mhi_uci_open,
|
||||
.release = mhi_uci_release,
|
||||
.read = mhi_uci_read_mutex,
|
||||
.write = mhi_uci_write_mutex,
|
||||
.poll = mhi_uci_poll,
|
||||
.unlocked_ioctl = mhi_uci_ioctl,
|
||||
};
|
||||
|
||||
static void mhi_uci_remove(struct mhi_device *mhi_dev)
|
||||
{
|
||||
struct uci_dev *uci_dev = mhi_device_get_devdata(mhi_dev);
|
||||
|
||||
MSG_LOG("Enter\n");
|
||||
|
||||
|
||||
mutex_lock(&mhi_uci_drv.lock);
|
||||
mutex_lock(&uci_dev->mutex);
|
||||
|
||||
/* disable the node */
|
||||
spin_lock_irq(&uci_dev->dl_chan.lock);
|
||||
spin_lock_irq(&uci_dev->ul_chan.lock);
|
||||
uci_dev->enabled = false;
|
||||
spin_unlock_irq(&uci_dev->ul_chan.lock);
|
||||
spin_unlock_irq(&uci_dev->dl_chan.lock);
|
||||
wake_up(&uci_dev->dl_chan.wq);
|
||||
wake_up(&uci_dev->ul_chan.wq);
|
||||
|
||||
/* delete the node to prevent new opens */
|
||||
device_destroy(mhi_uci_drv.class, uci_dev->devt);
|
||||
uci_dev->dev = NULL;
|
||||
list_del(&uci_dev->node);
|
||||
|
||||
#ifdef QUEC_MHI_UCI_ALWAYS_OPEN
|
||||
if (uci_dev->ref_count > 0)
|
||||
uci_dev->ref_count--;
|
||||
#endif
|
||||
|
||||
/* safe to free memory only if all file nodes are closed */
|
||||
if (!uci_dev->ref_count) {
|
||||
mutex_unlock(&uci_dev->mutex);
|
||||
mutex_destroy(&uci_dev->mutex);
|
||||
clear_bit(MINOR(uci_dev->devt), uci_minors);
|
||||
kfree(uci_dev);
|
||||
mutex_unlock(&mhi_uci_drv.lock);
|
||||
return;
|
||||
}
|
||||
|
||||
MSG_LOG("Exit\n");
|
||||
mutex_unlock(&uci_dev->mutex);
|
||||
mutex_unlock(&mhi_uci_drv.lock);
|
||||
|
||||
}
|
||||
|
||||
static int mhi_uci_probe(struct mhi_device *mhi_dev,
|
||||
const struct mhi_device_id *id)
|
||||
{
|
||||
struct uci_dev *uci_dev;
|
||||
int minor;
|
||||
char node_name[32];
|
||||
int dir;
|
||||
|
||||
uci_dev = kzalloc(sizeof(*uci_dev), GFP_KERNEL);
|
||||
if (!uci_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&uci_dev->mutex);
|
||||
mutex_init(&uci_dev->r_mutex);
|
||||
mutex_init(&uci_dev->w_mutex);
|
||||
uci_dev->mhi_dev = mhi_dev;
|
||||
|
||||
minor = find_first_zero_bit(uci_minors, MAX_UCI_DEVICES);
|
||||
if (minor >= MAX_UCI_DEVICES) {
|
||||
kfree(uci_dev);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
mutex_lock(&uci_dev->mutex);
|
||||
mutex_lock(&mhi_uci_drv.lock);
|
||||
|
||||
uci_dev->devt = MKDEV(mhi_uci_drv.major, minor);
|
||||
#if 1
|
||||
if (mhi_dev->mhi_cntrl->cntrl_idx)
|
||||
uci_dev->dev = device_create(mhi_uci_drv.class, &mhi_dev->dev,
|
||||
uci_dev->devt, uci_dev,
|
||||
DEVICE_NAME "_%s%d",
|
||||
mhi_dev->chan_name, mhi_dev->mhi_cntrl->cntrl_idx);
|
||||
else
|
||||
uci_dev->dev = device_create(mhi_uci_drv.class, &mhi_dev->dev,
|
||||
uci_dev->devt, uci_dev,
|
||||
DEVICE_NAME "_%s",
|
||||
mhi_dev->chan_name);
|
||||
#else
|
||||
uci_dev->dev = device_create(mhi_uci_drv.class, &mhi_dev->dev,
|
||||
uci_dev->devt, uci_dev,
|
||||
DEVICE_NAME "_%04x_%02u.%02u.%02u%s%d",
|
||||
mhi_dev->dev_id, mhi_dev->domain,
|
||||
mhi_dev->bus, mhi_dev->slot, "_pipe_",
|
||||
mhi_dev->ul_chan_id);
|
||||
#endif
|
||||
|
||||
set_bit(minor, uci_minors);
|
||||
|
||||
/* create debugging buffer */
|
||||
snprintf(node_name, sizeof(node_name), "mhi_uci_%04x_%02u.%02u.%02u_%d",
|
||||
mhi_dev->dev_id, mhi_dev->domain, mhi_dev->bus, mhi_dev->slot,
|
||||
mhi_dev->ul_chan_id);
|
||||
|
||||
for (dir = 0; dir < 2; dir++) {
|
||||
struct uci_chan *uci_chan = (dir) ?
|
||||
&uci_dev->ul_chan : &uci_dev->dl_chan;
|
||||
spin_lock_init(&uci_chan->lock);
|
||||
init_waitqueue_head(&uci_chan->wq);
|
||||
INIT_LIST_HEAD(&uci_chan->pending);
|
||||
}
|
||||
|
||||
uci_dev->termios = tty_std_termios;
|
||||
|
||||
uci_dev->mtu = min_t(size_t, id->driver_data, mhi_dev->mtu);
|
||||
mhi_device_set_devdata(mhi_dev, uci_dev);
|
||||
uci_dev->enabled = true;
|
||||
|
||||
list_add(&uci_dev->node, &mhi_uci_drv.head);
|
||||
mutex_unlock(&mhi_uci_drv.lock);
|
||||
mutex_unlock(&uci_dev->mutex);
|
||||
|
||||
MSG_LOG("channel:%s successfully probed\n", mhi_dev->chan_name);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static void mhi_ul_xfer_cb(struct mhi_device *mhi_dev,
|
||||
struct mhi_result *mhi_result)
|
||||
{
|
||||
struct uci_dev *uci_dev = mhi_device_get_devdata(mhi_dev);
|
||||
struct uci_chan *uci_chan = &uci_dev->ul_chan;
|
||||
|
||||
MSG_VERB("status:%d xfer_len:%zu\n", mhi_result->transaction_status,
|
||||
mhi_result->bytes_xferd);
|
||||
|
||||
kfree(mhi_result->buf_addr);
|
||||
if (!mhi_result->transaction_status)
|
||||
wake_up(&uci_chan->wq);
|
||||
}
|
||||
|
||||
static void mhi_dl_xfer_cb(struct mhi_device *mhi_dev,
|
||||
struct mhi_result *mhi_result)
|
||||
{
|
||||
struct uci_dev *uci_dev = mhi_device_get_devdata(mhi_dev);
|
||||
struct uci_chan *uci_chan = &uci_dev->dl_chan;
|
||||
unsigned long flags;
|
||||
struct uci_buf *buf;
|
||||
unsigned nr_trb = uci_dev->nr_trb;
|
||||
|
||||
buf = &uci_dev->uci_buf[nr_trb];
|
||||
if (buf->nr_trb != nr_trb || buf->data != mhi_result->buf_addr)
|
||||
{
|
||||
uci_dev->rx_error++;
|
||||
MSG_ERR("chan[%d]: uci_buf[%u] = %p , mhi_result[%u] = %p\n",
|
||||
mhi_dev->dl_chan_id, buf->nr_trb, buf->data, nr_trb, mhi_result->buf_addr);
|
||||
return;
|
||||
}
|
||||
|
||||
uci_dev->nr_trb++;
|
||||
if (uci_dev->nr_trb == uci_dev->nr_trbs)
|
||||
uci_dev->nr_trb = 0;
|
||||
|
||||
if (mhi_result->transaction_status == -ENOTCONN) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mhi_result->bytes_xferd > uci_dev->mtu || mhi_result->bytes_xferd <= 0)
|
||||
{
|
||||
MSG_ERR("chan[%d]: bytes_xferd = %zd , mtu = %zd\n",
|
||||
mhi_dev->dl_chan_id, mhi_result->bytes_xferd, uci_dev->mtu);
|
||||
return;
|
||||
}
|
||||
if (mhi_result->bytes_xferd > uci_dev->bytes_xferd)
|
||||
{
|
||||
uci_dev->bytes_xferd = mhi_result->bytes_xferd;
|
||||
//MSG_ERR("chan[%d]: bytes_xferd = %zd , mtu = %zd\n",
|
||||
// mhi_dev->dl_chan_id, mhi_result->bytes_xferd, uci_dev->mtu);
|
||||
}
|
||||
|
||||
MSG_VERB("status:%d receive_len:%zu\n", mhi_result->transaction_status,
|
||||
mhi_result->bytes_xferd);
|
||||
|
||||
spin_lock_irqsave(&uci_chan->lock, flags);
|
||||
#if 0
|
||||
buf = mhi_result->buf_addr + uci_dev->mtu;
|
||||
buf->data = mhi_result->buf_addr;
|
||||
#endif
|
||||
buf->len = mhi_result->bytes_xferd;
|
||||
if (mhi_dev->dl_chan_id == MHI_CLIENT_DUN_IN
|
||||
|| mhi_dev->dl_chan_id == MHI_CLIENT_QMI_IN
|
||||
|| mhi_dev->dl_chan_id == MHI_CLIENT_MBIM_IN)
|
||||
{
|
||||
struct uci_buf *tmp_buf = NULL;
|
||||
int skip_buf = 0;
|
||||
|
||||
#ifdef QUEC_MHI_UCI_ALWAYS_OPEN
|
||||
if (uci_dev->ref_count == 1)
|
||||
skip_buf++;
|
||||
#endif
|
||||
if (!skip_buf)
|
||||
tmp_buf = (struct uci_buf *)kmalloc(buf->len + sizeof(struct uci_buf), GFP_ATOMIC);;
|
||||
|
||||
if (tmp_buf) {
|
||||
tmp_buf->page = NULL;
|
||||
tmp_buf->data = ((void *)tmp_buf) + sizeof(struct uci_buf);
|
||||
tmp_buf->len = buf->len;
|
||||
memcpy(tmp_buf->data, buf->data, buf->len);
|
||||
}
|
||||
|
||||
if (buf) {
|
||||
struct uci_buf *uci_buf = buf;
|
||||
unsigned nr_trb = uci_buf->nr_trb ? (uci_buf->nr_trb - 1) : (uci_dev->nr_trbs - 1);
|
||||
|
||||
uci_buf = &uci_dev->uci_buf[nr_trb];
|
||||
mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, uci_buf->data, uci_dev->mtu, MHI_EOT);
|
||||
}
|
||||
|
||||
buf = tmp_buf;
|
||||
}
|
||||
|
||||
if (buf)
|
||||
list_add_tail(&buf->node, &uci_chan->pending);
|
||||
spin_unlock_irqrestore(&uci_chan->lock, flags);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
if (mhi_dev->dev.power.wakeup)
|
||||
__pm_wakeup_event(mhi_dev->dev.power.wakeup, 0);
|
||||
#endif
|
||||
|
||||
wake_up(&uci_chan->wq);
|
||||
}
|
||||
|
||||
#define DIAG_MAX_PCIE_PKT_SZ 2048 //define by module
|
||||
|
||||
/* .driver_data stores max mtu */
|
||||
static const struct mhi_device_id mhi_uci_match_table[] = {
|
||||
{ .chan = "LOOPBACK", .driver_data = 0x1000 },
|
||||
{ .chan = "SAHARA", .driver_data = 0x4000 },
|
||||
{ .chan = "EDL", .driver_data = 0x4000 },
|
||||
{ .chan = "DIAG", .driver_data = DIAG_MAX_PCIE_PKT_SZ },
|
||||
{ .chan = "MBIM", .driver_data = 0x1000 },
|
||||
{ .chan = "QMI0", .driver_data = 0x1000 },
|
||||
{ .chan = "QMI1", .driver_data = 0x1000 },
|
||||
{ .chan = "DUN", .driver_data = 0x1000 },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct mhi_driver mhi_uci_driver = {
|
||||
.id_table = mhi_uci_match_table,
|
||||
.remove = mhi_uci_remove,
|
||||
.probe = mhi_uci_probe,
|
||||
.ul_xfer_cb = mhi_ul_xfer_cb,
|
||||
.dl_xfer_cb = mhi_dl_xfer_cb,
|
||||
.driver = {
|
||||
.name = MHI_UCI_DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
int mhi_device_uci_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = register_chrdev(0, MHI_UCI_DRIVER_NAME, &mhidev_fops);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mhi_uci_drv.major = ret;
|
||||
mhi_uci_drv.class = class_create(THIS_MODULE, MHI_UCI_DRIVER_NAME);
|
||||
if (IS_ERR(mhi_uci_drv.class)) {
|
||||
unregister_chrdev(mhi_uci_drv.major, MHI_UCI_DRIVER_NAME);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mutex_init(&mhi_uci_drv.lock);
|
||||
INIT_LIST_HEAD(&mhi_uci_drv.head);
|
||||
|
||||
ret = mhi_driver_register(&mhi_uci_driver);
|
||||
if (ret) {
|
||||
class_destroy(mhi_uci_drv.class);
|
||||
unregister_chrdev(mhi_uci_drv.major, MHI_UCI_DRIVER_NAME);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mhi_device_uci_exit(void)
|
||||
{
|
||||
mhi_driver_unregister(&mhi_uci_driver);
|
||||
class_destroy(mhi_uci_drv.class);
|
||||
unregister_chrdev(mhi_uci_drv.major, MHI_UCI_DRIVER_NAME);
|
||||
}
|
||||
13
applications/quectel_MHI/src/devices/rmnet/Kconfig
Normal file
13
applications/quectel_MHI/src/devices/rmnet/Kconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
#
|
||||
# RMNET MAP driver
|
||||
#
|
||||
|
||||
menuconfig RMNET
|
||||
tristate "RmNet MAP driver"
|
||||
default n
|
||||
select GRO_CELLS
|
||||
---help---
|
||||
If you select this, you will enable the RMNET module which is used
|
||||
for handling data in the multiplexing and aggregation protocol (MAP)
|
||||
format in the embedded data path. RMNET devices can be attached to
|
||||
any IP mode physical device.
|
||||
11
applications/quectel_MHI/src/devices/rmnet/Makefile
Normal file
11
applications/quectel_MHI/src/devices/rmnet/Makefile
Normal file
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# Makefile for the RMNET module
|
||||
#
|
||||
|
||||
rmnet-y := rmnet_config.o
|
||||
rmnet-y += rmnet_vnd.o
|
||||
rmnet-y += rmnet_handlers.o
|
||||
rmnet-y += rmnet_map_data.o
|
||||
rmnet-y += rmnet_map_command.o
|
||||
rmnet-y += rmnet_descriptor.o
|
||||
obj-$(CONFIG_RMNET) += rmnet.o
|
||||
141
applications/quectel_MHI/src/devices/rmnet/rmnet_config.c
Normal file
141
applications/quectel_MHI/src/devices/rmnet/rmnet_config.c
Normal file
@@ -0,0 +1,141 @@
|
||||
/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* RMNET configuration engine
|
||||
*
|
||||
*/
|
||||
|
||||
#include <net/sock.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include "rmnet_config.h"
|
||||
#include "rmnet_handlers.h"
|
||||
#include "rmnet_vnd.h"
|
||||
#include "rmnet_private.h"
|
||||
#include "rmnet_map.h"
|
||||
#include "rmnet_descriptor.h"
|
||||
|
||||
/* Locking scheme -
|
||||
* The shared resource which needs to be protected is realdev->rx_handler_data.
|
||||
* For the writer path, this is using rtnl_lock(). The writer paths are
|
||||
* rmnet_newlink(), rmnet_dellink() and rmnet_force_unassociate_device(). These
|
||||
* paths are already called with rtnl_lock() acquired in. There is also an
|
||||
* ASSERT_RTNL() to ensure that we are calling with rtnl acquired. For
|
||||
* dereference here, we will need to use rtnl_dereference(). Dev list writing
|
||||
* needs to happen with rtnl_lock() acquired for netdev_master_upper_dev_link().
|
||||
* For the reader path, the real_dev->rx_handler_data is called in the TX / RX
|
||||
* path. We only need rcu_read_lock() for these scenarios. In these cases,
|
||||
* the rcu_read_lock() is held in __dev_queue_xmit() and
|
||||
* netif_receive_skb_internal(), so readers need to use rcu_dereference_rtnl()
|
||||
* to get the relevant information. For dev list reading, we again acquire
|
||||
* rcu_read_lock() in rmnet_dellink() for netdev_master_upper_dev_get_rcu().
|
||||
* We also use unregister_netdevice_many() to free all rmnet devices in
|
||||
* rmnet_force_unassociate_device() so we dont lose the rtnl_lock() and free in
|
||||
* same context.
|
||||
*/
|
||||
|
||||
/* Local Definitions and Declarations */
|
||||
|
||||
static int rmnet_is_real_dev_registered(const struct net_device *real_dev)
|
||||
{
|
||||
return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler;
|
||||
}
|
||||
|
||||
/* Needs rtnl lock */
|
||||
static struct rmnet_port*
|
||||
rmnet_get_port_rtnl(const struct net_device *real_dev)
|
||||
{
|
||||
return rtnl_dereference(real_dev->rx_handler_data);
|
||||
}
|
||||
|
||||
static int rmnet_unregister_real_device(struct net_device *real_dev,
|
||||
struct rmnet_port *port)
|
||||
{
|
||||
if (port->nr_rmnet_devs)
|
||||
return -EINVAL;
|
||||
|
||||
rmnet_map_cmd_exit(port);
|
||||
rmnet_map_tx_aggregate_exit(port);
|
||||
|
||||
rmnet_descriptor_deinit(port);
|
||||
|
||||
kfree(port);
|
||||
|
||||
netdev_rx_handler_unregister(real_dev);
|
||||
|
||||
/* release reference on real_dev */
|
||||
dev_put(real_dev);
|
||||
|
||||
netdev_dbg(real_dev, "Removed from rmnet\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmnet_register_real_device(struct net_device *real_dev)
|
||||
{
|
||||
struct rmnet_port *port;
|
||||
int rc, entry;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (rmnet_is_real_dev_registered(real_dev))
|
||||
return 0;
|
||||
|
||||
port = kzalloc(sizeof(*port), GFP_ATOMIC);
|
||||
if (!port)
|
||||
return -ENOMEM;
|
||||
|
||||
port->dev = real_dev;
|
||||
rc = netdev_rx_handler_register(real_dev, rmnet_rx_handler, port);
|
||||
if (rc) {
|
||||
kfree(port);
|
||||
return -EBUSY;
|
||||
}
|
||||
/* hold on to real dev for MAP data */
|
||||
dev_hold(real_dev);
|
||||
|
||||
for (entry = 0; entry < RMNET_MAX_LOGICAL_EP; entry++)
|
||||
INIT_HLIST_HEAD(&port->muxed_ep[entry]);
|
||||
|
||||
rc = rmnet_descriptor_init(port);
|
||||
if (rc) {
|
||||
rmnet_descriptor_deinit(port);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rmnet_map_tx_aggregate_init(port);
|
||||
rmnet_map_cmd_init(port);
|
||||
|
||||
netdev_dbg(real_dev, "registered with rmnet\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Needs either rcu_read_lock() or rtnl lock */
|
||||
static struct rmnet_port *rmnet_get_port(struct net_device *real_dev)
|
||||
{
|
||||
if (rmnet_is_real_dev_registered(real_dev))
|
||||
return rcu_dereference_rtnl(real_dev->rx_handler_data);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id)
|
||||
{
|
||||
struct rmnet_endpoint *ep;
|
||||
|
||||
hlist_for_each_entry_rcu(ep, &port->muxed_ep[mux_id], hlnode) {
|
||||
if (ep->mux_id == mux_id)
|
||||
return ep;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
174
applications/quectel_MHI/src/devices/rmnet/rmnet_config.h
Normal file
174
applications/quectel_MHI/src/devices/rmnet/rmnet_config.h
Normal file
@@ -0,0 +1,174 @@
|
||||
/* Copyright (c) 2013-2017, 2019 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* RMNET Data configuration engine
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/gro_cells.h>
|
||||
|
||||
#ifndef _RMNET_CONFIG_H_
|
||||
#define _RMNET_CONFIG_H_
|
||||
|
||||
#define RMNET_MAX_LOGICAL_EP 255
|
||||
#define RMNET_MAX_VEID 4
|
||||
|
||||
struct rmnet_endpoint {
|
||||
u8 mux_id;
|
||||
struct net_device *egress_dev;
|
||||
struct hlist_node hlnode;
|
||||
};
|
||||
|
||||
struct rmnet_port_priv_stats {
|
||||
u64 dl_hdr_last_qmap_vers;
|
||||
u64 dl_hdr_last_ep_id;
|
||||
u64 dl_hdr_last_trans_id;
|
||||
u64 dl_hdr_last_seq;
|
||||
u64 dl_hdr_last_bytes;
|
||||
u64 dl_hdr_last_pkts;
|
||||
u64 dl_hdr_last_flows;
|
||||
u64 dl_hdr_count;
|
||||
u64 dl_hdr_total_bytes;
|
||||
u64 dl_hdr_total_pkts;
|
||||
u64 dl_trl_last_seq;
|
||||
u64 dl_trl_count;
|
||||
};
|
||||
|
||||
struct rmnet_egress_agg_params {
|
||||
u16 agg_size;
|
||||
u16 agg_count;
|
||||
u32 agg_time;
|
||||
};
|
||||
|
||||
/* One instance of this structure is instantiated for each real_dev associated
|
||||
* with rmnet.
|
||||
*/
|
||||
struct rmnet_port {
|
||||
struct net_device *dev;
|
||||
u32 data_format;
|
||||
u8 nr_rmnet_devs;
|
||||
u8 rmnet_mode;
|
||||
struct hlist_head muxed_ep[RMNET_MAX_LOGICAL_EP];
|
||||
struct net_device *bridge_ep;
|
||||
void *rmnet_perf;
|
||||
|
||||
struct rmnet_egress_agg_params egress_agg_params;
|
||||
|
||||
/* Protect aggregation related elements */
|
||||
spinlock_t agg_lock;
|
||||
|
||||
struct sk_buff *agg_skb;
|
||||
int agg_state;
|
||||
u8 agg_count;
|
||||
struct timespec agg_time;
|
||||
struct timespec agg_last;
|
||||
struct hrtimer hrtimer;
|
||||
struct work_struct agg_wq;
|
||||
|
||||
/* dl marker elements */
|
||||
struct list_head dl_list;
|
||||
struct rmnet_port_priv_stats stats;
|
||||
int dl_marker_flush;
|
||||
|
||||
/* Descriptor pool */
|
||||
spinlock_t desc_pool_lock;
|
||||
struct rmnet_frag_descriptor_pool *frag_desc_pool;
|
||||
struct sk_buff *chain_head;
|
||||
struct sk_buff *chain_tail;
|
||||
};
|
||||
|
||||
extern struct rtnl_link_ops rmnet_link_ops;
|
||||
|
||||
struct rmnet_vnd_stats {
|
||||
u64 rx_pkts;
|
||||
u64 rx_bytes;
|
||||
u64 tx_pkts;
|
||||
u64 tx_bytes;
|
||||
u32 tx_drops;
|
||||
};
|
||||
|
||||
struct rmnet_pcpu_stats {
|
||||
struct rmnet_vnd_stats stats;
|
||||
struct u64_stats_sync syncp;
|
||||
};
|
||||
|
||||
struct rmnet_coal_close_stats {
|
||||
u64 non_coal;
|
||||
u64 ip_miss;
|
||||
u64 trans_miss;
|
||||
u64 hw_nl;
|
||||
u64 hw_pkt;
|
||||
u64 hw_byte;
|
||||
u64 hw_time;
|
||||
u64 hw_evict;
|
||||
u64 coal;
|
||||
};
|
||||
|
||||
struct rmnet_coal_stats {
|
||||
u64 coal_rx;
|
||||
u64 coal_pkts;
|
||||
u64 coal_hdr_nlo_err;
|
||||
u64 coal_hdr_pkt_err;
|
||||
u64 coal_csum_err;
|
||||
u64 coal_reconstruct;
|
||||
u64 coal_ip_invalid;
|
||||
u64 coal_trans_invalid;
|
||||
struct rmnet_coal_close_stats close;
|
||||
u64 coal_veid[RMNET_MAX_VEID];
|
||||
};
|
||||
|
||||
struct rmnet_priv_stats {
|
||||
u64 csum_ok;
|
||||
u64 csum_valid_unset;
|
||||
u64 csum_validation_failed;
|
||||
u64 csum_err_bad_buffer;
|
||||
u64 csum_err_invalid_ip_version;
|
||||
u64 csum_err_invalid_transport;
|
||||
u64 csum_fragmented_pkt;
|
||||
u64 csum_skipped;
|
||||
u64 csum_sw;
|
||||
u64 csum_hw;
|
||||
struct rmnet_coal_stats coal;
|
||||
};
|
||||
|
||||
struct rmnet_priv {
|
||||
u8 mux_id;
|
||||
struct net_device *real_dev;
|
||||
struct rmnet_pcpu_stats __percpu *pcpu_stats;
|
||||
struct gro_cells gro_cells;
|
||||
struct rmnet_priv_stats stats;
|
||||
};
|
||||
|
||||
enum rmnet_dl_marker_prio {
|
||||
RMNET_PERF,
|
||||
RMNET_SHS,
|
||||
};
|
||||
|
||||
enum rmnet_trace_func {
|
||||
RMNET_MODULE,
|
||||
NW_STACK_MODULE,
|
||||
};
|
||||
|
||||
enum rmnet_trace_evt {
|
||||
RMNET_DLVR_SKB,
|
||||
RMNET_RCV_FROM_PND,
|
||||
RMNET_TX_UL_PKT,
|
||||
NW_STACK_DEV_Q_XMIT,
|
||||
NW_STACK_NAPI_GRO_FLUSH,
|
||||
NW_STACK_RX,
|
||||
NW_STACK_TX,
|
||||
};
|
||||
|
||||
static int rmnet_is_real_dev_registered(const struct net_device *real_dev);
|
||||
static struct rmnet_port *rmnet_get_port(struct net_device *real_dev);
|
||||
static struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id);
|
||||
#endif /* _RMNET_CONFIG_H_ */
|
||||
1150
applications/quectel_MHI/src/devices/rmnet/rmnet_data.c
Normal file
1150
applications/quectel_MHI/src/devices/rmnet/rmnet_data.c
Normal file
File diff suppressed because it is too large
Load Diff
661
applications/quectel_MHI/src/devices/rmnet/rmnet_descriptor.c
Normal file
661
applications/quectel_MHI/src/devices/rmnet/rmnet_descriptor.c
Normal file
@@ -0,0 +1,661 @@
|
||||
/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* RMNET Packet Descriptor Framework
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/ip6_checksum.h>
|
||||
#include "rmnet_config.h"
|
||||
#include "rmnet_descriptor.h"
|
||||
#include "rmnet_handlers.h"
|
||||
#include "rmnet_private.h"
|
||||
#include "rmnet_vnd.h"
|
||||
|
||||
#define RMNET_FRAG_DESCRIPTOR_POOL_SIZE 64
|
||||
#define RMNET_DL_IND_HDR_SIZE (sizeof(struct rmnet_map_dl_ind_hdr) + \
|
||||
sizeof(struct rmnet_map_header) + \
|
||||
sizeof(struct rmnet_map_control_command_header))
|
||||
#define RMNET_DL_IND_TRL_SIZE (sizeof(struct rmnet_map_dl_ind_trl) + \
|
||||
sizeof(struct rmnet_map_header) + \
|
||||
sizeof(struct rmnet_map_control_command_header))
|
||||
|
||||
typedef void (*rmnet_perf_desc_hook_t)(struct rmnet_frag_descriptor *frag_desc,
|
||||
struct rmnet_port *port);
|
||||
typedef void (*rmnet_perf_chain_hook_t)(void);
|
||||
|
||||
static struct rmnet_frag_descriptor *
|
||||
rmnet_get_frag_descriptor(struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool;
|
||||
struct rmnet_frag_descriptor *frag_desc;
|
||||
|
||||
spin_lock(&port->desc_pool_lock);
|
||||
if (!list_empty(&pool->free_list)) {
|
||||
frag_desc = list_first_entry(&pool->free_list,
|
||||
struct rmnet_frag_descriptor,
|
||||
list);
|
||||
list_del_init(&frag_desc->list);
|
||||
} else {
|
||||
frag_desc = kzalloc(sizeof(*frag_desc), GFP_ATOMIC);
|
||||
if (!frag_desc)
|
||||
goto out;
|
||||
|
||||
INIT_LIST_HEAD(&frag_desc->list);
|
||||
INIT_LIST_HEAD(&frag_desc->sub_frags);
|
||||
pool->pool_size++;
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock(&port->desc_pool_lock);
|
||||
return frag_desc;
|
||||
}
|
||||
|
||||
static void rmnet_recycle_frag_descriptor(struct rmnet_frag_descriptor *frag_desc,
|
||||
struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool;
|
||||
struct page *page = skb_frag_page(&frag_desc->frag);
|
||||
|
||||
list_del(&frag_desc->list);
|
||||
if (page)
|
||||
put_page(page);
|
||||
|
||||
memset(frag_desc, 0, sizeof(*frag_desc));
|
||||
INIT_LIST_HEAD(&frag_desc->list);
|
||||
INIT_LIST_HEAD(&frag_desc->sub_frags);
|
||||
spin_lock(&port->desc_pool_lock);
|
||||
list_add_tail(&frag_desc->list, &pool->free_list);
|
||||
spin_unlock(&port->desc_pool_lock);
|
||||
}
|
||||
|
||||
static void rmnet_descriptor_add_frag(struct rmnet_port *port, struct list_head *list,
|
||||
struct page *p, u32 page_offset, u32 len)
|
||||
{
|
||||
struct rmnet_frag_descriptor *frag_desc;
|
||||
|
||||
frag_desc = rmnet_get_frag_descriptor(port);
|
||||
if (!frag_desc)
|
||||
return;
|
||||
|
||||
rmnet_frag_fill(frag_desc, p, page_offset, len);
|
||||
list_add_tail(&frag_desc->list, list);
|
||||
}
|
||||
|
||||
static u8 rmnet_frag_do_flow_control(struct rmnet_map_header *qmap,
|
||||
struct rmnet_port *port,
|
||||
int enable)
|
||||
{
|
||||
struct rmnet_map_control_command *cmd;
|
||||
struct rmnet_endpoint *ep;
|
||||
struct net_device *vnd;
|
||||
u16 ip_family;
|
||||
u16 fc_seq;
|
||||
u32 qos_id;
|
||||
u8 mux_id;
|
||||
int r;
|
||||
|
||||
mux_id = qmap->mux_id;
|
||||
cmd = (struct rmnet_map_control_command *)
|
||||
((char *)qmap + sizeof(*qmap));
|
||||
|
||||
if (mux_id >= RMNET_MAX_LOGICAL_EP)
|
||||
return RX_HANDLER_CONSUMED;
|
||||
|
||||
ep = rmnet_get_endpoint(port, mux_id);
|
||||
if (!ep)
|
||||
return RX_HANDLER_CONSUMED;
|
||||
|
||||
vnd = ep->egress_dev;
|
||||
|
||||
ip_family = cmd->flow_control.ip_family;
|
||||
fc_seq = ntohs(cmd->flow_control.flow_control_seq_num);
|
||||
qos_id = ntohl(cmd->flow_control.qos_id);
|
||||
|
||||
/* Ignore the ip family and pass the sequence number for both v4 and v6
|
||||
* sequence. User space does not support creating dedicated flows for
|
||||
* the 2 protocols
|
||||
*/
|
||||
r = rmnet_vnd_do_flow_control(vnd, enable);
|
||||
if (r)
|
||||
return RMNET_MAP_COMMAND_UNSUPPORTED;
|
||||
else
|
||||
return RMNET_MAP_COMMAND_ACK;
|
||||
}
|
||||
|
||||
static void rmnet_frag_send_ack(struct rmnet_map_header *qmap,
|
||||
unsigned char type,
|
||||
struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_map_control_command *cmd;
|
||||
struct net_device *dev = port->dev;
|
||||
struct sk_buff *skb;
|
||||
u16 alloc_len = ntohs(qmap->pkt_len) + sizeof(*qmap);
|
||||
|
||||
skb = alloc_skb(alloc_len, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
skb->protocol = htons(ETH_P_MAP);
|
||||
skb->dev = dev;
|
||||
|
||||
cmd = rmnet_map_get_cmd_start(skb);
|
||||
cmd->cmd_type = type & 0x03;
|
||||
|
||||
netif_tx_lock(dev);
|
||||
dev->netdev_ops->ndo_start_xmit(skb, dev);
|
||||
netif_tx_unlock(dev);
|
||||
}
|
||||
|
||||
|
||||
/* Process MAP command frame and send N/ACK message as appropriate. Message cmd
|
||||
* name is decoded here and appropriate handler is called.
|
||||
*/
|
||||
static void rmnet_frag_command(struct rmnet_map_header *qmap, struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_map_control_command *cmd;
|
||||
unsigned char command_name;
|
||||
unsigned char rc = 0;
|
||||
|
||||
cmd = (struct rmnet_map_control_command *)
|
||||
((char *)qmap + sizeof(*qmap));
|
||||
command_name = cmd->command_name;
|
||||
|
||||
switch (command_name) {
|
||||
case RMNET_MAP_COMMAND_FLOW_ENABLE:
|
||||
rc = rmnet_frag_do_flow_control(qmap, port, 1);
|
||||
break;
|
||||
|
||||
case RMNET_MAP_COMMAND_FLOW_DISABLE:
|
||||
rc = rmnet_frag_do_flow_control(qmap, port, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = RMNET_MAP_COMMAND_UNSUPPORTED;
|
||||
break;
|
||||
}
|
||||
if (rc == RMNET_MAP_COMMAND_ACK)
|
||||
rmnet_frag_send_ack(qmap, rc, port);
|
||||
}
|
||||
|
||||
static void rmnet_frag_deaggregate(skb_frag_t *frag, struct rmnet_port *port,
|
||||
struct list_head *list)
|
||||
{
|
||||
struct rmnet_map_header *maph;
|
||||
u8 *data = skb_frag_address(frag);
|
||||
u32 offset = 0;
|
||||
u32 packet_len;
|
||||
|
||||
while (offset < skb_frag_size(frag)) {
|
||||
maph = (struct rmnet_map_header *)data;
|
||||
packet_len = ntohs(maph->pkt_len);
|
||||
|
||||
/* Some hardware can send us empty frames. Catch them */
|
||||
if (packet_len == 0)
|
||||
return;
|
||||
|
||||
packet_len += sizeof(*maph);
|
||||
|
||||
if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
|
||||
packet_len += sizeof(struct rmnet_map_dl_csum_trailer);
|
||||
WARN_ON(1);
|
||||
} else if (port->data_format &
|
||||
(RMNET_FLAGS_INGRESS_MAP_CKSUMV5 |
|
||||
RMNET_FLAGS_INGRESS_COALESCE) && !maph->cd_bit) {
|
||||
u32 hsize = 0;
|
||||
u8 type;
|
||||
|
||||
type = ((struct rmnet_map_v5_coal_header *)
|
||||
(data + sizeof(*maph)))->header_type;
|
||||
switch (type) {
|
||||
case RMNET_MAP_HEADER_TYPE_COALESCING:
|
||||
hsize = sizeof(struct rmnet_map_v5_coal_header);
|
||||
break;
|
||||
case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD:
|
||||
hsize = sizeof(struct rmnet_map_v5_csum_header);
|
||||
break;
|
||||
}
|
||||
|
||||
packet_len += hsize;
|
||||
}
|
||||
else {
|
||||
//qmap_hex_dump(__func__, data, 64);
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
if ((int)skb_frag_size(frag) - (int)packet_len < 0)
|
||||
return;
|
||||
|
||||
rmnet_descriptor_add_frag(port, list, skb_frag_page(frag),
|
||||
frag->page_offset + offset,
|
||||
packet_len);
|
||||
|
||||
offset += packet_len;
|
||||
data += packet_len;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate and populate an skb to contain the packet represented by the
|
||||
* frag descriptor.
|
||||
*/
|
||||
static struct sk_buff *rmnet_alloc_skb(struct rmnet_frag_descriptor *frag_desc,
|
||||
struct rmnet_port *port)
|
||||
{
|
||||
struct sk_buff *head_skb, *current_skb, *skb;
|
||||
struct skb_shared_info *shinfo;
|
||||
struct rmnet_frag_descriptor *sub_frag, *tmp;
|
||||
|
||||
/* Use the exact sizes if we know them (i.e. RSB/RSC, rmnet_perf) */
|
||||
if (frag_desc->hdrs_valid) {
|
||||
u16 hdr_len = frag_desc->ip_len + frag_desc->trans_len;
|
||||
|
||||
head_skb = alloc_skb(hdr_len + RMNET_MAP_DESC_HEADROOM,
|
||||
GFP_ATOMIC);
|
||||
if (!head_skb)
|
||||
return NULL;
|
||||
|
||||
skb_reserve(head_skb, RMNET_MAP_DESC_HEADROOM);
|
||||
skb_put_data(head_skb, frag_desc->hdr_ptr, hdr_len);
|
||||
skb_reset_network_header(head_skb);
|
||||
|
||||
if (frag_desc->trans_len)
|
||||
skb_set_transport_header(head_skb, frag_desc->ip_len);
|
||||
|
||||
/* Packets that have no data portion don't need any frags */
|
||||
if (hdr_len == skb_frag_size(&frag_desc->frag))
|
||||
goto skip_frags;
|
||||
|
||||
/* If the headers we added are the start of the page,
|
||||
* we don't want to add them twice
|
||||
*/
|
||||
if (frag_desc->hdr_ptr == rmnet_frag_data_ptr(frag_desc)) {
|
||||
if (!rmnet_frag_pull(frag_desc, port, hdr_len)) {
|
||||
kfree_skb(head_skb);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Allocate enough space to avoid penalties in the stack
|
||||
* from __pskb_pull_tail()
|
||||
*/
|
||||
head_skb = alloc_skb(256 + RMNET_MAP_DESC_HEADROOM,
|
||||
GFP_ATOMIC);
|
||||
if (!head_skb)
|
||||
return NULL;
|
||||
|
||||
skb_reserve(head_skb, RMNET_MAP_DESC_HEADROOM);
|
||||
}
|
||||
|
||||
/* Add main fragment */
|
||||
get_page(skb_frag_page(&frag_desc->frag));
|
||||
skb_add_rx_frag(head_skb, 0, skb_frag_page(&frag_desc->frag),
|
||||
frag_desc->frag.page_offset,
|
||||
skb_frag_size(&frag_desc->frag),
|
||||
skb_frag_size(&frag_desc->frag));
|
||||
|
||||
shinfo = skb_shinfo(head_skb);
|
||||
current_skb = head_skb;
|
||||
|
||||
/* Add in any frags from rmnet_perf */
|
||||
list_for_each_entry_safe(sub_frag, tmp, &frag_desc->sub_frags, list) {
|
||||
skb_frag_t *frag;
|
||||
u32 frag_size;
|
||||
|
||||
frag = &sub_frag->frag;
|
||||
frag_size = skb_frag_size(frag);
|
||||
|
||||
add_frag:
|
||||
if (shinfo->nr_frags < MAX_SKB_FRAGS) {
|
||||
get_page(skb_frag_page(frag));
|
||||
skb_add_rx_frag(current_skb, shinfo->nr_frags,
|
||||
skb_frag_page(frag), frag->page_offset,
|
||||
frag_size, frag_size);
|
||||
if (current_skb != head_skb) {
|
||||
head_skb->len += frag_size;
|
||||
head_skb->data_len += frag_size;
|
||||
}
|
||||
} else {
|
||||
/* Alloc a new skb and try again */
|
||||
skb = alloc_skb(0, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
break;
|
||||
|
||||
if (current_skb == head_skb)
|
||||
shinfo->frag_list = skb;
|
||||
else
|
||||
current_skb->next = skb;
|
||||
|
||||
current_skb = skb;
|
||||
shinfo = skb_shinfo(current_skb);
|
||||
goto add_frag;
|
||||
}
|
||||
|
||||
rmnet_recycle_frag_descriptor(sub_frag, port);
|
||||
}
|
||||
|
||||
skip_frags:
|
||||
head_skb->dev = frag_desc->dev;
|
||||
rmnet_set_skb_proto(head_skb);
|
||||
|
||||
/* Handle any header metadata that needs to be updated after RSB/RSC
|
||||
* segmentation
|
||||
*/
|
||||
if (frag_desc->ip_id_set) {
|
||||
struct iphdr *iph;
|
||||
|
||||
iph = (struct iphdr *)rmnet_map_data_ptr(head_skb);
|
||||
csum_replace2(&iph->check, iph->id, frag_desc->ip_id);
|
||||
iph->id = frag_desc->ip_id;
|
||||
}
|
||||
|
||||
if (frag_desc->tcp_seq_set) {
|
||||
struct tcphdr *th;
|
||||
|
||||
th = (struct tcphdr *)
|
||||
(rmnet_map_data_ptr(head_skb) + frag_desc->ip_len);
|
||||
th->seq = frag_desc->tcp_seq;
|
||||
}
|
||||
|
||||
/* Handle csum offloading */
|
||||
if (frag_desc->csum_valid && frag_desc->hdrs_valid) {
|
||||
/* Set the partial checksum information */
|
||||
//rmnet_frag_partial_csum(head_skb, frag_desc);
|
||||
WARN_ON(1);
|
||||
} else if (frag_desc->csum_valid) {
|
||||
/* Non-RSB/RSC/perf packet. The current checksum is fine */
|
||||
head_skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
} else if (frag_desc->hdrs_valid &&
|
||||
(frag_desc->trans_proto == IPPROTO_TCP ||
|
||||
frag_desc->trans_proto == IPPROTO_UDP)) {
|
||||
/* Unfortunately, we have to fake a bad checksum here, since
|
||||
* the original bad value is lost by the hardware. The only
|
||||
* reliable way to do it is to calculate the actual checksum
|
||||
* and corrupt it.
|
||||
*/
|
||||
__sum16 *check;
|
||||
__wsum csum;
|
||||
unsigned int offset = skb_transport_offset(head_skb);
|
||||
__sum16 pseudo;
|
||||
|
||||
WARN_ON(1);
|
||||
/* Calculate pseudo header and update header fields */
|
||||
if (frag_desc->ip_proto == 4) {
|
||||
struct iphdr *iph = ip_hdr(head_skb);
|
||||
__be16 tot_len = htons(head_skb->len);
|
||||
|
||||
csum_replace2(&iph->check, iph->tot_len, tot_len);
|
||||
iph->tot_len = tot_len;
|
||||
pseudo = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
|
||||
head_skb->len -
|
||||
frag_desc->ip_len,
|
||||
frag_desc->trans_proto, 0);
|
||||
} else {
|
||||
struct ipv6hdr *ip6h = ipv6_hdr(head_skb);
|
||||
|
||||
ip6h->payload_len = htons(head_skb->len -
|
||||
sizeof(*ip6h));
|
||||
pseudo = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
|
||||
head_skb->len -
|
||||
frag_desc->ip_len,
|
||||
frag_desc->trans_proto, 0);
|
||||
}
|
||||
|
||||
if (frag_desc->trans_proto == IPPROTO_TCP) {
|
||||
check = &tcp_hdr(head_skb)->check;
|
||||
} else {
|
||||
udp_hdr(head_skb)->len = htons(head_skb->len -
|
||||
frag_desc->ip_len);
|
||||
check = &udp_hdr(head_skb)->check;
|
||||
}
|
||||
|
||||
*check = pseudo;
|
||||
csum = skb_checksum(head_skb, offset, head_skb->len - offset,
|
||||
0);
|
||||
/* Add 1 to corrupt. This cannot produce a final value of 0
|
||||
* since csum_fold() can't return a value of 0xFFFF
|
||||
*/
|
||||
*check = csum16_add(csum_fold(csum), htons(1));
|
||||
head_skb->ip_summed = CHECKSUM_NONE;
|
||||
}
|
||||
|
||||
/* Handle any rmnet_perf metadata */
|
||||
if (frag_desc->hash) {
|
||||
head_skb->hash = frag_desc->hash;
|
||||
head_skb->sw_hash = 1;
|
||||
}
|
||||
|
||||
if (frag_desc->flush_shs)
|
||||
head_skb->cb[0] = 1;
|
||||
|
||||
/* Handle coalesced packets */
|
||||
//if (frag_desc->gso_segs > 1)
|
||||
// rmnet_frag_gso_stamp(head_skb, frag_desc);
|
||||
|
||||
return head_skb;
|
||||
}
|
||||
|
||||
/* Deliver the packets contained within a frag descriptor */
|
||||
static void rmnet_frag_deliver(struct rmnet_frag_descriptor *frag_desc,
|
||||
struct rmnet_port *port)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = rmnet_alloc_skb(frag_desc, port);
|
||||
if (skb)
|
||||
rmnet_deliver_skb(skb, port);
|
||||
rmnet_recycle_frag_descriptor(frag_desc, port);
|
||||
}
|
||||
|
||||
/* Process a QMAPv5 packet header */
|
||||
static int rmnet_frag_process_next_hdr_packet(struct rmnet_frag_descriptor *frag_desc,
|
||||
struct rmnet_port *port,
|
||||
struct list_head *list,
|
||||
u16 len)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
switch (rmnet_frag_get_next_hdr_type(frag_desc)) {
|
||||
case RMNET_MAP_HEADER_TYPE_COALESCING:
|
||||
rc = -1;
|
||||
WARN_ON(1);
|
||||
break;
|
||||
case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD:
|
||||
if (rmnet_frag_get_csum_valid(frag_desc)) {
|
||||
frag_desc->csum_valid = true;
|
||||
} else {
|
||||
}
|
||||
|
||||
if (!rmnet_frag_pull(frag_desc, port,
|
||||
sizeof(struct rmnet_map_header) +
|
||||
sizeof(struct rmnet_map_v5_csum_header))) {
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
frag_desc->hdr_ptr = rmnet_frag_data_ptr(frag_desc);
|
||||
|
||||
/* Remove padding only for csum offload packets.
|
||||
* Coalesced packets should never have padding.
|
||||
*/
|
||||
if (!rmnet_frag_trim(frag_desc, port, len)) {
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
list_del_init(&frag_desc->list);
|
||||
list_add_tail(&frag_desc->list, list);
|
||||
break;
|
||||
default:
|
||||
//qmap_hex_dump(__func__, rmnet_frag_data_ptr(frag_desc), 64);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
__rmnet_frag_ingress_handler(struct rmnet_frag_descriptor *frag_desc,
|
||||
struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_map_header *qmap;
|
||||
struct rmnet_endpoint *ep;
|
||||
struct rmnet_frag_descriptor *frag, *tmp;
|
||||
LIST_HEAD(segs);
|
||||
u16 len, pad;
|
||||
u8 mux_id;
|
||||
|
||||
qmap = (struct rmnet_map_header *)skb_frag_address(&frag_desc->frag);
|
||||
mux_id = qmap->mux_id;
|
||||
pad = qmap->pad_len;
|
||||
len = ntohs(qmap->pkt_len) - pad;
|
||||
|
||||
if (qmap->cd_bit) {
|
||||
if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) {
|
||||
//rmnet_frag_flow_command(qmap, port, len);
|
||||
goto recycle;
|
||||
}
|
||||
|
||||
if (port->data_format & RMNET_FLAGS_INGRESS_MAP_COMMANDS)
|
||||
rmnet_frag_command(qmap, port);
|
||||
|
||||
goto recycle;
|
||||
}
|
||||
|
||||
if (mux_id >= RMNET_MAX_LOGICAL_EP)
|
||||
goto recycle;
|
||||
|
||||
ep = rmnet_get_endpoint(port, mux_id);
|
||||
if (!ep)
|
||||
goto recycle;
|
||||
|
||||
frag_desc->dev = ep->egress_dev;
|
||||
|
||||
/* Handle QMAPv5 packet */
|
||||
if (qmap->next_hdr &&
|
||||
(port->data_format & (RMNET_FLAGS_INGRESS_COALESCE |
|
||||
RMNET_FLAGS_INGRESS_MAP_CKSUMV5))) {
|
||||
if (rmnet_frag_process_next_hdr_packet(frag_desc, port, &segs,
|
||||
len))
|
||||
goto recycle;
|
||||
} else {
|
||||
/* We only have the main QMAP header to worry about */
|
||||
if (!rmnet_frag_pull(frag_desc, port, sizeof(*qmap)))
|
||||
return;
|
||||
|
||||
frag_desc->hdr_ptr = rmnet_frag_data_ptr(frag_desc);
|
||||
|
||||
if (!rmnet_frag_trim(frag_desc, port, len))
|
||||
return;
|
||||
|
||||
list_add_tail(&frag_desc->list, &segs);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(frag, tmp, &segs, list) {
|
||||
list_del_init(&frag->list);
|
||||
rmnet_frag_deliver(frag, port);
|
||||
}
|
||||
return;
|
||||
|
||||
recycle:
|
||||
rmnet_recycle_frag_descriptor(frag_desc, port);
|
||||
}
|
||||
|
||||
static void rmnet_frag_ingress_handler(struct sk_buff *skb,
|
||||
struct rmnet_port *port)
|
||||
{
|
||||
LIST_HEAD(desc_list);
|
||||
int i = 0;
|
||||
struct rmnet_nss_cb *nss_cb;
|
||||
|
||||
/* Deaggregation and freeing of HW originating
|
||||
* buffers is done within here
|
||||
*/
|
||||
while (skb) {
|
||||
struct sk_buff *skb_frag;
|
||||
|
||||
port->chain_head = NULL;
|
||||
port->chain_tail = NULL;
|
||||
|
||||
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
|
||||
rmnet_frag_deaggregate(&skb_shinfo(skb)->frags[i], port,
|
||||
&desc_list);
|
||||
if (!list_empty(&desc_list)) {
|
||||
struct rmnet_frag_descriptor *frag_desc, *tmp;
|
||||
|
||||
list_for_each_entry_safe(frag_desc, tmp,
|
||||
&desc_list, list) {
|
||||
list_del_init(&frag_desc->list);
|
||||
__rmnet_frag_ingress_handler(frag_desc,
|
||||
port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nss_cb = rcu_dereference(rmnet_nss_callbacks);
|
||||
if (nss_cb && port->chain_head) {
|
||||
port->chain_head->cb[0] = 0;
|
||||
netif_receive_skb(port->chain_head);
|
||||
}
|
||||
|
||||
skb_frag = skb_shinfo(skb)->frag_list;
|
||||
skb_shinfo(skb)->frag_list = NULL;
|
||||
consume_skb(skb);
|
||||
skb = skb_frag;
|
||||
}
|
||||
}
|
||||
|
||||
void rmnet_descriptor_deinit(struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_frag_descriptor_pool *pool;
|
||||
struct rmnet_frag_descriptor *frag_desc, *tmp;
|
||||
|
||||
pool = port->frag_desc_pool;
|
||||
|
||||
list_for_each_entry_safe(frag_desc, tmp, &pool->free_list, list) {
|
||||
kfree(frag_desc);
|
||||
pool->pool_size--;
|
||||
}
|
||||
|
||||
kfree(pool);
|
||||
}
|
||||
|
||||
int rmnet_descriptor_init(struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_frag_descriptor_pool *pool;
|
||||
int i;
|
||||
|
||||
spin_lock_init(&port->desc_pool_lock);
|
||||
pool = kzalloc(sizeof(*pool), GFP_ATOMIC);
|
||||
if (!pool)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&pool->free_list);
|
||||
port->frag_desc_pool = pool;
|
||||
|
||||
for (i = 0; i < RMNET_FRAG_DESCRIPTOR_POOL_SIZE; i++) {
|
||||
struct rmnet_frag_descriptor *frag_desc;
|
||||
|
||||
frag_desc = kzalloc(sizeof(*frag_desc), GFP_ATOMIC);
|
||||
if (!frag_desc)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&frag_desc->list);
|
||||
INIT_LIST_HEAD(&frag_desc->sub_frags);
|
||||
list_add_tail(&frag_desc->list, &pool->free_list);
|
||||
pool->pool_size++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
146
applications/quectel_MHI/src/devices/rmnet/rmnet_descriptor.h
Normal file
146
applications/quectel_MHI/src/devices/rmnet/rmnet_descriptor.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* RMNET Packet Descriptor Framework
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _RMNET_DESCRIPTOR_H_
|
||||
#define _RMNET_DESCRIPTOR_H_
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include "rmnet_config.h"
|
||||
#include "rmnet_map.h"
|
||||
|
||||
struct rmnet_frag_descriptor_pool {
|
||||
struct list_head free_list;
|
||||
u32 pool_size;
|
||||
};
|
||||
|
||||
struct rmnet_frag_descriptor {
|
||||
struct list_head list;
|
||||
struct list_head sub_frags;
|
||||
skb_frag_t frag;
|
||||
u8 *hdr_ptr;
|
||||
struct net_device *dev;
|
||||
u32 hash;
|
||||
__be32 tcp_seq;
|
||||
__be16 ip_id;
|
||||
u16 data_offset;
|
||||
u16 gso_size;
|
||||
u16 gso_segs;
|
||||
u16 ip_len;
|
||||
u16 trans_len;
|
||||
u8 ip_proto;
|
||||
u8 trans_proto;
|
||||
u8 pkt_id;
|
||||
u8 csum_valid:1,
|
||||
hdrs_valid:1,
|
||||
ip_id_set:1,
|
||||
tcp_seq_set:1,
|
||||
flush_shs:1,
|
||||
reserved:3;
|
||||
};
|
||||
|
||||
/* Descriptor management */
|
||||
static struct rmnet_frag_descriptor *
|
||||
rmnet_get_frag_descriptor(struct rmnet_port *port);
|
||||
static void rmnet_recycle_frag_descriptor(struct rmnet_frag_descriptor *frag_desc,
|
||||
struct rmnet_port *port);
|
||||
static void rmnet_descriptor_add_frag(struct rmnet_port *port, struct list_head *list,
|
||||
struct page *p, u32 page_offset, u32 len);
|
||||
|
||||
/* QMAP command packets */
|
||||
|
||||
/* Ingress data handlers */
|
||||
static void rmnet_frag_deaggregate(skb_frag_t *frag, struct rmnet_port *port,
|
||||
struct list_head *list);
|
||||
static void rmnet_frag_deliver(struct rmnet_frag_descriptor *frag_desc,
|
||||
struct rmnet_port *port);
|
||||
static int rmnet_frag_process_next_hdr_packet(struct rmnet_frag_descriptor *frag_desc,
|
||||
struct rmnet_port *port,
|
||||
struct list_head *list,
|
||||
u16 len);
|
||||
static void rmnet_frag_ingress_handler(struct sk_buff *skb,
|
||||
struct rmnet_port *port);
|
||||
|
||||
static int rmnet_descriptor_init(struct rmnet_port *port);
|
||||
static void rmnet_descriptor_deinit(struct rmnet_port *port);
|
||||
|
||||
static inline void *rmnet_frag_data_ptr(struct rmnet_frag_descriptor *frag_desc)
|
||||
{
|
||||
return skb_frag_address(&frag_desc->frag);
|
||||
}
|
||||
|
||||
static inline void *rmnet_frag_pull(struct rmnet_frag_descriptor *frag_desc,
|
||||
struct rmnet_port *port,
|
||||
unsigned int size)
|
||||
{
|
||||
if (size >= skb_frag_size(&frag_desc->frag)) {
|
||||
pr_info("%s(): Pulling %u bytes from %u byte pkt. Dropping\n",
|
||||
__func__, size, skb_frag_size(&frag_desc->frag));
|
||||
rmnet_recycle_frag_descriptor(frag_desc, port);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
frag_desc->frag.page_offset += size;
|
||||
skb_frag_size_sub(&frag_desc->frag, size);
|
||||
|
||||
return rmnet_frag_data_ptr(frag_desc);
|
||||
}
|
||||
|
||||
static inline void *rmnet_frag_trim(struct rmnet_frag_descriptor *frag_desc,
|
||||
struct rmnet_port *port,
|
||||
unsigned int size)
|
||||
{
|
||||
if (!size) {
|
||||
pr_info("%s(): Trimming %u byte pkt to 0. Dropping\n",
|
||||
__func__, skb_frag_size(&frag_desc->frag));
|
||||
rmnet_recycle_frag_descriptor(frag_desc, port);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size < skb_frag_size(&frag_desc->frag))
|
||||
skb_frag_size_set(&frag_desc->frag, size);
|
||||
|
||||
return rmnet_frag_data_ptr(frag_desc);
|
||||
}
|
||||
|
||||
static inline void rmnet_frag_fill(struct rmnet_frag_descriptor *frag_desc,
|
||||
struct page *p, u32 page_offset, u32 len)
|
||||
{
|
||||
get_page(p);
|
||||
__skb_frag_set_page(&frag_desc->frag, p);
|
||||
skb_frag_size_set(&frag_desc->frag, len);
|
||||
frag_desc->frag.page_offset = page_offset;
|
||||
}
|
||||
|
||||
static inline u8
|
||||
rmnet_frag_get_next_hdr_type(struct rmnet_frag_descriptor *frag_desc)
|
||||
{
|
||||
unsigned char *data = rmnet_frag_data_ptr(frag_desc);
|
||||
|
||||
data += sizeof(struct rmnet_map_header);
|
||||
return ((struct rmnet_map_v5_coal_header *)data)->header_type;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
rmnet_frag_get_csum_valid(struct rmnet_frag_descriptor *frag_desc)
|
||||
{
|
||||
unsigned char *data = rmnet_frag_data_ptr(frag_desc);
|
||||
|
||||
data += sizeof(struct rmnet_map_header);
|
||||
return ((struct rmnet_map_v5_csum_header *)data)->csum_valid_required;
|
||||
}
|
||||
|
||||
#endif /* _RMNET_DESCRIPTOR_H_ */
|
||||
374
applications/quectel_MHI/src/devices/rmnet/rmnet_handlers.c
Normal file
374
applications/quectel_MHI/src/devices/rmnet/rmnet_handlers.c
Normal file
@@ -0,0 +1,374 @@
|
||||
/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* RMNET Data ingress/egress handler
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/netdev_features.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/tracepoint.h>
|
||||
#include "rmnet_private.h"
|
||||
#include "rmnet_config.h"
|
||||
#include "rmnet_vnd.h"
|
||||
#include "rmnet_map.h"
|
||||
#include "rmnet_handlers.h"
|
||||
#include "rmnet_descriptor.h"
|
||||
|
||||
#define RMNET_IP_VERSION_4 0x40
|
||||
#define RMNET_IP_VERSION_6 0x60
|
||||
|
||||
/* Helper Functions */
|
||||
|
||||
static void rmnet_set_skb_proto(struct sk_buff *skb)
|
||||
{
|
||||
switch (rmnet_map_data_ptr(skb)[0] & 0xF0) {
|
||||
case RMNET_IP_VERSION_4:
|
||||
skb->protocol = htons(ETH_P_IP);
|
||||
break;
|
||||
case RMNET_IP_VERSION_6:
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
break;
|
||||
default:
|
||||
skb->protocol = htons(ETH_P_MAP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generic handler */
|
||||
|
||||
static void
|
||||
rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_nss_cb *nss_cb;
|
||||
|
||||
rmnet_vnd_rx_fixup(skb->dev, skb->len);
|
||||
|
||||
/* Pass off the packet to NSS driver if we can */
|
||||
nss_cb = rcu_dereference(rmnet_nss_callbacks);
|
||||
if (nss_cb) {
|
||||
if (!port->chain_head)
|
||||
port->chain_head = skb;
|
||||
else
|
||||
skb_shinfo(port->chain_tail)->frag_list = skb;
|
||||
|
||||
port->chain_tail = skb;
|
||||
return;
|
||||
}
|
||||
|
||||
skb_reset_transport_header(skb);
|
||||
skb_reset_network_header(skb);
|
||||
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
skb_set_mac_header(skb, 0);
|
||||
|
||||
//if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) {
|
||||
//} else {
|
||||
//if (!rmnet_check_skb_can_gro(skb))
|
||||
// gro_cells_receive(&priv->gro_cells, skb);
|
||||
//else
|
||||
netif_receive_skb(skb);
|
||||
//}
|
||||
}
|
||||
|
||||
/* Deliver a list of skbs after undoing coalescing */
|
||||
static void rmnet_deliver_skb_list(struct sk_buff_head *head,
|
||||
struct rmnet_port *port)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
while ((skb = __skb_dequeue(head))) {
|
||||
rmnet_set_skb_proto(skb);
|
||||
rmnet_deliver_skb(skb, port);
|
||||
}
|
||||
}
|
||||
|
||||
/* MAP handler */
|
||||
|
||||
static void
|
||||
_rmnet_map_ingress_handler(struct sk_buff *skb,
|
||||
struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_map_header *qmap;
|
||||
struct rmnet_endpoint *ep;
|
||||
struct sk_buff_head list;
|
||||
u16 len, pad;
|
||||
u8 mux_id;
|
||||
|
||||
/* We don't need the spinlock since only we touch this */
|
||||
__skb_queue_head_init(&list);
|
||||
|
||||
qmap = (struct rmnet_map_header *)rmnet_map_data_ptr(skb);
|
||||
if (qmap->cd_bit) {
|
||||
if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) {
|
||||
//if (!rmnet_map_flow_command(skb, port, false))
|
||||
return;
|
||||
}
|
||||
|
||||
if (port->data_format & RMNET_FLAGS_INGRESS_MAP_COMMANDS)
|
||||
return rmnet_map_command(skb, port);
|
||||
|
||||
goto free_skb;
|
||||
}
|
||||
|
||||
mux_id = qmap->mux_id;
|
||||
pad = qmap->pad_len;
|
||||
len = ntohs(qmap->pkt_len) - pad;
|
||||
|
||||
if (mux_id >= RMNET_MAX_LOGICAL_EP)
|
||||
goto free_skb;
|
||||
|
||||
ep = rmnet_get_endpoint(port, mux_id);
|
||||
if (!ep)
|
||||
goto free_skb;
|
||||
|
||||
skb->dev = ep->egress_dev;
|
||||
|
||||
/* Handle QMAPv5 packet */
|
||||
if (qmap->next_hdr &&
|
||||
(port->data_format & (RMNET_FLAGS_INGRESS_COALESCE |
|
||||
RMNET_FLAGS_INGRESS_MAP_CKSUMV5))) {
|
||||
if (rmnet_map_process_next_hdr_packet(skb, &list, len))
|
||||
goto free_skb;
|
||||
} else {
|
||||
/* We only have the main QMAP header to worry about */
|
||||
pskb_pull(skb, sizeof(*qmap));
|
||||
|
||||
rmnet_set_skb_proto(skb);
|
||||
|
||||
if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
|
||||
//if (!rmnet_map_checksum_downlink_packet(skb, len + pad))
|
||||
// skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
}
|
||||
|
||||
pskb_trim(skb, len);
|
||||
|
||||
/* Push the single packet onto the list */
|
||||
__skb_queue_tail(&list, skb);
|
||||
}
|
||||
|
||||
rmnet_deliver_skb_list(&list, port);
|
||||
return;
|
||||
|
||||
free_skb:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static void
|
||||
rmnet_map_ingress_handler(struct sk_buff *skb,
|
||||
struct rmnet_port *port)
|
||||
{
|
||||
struct sk_buff *skbn;
|
||||
|
||||
if (port->data_format & (RMNET_FLAGS_INGRESS_COALESCE |
|
||||
RMNET_FLAGS_INGRESS_MAP_CKSUMV5)) {
|
||||
if (skb_is_nonlinear(skb)) {
|
||||
rmnet_frag_ingress_handler(skb, port);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Deaggregation and freeing of HW originating
|
||||
* buffers is done within here
|
||||
*/
|
||||
while (skb) {
|
||||
struct sk_buff *skb_frag = skb_shinfo(skb)->frag_list;
|
||||
|
||||
skb_shinfo(skb)->frag_list = NULL;
|
||||
while ((skbn = rmnet_map_deaggregate(skb, port)) != NULL) {
|
||||
_rmnet_map_ingress_handler(skbn, port);
|
||||
|
||||
if (skbn == skb)
|
||||
goto next_skb;
|
||||
}
|
||||
|
||||
consume_skb(skb);
|
||||
next_skb:
|
||||
skb = skb_frag;
|
||||
}
|
||||
}
|
||||
|
||||
static int rmnet_map_egress_handler(struct sk_buff *skb,
|
||||
struct rmnet_port *port, u8 mux_id,
|
||||
struct net_device *orig_dev)
|
||||
{
|
||||
int required_headroom, additional_header_len, csum_type;
|
||||
struct rmnet_map_header *map_header;
|
||||
|
||||
additional_header_len = 0;
|
||||
required_headroom = sizeof(struct rmnet_map_header);
|
||||
csum_type = 0;
|
||||
|
||||
if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) {
|
||||
additional_header_len = sizeof(struct rmnet_map_ul_csum_header);
|
||||
csum_type = RMNET_FLAGS_EGRESS_MAP_CKSUMV4;
|
||||
} else if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV5) {
|
||||
additional_header_len = sizeof(struct rmnet_map_v5_csum_header);
|
||||
csum_type = RMNET_FLAGS_EGRESS_MAP_CKSUMV5;
|
||||
}
|
||||
|
||||
required_headroom += additional_header_len;
|
||||
|
||||
if (skb_headroom(skb) < required_headroom) {
|
||||
if (pskb_expand_head(skb, required_headroom, 0, GFP_ATOMIC))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (csum_type)
|
||||
rmnet_map_checksum_uplink_packet(skb, orig_dev, csum_type);
|
||||
|
||||
map_header = rmnet_map_add_map_header(skb, additional_header_len, 0,
|
||||
port);
|
||||
if (!map_header)
|
||||
return -ENOMEM;
|
||||
|
||||
map_header->mux_id = mux_id;
|
||||
|
||||
if (port->data_format & RMNET_EGRESS_FORMAT_AGGREGATION) {
|
||||
if (rmnet_map_tx_agg_skip(skb, required_headroom))
|
||||
goto done;
|
||||
|
||||
rmnet_map_tx_aggregate(skb, port);
|
||||
return -EINPROGRESS;
|
||||
}
|
||||
|
||||
done:
|
||||
skb->protocol = htons(ETH_P_MAP);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
rmnet_bridge_handler(struct sk_buff *skb, struct net_device *bridge_dev)
|
||||
{
|
||||
if (bridge_dev) {
|
||||
skb->dev = bridge_dev;
|
||||
dev_queue_xmit(skb);
|
||||
}
|
||||
}
|
||||
|
||||
/* Ingress / Egress Entry Points */
|
||||
|
||||
/* Processes packet as per ingress data format for receiving device. Logical
|
||||
* endpoint is determined from packet inspection. Packet is then sent to the
|
||||
* egress device listed in the logical endpoint configuration.
|
||||
*/
|
||||
static rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb)
|
||||
{
|
||||
struct sk_buff *skb = *pskb;
|
||||
struct rmnet_port *port;
|
||||
struct net_device *dev;
|
||||
|
||||
if (!skb)
|
||||
goto done;
|
||||
|
||||
if (skb->pkt_type == PACKET_LOOPBACK)
|
||||
return RX_HANDLER_PASS;
|
||||
|
||||
dev = skb->dev;
|
||||
port = rmnet_get_port(dev);
|
||||
|
||||
port->chain_head = NULL;
|
||||
port->chain_tail = NULL;
|
||||
|
||||
switch (port->rmnet_mode) {
|
||||
case RMNET_EPMODE_VND:
|
||||
rmnet_map_ingress_handler(skb, port);
|
||||
break;
|
||||
case RMNET_EPMODE_BRIDGE:
|
||||
rmnet_bridge_handler(skb, port->bridge_ep);
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
|
||||
static rx_handler_result_t rmnet_rx_priv_handler(struct sk_buff **pskb)
|
||||
{
|
||||
struct sk_buff *skb = *pskb;
|
||||
struct rmnet_nss_cb *nss_cb;
|
||||
|
||||
if (!skb)
|
||||
return RX_HANDLER_CONSUMED;
|
||||
if (nss_debug) printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len);
|
||||
|
||||
if (skb->pkt_type == PACKET_LOOPBACK)
|
||||
return RX_HANDLER_PASS;
|
||||
|
||||
/* Check this so that we dont loop around netif_receive_skb */
|
||||
if (skb->cb[0] == 1) {
|
||||
skb->cb[0] = 0;
|
||||
|
||||
skb->dev->stats.rx_packets++;
|
||||
return RX_HANDLER_PASS;
|
||||
}
|
||||
|
||||
while (skb) {
|
||||
struct sk_buff *skb_frag = skb_shinfo(skb)->frag_list;
|
||||
|
||||
skb_shinfo(skb)->frag_list = NULL;
|
||||
|
||||
nss_cb = rcu_dereference(rmnet_nss_callbacks);
|
||||
if (nss_cb)
|
||||
nss_cb->nss_tx(skb);
|
||||
|
||||
skb = skb_frag;
|
||||
}
|
||||
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
|
||||
/* Modifies packet as per logical endpoint configuration and egress data format
|
||||
* for egress device configured in logical endpoint. Packet is then transmitted
|
||||
* on the egress device.
|
||||
*/
|
||||
static void rmnet_egress_handler(struct sk_buff *skb)
|
||||
{
|
||||
struct net_device *orig_dev;
|
||||
struct rmnet_port *port;
|
||||
struct rmnet_priv *priv;
|
||||
u8 mux_id;
|
||||
int err;
|
||||
u32 skb_len;
|
||||
|
||||
skb_orphan(skb);
|
||||
|
||||
orig_dev = skb->dev;
|
||||
priv = netdev_priv(orig_dev);
|
||||
skb->dev = priv->real_dev;
|
||||
mux_id = priv->mux_id;
|
||||
|
||||
port = rmnet_get_port(skb->dev);
|
||||
if (!port)
|
||||
goto drop;
|
||||
|
||||
skb_len = skb->len;
|
||||
err = rmnet_map_egress_handler(skb, port, mux_id, orig_dev);
|
||||
if (err == -ENOMEM)
|
||||
goto drop;
|
||||
else if (err == -EINPROGRESS) {
|
||||
rmnet_vnd_tx_fixup(orig_dev, skb_len);
|
||||
return;
|
||||
}
|
||||
|
||||
rmnet_vnd_tx_fixup(orig_dev, skb_len);
|
||||
|
||||
dev_queue_xmit(skb);
|
||||
return;
|
||||
|
||||
drop:
|
||||
this_cpu_inc(priv->pcpu_stats->stats.tx_drops);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
32
applications/quectel_MHI/src/devices/rmnet/rmnet_handlers.h
Normal file
32
applications/quectel_MHI/src/devices/rmnet/rmnet_handlers.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/* Copyright (c) 2013, 2016-2017, 2019
|
||||
* The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* RMNET Data ingress/egress handler
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _RMNET_HANDLERS_H_
|
||||
#define _RMNET_HANDLERS_H_
|
||||
|
||||
#include "rmnet_config.h"
|
||||
|
||||
enum rmnet_packet_context {
|
||||
RMNET_NET_RX_CTX,
|
||||
RMNET_WQ_CTX,
|
||||
};
|
||||
|
||||
static void rmnet_egress_handler(struct sk_buff *skb);
|
||||
static void rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_port *port);
|
||||
static void rmnet_set_skb_proto(struct sk_buff *skb);
|
||||
static rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb);
|
||||
static rx_handler_result_t rmnet_rx_priv_handler(struct sk_buff **pskb);
|
||||
#endif /* _RMNET_HANDLERS_H_ */
|
||||
272
applications/quectel_MHI/src/devices/rmnet/rmnet_map.h
Normal file
272
applications/quectel_MHI/src/devices/rmnet/rmnet_map.h
Normal file
@@ -0,0 +1,272 @@
|
||||
/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _RMNET_MAP_H_
|
||||
#define _RMNET_MAP_H_
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include "rmnet_config.h"
|
||||
|
||||
struct rmnet_map_control_command {
|
||||
u8 command_name;
|
||||
u8 cmd_type:2;
|
||||
u8 reserved:6;
|
||||
u16 reserved2;
|
||||
u32 transaction_id;
|
||||
union {
|
||||
struct {
|
||||
u16 ip_family:2;
|
||||
u16 reserved:14;
|
||||
__be16 flow_control_seq_num;
|
||||
__be32 qos_id;
|
||||
} flow_control;
|
||||
u8 data[0];
|
||||
};
|
||||
} __aligned(1);
|
||||
|
||||
enum rmnet_map_commands {
|
||||
RMNET_MAP_COMMAND_NONE,
|
||||
RMNET_MAP_COMMAND_FLOW_DISABLE,
|
||||
RMNET_MAP_COMMAND_FLOW_ENABLE,
|
||||
RMNET_MAP_COMMAND_FLOW_START = 7,
|
||||
RMNET_MAP_COMMAND_FLOW_END = 8,
|
||||
/* These should always be the last 2 elements */
|
||||
RMNET_MAP_COMMAND_UNKNOWN,
|
||||
RMNET_MAP_COMMAND_ENUM_LENGTH
|
||||
};
|
||||
|
||||
enum rmnet_map_v5_header_type {
|
||||
RMNET_MAP_HEADER_TYPE_UNKNOWN,
|
||||
RMNET_MAP_HEADER_TYPE_COALESCING = 0x1,
|
||||
RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD = 0x2,
|
||||
RMNET_MAP_HEADER_TYPE_ENUM_LENGTH
|
||||
};
|
||||
|
||||
enum rmnet_map_v5_close_type {
|
||||
RMNET_MAP_COAL_CLOSE_NON_COAL,
|
||||
RMNET_MAP_COAL_CLOSE_IP_MISS,
|
||||
RMNET_MAP_COAL_CLOSE_TRANS_MISS,
|
||||
RMNET_MAP_COAL_CLOSE_HW,
|
||||
RMNET_MAP_COAL_CLOSE_COAL,
|
||||
};
|
||||
|
||||
enum rmnet_map_v5_close_value {
|
||||
RMNET_MAP_COAL_CLOSE_HW_NL,
|
||||
RMNET_MAP_COAL_CLOSE_HW_PKT,
|
||||
RMNET_MAP_COAL_CLOSE_HW_BYTE,
|
||||
RMNET_MAP_COAL_CLOSE_HW_TIME,
|
||||
RMNET_MAP_COAL_CLOSE_HW_EVICT,
|
||||
};
|
||||
|
||||
/* Main QMAP header */
|
||||
struct rmnet_map_header {
|
||||
u8 pad_len:6;
|
||||
u8 next_hdr:1;
|
||||
u8 cd_bit:1;
|
||||
u8 mux_id;
|
||||
__be16 pkt_len;
|
||||
} __aligned(1);
|
||||
|
||||
/* QMAP v5 headers */
|
||||
struct rmnet_map_v5_csum_header {
|
||||
u8 next_hdr:1;
|
||||
u8 header_type:7;
|
||||
u8 hw_reserved:7;
|
||||
u8 csum_valid_required:1;
|
||||
__be16 reserved;
|
||||
} __aligned(1);
|
||||
|
||||
struct rmnet_map_v5_nl_pair {
|
||||
__be16 pkt_len;
|
||||
u8 csum_error_bitmap;
|
||||
u8 num_packets;
|
||||
} __aligned(1);
|
||||
|
||||
/* NLO: Number-length object */
|
||||
#define RMNET_MAP_V5_MAX_NLOS (6)
|
||||
#define RMNET_MAP_V5_MAX_PACKETS (48)
|
||||
|
||||
struct rmnet_map_v5_coal_header {
|
||||
u8 next_hdr:1;
|
||||
u8 header_type:7;
|
||||
u8 reserved1:4;
|
||||
u8 num_nlos:3;
|
||||
u8 csum_valid:1;
|
||||
u8 close_type:4;
|
||||
u8 close_value:4;
|
||||
u8 reserved2:4;
|
||||
u8 virtual_channel_id:4;
|
||||
|
||||
struct rmnet_map_v5_nl_pair nl_pairs[RMNET_MAP_V5_MAX_NLOS];
|
||||
} __aligned(1);
|
||||
|
||||
/* QMAP v4 headers */
|
||||
struct rmnet_map_dl_csum_trailer {
|
||||
u8 reserved1;
|
||||
u8 valid:1;
|
||||
u8 reserved2:7;
|
||||
u16 csum_start_offset;
|
||||
u16 csum_length;
|
||||
__be16 csum_value;
|
||||
} __aligned(1);
|
||||
|
||||
struct rmnet_map_ul_csum_header {
|
||||
__be16 csum_start_offset;
|
||||
u16 csum_insert_offset:14;
|
||||
u16 udp_ind:1;
|
||||
u16 csum_enabled:1;
|
||||
} __aligned(1);
|
||||
|
||||
struct rmnet_map_control_command_header {
|
||||
u8 command_name;
|
||||
u8 cmd_type:2;
|
||||
u8 reserved:5;
|
||||
u8 e:1;
|
||||
u16 source_id:15;
|
||||
u16 ext:1;
|
||||
u32 transaction_id;
|
||||
} __aligned(1);
|
||||
|
||||
struct rmnet_map_flow_info_le {
|
||||
__be32 mux_id;
|
||||
__be32 flow_id;
|
||||
__be32 bytes;
|
||||
__be32 pkts;
|
||||
} __aligned(1);
|
||||
|
||||
struct rmnet_map_flow_info_be {
|
||||
u32 mux_id;
|
||||
u32 flow_id;
|
||||
u32 bytes;
|
||||
u32 pkts;
|
||||
} __aligned(1);
|
||||
|
||||
struct rmnet_map_dl_ind_hdr {
|
||||
union {
|
||||
struct {
|
||||
u32 seq;
|
||||
u32 bytes;
|
||||
u32 pkts;
|
||||
u32 flows;
|
||||
struct rmnet_map_flow_info_le flow[0];
|
||||
} le __aligned(1);
|
||||
struct {
|
||||
__be32 seq;
|
||||
__be32 bytes;
|
||||
__be32 pkts;
|
||||
__be32 flows;
|
||||
struct rmnet_map_flow_info_be flow[0];
|
||||
} be __aligned(1);
|
||||
} __aligned(1);
|
||||
} __aligned(1);
|
||||
|
||||
struct rmnet_map_dl_ind_trl {
|
||||
union {
|
||||
__be32 seq_be;
|
||||
u32 seq_le;
|
||||
} __aligned(1);
|
||||
} __aligned(1);
|
||||
|
||||
struct rmnet_map_dl_ind {
|
||||
u8 priority;
|
||||
union {
|
||||
void (*dl_hdr_handler)(struct rmnet_map_dl_ind_hdr *);
|
||||
void (*dl_hdr_handler_v2)(struct rmnet_map_dl_ind_hdr *,
|
||||
struct
|
||||
rmnet_map_control_command_header *);
|
||||
} __aligned(1);
|
||||
union {
|
||||
void (*dl_trl_handler)(struct rmnet_map_dl_ind_trl *);
|
||||
void (*dl_trl_handler_v2)(struct rmnet_map_dl_ind_trl *,
|
||||
struct
|
||||
rmnet_map_control_command_header *);
|
||||
} __aligned(1);
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
#define RMNET_MAP_GET_MUX_ID(Y) (((struct rmnet_map_header *) \
|
||||
(Y)->data)->mux_id)
|
||||
#define RMNET_MAP_GET_CD_BIT(Y) (((struct rmnet_map_header *) \
|
||||
(Y)->data)->cd_bit)
|
||||
#define RMNET_MAP_GET_PAD(Y) (((struct rmnet_map_header *) \
|
||||
(Y)->data)->pad_len)
|
||||
#define RMNET_MAP_GET_CMD_START(Y) ((struct rmnet_map_control_command *) \
|
||||
((Y)->data + \
|
||||
sizeof(struct rmnet_map_header)))
|
||||
#define RMNET_MAP_GET_LENGTH(Y) (ntohs(((struct rmnet_map_header *) \
|
||||
(Y)->data)->pkt_len))
|
||||
|
||||
#define RMNET_MAP_DEAGGR_SPACING 64
|
||||
#define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2)
|
||||
#define RMNET_MAP_DESC_HEADROOM 128
|
||||
|
||||
#define RMNET_MAP_COMMAND_REQUEST 0
|
||||
#define RMNET_MAP_COMMAND_ACK 1
|
||||
#define RMNET_MAP_COMMAND_UNSUPPORTED 2
|
||||
#define RMNET_MAP_COMMAND_INVALID 3
|
||||
|
||||
#define RMNET_MAP_NO_PAD_BYTES 0
|
||||
#define RMNET_MAP_ADD_PAD_BYTES 1
|
||||
|
||||
static inline unsigned char *rmnet_map_data_ptr(struct sk_buff *skb)
|
||||
{
|
||||
/* Nonlinear packets we receive are entirely within frag 0 */
|
||||
if (skb_is_nonlinear(skb) && skb->len == skb->data_len)
|
||||
return skb_frag_address(skb_shinfo(skb)->frags);
|
||||
|
||||
return skb->data;
|
||||
}
|
||||
|
||||
static inline struct rmnet_map_control_command *
|
||||
rmnet_map_get_cmd_start(struct sk_buff *skb)
|
||||
{
|
||||
unsigned char *data = rmnet_map_data_ptr(skb);
|
||||
|
||||
data += sizeof(struct rmnet_map_header);
|
||||
return (struct rmnet_map_control_command *)data;
|
||||
}
|
||||
|
||||
static inline u8 rmnet_map_get_next_hdr_type(struct sk_buff *skb)
|
||||
{
|
||||
unsigned char *data = rmnet_map_data_ptr(skb);
|
||||
|
||||
data += sizeof(struct rmnet_map_header);
|
||||
return ((struct rmnet_map_v5_coal_header *)data)->header_type;
|
||||
}
|
||||
|
||||
static inline bool rmnet_map_get_csum_valid(struct sk_buff *skb)
|
||||
{
|
||||
unsigned char *data = rmnet_map_data_ptr(skb);
|
||||
|
||||
data += sizeof(struct rmnet_map_header);
|
||||
return ((struct rmnet_map_v5_csum_header *)data)->csum_valid_required;
|
||||
}
|
||||
|
||||
static struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
|
||||
struct rmnet_port *port);
|
||||
static struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb,
|
||||
int hdrlen, int pad,
|
||||
struct rmnet_port *port);
|
||||
static void rmnet_map_command(struct sk_buff *skb, struct rmnet_port *port);
|
||||
static void rmnet_map_checksum_uplink_packet(struct sk_buff *skb,
|
||||
struct net_device *orig_dev,
|
||||
int csum_type);
|
||||
static int rmnet_map_process_next_hdr_packet(struct sk_buff *skb,
|
||||
struct sk_buff_head *list,
|
||||
u16 len);
|
||||
static int rmnet_map_tx_agg_skip(struct sk_buff *skb, int offset);
|
||||
static void rmnet_map_tx_aggregate(struct sk_buff *skb, struct rmnet_port *port);
|
||||
static void rmnet_map_tx_aggregate_init(struct rmnet_port *port);
|
||||
static void rmnet_map_tx_aggregate_exit(struct rmnet_port *port);
|
||||
static void rmnet_map_cmd_init(struct rmnet_port *port);
|
||||
static void rmnet_map_cmd_exit(struct rmnet_port *port);
|
||||
#endif /* _RMNET_MAP_H_ */
|
||||
143
applications/quectel_MHI/src/devices/rmnet/rmnet_map_command.c
Normal file
143
applications/quectel_MHI/src/devices/rmnet/rmnet_map_command.c
Normal file
@@ -0,0 +1,143 @@
|
||||
/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include "rmnet_config.h"
|
||||
#include "rmnet_map.h"
|
||||
#include "rmnet_private.h"
|
||||
#include "rmnet_vnd.h"
|
||||
|
||||
#define RMNET_DL_IND_HDR_SIZE (sizeof(struct rmnet_map_dl_ind_hdr) + \
|
||||
sizeof(struct rmnet_map_header) + \
|
||||
sizeof(struct rmnet_map_control_command_header))
|
||||
|
||||
#define RMNET_MAP_CMD_SIZE (sizeof(struct rmnet_map_header) + \
|
||||
sizeof(struct rmnet_map_control_command_header))
|
||||
|
||||
#define RMNET_DL_IND_TRL_SIZE (sizeof(struct rmnet_map_dl_ind_trl) + \
|
||||
sizeof(struct rmnet_map_header) + \
|
||||
sizeof(struct rmnet_map_control_command_header))
|
||||
|
||||
static u8 rmnet_map_do_flow_control(struct sk_buff *skb,
|
||||
struct rmnet_port *port,
|
||||
int enable)
|
||||
{
|
||||
struct rmnet_map_header *qmap;
|
||||
struct rmnet_map_control_command *cmd;
|
||||
struct rmnet_endpoint *ep;
|
||||
struct net_device *vnd;
|
||||
u16 ip_family;
|
||||
u16 fc_seq;
|
||||
u32 qos_id;
|
||||
u8 mux_id;
|
||||
int r;
|
||||
|
||||
qmap = (struct rmnet_map_header *)rmnet_map_data_ptr(skb);
|
||||
mux_id = qmap->mux_id;
|
||||
cmd = rmnet_map_get_cmd_start(skb);
|
||||
|
||||
if (mux_id >= RMNET_MAX_LOGICAL_EP) {
|
||||
kfree_skb(skb);
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
|
||||
ep = rmnet_get_endpoint(port, mux_id);
|
||||
if (!ep) {
|
||||
kfree_skb(skb);
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
|
||||
vnd = ep->egress_dev;
|
||||
|
||||
ip_family = cmd->flow_control.ip_family;
|
||||
fc_seq = ntohs(cmd->flow_control.flow_control_seq_num);
|
||||
qos_id = ntohl(cmd->flow_control.qos_id);
|
||||
|
||||
/* Ignore the ip family and pass the sequence number for both v4 and v6
|
||||
* sequence. User space does not support creating dedicated flows for
|
||||
* the 2 protocols
|
||||
*/
|
||||
r = rmnet_vnd_do_flow_control(vnd, enable);
|
||||
if (r) {
|
||||
kfree_skb(skb);
|
||||
return RMNET_MAP_COMMAND_UNSUPPORTED;
|
||||
} else {
|
||||
return RMNET_MAP_COMMAND_ACK;
|
||||
}
|
||||
}
|
||||
|
||||
static void rmnet_map_send_ack(struct sk_buff *skb,
|
||||
unsigned char type,
|
||||
struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_map_control_command *cmd;
|
||||
struct net_device *dev = skb->dev;
|
||||
|
||||
if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4)
|
||||
pskb_trim(skb,
|
||||
skb->len - sizeof(struct rmnet_map_dl_csum_trailer));
|
||||
|
||||
skb->protocol = htons(ETH_P_MAP);
|
||||
|
||||
cmd = rmnet_map_get_cmd_start(skb);
|
||||
cmd->cmd_type = type & 0x03;
|
||||
|
||||
netif_tx_lock(dev);
|
||||
dev->netdev_ops->ndo_start_xmit(skb, dev);
|
||||
netif_tx_unlock(dev);
|
||||
}
|
||||
|
||||
/* Process MAP command frame and send N/ACK message as appropriate. Message cmd
|
||||
* name is decoded here and appropriate handler is called.
|
||||
*/
|
||||
static void rmnet_map_command(struct sk_buff *skb, struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_map_control_command *cmd;
|
||||
unsigned char command_name;
|
||||
unsigned char rc = 0;
|
||||
|
||||
cmd = rmnet_map_get_cmd_start(skb);
|
||||
command_name = cmd->command_name;
|
||||
|
||||
switch (command_name) {
|
||||
case RMNET_MAP_COMMAND_FLOW_ENABLE:
|
||||
rc = rmnet_map_do_flow_control(skb, port, 1);
|
||||
break;
|
||||
|
||||
case RMNET_MAP_COMMAND_FLOW_DISABLE:
|
||||
rc = rmnet_map_do_flow_control(skb, port, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = RMNET_MAP_COMMAND_UNSUPPORTED;
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
if (rc == RMNET_MAP_COMMAND_ACK)
|
||||
rmnet_map_send_ack(skb, rc, port);
|
||||
}
|
||||
|
||||
|
||||
static void rmnet_map_cmd_exit(struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_map_dl_ind *tmp, *idx;
|
||||
|
||||
list_for_each_entry_safe(tmp, idx, &port->dl_list, list)
|
||||
list_del_rcu(&tmp->list);
|
||||
}
|
||||
|
||||
static void rmnet_map_cmd_init(struct rmnet_port *port)
|
||||
{
|
||||
INIT_LIST_HEAD(&port->dl_list);
|
||||
|
||||
port->dl_marker_flush = -1;
|
||||
}
|
||||
682
applications/quectel_MHI/src/devices/rmnet/rmnet_map_data.c
Normal file
682
applications/quectel_MHI/src/devices/rmnet/rmnet_map_data.c
Normal file
@@ -0,0 +1,682 @@
|
||||
/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* RMNET Data MAP protocol
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <net/ip6_checksum.h>
|
||||
#include "rmnet_config.h"
|
||||
#include "rmnet_map.h"
|
||||
#include "rmnet_private.h"
|
||||
#include "rmnet_handlers.h"
|
||||
|
||||
#define RMNET_MAP_PKT_COPY_THRESHOLD 64
|
||||
#define RMNET_MAP_DEAGGR_SPACING 64
|
||||
#define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2)
|
||||
|
||||
struct rmnet_map_coal_metadata {
|
||||
void *ip_header;
|
||||
void *trans_header;
|
||||
u16 ip_len;
|
||||
u16 trans_len;
|
||||
u16 data_offset;
|
||||
u16 data_len;
|
||||
u8 ip_proto;
|
||||
u8 trans_proto;
|
||||
u8 pkt_id;
|
||||
u8 pkt_count;
|
||||
};
|
||||
|
||||
static __sum16 *rmnet_map_get_csum_field(unsigned char protocol,
|
||||
const void *txporthdr)
|
||||
{
|
||||
__sum16 *check = NULL;
|
||||
|
||||
switch (protocol) {
|
||||
case IPPROTO_TCP:
|
||||
check = &(((struct tcphdr *)txporthdr)->check);
|
||||
break;
|
||||
|
||||
case IPPROTO_UDP:
|
||||
check = &(((struct udphdr *)txporthdr)->check);
|
||||
break;
|
||||
|
||||
default:
|
||||
check = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return check;
|
||||
}
|
||||
|
||||
static void rmnet_map_complement_ipv4_txporthdr_csum_field(void *iphdr)
|
||||
{
|
||||
struct iphdr *ip4h = (struct iphdr *)iphdr;
|
||||
void *txphdr;
|
||||
u16 *csum;
|
||||
|
||||
txphdr = iphdr + ip4h->ihl * 4;
|
||||
|
||||
if (ip4h->protocol == IPPROTO_TCP || ip4h->protocol == IPPROTO_UDP) {
|
||||
csum = (u16 *)rmnet_map_get_csum_field(ip4h->protocol, txphdr);
|
||||
*csum = ~(*csum);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rmnet_map_ipv4_ul_csum_header(void *iphdr,
|
||||
struct rmnet_map_ul_csum_header *ul_header,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct iphdr *ip4h = (struct iphdr *)iphdr;
|
||||
__be16 *hdr = (__be16 *)ul_header, offset;
|
||||
|
||||
offset = htons((__force u16)(skb_transport_header(skb) -
|
||||
(unsigned char *)iphdr));
|
||||
ul_header->csum_start_offset = offset;
|
||||
ul_header->csum_insert_offset = skb->csum_offset;
|
||||
ul_header->csum_enabled = 1;
|
||||
if (ip4h->protocol == IPPROTO_UDP)
|
||||
ul_header->udp_ind = 1;
|
||||
else
|
||||
ul_header->udp_ind = 0;
|
||||
|
||||
/* Changing remaining fields to network order */
|
||||
hdr++;
|
||||
*hdr = htons((__force u16)*hdr);
|
||||
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
||||
rmnet_map_complement_ipv4_txporthdr_csum_field(iphdr);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
static void rmnet_map_complement_ipv6_txporthdr_csum_field(void *ip6hdr)
|
||||
{
|
||||
struct ipv6hdr *ip6h = (struct ipv6hdr *)ip6hdr;
|
||||
void *txphdr;
|
||||
u16 *csum;
|
||||
|
||||
txphdr = ip6hdr + sizeof(struct ipv6hdr);
|
||||
|
||||
if (ip6h->nexthdr == IPPROTO_TCP || ip6h->nexthdr == IPPROTO_UDP) {
|
||||
csum = (u16 *)rmnet_map_get_csum_field(ip6h->nexthdr, txphdr);
|
||||
*csum = ~(*csum);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rmnet_map_ipv6_ul_csum_header(void *ip6hdr,
|
||||
struct rmnet_map_ul_csum_header *ul_header,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ipv6hdr *ip6h = (struct ipv6hdr *)ip6hdr;
|
||||
__be16 *hdr = (__be16 *)ul_header, offset;
|
||||
|
||||
offset = htons((__force u16)(skb_transport_header(skb) -
|
||||
(unsigned char *)ip6hdr));
|
||||
ul_header->csum_start_offset = offset;
|
||||
ul_header->csum_insert_offset = skb->csum_offset;
|
||||
ul_header->csum_enabled = 1;
|
||||
|
||||
if (ip6h->nexthdr == IPPROTO_UDP)
|
||||
ul_header->udp_ind = 1;
|
||||
else
|
||||
ul_header->udp_ind = 0;
|
||||
|
||||
/* Changing remaining fields to network order */
|
||||
hdr++;
|
||||
*hdr = htons((__force u16)*hdr);
|
||||
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
||||
rmnet_map_complement_ipv6_txporthdr_csum_field(ip6hdr);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Adds MAP header to front of skb->data
|
||||
* Padding is calculated and set appropriately in MAP header. Mux ID is
|
||||
* initialized to 0.
|
||||
*/
|
||||
static struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb,
|
||||
int hdrlen, int pad,
|
||||
struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_map_header *map_header;
|
||||
u32 padding, map_datalen;
|
||||
u8 *padbytes;
|
||||
|
||||
map_datalen = skb->len - hdrlen;
|
||||
map_header = (struct rmnet_map_header *)
|
||||
skb_push(skb, sizeof(struct rmnet_map_header));
|
||||
memset(map_header, 0, sizeof(struct rmnet_map_header));
|
||||
|
||||
/* Set next_hdr bit for csum offload packets */
|
||||
if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV5)
|
||||
map_header->next_hdr = 1;
|
||||
|
||||
if (pad == RMNET_MAP_NO_PAD_BYTES) {
|
||||
map_header->pkt_len = htons(map_datalen);
|
||||
return map_header;
|
||||
}
|
||||
|
||||
padding = ALIGN(map_datalen, 4) - map_datalen;
|
||||
|
||||
if (padding == 0)
|
||||
goto done;
|
||||
|
||||
if (skb_tailroom(skb) < padding)
|
||||
return NULL;
|
||||
|
||||
padbytes = (u8 *)skb_put(skb, padding);
|
||||
memset(padbytes, 0, padding);
|
||||
|
||||
done:
|
||||
map_header->pkt_len = htons(map_datalen + padding);
|
||||
map_header->pad_len = padding & 0x3F;
|
||||
|
||||
return map_header;
|
||||
}
|
||||
|
||||
/* Deaggregates a single packet
|
||||
* A whole new buffer is allocated for each portion of an aggregated frame.
|
||||
* Caller should keep calling deaggregate() on the source skb until 0 is
|
||||
* returned, indicating that there are no more packets to deaggregate. Caller
|
||||
* is responsible for freeing the original skb.
|
||||
*/
|
||||
static struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
|
||||
struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_map_header *maph;
|
||||
struct sk_buff *skbn;
|
||||
unsigned char *data = rmnet_map_data_ptr(skb), *next_hdr = NULL;
|
||||
u32 packet_len;
|
||||
|
||||
if (skb->len == 0)
|
||||
return NULL;
|
||||
|
||||
maph = (struct rmnet_map_header *)data;
|
||||
packet_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header);
|
||||
|
||||
if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4)
|
||||
packet_len += sizeof(struct rmnet_map_dl_csum_trailer);
|
||||
else if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) {
|
||||
if (!maph->cd_bit) {
|
||||
packet_len += sizeof(struct rmnet_map_v5_csum_header);
|
||||
|
||||
/* Coalescing headers require MAPv5 */
|
||||
next_hdr = data + sizeof(*maph);
|
||||
}
|
||||
}
|
||||
|
||||
if (((int)skb->len - (int)packet_len) < 0)
|
||||
return NULL;
|
||||
|
||||
/* Some hardware can send us empty frames. Catch them */
|
||||
if (ntohs(maph->pkt_len) == 0)
|
||||
return NULL;
|
||||
|
||||
if (next_hdr &&
|
||||
((struct rmnet_map_v5_coal_header *)next_hdr)->header_type ==
|
||||
RMNET_MAP_HEADER_TYPE_COALESCING)
|
||||
return skb;
|
||||
|
||||
if (skb_is_nonlinear(skb)) {
|
||||
skb_frag_t *frag0 = skb_shinfo(skb)->frags;
|
||||
struct page *page = skb_frag_page(frag0);
|
||||
|
||||
skbn = alloc_skb(RMNET_MAP_DEAGGR_HEADROOM, GFP_ATOMIC);
|
||||
if (!skbn)
|
||||
return NULL;
|
||||
|
||||
skb_append_pagefrags(skbn, page, frag0->page_offset,
|
||||
packet_len);
|
||||
skbn->data_len += packet_len;
|
||||
skbn->len += packet_len;
|
||||
} else {
|
||||
skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING,
|
||||
GFP_ATOMIC);
|
||||
if (!skbn)
|
||||
return NULL;
|
||||
|
||||
skb_reserve(skbn, RMNET_MAP_DEAGGR_HEADROOM);
|
||||
skb_put(skbn, packet_len);
|
||||
memcpy(skbn->data, data, packet_len);
|
||||
}
|
||||
|
||||
pskb_pull(skb, packet_len);
|
||||
|
||||
return skbn;
|
||||
}
|
||||
|
||||
static void rmnet_map_v4_checksum_uplink_packet(struct sk_buff *skb,
|
||||
struct net_device *orig_dev)
|
||||
{
|
||||
struct rmnet_priv *priv = netdev_priv(orig_dev);
|
||||
struct rmnet_map_ul_csum_header *ul_header;
|
||||
void *iphdr;
|
||||
|
||||
ul_header = (struct rmnet_map_ul_csum_header *)
|
||||
skb_push(skb, sizeof(struct rmnet_map_ul_csum_header));
|
||||
|
||||
if (unlikely(!(orig_dev->features &
|
||||
(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))))
|
||||
goto sw_csum;
|
||||
|
||||
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
||||
iphdr = (char *)ul_header +
|
||||
sizeof(struct rmnet_map_ul_csum_header);
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP)) {
|
||||
rmnet_map_ipv4_ul_csum_header(iphdr, ul_header, skb);
|
||||
priv->stats.csum_hw++;
|
||||
return;
|
||||
} else if (skb->protocol == htons(ETH_P_IPV6)) {
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
rmnet_map_ipv6_ul_csum_header(iphdr, ul_header, skb);
|
||||
priv->stats.csum_hw++;
|
||||
return;
|
||||
#else
|
||||
priv->stats.csum_err_invalid_ip_version++;
|
||||
goto sw_csum;
|
||||
#endif
|
||||
} else {
|
||||
priv->stats.csum_err_invalid_ip_version++;
|
||||
}
|
||||
}
|
||||
|
||||
sw_csum:
|
||||
ul_header->csum_start_offset = 0;
|
||||
ul_header->csum_insert_offset = 0;
|
||||
ul_header->csum_enabled = 0;
|
||||
ul_header->udp_ind = 0;
|
||||
|
||||
priv->stats.csum_sw++;
|
||||
}
|
||||
|
||||
static void rmnet_map_v5_checksum_uplink_packet(struct sk_buff *skb,
|
||||
struct net_device *orig_dev)
|
||||
{
|
||||
struct rmnet_priv *priv = netdev_priv(orig_dev);
|
||||
struct rmnet_map_v5_csum_header *ul_header;
|
||||
|
||||
ul_header = (struct rmnet_map_v5_csum_header *)
|
||||
skb_push(skb, sizeof(*ul_header));
|
||||
memset(ul_header, 0, sizeof(*ul_header));
|
||||
ul_header->header_type = RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD;
|
||||
|
||||
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
||||
void *iph = (char *)ul_header + sizeof(*ul_header);
|
||||
void *trans;
|
||||
__sum16 *check;
|
||||
u8 proto;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP)) {
|
||||
u16 ip_len = ((struct iphdr *)iph)->ihl * 4;
|
||||
|
||||
proto = ((struct iphdr *)iph)->protocol;
|
||||
trans = iph + ip_len;
|
||||
} else if (skb->protocol == htons(ETH_P_IPV6)) {
|
||||
u16 ip_len = sizeof(struct ipv6hdr);
|
||||
|
||||
proto = ((struct ipv6hdr *)iph)->nexthdr;
|
||||
trans = iph + ip_len;
|
||||
} else {
|
||||
priv->stats.csum_err_invalid_ip_version++;
|
||||
goto sw_csum;
|
||||
}
|
||||
|
||||
check = rmnet_map_get_csum_field(proto, trans);
|
||||
if (check) {
|
||||
*check = 0;
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
/* Ask for checksum offloading */
|
||||
ul_header->csum_valid_required = 1;
|
||||
priv->stats.csum_hw++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sw_csum:
|
||||
priv->stats.csum_sw++;
|
||||
}
|
||||
|
||||
|
||||
/* Generates UL checksum meta info header for IPv4 and IPv6 over TCP and UDP
|
||||
* packets that are supported for UL checksum offload.
|
||||
*/
|
||||
void rmnet_map_checksum_uplink_packet(struct sk_buff *skb,
|
||||
struct net_device *orig_dev,
|
||||
int csum_type)
|
||||
{
|
||||
switch (csum_type) {
|
||||
case RMNET_FLAGS_EGRESS_MAP_CKSUMV4:
|
||||
rmnet_map_v4_checksum_uplink_packet(skb, orig_dev);
|
||||
break;
|
||||
case RMNET_FLAGS_EGRESS_MAP_CKSUMV5:
|
||||
rmnet_map_v5_checksum_uplink_packet(skb, orig_dev);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void rmnet_map_move_headers(struct sk_buff *skb)
|
||||
{
|
||||
struct iphdr *iph;
|
||||
u16 ip_len;
|
||||
u16 trans_len = 0;
|
||||
u8 proto;
|
||||
|
||||
/* This only applies to non-linear SKBs */
|
||||
if (!skb_is_nonlinear(skb))
|
||||
return;
|
||||
|
||||
iph = (struct iphdr *)rmnet_map_data_ptr(skb);
|
||||
if (iph->version == 4) {
|
||||
ip_len = iph->ihl * 4;
|
||||
proto = iph->protocol;
|
||||
if (iph->frag_off & htons(IP_OFFSET))
|
||||
/* No transport header information */
|
||||
goto pull;
|
||||
} else if (iph->version == 6) {
|
||||
struct ipv6hdr *ip6h = (struct ipv6hdr *)iph;
|
||||
__be16 frag_off;
|
||||
u8 nexthdr = ip6h->nexthdr;
|
||||
|
||||
ip_len = ipv6_skip_exthdr(skb, sizeof(*ip6h), &nexthdr,
|
||||
&frag_off);
|
||||
if (ip_len < 0)
|
||||
return;
|
||||
|
||||
proto = nexthdr;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (proto == IPPROTO_TCP) {
|
||||
struct tcphdr *tp = (struct tcphdr *)((u8 *)iph + ip_len);
|
||||
|
||||
trans_len = tp->doff * 4;
|
||||
} else if (proto == IPPROTO_UDP) {
|
||||
trans_len = sizeof(struct udphdr);
|
||||
} else if (proto == NEXTHDR_FRAGMENT) {
|
||||
/* Non-first fragments don't have the fragment length added by
|
||||
* ipv6_skip_exthdr() and sho up as proto NEXTHDR_FRAGMENT, so
|
||||
* we account for the length here.
|
||||
*/
|
||||
ip_len += sizeof(struct frag_hdr);
|
||||
}
|
||||
|
||||
pull:
|
||||
__pskb_pull_tail(skb, ip_len + trans_len);
|
||||
skb_reset_network_header(skb);
|
||||
if (trans_len)
|
||||
skb_set_transport_header(skb, ip_len);
|
||||
}
|
||||
|
||||
|
||||
/* Process a QMAPv5 packet header */
|
||||
static int rmnet_map_process_next_hdr_packet(struct sk_buff *skb,
|
||||
struct sk_buff_head *list,
|
||||
u16 len)
|
||||
{
|
||||
struct rmnet_priv *priv = netdev_priv(skb->dev);
|
||||
int rc = 0;
|
||||
|
||||
switch (rmnet_map_get_next_hdr_type(skb)) {
|
||||
case RMNET_MAP_HEADER_TYPE_COALESCING:
|
||||
priv->stats.coal.coal_rx++;
|
||||
break;
|
||||
case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD:
|
||||
if (rmnet_map_get_csum_valid(skb)) {
|
||||
priv->stats.csum_ok++;
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
} else {
|
||||
priv->stats.csum_valid_unset++;
|
||||
}
|
||||
|
||||
/* Pull unnecessary headers and move the rest to the linear
|
||||
* section of the skb.
|
||||
*/
|
||||
pskb_pull(skb,
|
||||
(sizeof(struct rmnet_map_header) +
|
||||
sizeof(struct rmnet_map_v5_csum_header)));
|
||||
rmnet_map_move_headers(skb);
|
||||
|
||||
/* Remove padding only for csum offload packets.
|
||||
* Coalesced packets should never have padding.
|
||||
*/
|
||||
pskb_trim(skb, len);
|
||||
__skb_queue_tail(list, skb);
|
||||
break;
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
long rmnet_agg_time_limit __read_mostly = 1000000L;
|
||||
long rmnet_agg_bypass_time __read_mostly = 10000000L;
|
||||
|
||||
static int rmnet_map_tx_agg_skip(struct sk_buff *skb, int offset)
|
||||
{
|
||||
u8 *packet_start = skb->data + offset;
|
||||
int is_icmp = 0;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP)) {
|
||||
struct iphdr *ip4h = (struct iphdr *)(packet_start);
|
||||
|
||||
if (ip4h->protocol == IPPROTO_ICMP)
|
||||
is_icmp = 1;
|
||||
} else if (skb->protocol == htons(ETH_P_IPV6)) {
|
||||
struct ipv6hdr *ip6h = (struct ipv6hdr *)(packet_start);
|
||||
|
||||
if (ip6h->nexthdr == IPPROTO_ICMPV6) {
|
||||
is_icmp = 1;
|
||||
} else if (ip6h->nexthdr == NEXTHDR_FRAGMENT) {
|
||||
struct frag_hdr *frag;
|
||||
|
||||
frag = (struct frag_hdr *)(packet_start
|
||||
+ sizeof(struct ipv6hdr));
|
||||
if (frag->nexthdr == IPPROTO_ICMPV6)
|
||||
is_icmp = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return is_icmp;
|
||||
}
|
||||
|
||||
static void rmnet_map_flush_tx_packet_work(struct work_struct *work)
|
||||
{
|
||||
struct sk_buff *skb = NULL;
|
||||
struct rmnet_port *port;
|
||||
unsigned long flags;
|
||||
|
||||
port = container_of(work, struct rmnet_port, agg_wq);
|
||||
|
||||
spin_lock_irqsave(&port->agg_lock, flags);
|
||||
if (likely(port->agg_state == -EINPROGRESS)) {
|
||||
/* Buffer may have already been shipped out */
|
||||
if (likely(port->agg_skb)) {
|
||||
skb = port->agg_skb;
|
||||
port->agg_skb = NULL;
|
||||
port->agg_count = 0;
|
||||
memset(&port->agg_time, 0, sizeof(struct timespec));
|
||||
}
|
||||
port->agg_state = 0;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&port->agg_lock, flags);
|
||||
if (skb)
|
||||
dev_queue_xmit(skb);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart rmnet_map_flush_tx_packet_queue(struct hrtimer *t)
|
||||
{
|
||||
struct rmnet_port *port;
|
||||
|
||||
port = container_of(t, struct rmnet_port, hrtimer);
|
||||
|
||||
schedule_work(&port->agg_wq);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static void rmnet_map_linearize_copy(struct sk_buff *dst, struct sk_buff *src)
|
||||
{
|
||||
unsigned int linear = src->len - src->data_len, target = src->len;
|
||||
unsigned char *src_buf;
|
||||
struct sk_buff *skb;
|
||||
|
||||
src_buf = src->data;
|
||||
skb_put_data(dst, src_buf, linear);
|
||||
target -= linear;
|
||||
|
||||
skb = src;
|
||||
|
||||
while (target) {
|
||||
unsigned int i = 0, non_linear = 0;
|
||||
|
||||
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
|
||||
non_linear = skb_frag_size(&skb_shinfo(skb)->frags[i]);
|
||||
src_buf = skb_frag_address(&skb_shinfo(skb)->frags[i]);
|
||||
|
||||
skb_put_data(dst, src_buf, non_linear);
|
||||
target -= non_linear;
|
||||
}
|
||||
|
||||
if (skb_shinfo(skb)->frag_list) {
|
||||
skb = skb_shinfo(skb)->frag_list;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (skb->next)
|
||||
skb = skb->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void rmnet_map_tx_aggregate(struct sk_buff *skb, struct rmnet_port *port)
|
||||
{
|
||||
struct timespec diff, last;
|
||||
int size, agg_count = 0;
|
||||
struct sk_buff *agg_skb;
|
||||
unsigned long flags;
|
||||
|
||||
new_packet:
|
||||
spin_lock_irqsave(&port->agg_lock, flags);
|
||||
memcpy(&last, &port->agg_last, sizeof(struct timespec));
|
||||
getnstimeofday(&port->agg_last);
|
||||
|
||||
if (!port->agg_skb) {
|
||||
/* Check to see if we should agg first. If the traffic is very
|
||||
* sparse, don't aggregate. We will need to tune this later
|
||||
*/
|
||||
diff = timespec_sub(port->agg_last, last);
|
||||
size = port->egress_agg_params.agg_size - skb->len;
|
||||
|
||||
if (diff.tv_sec > 0 || diff.tv_nsec > rmnet_agg_bypass_time ||
|
||||
size <= 0) {
|
||||
spin_unlock_irqrestore(&port->agg_lock, flags);
|
||||
skb->protocol = htons(ETH_P_MAP);
|
||||
dev_queue_xmit(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
port->agg_skb = alloc_skb(port->egress_agg_params.agg_size,
|
||||
GFP_ATOMIC);
|
||||
if (!port->agg_skb) {
|
||||
port->agg_skb = 0;
|
||||
port->agg_count = 0;
|
||||
memset(&port->agg_time, 0, sizeof(struct timespec));
|
||||
spin_unlock_irqrestore(&port->agg_lock, flags);
|
||||
skb->protocol = htons(ETH_P_MAP);
|
||||
dev_queue_xmit(skb);
|
||||
return;
|
||||
}
|
||||
rmnet_map_linearize_copy(port->agg_skb, skb);
|
||||
port->agg_skb->dev = skb->dev;
|
||||
port->agg_skb->protocol = htons(ETH_P_MAP);
|
||||
port->agg_count = 1;
|
||||
getnstimeofday(&port->agg_time);
|
||||
dev_kfree_skb_any(skb);
|
||||
goto schedule;
|
||||
}
|
||||
diff = timespec_sub(port->agg_last, port->agg_time);
|
||||
size = port->egress_agg_params.agg_size - port->agg_skb->len;
|
||||
|
||||
if (skb->len > size ||
|
||||
port->agg_count >= port->egress_agg_params.agg_count ||
|
||||
diff.tv_sec > 0 || diff.tv_nsec > rmnet_agg_time_limit) {
|
||||
agg_skb = port->agg_skb;
|
||||
agg_count = port->agg_count;
|
||||
port->agg_skb = 0;
|
||||
port->agg_count = 0;
|
||||
memset(&port->agg_time, 0, sizeof(struct timespec));
|
||||
port->agg_state = 0;
|
||||
spin_unlock_irqrestore(&port->agg_lock, flags);
|
||||
hrtimer_cancel(&port->hrtimer);
|
||||
dev_queue_xmit(agg_skb);
|
||||
goto new_packet;
|
||||
}
|
||||
|
||||
rmnet_map_linearize_copy(port->agg_skb, skb);
|
||||
port->agg_count++;
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
schedule:
|
||||
if (port->agg_state != -EINPROGRESS) {
|
||||
port->agg_state = -EINPROGRESS;
|
||||
hrtimer_start(&port->hrtimer,
|
||||
ns_to_ktime(port->egress_agg_params.agg_time),
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
spin_unlock_irqrestore(&port->agg_lock, flags);
|
||||
}
|
||||
|
||||
static void rmnet_map_tx_aggregate_init(struct rmnet_port *port)
|
||||
{
|
||||
hrtimer_init(&port->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
port->hrtimer.function = rmnet_map_flush_tx_packet_queue;
|
||||
port->egress_agg_params.agg_size = 8192;
|
||||
port->egress_agg_params.agg_count = 20;
|
||||
port->egress_agg_params.agg_time = 3000000;
|
||||
spin_lock_init(&port->agg_lock);
|
||||
|
||||
INIT_WORK(&port->agg_wq, rmnet_map_flush_tx_packet_work);
|
||||
}
|
||||
|
||||
static void rmnet_map_tx_aggregate_exit(struct rmnet_port *port)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
hrtimer_cancel(&port->hrtimer);
|
||||
cancel_work_sync(&port->agg_wq);
|
||||
|
||||
spin_lock_irqsave(&port->agg_lock, flags);
|
||||
if (port->agg_state == -EINPROGRESS) {
|
||||
if (port->agg_skb) {
|
||||
kfree_skb(port->agg_skb);
|
||||
port->agg_skb = NULL;
|
||||
port->agg_count = 0;
|
||||
memset(&port->agg_time, 0, sizeof(struct timespec));
|
||||
}
|
||||
|
||||
port->agg_state = 0;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&port->agg_lock, flags);
|
||||
}
|
||||
34
applications/quectel_MHI/src/devices/rmnet/rmnet_private.h
Normal file
34
applications/quectel_MHI/src/devices/rmnet/rmnet_private.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* Copyright (c) 2013-2014, 2016-2019 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _RMNET_PRIVATE_H_
|
||||
#define _RMNET_PRIVATE_H_
|
||||
|
||||
#define RMNET_MAX_PACKET_SIZE 16384
|
||||
#define RMNET_DFLT_PACKET_SIZE 1500
|
||||
#define RMNET_NEEDED_HEADROOM 16
|
||||
#define RMNET_TX_QUEUE_LEN 1000
|
||||
|
||||
/* Constants */
|
||||
#define RMNET_EGRESS_FORMAT_AGGREGATION BIT(31)
|
||||
#define RMNET_INGRESS_FORMAT_DL_MARKER_V1 BIT(30)
|
||||
#define RMNET_INGRESS_FORMAT_DL_MARKER_V2 BIT(29)
|
||||
|
||||
#define RMNET_INGRESS_FORMAT_DL_MARKER (RMNET_INGRESS_FORMAT_DL_MARKER_V1 |\
|
||||
RMNET_INGRESS_FORMAT_DL_MARKER_V2)
|
||||
|
||||
/* Replace skb->dev to a virtual rmnet device and pass up the stack */
|
||||
#define RMNET_EPMODE_VND (1)
|
||||
/* Pass the frame directly to another device with dev_queue_xmit() */
|
||||
#define RMNET_EPMODE_BRIDGE (2)
|
||||
|
||||
#endif /* _RMNET_PRIVATE_H_ */
|
||||
257
applications/quectel_MHI/src/devices/rmnet/rmnet_trace.h
Normal file
257
applications/quectel_MHI/src/devices/rmnet/rmnet_trace.h
Normal file
@@ -0,0 +1,257 @@
|
||||
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM rmnet
|
||||
#define TRACE_INCLUDE_FILE rmnet_trace
|
||||
|
||||
#if !defined(_RMNET_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _RMNET_TRACE_H_
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Trace events for rmnet module */
|
||||
/*****************************************************************************/
|
||||
DECLARE_EVENT_CLASS
|
||||
(rmnet_mod_template,
|
||||
|
||||
TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
|
||||
u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
|
||||
|
||||
TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(u8, func)
|
||||
__field(u8, evt)
|
||||
__field(u32, uint1)
|
||||
__field(u32, uint2)
|
||||
__field(u64, ulong1)
|
||||
__field(u64, ulong2)
|
||||
__field(void *, ptr1)
|
||||
__field(void *, ptr2)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->func = func;
|
||||
__entry->evt = evt;
|
||||
__entry->uint1 = uint1;
|
||||
__entry->uint2 = uint2;
|
||||
__entry->ulong1 = ulong1;
|
||||
__entry->ulong2 = ulong2;
|
||||
__entry->ptr1 = ptr1;
|
||||
__entry->ptr2 = ptr2;
|
||||
),
|
||||
|
||||
TP_printk("fun:%u ev:%u u1:%u u2:%u ul1:%llu ul2:%llu p1:0x%pK p2:0x%pK",
|
||||
__entry->func, __entry->evt,
|
||||
__entry->uint1, __entry->uint2,
|
||||
__entry->ulong1, __entry->ulong2,
|
||||
__entry->ptr1, __entry->ptr2)
|
||||
)
|
||||
|
||||
DEFINE_EVENT
|
||||
(rmnet_mod_template, rmnet_low,
|
||||
|
||||
TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
|
||||
u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
|
||||
|
||||
TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
|
||||
|
||||
);
|
||||
|
||||
DEFINE_EVENT
|
||||
(rmnet_mod_template, rmnet_high,
|
||||
|
||||
TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
|
||||
u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
|
||||
|
||||
TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
|
||||
|
||||
);
|
||||
|
||||
DEFINE_EVENT
|
||||
(rmnet_mod_template, rmnet_err,
|
||||
|
||||
TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
|
||||
u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
|
||||
|
||||
TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
|
||||
|
||||
);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Trace events for rmnet_perf module */
|
||||
/*****************************************************************************/
|
||||
DEFINE_EVENT
|
||||
(rmnet_mod_template, rmnet_perf_low,
|
||||
|
||||
TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
|
||||
u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
|
||||
|
||||
TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
|
||||
|
||||
);
|
||||
|
||||
DEFINE_EVENT
|
||||
(rmnet_mod_template, rmnet_perf_high,
|
||||
|
||||
TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
|
||||
u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
|
||||
|
||||
TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
|
||||
|
||||
);
|
||||
|
||||
DEFINE_EVENT
|
||||
(rmnet_mod_template, rmnet_perf_err,
|
||||
|
||||
TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
|
||||
u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
|
||||
|
||||
TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
|
||||
|
||||
);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Trace events for rmnet_shs module */
|
||||
/*****************************************************************************/
|
||||
DEFINE_EVENT
|
||||
(rmnet_mod_template, rmnet_shs_low,
|
||||
|
||||
TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
|
||||
u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
|
||||
|
||||
TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
|
||||
|
||||
);
|
||||
|
||||
DEFINE_EVENT
|
||||
(rmnet_mod_template, rmnet_shs_high,
|
||||
|
||||
TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
|
||||
u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
|
||||
|
||||
TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
|
||||
|
||||
);
|
||||
|
||||
DEFINE_EVENT
|
||||
(rmnet_mod_template, rmnet_shs_err,
|
||||
|
||||
TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
|
||||
u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
|
||||
|
||||
TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
|
||||
|
||||
);
|
||||
|
||||
DEFINE_EVENT
|
||||
(rmnet_mod_template, rmnet_shs_wq_low,
|
||||
|
||||
TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
|
||||
u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
|
||||
|
||||
TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
|
||||
|
||||
);
|
||||
|
||||
DEFINE_EVENT
|
||||
(rmnet_mod_template, rmnet_shs_wq_high,
|
||||
|
||||
TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
|
||||
u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
|
||||
|
||||
TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
|
||||
|
||||
);
|
||||
|
||||
DEFINE_EVENT
|
||||
(rmnet_mod_template, rmnet_shs_wq_err,
|
||||
|
||||
TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
|
||||
u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
|
||||
|
||||
TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
|
||||
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS
|
||||
(rmnet_freq_template,
|
||||
|
||||
TP_PROTO(u8 core, u32 newfreq),
|
||||
|
||||
TP_ARGS(core, newfreq),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(u8, core)
|
||||
__field(u32, newfreq)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->core = core;
|
||||
__entry->newfreq = newfreq;
|
||||
|
||||
),
|
||||
|
||||
TP_printk("freq policy core:%u freq floor :%u",
|
||||
__entry->core, __entry->newfreq)
|
||||
|
||||
);
|
||||
|
||||
DEFINE_EVENT
|
||||
(rmnet_freq_template, rmnet_freq_boost,
|
||||
|
||||
TP_PROTO(u8 core, u32 newfreq),
|
||||
|
||||
TP_ARGS(core, newfreq)
|
||||
);
|
||||
|
||||
DEFINE_EVENT
|
||||
(rmnet_freq_template, rmnet_freq_reset,
|
||||
|
||||
TP_PROTO(u8 core, u32 newfreq),
|
||||
|
||||
TP_ARGS(core, newfreq)
|
||||
);
|
||||
|
||||
TRACE_EVENT
|
||||
(rmnet_freq_update,
|
||||
|
||||
TP_PROTO(u8 core, u32 lowfreq, u32 highfreq),
|
||||
|
||||
TP_ARGS(core, lowfreq, highfreq),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(u8, core)
|
||||
__field(u32, lowfreq)
|
||||
__field(u32, highfreq)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->core = core;
|
||||
__entry->lowfreq = lowfreq;
|
||||
__entry->highfreq = highfreq;
|
||||
|
||||
),
|
||||
|
||||
TP_printk("freq policy update core:%u policy freq floor :%u freq ceil :%u",
|
||||
__entry->core, __entry->lowfreq, __entry->highfreq)
|
||||
);
|
||||
#endif /* _RMNET_TRACE_H_ */
|
||||
|
||||
/* This part must be outside protection */
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH ../drivers/net/ethernet/qualcomm/rmnet
|
||||
#include <trace/define_trace.h>
|
||||
382
applications/quectel_MHI/src/devices/rmnet/rmnet_vnd.c
Normal file
382
applications/quectel_MHI/src/devices/rmnet/rmnet_vnd.c
Normal file
@@ -0,0 +1,382 @@
|
||||
/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* RMNET Data virtual network driver
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/ip.h>
|
||||
#include <net/pkt_sched.h>
|
||||
#include "rmnet_config.h"
|
||||
#include "rmnet_handlers.h"
|
||||
#include "rmnet_private.h"
|
||||
#include "rmnet_map.h"
|
||||
#include "rmnet_vnd.h"
|
||||
|
||||
/* RX/TX Fixup */
|
||||
|
||||
static void rmnet_vnd_rx_fixup(struct net_device *dev, u32 skb_len)
|
||||
{
|
||||
struct rmnet_priv *priv = netdev_priv(dev);
|
||||
struct rmnet_pcpu_stats *pcpu_ptr;
|
||||
|
||||
pcpu_ptr = this_cpu_ptr(priv->pcpu_stats);
|
||||
|
||||
u64_stats_update_begin(&pcpu_ptr->syncp);
|
||||
pcpu_ptr->stats.rx_pkts++;
|
||||
pcpu_ptr->stats.rx_bytes += skb_len;
|
||||
u64_stats_update_end(&pcpu_ptr->syncp);
|
||||
}
|
||||
|
||||
static void rmnet_vnd_tx_fixup(struct net_device *dev, u32 skb_len)
|
||||
{
|
||||
struct rmnet_priv *priv = netdev_priv(dev);
|
||||
struct rmnet_pcpu_stats *pcpu_ptr;
|
||||
|
||||
pcpu_ptr = this_cpu_ptr(priv->pcpu_stats);
|
||||
|
||||
u64_stats_update_begin(&pcpu_ptr->syncp);
|
||||
pcpu_ptr->stats.tx_pkts++;
|
||||
pcpu_ptr->stats.tx_bytes += skb_len;
|
||||
u64_stats_update_end(&pcpu_ptr->syncp);
|
||||
}
|
||||
|
||||
/* Network Device Operations */
|
||||
|
||||
static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct rmnet_priv *priv;
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
if (priv->real_dev) {
|
||||
rmnet_egress_handler(skb);
|
||||
} else {
|
||||
this_cpu_inc(priv->pcpu_stats->stats.tx_drops);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu)
|
||||
{
|
||||
if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
rmnet_dev->mtu = new_mtu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmnet_vnd_get_iflink(const struct net_device *dev)
|
||||
{
|
||||
struct rmnet_priv *priv = netdev_priv(dev);
|
||||
|
||||
return priv->real_dev->ifindex;
|
||||
}
|
||||
|
||||
static int rmnet_vnd_init(struct net_device *dev)
|
||||
{
|
||||
struct rmnet_priv *priv = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
priv->pcpu_stats = alloc_percpu(struct rmnet_pcpu_stats);
|
||||
if (!priv->pcpu_stats)
|
||||
return -ENOMEM;
|
||||
|
||||
err = gro_cells_init(&priv->gro_cells, dev);
|
||||
if (err) {
|
||||
free_percpu(priv->pcpu_stats);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmnet_vnd_uninit(struct net_device *dev)
|
||||
{
|
||||
struct rmnet_priv *priv = netdev_priv(dev);
|
||||
|
||||
gro_cells_destroy(&priv->gro_cells);
|
||||
free_percpu(priv->pcpu_stats);
|
||||
}
|
||||
|
||||
static struct rtnl_link_stats64* rmnet_get_stats64(struct net_device *dev,
|
||||
struct rtnl_link_stats64 *s)
|
||||
{
|
||||
struct rmnet_priv *priv = netdev_priv(dev);
|
||||
struct rmnet_vnd_stats total_stats;
|
||||
struct rmnet_pcpu_stats *pcpu_ptr;
|
||||
unsigned int cpu, start;
|
||||
|
||||
memset(&total_stats, 0, sizeof(struct rmnet_vnd_stats));
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
pcpu_ptr = per_cpu_ptr(priv->pcpu_stats, cpu);
|
||||
|
||||
do {
|
||||
start = u64_stats_fetch_begin_irq(&pcpu_ptr->syncp);
|
||||
total_stats.rx_pkts += pcpu_ptr->stats.rx_pkts;
|
||||
total_stats.rx_bytes += pcpu_ptr->stats.rx_bytes;
|
||||
total_stats.tx_pkts += pcpu_ptr->stats.tx_pkts;
|
||||
total_stats.tx_bytes += pcpu_ptr->stats.tx_bytes;
|
||||
} while (u64_stats_fetch_retry_irq(&pcpu_ptr->syncp, start));
|
||||
|
||||
total_stats.tx_drops += pcpu_ptr->stats.tx_drops;
|
||||
}
|
||||
|
||||
s->rx_packets = total_stats.rx_pkts;
|
||||
s->rx_bytes = total_stats.rx_bytes;
|
||||
s->tx_packets = total_stats.tx_pkts;
|
||||
s->tx_bytes = total_stats.tx_bytes;
|
||||
s->tx_dropped = total_stats.tx_drops;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static const struct net_device_ops rmnet_vnd_ops = {
|
||||
.ndo_start_xmit = rmnet_vnd_start_xmit,
|
||||
.ndo_change_mtu = rmnet_vnd_change_mtu,
|
||||
.ndo_get_iflink = rmnet_vnd_get_iflink,
|
||||
//.ndo_add_slave = rmnet_add_bridge,
|
||||
//.ndo_del_slave = rmnet_del_bridge,
|
||||
.ndo_init = rmnet_vnd_init,
|
||||
.ndo_uninit = rmnet_vnd_uninit,
|
||||
.ndo_get_stats64 = rmnet_get_stats64,
|
||||
};
|
||||
|
||||
static const char rmnet_gstrings_stats[][ETH_GSTRING_LEN] = {
|
||||
"Checksum ok",
|
||||
"Checksum valid bit not set",
|
||||
"Checksum validation failed",
|
||||
"Checksum error bad buffer",
|
||||
"Checksum error bad ip version",
|
||||
"Checksum error bad transport",
|
||||
"Checksum skipped on ip fragment",
|
||||
"Checksum skipped",
|
||||
"Checksum computed in software",
|
||||
"Checksum computed in hardware",
|
||||
"Coalescing packets received",
|
||||
"Coalesced packets",
|
||||
"Coalescing header NLO errors",
|
||||
"Coalescing header pcount errors",
|
||||
"Coalescing checksum errors",
|
||||
"Coalescing packet reconstructs",
|
||||
"Coalescing IP version invalid",
|
||||
"Coalescing L4 header invalid",
|
||||
"Coalescing close Non-coalescable",
|
||||
"Coalescing close L3 mismatch",
|
||||
"Coalescing close L4 mismatch",
|
||||
"Coalescing close HW NLO limit",
|
||||
"Coalescing close HW packet limit",
|
||||
"Coalescing close HW byte limit",
|
||||
"Coalescing close HW time limit",
|
||||
"Coalescing close HW eviction",
|
||||
"Coalescing close Coalescable",
|
||||
"Coalescing packets over VEID0",
|
||||
"Coalescing packets over VEID1",
|
||||
"Coalescing packets over VEID2",
|
||||
"Coalescing packets over VEID3",
|
||||
};
|
||||
|
||||
static const char rmnet_port_gstrings_stats[][ETH_GSTRING_LEN] = {
|
||||
"MAP Cmd last version",
|
||||
"MAP Cmd last ep id",
|
||||
"MAP Cmd last transaction id",
|
||||
"DL header last seen sequence",
|
||||
"DL header last seen bytes",
|
||||
"DL header last seen packets",
|
||||
"DL header last seen flows",
|
||||
"DL header pkts received",
|
||||
"DL header total bytes received",
|
||||
"DL header total pkts received",
|
||||
"DL trailer last seen sequence",
|
||||
"DL trailer pkts received",
|
||||
};
|
||||
|
||||
static void rmnet_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
|
||||
{
|
||||
switch (stringset) {
|
||||
case ETH_SS_STATS:
|
||||
memcpy(buf, &rmnet_gstrings_stats,
|
||||
sizeof(rmnet_gstrings_stats));
|
||||
memcpy(buf + sizeof(rmnet_gstrings_stats),
|
||||
&rmnet_port_gstrings_stats,
|
||||
sizeof(rmnet_port_gstrings_stats));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int rmnet_get_sset_count(struct net_device *dev, int sset)
|
||||
{
|
||||
switch (sset) {
|
||||
case ETH_SS_STATS:
|
||||
return ARRAY_SIZE(rmnet_gstrings_stats) +
|
||||
ARRAY_SIZE(rmnet_port_gstrings_stats);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static void rmnet_get_ethtool_stats(struct net_device *dev,
|
||||
struct ethtool_stats *stats, u64 *data)
|
||||
{
|
||||
struct rmnet_priv *priv = netdev_priv(dev);
|
||||
struct rmnet_priv_stats *st = &priv->stats;
|
||||
struct rmnet_port_priv_stats *stp;
|
||||
struct rmnet_port *port;
|
||||
|
||||
port = rmnet_get_port(priv->real_dev);
|
||||
|
||||
if (!data || !port)
|
||||
return;
|
||||
|
||||
stp = &port->stats;
|
||||
|
||||
memcpy(data, st, ARRAY_SIZE(rmnet_gstrings_stats) * sizeof(u64));
|
||||
memcpy(data + ARRAY_SIZE(rmnet_gstrings_stats), stp,
|
||||
ARRAY_SIZE(rmnet_port_gstrings_stats) * sizeof(u64));
|
||||
}
|
||||
|
||||
static int rmnet_stats_reset(struct net_device *dev)
|
||||
{
|
||||
struct rmnet_priv *priv = netdev_priv(dev);
|
||||
struct rmnet_port_priv_stats *stp;
|
||||
struct rmnet_port *port;
|
||||
|
||||
port = rmnet_get_port(priv->real_dev);
|
||||
if (!port)
|
||||
return -EINVAL;
|
||||
|
||||
stp = &port->stats;
|
||||
|
||||
memset(stp, 0, sizeof(*stp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ethtool_ops rmnet_ethtool_ops = {
|
||||
.get_ethtool_stats = rmnet_get_ethtool_stats,
|
||||
.get_strings = rmnet_get_strings,
|
||||
.get_sset_count = rmnet_get_sset_count,
|
||||
.nway_reset = rmnet_stats_reset,
|
||||
};
|
||||
|
||||
/* Called by kernel whenever a new rmnet<n> device is created. Sets MTU,
|
||||
* flags, ARP type, needed headroom, etc...
|
||||
*/
|
||||
void rmnet_vnd_setup(struct net_device *rmnet_dev)
|
||||
{
|
||||
rmnet_dev->netdev_ops = &rmnet_vnd_ops;
|
||||
rmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE;
|
||||
rmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM;
|
||||
random_ether_addr(rmnet_dev->dev_addr);
|
||||
rmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN;
|
||||
|
||||
/* Raw IP mode */
|
||||
rmnet_dev->header_ops = NULL; /* No header */
|
||||
rmnet_dev->type = ARPHRD_RAWIP;
|
||||
rmnet_dev->hard_header_len = 0;
|
||||
rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
|
||||
|
||||
//rmnet_dev->needs_free_netdev = true;
|
||||
|
||||
rmnet_dev->hw_features = NETIF_F_RXCSUM;
|
||||
rmnet_dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
|
||||
//rmnet_dev->hw_features |= NETIF_F_SG;
|
||||
//rmnet_dev->hw_features |= NETIF_F_GRO_HW;
|
||||
}
|
||||
|
||||
/* Exposed API */
|
||||
|
||||
static int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,
|
||||
struct rmnet_port *port,
|
||||
struct net_device *real_dev,
|
||||
struct rmnet_endpoint *ep)
|
||||
{
|
||||
struct rmnet_priv *priv = netdev_priv(rmnet_dev);
|
||||
struct rmnet_nss_cb *nss_cb;
|
||||
int rc;
|
||||
|
||||
if (ep->egress_dev)
|
||||
return -EINVAL;
|
||||
|
||||
if (rmnet_get_endpoint(port, id))
|
||||
return -EBUSY;
|
||||
|
||||
rmnet_dev->hw_features = NETIF_F_RXCSUM;
|
||||
rmnet_dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
|
||||
rmnet_dev->hw_features |= NETIF_F_SG;
|
||||
|
||||
priv->real_dev = real_dev;
|
||||
|
||||
rc = register_netdevice(rmnet_dev);
|
||||
if (!rc) {
|
||||
ep->egress_dev = rmnet_dev;
|
||||
ep->mux_id = id;
|
||||
port->nr_rmnet_devs++;
|
||||
|
||||
//rmnet_dev->rtnl_link_ops = &rmnet_link_ops;
|
||||
|
||||
priv->mux_id = id;
|
||||
|
||||
netdev_dbg(rmnet_dev, "rmnet dev created\n");
|
||||
}
|
||||
|
||||
nss_cb = rcu_dereference(rmnet_nss_callbacks);
|
||||
if (nss_cb) {
|
||||
rc = nss_cb->nss_create(rmnet_dev);
|
||||
if (rc) {
|
||||
/* Log, but don't fail the device creation */
|
||||
netdev_err(rmnet_dev, "Device will not use NSS path: %d\n", rc);
|
||||
rc = 0;
|
||||
} else {
|
||||
netdev_dbg(rmnet_dev, "NSS context created\n");
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int rmnet_vnd_dellink(u8 id, struct rmnet_port *port,
|
||||
struct rmnet_endpoint *ep)
|
||||
{
|
||||
struct rmnet_nss_cb *nss_cb;
|
||||
|
||||
if (id >= RMNET_MAX_LOGICAL_EP || !ep->egress_dev)
|
||||
return -EINVAL;
|
||||
|
||||
if (ep->egress_dev) {
|
||||
nss_cb = rcu_dereference(rmnet_nss_callbacks);
|
||||
if (nss_cb)
|
||||
nss_cb->nss_free(ep->egress_dev);
|
||||
}
|
||||
ep->egress_dev = NULL;
|
||||
port->nr_rmnet_devs--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmnet_vnd_do_flow_control(struct net_device *rmnet_dev, int enable)
|
||||
{
|
||||
netdev_dbg(rmnet_dev, "Setting VND TX queue state to %d\n", enable);
|
||||
/* Although we expect similar number of enable/disable
|
||||
* commands, optimize for the disable. That is more
|
||||
* latency sensitive than enable
|
||||
*/
|
||||
if (unlikely(enable))
|
||||
netif_wake_queue(rmnet_dev);
|
||||
else
|
||||
netif_stop_queue(rmnet_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
29
applications/quectel_MHI/src/devices/rmnet/rmnet_vnd.h
Normal file
29
applications/quectel_MHI/src/devices/rmnet/rmnet_vnd.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* RMNET Data Virtual Network Device APIs
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _RMNET_VND_H_
|
||||
#define _RMNET_VND_H_
|
||||
|
||||
static int rmnet_vnd_do_flow_control(struct net_device *dev, int enable);
|
||||
static int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,
|
||||
struct rmnet_port *port,
|
||||
struct net_device *real_dev,
|
||||
struct rmnet_endpoint *ep);
|
||||
static int rmnet_vnd_dellink(u8 id, struct rmnet_port *port,
|
||||
struct rmnet_endpoint *ep);
|
||||
static void rmnet_vnd_rx_fixup(struct net_device *dev, u32 skb_len);
|
||||
static void rmnet_vnd_tx_fixup(struct net_device *dev, u32 skb_len);
|
||||
static void rmnet_vnd_setup(struct net_device *dev);
|
||||
#endif /* _RMNET_VND_H_ */
|
||||
1129
applications/quectel_MHI/src/devices/rmnet_handler.c
Normal file
1129
applications/quectel_MHI/src/devices/rmnet_handler.c
Normal file
File diff suppressed because it is too large
Load Diff
424
applications/quectel_MHI/src/devices/rmnet_nss.c
Normal file
424
applications/quectel_MHI/src/devices/rmnet_nss.c
Normal file
@@ -0,0 +1,424 @@
|
||||
/* Copyright (c) 2019, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/ip.h>
|
||||
#include <qca-nss-drv/nss_api_if.h>
|
||||
|
||||
#include <linux/rmnet_nss.h>
|
||||
|
||||
#define RMNET_NSS_HASH_BITS 8
|
||||
#define hash_add_ptr(table, node, key) \
|
||||
hlist_add_head(node, &table[hash_ptr(key, HASH_BITS(table))])
|
||||
|
||||
static DEFINE_HASHTABLE(rmnet_nss_ctx_hashtable, RMNET_NSS_HASH_BITS);
|
||||
|
||||
struct rmnet_nss_ctx {
|
||||
struct hlist_node hnode;
|
||||
struct net_device *rmnet_dev;
|
||||
struct nss_rmnet_rx_handle *nss_ctx;
|
||||
};
|
||||
|
||||
enum __rmnet_nss_stat {
|
||||
RMNET_NSS_RX_ETH,
|
||||
RMNET_NSS_RX_FAIL,
|
||||
RMNET_NSS_RX_NON_ETH,
|
||||
RMNET_NSS_RX_BUSY,
|
||||
RMNET_NSS_TX_NO_CTX,
|
||||
RMNET_NSS_TX_SUCCESS,
|
||||
RMNET_NSS_TX_FAIL,
|
||||
RMNET_NSS_TX_NONLINEAR,
|
||||
RMNET_NSS_TX_BAD_IP,
|
||||
RMNET_NSS_EXCEPTIONS,
|
||||
RMNET_NSS_EX_BAD_HDR,
|
||||
RMNET_NSS_EX_BAD_IP,
|
||||
RMNET_NSS_EX_SUCCESS,
|
||||
RMNET_NSS_TX_BAD_FRAGS,
|
||||
RMNET_NSS_TX_LINEARIZE_FAILS,
|
||||
RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS,
|
||||
RMNET_NSS_TX_BUSY_LOOP,
|
||||
RMNET_NSS_NUM_STATS,
|
||||
};
|
||||
|
||||
static unsigned long rmnet_nss_stats[RMNET_NSS_NUM_STATS];
|
||||
|
||||
#define RMNET_NSS_STAT(name, counter, desc) \
|
||||
module_param_named(name, rmnet_nss_stats[counter], ulong, 0444); \
|
||||
MODULE_PARM_DESC(name, desc)
|
||||
|
||||
RMNET_NSS_STAT(rmnet_nss_rx_ethernet, RMNET_NSS_RX_ETH,
|
||||
"Number of Ethernet headers successfully removed");
|
||||
RMNET_NSS_STAT(rmnet_nss_rx_fail, RMNET_NSS_RX_FAIL,
|
||||
"Number of Ethernet headers that could not be removed");
|
||||
RMNET_NSS_STAT(rmnet_nss_rx_non_ethernet, RMNET_NSS_RX_NON_ETH,
|
||||
"Number of non-Ethernet packets received");
|
||||
RMNET_NSS_STAT(rmnet_nss_rx_busy, RMNET_NSS_RX_BUSY,
|
||||
"Number of packets dropped decause rmnet_data device was busy");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_slow, RMNET_NSS_TX_NO_CTX,
|
||||
"Number of packets sent over non-NSS-accelerated rmnet device");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_fast, RMNET_NSS_TX_SUCCESS,
|
||||
"Number of packets sent over NSS-accelerated rmnet device");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_fail, RMNET_NSS_TX_FAIL,
|
||||
"Number of packets that NSS could not transmit");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_nonlinear, RMNET_NSS_TX_NONLINEAR,
|
||||
"Number of non linear sent over NSS-accelerated rmnet device");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_invalid_ip, RMNET_NSS_TX_BAD_IP,
|
||||
"Number of ingress packets with invalid IP headers");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_invalid_frags, RMNET_NSS_TX_BAD_FRAGS,
|
||||
"Number of ingress packets with invalid frag format");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_linearize_fail, RMNET_NSS_TX_LINEARIZE_FAILS,
|
||||
"Number of ingress packets where linearize in tx fails");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_exceptions, RMNET_NSS_EXCEPTIONS,
|
||||
"Number of times our DL exception handler was invoked");
|
||||
RMNET_NSS_STAT(rmnet_nss_exception_non_ethernet, RMNET_NSS_EX_BAD_HDR,
|
||||
"Number of non-Ethernet exception packets");
|
||||
RMNET_NSS_STAT(rmnet_nss_exception_invalid_ip, RMNET_NSS_EX_BAD_IP,
|
||||
"Number of exception packets with invalid IP headers");
|
||||
RMNET_NSS_STAT(rmnet_nss_exception_success, RMNET_NSS_EX_SUCCESS,
|
||||
"Number of exception packets handled successfully");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_non_zero_headlen_frags, RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS,
|
||||
"Number of packets with non zero headlen");
|
||||
RMNET_NSS_STAT(rmnet_nss_tx_busy_loop, RMNET_NSS_TX_BUSY_LOOP,
|
||||
"Number of times tx packets busy looped");
|
||||
|
||||
static void rmnet_nss_inc_stat(enum __rmnet_nss_stat stat)
|
||||
{
|
||||
if (stat >= 0 && stat < RMNET_NSS_NUM_STATS)
|
||||
rmnet_nss_stats[stat]++;
|
||||
}
|
||||
|
||||
static struct rmnet_nss_ctx *rmnet_nss_find_ctx(struct net_device *dev)
|
||||
{
|
||||
struct rmnet_nss_ctx *ctx;
|
||||
struct hlist_head *bucket;
|
||||
u32 hash;
|
||||
|
||||
hash = hash_ptr(dev, HASH_BITS(rmnet_nss_ctx_hashtable));
|
||||
bucket = &rmnet_nss_ctx_hashtable[hash];
|
||||
hlist_for_each_entry(ctx, bucket, hnode) {
|
||||
if (ctx->rmnet_dev == dev)
|
||||
return ctx;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rmnet_nss_free_ctx(struct rmnet_nss_ctx *ctx)
|
||||
{
|
||||
if (ctx) {
|
||||
hash_del(&ctx->hnode);
|
||||
nss_rmnet_rx_xmit_callback_unregister(ctx->nss_ctx);
|
||||
nss_rmnet_rx_destroy_sync(ctx->nss_ctx);
|
||||
kfree(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pull off an ethernet header, if possible */
|
||||
static int rmnet_nss_ethhdr_pull(struct sk_buff *skb)
|
||||
{
|
||||
if (!skb->protocol || skb->protocol == htons(ETH_P_802_3)) {
|
||||
void *ret = skb_pull(skb, sizeof(struct ethhdr));
|
||||
|
||||
rmnet_nss_inc_stat((ret) ? RMNET_NSS_RX_ETH :
|
||||
RMNET_NSS_RX_FAIL);
|
||||
return !ret;
|
||||
}
|
||||
|
||||
rmnet_nss_inc_stat(RMNET_NSS_RX_NON_ETH);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Copy headers to linear section for non linear packets */
|
||||
static int rmnet_nss_adjust_header(struct sk_buff *skb)
|
||||
{
|
||||
struct iphdr *iph;
|
||||
skb_frag_t *frag;
|
||||
int bytes = 0;
|
||||
u8 transport;
|
||||
|
||||
if (skb_shinfo(skb)->nr_frags != 1) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_FRAGS);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (skb_headlen(skb)) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
frag = &skb_shinfo(skb)->frags[0];
|
||||
|
||||
iph = (struct iphdr *)(skb_frag_address(frag));
|
||||
|
||||
if (iph->version == 4) {
|
||||
bytes = iph->ihl*4;
|
||||
transport = iph->protocol;
|
||||
} else if (iph->version == 6) {
|
||||
struct ipv6hdr *ip6h = (struct ipv6hdr *)iph;
|
||||
|
||||
bytes = sizeof(struct ipv6hdr);
|
||||
/* Dont have to account for extension headers yet */
|
||||
transport = ip6h->nexthdr;
|
||||
} else {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_IP);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (transport == IPPROTO_TCP) {
|
||||
struct tcphdr *th;
|
||||
|
||||
th = (struct tcphdr *)((u8 *)iph + bytes);
|
||||
bytes += th->doff * 4;
|
||||
} else if (transport == IPPROTO_UDP) {
|
||||
bytes += sizeof(struct udphdr);
|
||||
} else {
|
||||
/* cant do anything else here unfortunately so linearize */
|
||||
if (skb_linearize(skb)) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_LINEARIZE_FAILS);
|
||||
return -EINVAL;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes > skb_frag_size(frag)) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_FRAGS);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
skb_push(skb, bytes);
|
||||
memcpy(skb->data, iph, bytes);
|
||||
|
||||
/* subtract to account for skb_push */
|
||||
skb->len -= bytes;
|
||||
|
||||
frag->page_offset += bytes;
|
||||
skb_frag_size_sub(frag, bytes);
|
||||
|
||||
/* subtract to account for skb_frag_size_sub */
|
||||
skb->data_len -= bytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Main downlink handler
|
||||
* Looks up NSS contex associated with the device. If the context is found,
|
||||
* we add a dummy ethernet header with the approriate protocol field set,
|
||||
* the pass the packet off to NSS for hardware acceleration.
|
||||
*/
|
||||
int rmnet_nss_tx(struct sk_buff *skb)
|
||||
{
|
||||
struct ethhdr *eth;
|
||||
struct rmnet_nss_ctx *ctx;
|
||||
struct net_device *dev = skb->dev;
|
||||
nss_tx_status_t rc;
|
||||
unsigned int len;
|
||||
u8 version;
|
||||
|
||||
if (skb_is_nonlinear(skb)) {
|
||||
if (rmnet_nss_adjust_header(skb))
|
||||
goto fail;
|
||||
else
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_NONLINEAR);
|
||||
}
|
||||
|
||||
version = ((struct iphdr *)skb->data)->version;
|
||||
|
||||
ctx = rmnet_nss_find_ctx(dev);
|
||||
if (!ctx) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_NO_CTX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
eth = (struct ethhdr *)skb_push(skb, sizeof(*eth));
|
||||
memset(ð->h_dest, 0, ETH_ALEN * 2);
|
||||
if (version == 4) {
|
||||
eth->h_proto = htons(ETH_P_IP);
|
||||
} else if (version == 6) {
|
||||
eth->h_proto = htons(ETH_P_IPV6);
|
||||
} else {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_IP);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
skb->protocol = htons(ETH_P_802_3);
|
||||
/* Get length including ethhdr */
|
||||
len = skb->len;
|
||||
|
||||
transmit:
|
||||
rc = nss_rmnet_rx_tx_buf(ctx->nss_ctx, skb);
|
||||
if (rc == NSS_TX_SUCCESS) {
|
||||
/* Increment rmnet_data device stats.
|
||||
* Don't call rmnet_data_vnd_rx_fixup() to do this, as
|
||||
* there's no guarantee the skb pointer is still valid.
|
||||
*/
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += len;
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_SUCCESS);
|
||||
return 0;
|
||||
} else if (rc == NSS_TX_FAILURE_QUEUE) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_BUSY_LOOP);
|
||||
goto transmit;
|
||||
}
|
||||
|
||||
fail:
|
||||
rmnet_nss_inc_stat(RMNET_NSS_TX_FAIL);
|
||||
kfree_skb(skb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Called by NSS in the DL exception case.
|
||||
* Since the packet cannot be sent over the accelerated path, we need to
|
||||
* handle it. Remove the ethernet header and pass it onward to the stack
|
||||
* if possible.
|
||||
*/
|
||||
void rmnet_nss_receive(struct net_device *dev, struct sk_buff *skb,
|
||||
struct napi_struct *napi)
|
||||
{
|
||||
rmnet_nss_inc_stat(RMNET_NSS_EXCEPTIONS);
|
||||
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
if (rmnet_nss_ethhdr_pull(skb)) {
|
||||
rmnet_nss_inc_stat(RMNET_NSS_EX_BAD_HDR);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/* reset header pointers */
|
||||
skb_reset_transport_header(skb);
|
||||
skb_reset_network_header(skb);
|
||||
skb_reset_mac_header(skb);
|
||||
|
||||
/* reset packet type */
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
|
||||
skb->dev = dev;
|
||||
|
||||
/* reset protocol type */
|
||||
switch (skb->data[0] & 0xF0) {
|
||||
case 0x40:
|
||||
skb->protocol = htons(ETH_P_IP);
|
||||
break;
|
||||
case 0x60:
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
break;
|
||||
default:
|
||||
rmnet_nss_inc_stat(RMNET_NSS_EX_BAD_IP);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
rmnet_nss_inc_stat(RMNET_NSS_EX_SUCCESS);
|
||||
|
||||
/* Set this so that we dont loop around netif_receive_skb */
|
||||
|
||||
skb->cb[0] = 1;
|
||||
|
||||
netif_receive_skb(skb);
|
||||
return;
|
||||
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
/* Called by NSS in the UL acceleration case.
|
||||
* We are guaranteed to have an ethernet packet here from the NSS hardware,
|
||||
* We need to pull the header off and invoke our ndo_start_xmit function
|
||||
* to handle transmitting the packet to the network stack.
|
||||
*/
|
||||
void rmnet_nss_xmit(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
netdev_tx_t ret;
|
||||
|
||||
skb_pull(skb, sizeof(struct ethhdr));
|
||||
rmnet_nss_inc_stat(RMNET_NSS_RX_ETH);
|
||||
|
||||
/* NSS takes care of shaping, so bypassing Qdiscs like this is OK */
|
||||
ret = dev->netdev_ops->ndo_start_xmit(skb, dev);
|
||||
if (unlikely(ret == NETDEV_TX_BUSY)) {
|
||||
dev_kfree_skb_any(skb);
|
||||
rmnet_nss_inc_stat(RMNET_NSS_RX_BUSY);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create and register an NSS context for an rmnet_data device */
|
||||
int rmnet_nss_create_vnd(struct net_device *dev)
|
||||
{
|
||||
struct rmnet_nss_ctx *ctx;
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->rmnet_dev = dev;
|
||||
ctx->nss_ctx = nss_rmnet_rx_create_sync_nexthop(dev, NSS_N2H_INTERFACE,
|
||||
NSS_C2C_TX_INTERFACE);
|
||||
if (!ctx->nss_ctx) {
|
||||
kfree(ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
nss_rmnet_rx_register(ctx->nss_ctx, rmnet_nss_receive, dev);
|
||||
nss_rmnet_rx_xmit_callback_register(ctx->nss_ctx, rmnet_nss_xmit);
|
||||
hash_add_ptr(rmnet_nss_ctx_hashtable, &ctx->hnode, dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unregister and destroy the NSS context for an rmnet_data device */
|
||||
int rmnet_nss_free_vnd(struct net_device *dev)
|
||||
{
|
||||
struct rmnet_nss_ctx *ctx;
|
||||
|
||||
ctx = rmnet_nss_find_ctx(dev);
|
||||
rmnet_nss_free_ctx(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rmnet_nss_cb rmnet_nss = {
|
||||
.nss_create = rmnet_nss_create_vnd,
|
||||
.nss_free = rmnet_nss_free_vnd,
|
||||
.nss_tx = rmnet_nss_tx,
|
||||
};
|
||||
|
||||
int __init rmnet_nss_init(void)
|
||||
{
|
||||
pr_err("%s(): initializing rmnet_nss\n", __func__);
|
||||
RCU_INIT_POINTER(rmnet_nss_callbacks, &rmnet_nss);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __exit rmnet_nss_exit(void)
|
||||
{
|
||||
struct hlist_node *tmp;
|
||||
struct rmnet_nss_ctx *ctx;
|
||||
int bkt;
|
||||
|
||||
pr_err("%s(): exiting rmnet_nss\n", __func__);
|
||||
RCU_INIT_POINTER(rmnet_nss_callbacks, NULL);
|
||||
|
||||
/* Tear down all NSS contexts */
|
||||
hash_for_each_safe(rmnet_nss_ctx_hashtable, bkt, tmp, ctx, hnode)
|
||||
rmnet_nss_free_ctx(ctx);
|
||||
}
|
||||
|
||||
#if 0
|
||||
MODULE_LICENSE("GPL v2");
|
||||
module_init(rmnet_nss_init);
|
||||
module_exit(rmnet_nss_exit);
|
||||
#endif
|
||||
31
applications/quectel_MHI/src/log/AT_OVER_PCIE.txt
Normal file
31
applications/quectel_MHI/src/log/AT_OVER_PCIE.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
root@imx6qsabresd:~# busybox microcom /dev/mhi_DUN
|
||||
[ 384.652992] [I][mhi_uci_open] Node open, ref counts 1
|
||||
[ 384.658144] [I][mhi_uci_open] Starting channel
|
||||
[ 384.662612] [I][__mhi_prepare_channel] Entered: preparing channel:32
|
||||
[ 384.680397] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 384.685890] [I][__mhi_prepare_channel] Chan:32 successfully moved to start state
|
||||
[ 384.693312] [I][__mhi_prepare_channel] Entered: preparing channel:33
|
||||
[ 384.708692] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 384.714324] [I][__mhi_prepare_channel] Chan:33 successfully moved to start state
|
||||
|
||||
RDY
|
||||
|
||||
+CFUN: 1
|
||||
|
||||
+CPIN: READY
|
||||
|
||||
+QUSIM: 1
|
||||
|
||||
+QIND: SMS DONE
|
||||
|
||||
+QIND: PB DONE
|
||||
ati
|
||||
Quectel
|
||||
EM20
|
||||
Revision: EM20GR01A01M4G
|
||||
|
||||
OK
|
||||
at+cpin?
|
||||
+CPIN: READY
|
||||
|
||||
OK
|
||||
145
applications/quectel_MHI/src/log/MBIM_OVER_PCIE.txt
Normal file
145
applications/quectel_MHI/src/log/MBIM_OVER_PCIE.txt
Normal file
@@ -0,0 +1,145 @@
|
||||
root@OpenWrt:~# insmod pcie_mhi.ko mhi_mbim_enabled=1
|
||||
root@OpenWrt:~# dmesg | grep mhi
|
||||
[ 65.587160] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.6
|
||||
[ 65.597089] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306
|
||||
[ 65.602250] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x20300000-0x20300fff 64bit]
|
||||
[ 65.611690] mhi_q 0000:01:00.0: enabling device (0140 -> 0142)
|
||||
[ 65.619307] [I][mhi_init_pci_dev] msi_required = 5, msi_allocated = 5, msi_irq = 63
|
||||
[ 65.619327] [I][mhi_power_up] dev_state:RESET
|
||||
[ 65.619331] [I][mhi_async_power_up] Requested to power on
|
||||
[ 65.619449] [I][mhi_alloc_coherent] size = 114688, dma_handle = 6fca0000
|
||||
[ 65.619462] [I][mhi_init_dev_ctxt] mhi_ctxt->ctrl_seg = c221e000
|
||||
[ 65.619731] [I][mhi_async_power_up] dev_state:RESET ee:AMSS
|
||||
[ 65.619747] [I][mhi_pm_st_worker] Transition to state:READY
|
||||
[ 65.619760] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
|
||||
[ 65.619764] [I][mhi_ready_state_transition] Waiting to enter READY state
|
||||
[ 65.619885] [I][mhi_async_power_up] Power on setup success
|
||||
[ 65.619897] [I][mhi_pci_probe] Return successful
|
||||
[ 65.665114] [I][mhi_ready_state_transition] Device in READY State
|
||||
[ 65.665125] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, pm_state:POR
|
||||
[ 65.665131] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, INVALID_EE
|
||||
[ 65.665133] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:POR
|
||||
[ 65.665137] [I][mhi_init_mmio] Initializing MMIO
|
||||
[ 65.665142] [I][mhi_init_mmio] CHDBOFF:0x300
|
||||
[ 65.665151] [I][mhi_init_mmio] ERDBOFF:0x700
|
||||
[ 65.665156] [I][mhi_init_mmio] Programming all MMIO values.
|
||||
[ 65.786283] [I][mhi_dump_tre] carl_ev evt_state_change mhistate=2
|
||||
[ 65.786289] [I][mhi_process_ctrl_ev_ring] MHI state change event to state:M0
|
||||
[ 65.786295] [I][mhi_pm_m0_transition] Entered With State:READY PM_STATE:POR
|
||||
[ 65.786300] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:M0
|
||||
[ 65.789734] [I][mhi_dump_tre] carl_ev evt_ee_state execenv=2
|
||||
[ 65.789739] [I][mhi_process_ctrl_ev_ring] MHI EE received event:AMSS
|
||||
[ 65.789756] [I][mhi_pm_st_worker] Transition to state:MISSION MODE
|
||||
[ 65.789767] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
|
||||
[ 65.789771] [I][mhi_pm_mission_mode_transition] Processing Mission Mode Transition
|
||||
[ 65.789787] [I][mhi_init_timesync] No timesync capability found
|
||||
[ 65.789791] [I][mhi_pm_mission_mode_transition] Adding new devices
|
||||
[ 65.790570] [I][mhi_dtr_probe] Enter for DTR control channel
|
||||
[ 65.790577] [I][__mhi_prepare_channel] Entered: preparing channel:18
|
||||
[ 65.797036] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 65.797051] [I][__mhi_prepare_channel] Chan:18 successfully moved to start state
|
||||
[ 65.797055] [I][__mhi_prepare_channel] Entered: preparing channel:19
|
||||
[ 65.802457] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 65.802469] [I][__mhi_prepare_channel] Chan:19 successfully moved to start state
|
||||
[ 65.802485] [I][mhi_dtr_probe] Exit with ret:0
|
||||
[ 65.802748] [I][mhi_netdev_enable_iface] Prepare the channels for transfer
|
||||
[ 65.802772] [I][__mhi_prepare_channel] Entered: preparing channel:100
|
||||
[ 65.825279] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 65.825293] [I][__mhi_prepare_channel] Chan:100 successfully moved to start state
|
||||
[ 65.825297] [I][__mhi_prepare_channel] Entered: preparing channel:101
|
||||
[ 65.835565] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 65.835578] [I][__mhi_prepare_channel] Chan:101 successfully moved to start state
|
||||
[ 65.839141] [I][mhi_netdev_enable_iface] Exited.
|
||||
[ 65.839875] rmnet_vnd_register_device(rmnet_mhi0.1)=0
|
||||
[ 65.843278] net rmnet_mhi0 rmnet_mhi0.1: NSS context created
|
||||
[ 65.861808] [I][mhi_pm_mission_mode_transition] Exit with ret:0
|
||||
[ 68.625595] [I][__mhi_prepare_channel] Entered: preparing channel:12
|
||||
[ 68.634610] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 68.634622] [I][__mhi_prepare_channel] Chan:12 successfully moved to start state
|
||||
[ 68.634625] [I][__mhi_prepare_channel] Entered: preparing channel:13
|
||||
[ 68.644978] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 68.644987] [I][__mhi_prepare_channel] Chan:13 successfully moved to start state
|
||||
[ 69.170666] net rmnet_mhi0: link_state 0x0 -> 0x1
|
||||
[ 69.177035] [I][mhi_netdev_open] Opened net dev interface
|
||||
[ 71.655431] [I][mhi_netdev_open] Opened net dev interface
|
||||
|
||||
root@OpenWrt:~# ./quectel-CM &
|
||||
[04-02_04:14:12:134] Quectel_QConnectManager_Linux_V1.6.0.5
|
||||
[04-02_04:14:12:134] Find /sys/bus/usb/devices/4-1 idVendor=0x2c7c idProduct=0x800, bus=0x004, dev=0x002
|
||||
[04-02_04:14:12:135] network interface '' or qmidev '' is not exist
|
||||
[04-02_04:14:12:135] netcard driver = pcie_mhi, driver version = V1.3.0.6
|
||||
[04-02_04:14:12:135] Modem works in MBIM mode
|
||||
[04-02_04:14:12:135] apn (null), user (null), passwd (null), auth 0
|
||||
[04-02_04:14:12:135] IP Proto MBIMContextIPTypeIPv4
|
||||
[04-02_04:14:12:154] mbim_read_thread is created
|
||||
sh: can't create /sys/class/net/rmnet_mhi0/mbim/link_state: nonexistent directory
|
||||
[04-02_04:14:12:156] system(echo 0 > /sys/class/net/rmnet_mhi0/mbim/link_state)=256
|
||||
[04-02_04:14:12:185] system(ip address flush dev rmnet_mhi0)=0
|
||||
[04-02_04:14:12:187] system(ip link set dev rmnet_mhi0 down)=0
|
||||
[04-02_04:14:12:188] mbim_open_device()
|
||||
[04-02_04:14:12:605] mbim_device_caps_query()
|
||||
[04-02_04:14:12:610] DeviceId: 869710030002905
|
||||
[04-02_04:14:12:610] HardwareInfo: 0
|
||||
[04-02_04:14:12:610] mbim_set_radio_state( 1 )
|
||||
[04-02_04:14:12:613] HwRadioState: 1, SwRadioState: 1
|
||||
[04-02_04:14:12:613] mbim_subscriber_status_query()
|
||||
[04-02_04:14:12:620] SubscriberReadyState NotInitialized -> Initialized
|
||||
[04-02_04:14:12:620] mbim_register_state_query()
|
||||
[04-02_04:14:12:625] RegisterState Unknown -> Home
|
||||
[04-02_04:14:12:625] mbim_packet_service_query()
|
||||
[04-02_04:14:12:629] PacketServiceState Unknown -> Attached
|
||||
[04-02_04:14:12:629] mbim_query_connect(sessionID=0)
|
||||
[04-02_04:14:12:633] ActivationState Unknown -> Deactivated
|
||||
[04-02_04:14:12:633] mbim_set_connect(onoff=1, sessionID=0)
|
||||
[ 69.170666] net rmnet_mhi0: link_state 0x0 -> 0x1
|
||||
[04-02_04:14:12:680] ActivationState Deactivated -> Activated
|
||||
[ 69.177035] [I][mhi_netdev_open] Opened net dev interface
|
||||
[04-02_04:14:12:680] mbim_ip_config(sessionID=0)
|
||||
[04-02_04:14:12:683] < SessionId = 0
|
||||
[04-02_04:14:12:683] < IPv4ConfigurationAvailable = 0xf
|
||||
[04-02_04:14:12:683] < IPv6ConfigurationAvailable = 0x0
|
||||
[04-02_04:14:12:683] < IPv4AddressCount = 0x1
|
||||
[04-02_04:14:12:683] < IPv4AddressOffset = 0x3c
|
||||
[04-02_04:14:12:683] < IPv6AddressCount = 0x0
|
||||
[04-02_04:14:12:683] < IPv6AddressOffset = 0x0
|
||||
[04-02_04:14:12:683] < IPv4 = 10.129.59.93/30
|
||||
[04-02_04:14:12:683] < gw = 10.129.59.94
|
||||
[04-02_04:14:12:683] < dns1 = 211.138.180.2
|
||||
[04-02_04:14:12:683] < dns2 = 211.138.180.3
|
||||
[04-02_04:14:12:683] < ipv4 mtu = 1500
|
||||
sh: can't create /sys/class/net/rmnet_mhi0/mbim/link_state: nonexistent directory
|
||||
[04-02_04:14:12:684] system(echo 1 > /sys/class/net/rmnet_mhi0/mbim/link_state)=256
|
||||
[04-02_04:14:12:689] system(ip link set dev rmnet_mhi0 up)=0
|
||||
[04-02_04:14:12:692] system(ip -4 address flush dev rmnet_mhi0)=0
|
||||
[04-02_04:14:12:694] system(ip -4 address add 10.129.59.93/30 dev rmnet_mhi0)=0
|
||||
[04-02_04:14:12:697] system(ip -4 route add default via 10.129.59.94 dev rmnet_mhi0)=0
|
||||
[04-02_04:14:12:699] system(ip -4 link set dev rmnet_mhi0 mtu 1500)=0
|
||||
|
||||
root@OpenWrt:~# ifconfig rmnet_mhi0
|
||||
rmnet_mhi0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
|
||||
UP RUNNING NOARP MTU:1500 Metric:1
|
||||
RX packets:99379 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:176569 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:1528181052 (1.4 GiB) TX bytes:62467192 (59.5 MiB)
|
||||
|
||||
root@OpenWrt:~# ifconfig rmnet_mhi0.1
|
||||
rmnet_mhi0.1 Link encap:UNSPEC HWaddr 02-50-F4-00-00-00-00-00-00-00-00-00-00-00-00-00
|
||||
inet addr:10.129.59.93 Mask:255.255.255.252
|
||||
inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link
|
||||
UP RUNNING NOARP MTU:1500 Metric:1
|
||||
RX packets:1089360 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:176581 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:1521449058 (1.4 GiB) TX bytes:57525792 (54.8 MiB)
|
||||
|
||||
# adjust CPU load balancing
|
||||
root@OpenWrt:~# echo 2 > /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
|
||||
root@OpenWrt:~# echo 4 > /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
|
||||
root@OpenWrt:~# echo 2000 > /proc/sys/net/core/netdev_max_backlog
|
||||
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
|
||||
2
|
||||
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
|
||||
4
|
||||
root@OpenWrt:~# cat /proc/sys/net/core/netdev_max_backlog
|
||||
2000
|
||||
134
applications/quectel_MHI/src/log/QMI_OVER_PCIE.txt
Normal file
134
applications/quectel_MHI/src/log/QMI_OVER_PCIE.txt
Normal file
@@ -0,0 +1,134 @@
|
||||
disable ccflags-y += -DCONFIG_MHI_NETDEV_MBIM in pcie_mhi/Makefile
|
||||
|
||||
root@OpenWrt:~# insmod pcie_mhi.ko
|
||||
|
||||
root@OpenWrt:~# dmesg | grep mhi
|
||||
[ 138.483252] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.6
|
||||
[ 138.492350] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306
|
||||
[ 138.497564] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x20300000-0x20300fff 64bit]
|
||||
[ 138.506952] mhi_q 0000:01:00.0: enabling device (0140 -> 0142)
|
||||
[ 138.514562] [I][mhi_init_pci_dev] msi_required = 5, msi_allocated = 5, msi_irq = 63
|
||||
[ 138.514581] [I][mhi_power_up] dev_state:RESET
|
||||
[ 138.514587] [I][mhi_async_power_up] Requested to power on
|
||||
[ 138.514728] [I][mhi_alloc_coherent] size = 114688, dma_handle = 72160000
|
||||
[ 138.514734] [I][mhi_init_dev_ctxt] mhi_ctxt->ctrl_seg = c221f000
|
||||
[ 138.515030] [I][mhi_async_power_up] dev_state:RESET ee:AMSS
|
||||
[ 138.515056] [I][mhi_pm_st_worker] Transition to state:READY
|
||||
[ 138.515067] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
|
||||
[ 138.515073] [I][mhi_ready_state_transition] Waiting to enter READY state
|
||||
[ 138.515210] [I][mhi_async_power_up] Power on setup success
|
||||
[ 138.515227] [I][mhi_pci_probe] Return successful
|
||||
[ 138.589013] [I][mhi_ready_state_transition] Device in READY State
|
||||
[ 138.589029] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, pm_state:POR
|
||||
[ 138.589038] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, INVALID_EE
|
||||
[ 138.589041] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:POR
|
||||
[ 138.589046] [I][mhi_init_mmio] Initializing MMIO
|
||||
[ 138.589050] [I][mhi_init_mmio] CHDBOFF:0x300
|
||||
[ 138.589060] [I][mhi_init_mmio] ERDBOFF:0x700
|
||||
[ 138.589065] [I][mhi_init_mmio] Programming all MMIO values.
|
||||
[ 138.706124] [I][mhi_dump_tre] carl_ev evt_state_change mhistate=2
|
||||
[ 138.706132] [I][mhi_process_ctrl_ev_ring] MHI state change event to state:M0
|
||||
[ 138.706140] [I][mhi_pm_m0_transition] Entered With State:READY PM_STATE:POR
|
||||
[ 138.706146] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:M0
|
||||
[ 138.708699] [I][mhi_dump_tre] carl_ev evt_ee_state execenv=2
|
||||
[ 138.708706] [I][mhi_process_ctrl_ev_ring] MHI EE received event:AMSS
|
||||
[ 138.708726] [I][mhi_pm_st_worker] Transition to state:MISSION MODE
|
||||
[ 138.708736] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
|
||||
[ 138.708742] [I][mhi_pm_mission_mode_transition] Processing Mission Mode Transition
|
||||
[ 138.708758] [I][mhi_init_timesync] No timesync capability found
|
||||
[ 138.708764] [I][mhi_pm_mission_mode_transition] Adding new devices
|
||||
[ 138.709785] [I][mhi_dtr_probe] Enter for DTR control channel
|
||||
[ 138.709794] [I][__mhi_prepare_channel] Entered: preparing channel:18
|
||||
[ 138.715378] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 138.715397] [I][__mhi_prepare_channel] Chan:18 successfully moved to start state
|
||||
[ 138.715403] [I][__mhi_prepare_channel] Entered: preparing channel:19
|
||||
[ 138.720201] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 138.720218] [I][__mhi_prepare_channel] Chan:19 successfully moved to start state
|
||||
[ 138.720236] [I][mhi_dtr_probe] Exit with ret:0
|
||||
[ 138.720590] [I][mhi_netdev_enable_iface] Prepare the channels for transfer
|
||||
[ 138.720630] [I][__mhi_prepare_channel] Entered: preparing channel:100
|
||||
[ 138.757230] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 138.757253] [I][__mhi_prepare_channel] Chan:100 successfully moved to start state
|
||||
[ 138.757259] [I][__mhi_prepare_channel] Entered: preparing channel:101
|
||||
[ 138.774352] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 138.774370] [I][__mhi_prepare_channel] Chan:101 successfully moved to start state
|
||||
[ 138.778137] [I][mhi_netdev_enable_iface] Exited.
|
||||
[ 138.779018] rmnet_vnd_register_device(rmnet_mhi0.1)=0
|
||||
[ 138.782283] net rmnet_mhi0 rmnet_mhi0.1: NSS context created
|
||||
[ 138.800865] [I][mhi_pm_mission_mode_transition] Exit with ret:0
|
||||
|
||||
root@OpenWrt:~# ./quectel-CM &
|
||||
root@OpenWrt:~# [04-02_04:12:16:477] Quectel_QConnectManager_Linux_V1.6.0.5
|
||||
[04-02_04:12:16:477] Find /sys/bus/usb/devices/4-1 idVendor=0x2c7c idProduct=0x800, bus=0x004, dev=0x002
|
||||
[04-02_04:12:16:478] network interface '' or qmidev '' is not exist
|
||||
[04-02_04:12:16:478] netcard driver = pcie_mhi, driver version = V1.3.0.6
|
||||
[04-02_04:12:16:479] qmap_mode = 1, qmap_version = 9, qmap_size = 16384, muxid = 0x81, qmap_netcard = rmnet_mhi0.1
|
||||
[04-02_04:12:16:479] Modem works in QMI mode
|
||||
[04-02_04:12:16:505] cdc_wdm_fd = 7
|
||||
[04-02_04:12:17:506] QmiThreadSendQMITimeout pthread_cond_timeout_np timeout
|
||||
[04-02_04:12:18:516] Get clientWDS = 19
|
||||
[04-02_04:12:18:520] Get clientDMS = 1
|
||||
[04-02_04:12:18:524] Get clientNAS = 3
|
||||
[04-02_04:12:18:527] Get clientUIM = 1
|
||||
[04-02_04:12:18:531] Get clientWDA = 1
|
||||
[04-02_04:12:18:535] requestBaseBandVersion RM500QGLAAR03A01M4G_BETA_20200107F 1 [Dec 30 2019 17:00:00]
|
||||
[04-02_04:12:18:539] qmap_settings.rx_urb_size = 16384
|
||||
[04-02_04:12:18:539] qmap_settings.ul_data_aggregation_max_datagrams = 16
|
||||
[04-02_04:12:18:539] qmap_settings.ul_data_aggregation_max_size = 8192
|
||||
[04-02_04:12:18:539] qmap_settings.dl_minimum_padding = 0
|
||||
[04-02_04:12:18:550] requestSetLoopBackState(loopback_state=1, replication_factor=14)
|
||||
[04-02_04:12:18:557] requestGetSIMStatus SIMStatus: SIM_ABSENT
|
||||
[04-02_04:12:18:560] requestGetProfile[1] ///0
|
||||
[04-02_04:12:18:563] requestRegistrationState2 MCC: 0, MNC: 0, PS: Detached, DataCap: UNKNOW
|
||||
[04-02_04:12:18:565] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED
|
||||
[04-02_04:12:18:566] ifconfig rmnet_mhi0.1 down
|
||||
[04-02_04:12:18:571] ifconfig rmnet_mhi0.1 0.0.0.0
|
||||
ifconfig: SIOCSIFFLAGS: Network is down
|
||||
[04-02_04:12:18:575] SetLoopBackInd: loopback_state=1, replication_factor=14
|
||||
[04-02_04:12:18:591] requestSetupDataCall WdsConnectionIPv4Handle: 0xe40182a0
|
||||
[04-02_04:12:18:601] ifconfig rmnet_mhi0 up
|
||||
[04-02_04:12:18:607] ifconfig rmnet_mhi0.1 up
|
||||
[04-02_04:12:18:613] you are use OpenWrt?
|
||||
[04-02_04:12:18:614] should not calling udhcpc manually?
|
||||
[04-02_04:12:18:614] should modify /etc/config/network as below?
|
||||
[04-02_04:12:18:614] config interface wan
|
||||
[04-02_04:12:18:614] option ifname rmnet_mhi0.1
|
||||
[04-02_04:12:18:614] option proto dhcp
|
||||
[04-02_04:12:18:614] should use "/sbin/ifstaus wan" to check rmnet_mhi0.1 's status?
|
||||
[04-02_04:12:18:614] busybox udhcpc -f -n -q -t 5 -i rmnet_mhi0.1
|
||||
udhcpc: started, v1.28.3
|
||||
udhcpc: sending discover
|
||||
udhcpc: sending select for 192.168.48.171
|
||||
udhcpc: lease of 192.168.48.171 obtained, lease time 7200
|
||||
[04-02_04:12:18:809] udhcpc: ifconfig rmnet_mhi0.1 192.168.48.171 netmask 255.255.255.248 broadcast +
|
||||
[04-02_04:12:18:819] udhcpc: setting default routers: 192.168.48.172
|
||||
|
||||
root@OpenWrt:~# ifconfig rmnet_mhi0
|
||||
rmnet_mhi0 Link encap:Ethernet HWaddr 02:50:F4:00:00:00
|
||||
inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link
|
||||
UP RUNNING NOARP MTU:1500 Metric:1
|
||||
RX packets:2 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:608 (608.0 B) TX bytes:672 (672.0 B)
|
||||
|
||||
root@OpenWrt:~# ifconfig rmnet_mhi0.1
|
||||
rmnet_mhi0.1 Link encap:UNSPEC HWaddr 02-50-F4-00-00-00-00-00-00-00-00-00-00-00-00-00
|
||||
inet addr:192.168.48.171 Mask:255.255.255.248
|
||||
inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link
|
||||
UP RUNNING NOARP MTU:1500 Metric:1
|
||||
RX packets:2 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:592 (592.0 B) TX bytes:656 (656.0 B)
|
||||
|
||||
# adjust CPU load balancing
|
||||
root@OpenWrt:~# echo 2 > /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
|
||||
root@OpenWrt:~# echo 4 > /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
|
||||
root@OpenWrt:~# echo 2000 > /proc/sys/net/core/netdev_max_backlog
|
||||
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
|
||||
2
|
||||
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
|
||||
4
|
||||
root@OpenWrt:~# cat /proc/sys/net/core/netdev_max_backlog
|
||||
2000
|
||||
14
applications/quectel_MHI/src/log/QXDM_OVER_PCIE.txt
Normal file
14
applications/quectel_MHI/src/log/QXDM_OVER_PCIE.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
root@imx6qsabresd:~# ./QLog -p /dev/mhi_DIAG -s log &
|
||||
root@imx6qsabresd:~# [000.000]QLog Version: Quectel_QLog_Linux&Android_V1.2.4
|
||||
[ 298.597963] [I][mhi_uci_open] Node open, ref counts 1
|
||||
[ 298.605601] [I][mhi_uci_open] Starting channel
|
||||
[ 298.612159] [I][__mhi_prepare_channel] Entered: preparing channel:4
|
||||
[ 298.629906] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 298.635415] [I][__mhi_prepare_channel] Chan:4 successfully moved to start state
|
||||
[ 298.642749] [I][__mhi_prepare_channel] Entered: preparing channel:5
|
||||
[ 298.658043] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 298.663543] [I][__mhi_prepare_channel] Chan:5 successfully moved to start state
|
||||
[000.075]open /dev/mhi_DIAG ttyfd = 3
|
||||
[000.075]Press CTRL+C to stop catch log.
|
||||
[000.096]qlog_logfile_create log/20160920_145758_0000.qmdl logfd=4
|
||||
[005.268]recv: 0M 70K 490B in 5181 msec
|
||||
39
applications/quectel_cm_5G/Makefile
Normal file
39
applications/quectel_cm_5G/Makefile
Normal file
@@ -0,0 +1,39 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:= quectel-CM-5G
|
||||
PKG_VERSION:=1.6.4
|
||||
PKG_RELEASE:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/quectel-CM-5G
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
TITLE:=quectel-CM-5G app
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
$(CP) ./src/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
$(MAKE) -C "$(PKG_BUILD_DIR)" \
|
||||
EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
|
||||
CROSS_COMPILE="$(TARGET_CROSS)" \
|
||||
ARCH="$(LINUX_KARCH)" \
|
||||
M="$(PKG_BUILD_DIR)" \
|
||||
CC="$(TARGET_CC)"
|
||||
endef
|
||||
|
||||
define Package/quectel-CM-5G/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin $(1)/lib/netifd/proto
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/quectel-CM $(1)/usr/bin
|
||||
$(INSTALL_BIN) ./files/rmnet_init.sh $(1)/lib/netifd
|
||||
$(INSTALL_BIN) ./files/rmnet.script $(1)/lib/netifd
|
||||
$(INSTALL_BIN) ./files/rmnet.sh $(1)/lib/netifd/proto
|
||||
$(INSTALL_BIN) ./files/rmnet6.sh $(1)/lib/netifd/proto
|
||||
$(INSTALL_BIN) ./files/rmnet6.script $(1)/lib/netifd
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,quectel-CM-5G))
|
||||
48
applications/quectel_cm_5G/files/dhcp
Normal file
48
applications/quectel_cm_5G/files/dhcp
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
config dnsmasq
|
||||
option domainneeded '1'
|
||||
option boguspriv '1'
|
||||
option filterwin2k '0'
|
||||
option localise_queries '1'
|
||||
option rebind_protection '1'
|
||||
option rebind_localhost '1'
|
||||
option local '/lan/'
|
||||
option domain 'lan'
|
||||
option expandhosts '1'
|
||||
option nonegcache '0'
|
||||
option authoritative '1'
|
||||
option readethers '1'
|
||||
option leasefile '/tmp/dhcp.leases'
|
||||
option resolvfile '/tmp/resolv.conf.auto'
|
||||
option nonwildcard '1'
|
||||
option localservice '1'
|
||||
|
||||
config dhcp 'lan'
|
||||
option interface 'lan'
|
||||
option start '100'
|
||||
option limit '150'
|
||||
option leasetime '12h'
|
||||
option ra 'relay'
|
||||
option dhcpv6 'disabled'
|
||||
option ndp 'relay'
|
||||
|
||||
config dhcp 'wan'
|
||||
option interface 'wan'
|
||||
option ignore '1'
|
||||
option ra 'relay'
|
||||
option dhcpv6 'disabled'
|
||||
option ndp 'relay'
|
||||
option ndproxy_routing '0'
|
||||
option master '1'
|
||||
|
||||
config dhcp 'wan6'
|
||||
option ra 'relay'
|
||||
option dhcpv6 'disabled'
|
||||
option ndp 'relay'
|
||||
option ndproxy_routing '0'
|
||||
option master '1'
|
||||
option interface 'wan6'
|
||||
|
||||
config odhcpd 'odhcpd'
|
||||
option loglevel '7'
|
||||
|
||||
66
applications/quectel_cm_5G/files/rmnet.script
Normal file
66
applications/quectel_cm_5G/files/rmnet.script
Normal file
@@ -0,0 +1,66 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) 2019 Qualcomm Technologies, Inc.
|
||||
# All Rights Reserved.
|
||||
# Confidential and Proprietary - Qualcomm Technologies, Inc.
|
||||
|
||||
|
||||
[ -z "$1" ] && echo "Error: should be run by rmnet" && exit 1
|
||||
[ -z "$2" ] && echo "Error: should be run by rmnet" && exit 1
|
||||
|
||||
. /lib/functions.sh
|
||||
. /lib/functions/network.sh
|
||||
. /lib/netifd/netifd-proto.sh
|
||||
|
||||
setup_interface() {
|
||||
INTERFACE=$1
|
||||
CONFIG=/tmp/rmnet_$2_ipv4config
|
||||
logger "rmnet setup_interface $1 $2 here"
|
||||
#Fetch information from lower.
|
||||
[ -f ${CONFIG} ] || {
|
||||
proto_notify_error "$INTERFACE" "RMNET data call Not ready"
|
||||
proto_block_restart "$INTERFACE"
|
||||
return
|
||||
}
|
||||
. ${CONFIG}
|
||||
ip=$PUBLIC_IP
|
||||
DNS=$DNSSERVERS
|
||||
router=$GATEWAY
|
||||
subnet=$NETMASK
|
||||
interface=$IFNAME
|
||||
#Send the information to the netifd
|
||||
proto_init_update "$interface" 1 1
|
||||
#ip and subnet
|
||||
proto_add_ipv4_address "$ip" "${subnet:-255.255.255.0}"
|
||||
|
||||
#Any router? if not, remove below scripts
|
||||
#router format should be separated by space
|
||||
for i in $router; do
|
||||
proto_add_ipv4_route "$i" 32 "" "$ip"
|
||||
proto_add_ipv4_route 0.0.0.0 0 "$i" "$ip"
|
||||
done
|
||||
|
||||
#dns information tell the netifd.
|
||||
for dns in $DNS; do
|
||||
proto_add_dns_server "$dns"
|
||||
done
|
||||
|
||||
#Domain information tell the netifd
|
||||
for domain in $domain; do
|
||||
proto_add_dns_search "$domain"
|
||||
done
|
||||
|
||||
#proto_add_data
|
||||
[ -n "$ZONE" ] && json_add_string zone "$ZONE"
|
||||
proto_close_data
|
||||
|
||||
proto_send_update "$INTERFACE"
|
||||
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
renew)
|
||||
setup_interface $2 $3
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
32
applications/quectel_cm_5G/files/rmnet.sh
Normal file
32
applications/quectel_cm_5G/files/rmnet.sh
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) 2019 Qualcomm Technologies, Inc.
|
||||
# All Rights Reserved.
|
||||
# Confidential and Proprietary - Qualcomm Technologies, Inc.
|
||||
|
||||
. /lib/functions.sh
|
||||
. /lib/functions/network.sh
|
||||
. ../netifd-proto.sh
|
||||
|
||||
init_proto "$@"
|
||||
|
||||
proto_rmnet_setup() {
|
||||
local cfg="$1"
|
||||
local iface="$2"
|
||||
|
||||
logger "rmnet started"
|
||||
#Call rmnet management script below!!
|
||||
logger "rmnet updated ${cfg} ${iface}"
|
||||
/lib/netifd/rmnet.script renew $cfg $iface
|
||||
}
|
||||
|
||||
proto_rmnet_teardown() {
|
||||
local cfg="$1"
|
||||
#Tear down rmnet manager script here.*/
|
||||
}
|
||||
|
||||
proto_rmnet_init_config() {
|
||||
#ddno_device=1
|
||||
available=1
|
||||
}
|
||||
|
||||
add_protocol rmnet
|
||||
61
applications/quectel_cm_5G/files/rmnet6.script
Normal file
61
applications/quectel_cm_5G/files/rmnet6.script
Normal file
@@ -0,0 +1,61 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) 2019 Qualcomm Technologies, Inc.
|
||||
# All Rights Reserved.
|
||||
# Confidential and Proprietary - Qualcomm Technologies, Inc.
|
||||
|
||||
|
||||
[ -z "$1" ] && echo "Error: should be run by rmnet" && exit 1
|
||||
[ -z "$2" ] && echo "Error: should be run by rmnet" && exit 1
|
||||
|
||||
. /lib/functions.sh
|
||||
. /lib/functions/network.sh
|
||||
. /lib/netifd/netifd-proto.sh
|
||||
|
||||
setup_interface() {
|
||||
INTERFACE=$1
|
||||
CONFIG=/tmp/rmnet_$2_ipv6config
|
||||
logger "rmnet setup_interface $1 $2 here"
|
||||
#Fetch information from lower.
|
||||
[ -f ${CONFIG} ] || {
|
||||
proto_notify_error "$INTERFACE" "RMNET data call NOT ready"
|
||||
proto_block_restart "$INTERFACE"
|
||||
return
|
||||
}
|
||||
. ${CONFIG}
|
||||
ADDRESSES=$PUBLIC_IP
|
||||
interface=$IFNAME
|
||||
#Send the information to the netifd
|
||||
proto_init_update "$interface" 1 1
|
||||
|
||||
#ip and subnet
|
||||
proto_add_ipv6_address "${PUBLIC_IP}" "128"
|
||||
proto_add_ipv6_prefix "${PUBLIC_IP}/${PrefixLength}"
|
||||
|
||||
#router format should be separated by space
|
||||
proto_add_ipv6_route "$GATEWAY" 128
|
||||
proto_add_ipv6_route "::0" 0 "$GATEWAY" "" "" "${PUBLIC_IP}/${PrefixLength}"
|
||||
|
||||
#dns information tell the netifd.
|
||||
for dns in $DNSSERVERS; do
|
||||
proto_add_dns_server "$dns"
|
||||
done
|
||||
|
||||
#Domain information tell the netifd
|
||||
for domain in $domain; do
|
||||
proto_add_dns_search "$domain"
|
||||
done
|
||||
|
||||
#proto_add_data
|
||||
[ -n "$ZONE" ] && json_add_string zone "$ZONE"
|
||||
proto_close_data
|
||||
|
||||
proto_send_update "$INTERFACE"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
renew|bound)
|
||||
setup_interface $2 $3
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
32
applications/quectel_cm_5G/files/rmnet6.sh
Normal file
32
applications/quectel_cm_5G/files/rmnet6.sh
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) 2019 Qualcomm Technologies, Inc.
|
||||
# All Rights Reserved.
|
||||
# Confidential and Proprietary - Qualcomm Technologies, Inc.
|
||||
|
||||
. /lib/functions.sh
|
||||
. /lib/functions/network.sh
|
||||
. ../netifd-proto.sh
|
||||
|
||||
init_proto "$@"
|
||||
|
||||
proto_rmnet6_setup() {
|
||||
local cfg="$1"
|
||||
local iface="$2"
|
||||
|
||||
logger "rmnet6 started"
|
||||
#Call rmnet management script below!!
|
||||
/lib/netifd/rmnet6.script renew $cfg $iface
|
||||
logger "rmnet6 updated"
|
||||
}
|
||||
|
||||
proto_rmnet6_teardown() {
|
||||
local cfg="$1"
|
||||
#Tear down rmnet manager script here.*/
|
||||
}
|
||||
|
||||
proto_rmnet6_init_config() {
|
||||
#ddno_device=1
|
||||
available=1
|
||||
}
|
||||
|
||||
add_protocol rmnet6
|
||||
31
applications/quectel_cm_5G/files/rmnet_init.sh
Normal file
31
applications/quectel_cm_5G/files/rmnet_init.sh
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
uci set network.wan='interface'
|
||||
uci set network.wan.ifname='wwan0'
|
||||
uci set network.wan.proto='rmnet'
|
||||
|
||||
uci set network.wan6='interface'
|
||||
uci set network.wan6.ifname='wwan0'
|
||||
uci set network.wan6.proto='rmnet6'
|
||||
|
||||
uci set dhcp.lan.ra='relay'
|
||||
uci set dhcp.lan.dhcpv6='disabled'
|
||||
uci set dhcp.lan.ndp='relay'
|
||||
|
||||
uci set dhcp.wan.ra='relay'
|
||||
uci set dhcp.wan.dhcpv6='disabled'
|
||||
uci set dhcp.wan.ndp='relay'
|
||||
uci set dhcp.wan.ndproxy_routing='0'
|
||||
|
||||
uci set dhcp.wan6=dhcp
|
||||
uci set dhcp.wan6.interface='wan6'
|
||||
uci set dhcp.wan6.ra='relay'
|
||||
uci set dhcp.wan6.dhcpv6='disabled'
|
||||
uci set dhcp.wan6.ndp='relay'
|
||||
uci set dhcp.wan6.ndproxy_routing='0'
|
||||
uci set dhcp.wan6.master='1'
|
||||
|
||||
uci set dhcp.odhcpd=odhcpd
|
||||
uci set dhcp.odhcpd.loglevel='7'
|
||||
|
||||
uci commit
|
||||
36
applications/quectel_cm_5G/src/CMakeLists.txt
Normal file
36
applications/quectel_cm_5G/src/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
cmake_minimum_required(VERSION 2.4)
|
||||
|
||||
project(quectel-CM)
|
||||
add_definitions(-Wall -Wextra -Werror -O1)
|
||||
option(USE_QRTR "Enable QRTR" OFF)
|
||||
|
||||
set( QL_CM_SRC
|
||||
QmiWwanCM.c GobiNetCM.c main.c MPQMUX.c QMIThread.c util.c qmap_bridge_mode.c mbim-cm.c device.c
|
||||
atc.c atchannel.c at_tok.c
|
||||
udhcpc.c
|
||||
)
|
||||
|
||||
if(USE_QRTR)
|
||||
add_definitions(-DCONFIG_QRTR)
|
||||
set( QRTR_SRC qrtr.c rmnetctl.c)
|
||||
endif()
|
||||
|
||||
add_executable(quectel-CM ${QL_CM_SRC} ${QRTR_SRC})
|
||||
target_link_libraries(quectel-CM PUBLIC pthread)
|
||||
install (TARGETS quectel-CM DESTINATION bin)
|
||||
|
||||
add_executable(quectel-qmi-proxy quectel-qmi-proxy.c)
|
||||
target_link_libraries(quectel-qmi-proxy PUBLIC pthread)
|
||||
install (TARGETS quectel-qmi-proxy DESTINATION bin)
|
||||
|
||||
add_executable(quectel-mbim-proxy quectel-mbim-proxy.c)
|
||||
target_link_libraries(quectel-mbim-proxy PUBLIC pthread)
|
||||
install (TARGETS quectel-mbim-proxy DESTINATION bin)
|
||||
|
||||
add_executable(quectel-atc-proxy quectel-atc-proxy.c atchannel.c at_tok.c util.c)
|
||||
target_link_libraries(quectel-atc-proxy PUBLIC pthread)
|
||||
install (TARGETS quectel-atc-proxy DESTINATION bin)
|
||||
|
||||
add_executable(quectel-qrtr-proxy quectel-qrtr-proxy.c)
|
||||
target_link_libraries(quectel-qrtr-proxy PUBLIC pthread)
|
||||
install (TARGETS quectel-qrtr-proxy DESTINATION bin)
|
||||
246
applications/quectel_cm_5G/src/GobiNetCM.c
Normal file
246
applications/quectel_cm_5G/src/GobiNetCM.c
Normal file
@@ -0,0 +1,246 @@
|
||||
/******************************************************************************
|
||||
@file GobiNetCM.c
|
||||
@brief GobiNet driver.
|
||||
|
||||
DESCRIPTION
|
||||
Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules.
|
||||
|
||||
INITIALIZATION AND SEQUENCING REQUIREMENTS
|
||||
None.
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 2016 - 2020 Quectel Wireless Solution, Co., Ltd. All Rights Reserved.
|
||||
Quectel Wireless Solution Proprietary and Confidential.
|
||||
---------------------------------------------------------------------------
|
||||
******************************************************************************/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include "QMIThread.h"
|
||||
|
||||
#ifdef CONFIG_GOBINET
|
||||
static int qmiclientId[QMUX_TYPE_ALL];
|
||||
|
||||
// IOCTL to generate a client ID for this service type
|
||||
#define IOCTL_QMI_GET_SERVICE_FILE 0x8BE0 + 1
|
||||
|
||||
// IOCTL to get the VIDPID of the device
|
||||
#define IOCTL_QMI_GET_DEVICE_VIDPID 0x8BE0 + 2
|
||||
|
||||
// IOCTL to get the MEID of the device
|
||||
#define IOCTL_QMI_GET_DEVICE_MEID 0x8BE0 + 3
|
||||
|
||||
static int GobiNetSendQMI(PQCQMIMSG pRequest) {
|
||||
int ret, fd;
|
||||
|
||||
fd = qmiclientId[pRequest->QMIHdr.QMIType];
|
||||
pRequest->QMIHdr.ClientId = (fd&0xFF) ? fd&0xFF : pRequest->QMIHdr.QMIType;
|
||||
|
||||
if (fd <= 0) {
|
||||
dbg_time("%s QMIType: %d has no clientID", __func__, pRequest->QMIHdr.QMIType);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
// Always ready to write
|
||||
if (1 == 1) {
|
||||
ssize_t nwrites = le16_to_cpu(pRequest->QMIHdr.Length) + 1 - sizeof(QCQMI_HDR);
|
||||
ret = write(fd, &pRequest->MUXMsg, nwrites);
|
||||
if (ret == nwrites) {
|
||||
ret = 0;
|
||||
} else {
|
||||
dbg_time("%s write=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
|
||||
}
|
||||
} else {
|
||||
dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int GobiNetGetClientID(const char *qcqmi, UCHAR QMIType) {
|
||||
int ClientId;
|
||||
ClientId = cm_open_dev(qcqmi);
|
||||
if (ClientId == -1) {
|
||||
dbg_time("failed to open %s, errno: %d (%s)", qcqmi, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(ClientId, IOCTL_QMI_GET_SERVICE_FILE, QMIType) != 0) {
|
||||
dbg_time("failed to get ClientID for 0x%02x errno: %d (%s)", QMIType, errno, strerror(errno));
|
||||
close(ClientId);
|
||||
ClientId = 0;
|
||||
}
|
||||
|
||||
switch (QMIType) {
|
||||
case QMUX_TYPE_WDS: dbg_time("Get clientWDS = %d", ClientId); break;
|
||||
case QMUX_TYPE_DMS: dbg_time("Get clientDMS = %d", ClientId); break;
|
||||
case QMUX_TYPE_NAS: dbg_time("Get clientNAS = %d", ClientId); break;
|
||||
case QMUX_TYPE_QOS: dbg_time("Get clientQOS = %d", ClientId); break;
|
||||
case QMUX_TYPE_WMS: dbg_time("Get clientWMS = %d", ClientId); break;
|
||||
case QMUX_TYPE_PDS: dbg_time("Get clientPDS = %d", ClientId); break;
|
||||
case QMUX_TYPE_UIM: dbg_time("Get clientUIM = %d", ClientId); break;
|
||||
case QMUX_TYPE_COEX: dbg_time("Get clientCOEX = %d", ClientId); break;
|
||||
case QMUX_TYPE_WDS_ADMIN: dbg_time("Get clientWDA = %d", ClientId);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return ClientId;
|
||||
}
|
||||
|
||||
static int GobiNetDeInit(void) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++)
|
||||
{
|
||||
if (qmiclientId[i] != 0)
|
||||
{
|
||||
close(qmiclientId[i]);
|
||||
qmiclientId[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void * GobiNetThread(void *pData) {
|
||||
PROFILE_T *profile = (PROFILE_T *)pData;
|
||||
const char *qcqmi = (const char *)profile->qmichannel;
|
||||
int wait_for_request_quit = 0;
|
||||
|
||||
qmiclientId[QMUX_TYPE_WDS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS);
|
||||
if (profile->enable_ipv6)
|
||||
qmiclientId[QMUX_TYPE_WDS_IPV6] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS);
|
||||
qmiclientId[QMUX_TYPE_DMS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_DMS);
|
||||
qmiclientId[QMUX_TYPE_NAS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_NAS);
|
||||
qmiclientId[QMUX_TYPE_UIM] = GobiNetGetClientID(qcqmi, QMUX_TYPE_UIM);
|
||||
#ifdef CONFIG_COEX_WWAN_STATE
|
||||
qmiclientId[QMUX_TYPE_COEX] = GobiNetGetClientID(qcqmi, QMUX_TYPE_COEX);
|
||||
#endif
|
||||
if (profile->qmap_mode == 0 || profile->loopback_state) {//when QMAP enabled, set data format in GobiNet Driver
|
||||
qmiclientId[QMUX_TYPE_WDS_ADMIN] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS_ADMIN);
|
||||
profile->wda_client = qmiclientId[QMUX_TYPE_WDS_ADMIN];
|
||||
}
|
||||
|
||||
//donot check clientWDA, there is only one client for WDA, if quectel-CM is killed by SIGKILL, i cannot get client ID for WDA again!
|
||||
if (qmiclientId[QMUX_TYPE_WDS] == 0) /*|| (clientWDA == -1)*/ {
|
||||
GobiNetDeInit();
|
||||
dbg_time("%s Failed to open %s, errno: %d (%s)", __func__, qcqmi, errno, strerror(errno));
|
||||
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
|
||||
pthread_exit(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED);
|
||||
|
||||
while (1) {
|
||||
struct pollfd pollfds[16] = {{qmidevice_control_fd[1], POLLIN, 0}};
|
||||
int ne, ret, nevents = 1;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++)
|
||||
{
|
||||
if (qmiclientId[i] != 0)
|
||||
{
|
||||
pollfds[nevents].fd = qmiclientId[i];
|
||||
pollfds[nevents].events = POLLIN;
|
||||
pollfds[nevents].revents = 0;
|
||||
nevents++;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
ret = poll(pollfds, nevents, wait_for_request_quit ? 1000: -1);
|
||||
} while ((ret < 0) && (errno == EINTR));
|
||||
|
||||
if (ret == 0 && wait_for_request_quit) {
|
||||
QmiThreadRecvQMI(NULL); //main thread may pending on QmiThreadSendQMI()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret <= 0) {
|
||||
dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
for (ne = 0; ne < nevents; ne++) {
|
||||
int fd = pollfds[ne].fd;
|
||||
short revents = pollfds[ne].revents;
|
||||
|
||||
if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
|
||||
dbg_time("%s poll err/hup/inval", __func__);
|
||||
dbg_time("epoll fd = %d, events = 0x%04x", fd, revents);
|
||||
if (fd == qmidevice_control_fd[1]) {
|
||||
} else {
|
||||
}
|
||||
if (revents & (POLLERR | POLLHUP | POLLNVAL))
|
||||
goto __GobiNetThread_quit;
|
||||
}
|
||||
|
||||
if ((revents & POLLIN) == 0)
|
||||
continue;
|
||||
|
||||
if (fd == qmidevice_control_fd[1]) {
|
||||
int triger_event;
|
||||
if (read(fd, &triger_event, sizeof(triger_event)) == sizeof(triger_event)) {
|
||||
//DBG("triger_event = 0x%x", triger_event);
|
||||
switch (triger_event) {
|
||||
case RIL_REQUEST_QUIT:
|
||||
goto __GobiNetThread_quit;
|
||||
break;
|
||||
case SIG_EVENT_STOP:
|
||||
wait_for_request_quit = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
ssize_t nreads;
|
||||
PQCQMIMSG pResponse = (PQCQMIMSG)cm_recv_buf;
|
||||
|
||||
nreads = read(fd, &pResponse->MUXMsg, sizeof(cm_recv_buf) - sizeof(QCQMI_HDR));
|
||||
if (nreads <= 0)
|
||||
{
|
||||
dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++)
|
||||
{
|
||||
if (qmiclientId[i] == fd)
|
||||
{
|
||||
pResponse->QMIHdr.QMIType = i;
|
||||
}
|
||||
}
|
||||
|
||||
pResponse->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI;
|
||||
pResponse->QMIHdr.Length = cpu_to_le16(nreads + sizeof(QCQMI_HDR) - 1);
|
||||
pResponse->QMIHdr.CtlFlags = 0x00;
|
||||
pResponse->QMIHdr.ClientId = (fd&0xFF) ? fd&0xFF : pResponse->QMIHdr.QMIType;;
|
||||
|
||||
QmiThreadRecvQMI(pResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__GobiNetThread_quit:
|
||||
GobiNetDeInit();
|
||||
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
|
||||
QmiThreadRecvQMI(NULL); //main thread may pending on QmiThreadSendQMI()
|
||||
dbg_time("%s exit", __func__);
|
||||
pthread_exit(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct qmi_device_ops gobi_qmidev_ops = {
|
||||
.deinit = GobiNetDeInit,
|
||||
.send = GobiNetSendQMI,
|
||||
.read = GobiNetThread,
|
||||
};
|
||||
#endif
|
||||
|
||||
390
applications/quectel_cm_5G/src/MPQCTL.h
Normal file
390
applications/quectel_cm_5G/src/MPQCTL.h
Normal file
@@ -0,0 +1,390 @@
|
||||
/*===========================================================================
|
||||
|
||||
M P Q C T L. H
|
||||
DESCRIPTION:
|
||||
|
||||
This module contains QMI QCTL module.
|
||||
|
||||
INITIALIZATION AND SEQUENCING REQUIREMENTS:
|
||||
|
||||
Copyright (C) 2011 by Qualcomm Technologies, Incorporated. All Rights Reserved.
|
||||
===========================================================================*/
|
||||
|
||||
#ifndef MPQCTL_H
|
||||
#define MPQCTL_H
|
||||
|
||||
#include "MPQMI.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
// ================= QMICTL ==================
|
||||
|
||||
// QMICTL Control Flags
|
||||
#define QMICTL_CTL_FLAG_CMD 0x00
|
||||
#define QMICTL_CTL_FLAG_RSP 0x01
|
||||
#define QMICTL_CTL_FLAG_IND 0x02
|
||||
|
||||
#if 0
|
||||
typedef struct _QMICTL_TRANSACTION_ITEM
|
||||
{
|
||||
LIST_ENTRY List;
|
||||
UCHAR TransactionId; // QMICTL transaction id
|
||||
PVOID Context; // Adapter or IocDev
|
||||
PIRP Irp;
|
||||
} QMICTL_TRANSACTION_ITEM, *PQMICTL_TRANSACTION_ITEM;
|
||||
#endif
|
||||
|
||||
typedef struct _QCQMICTL_MSG_HDR
|
||||
{
|
||||
UCHAR CtlFlags; // 00-cmd, 01-rsp, 10-ind
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType;
|
||||
USHORT Length;
|
||||
} __attribute__ ((packed)) QCQMICTL_MSG_HDR, *PQCQMICTL_MSG_HDR;
|
||||
|
||||
#define QCQMICTL_MSG_HDR_SIZE sizeof(QCQMICTL_MSG_HDR)
|
||||
|
||||
typedef struct _QCQMICTL_MSG_HDR_RESP
|
||||
{
|
||||
UCHAR CtlFlags; // 00-cmd, 01-rsp, 10-ind
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType;
|
||||
USHORT Length;
|
||||
UCHAR TLVType; // 0x02 - result code
|
||||
USHORT TLVLength; // 4
|
||||
USHORT QMUXResult; // QMI_RESULT_SUCCESS
|
||||
// QMI_RESULT_FAILURE
|
||||
USHORT QMUXError; // QMI_ERR_INVALID_ARG
|
||||
// QMI_ERR_NO_MEMORY
|
||||
// QMI_ERR_INTERNAL
|
||||
// QMI_ERR_FAULT
|
||||
} __attribute__ ((packed)) QCQMICTL_MSG_HDR_RESP, *PQCQMICTL_MSG_HDR_RESP;
|
||||
|
||||
typedef struct _QCQMICTL_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // 00-cmd, 01-rsp, 10-ind
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType;
|
||||
USHORT Length;
|
||||
UCHAR Payload;
|
||||
} __attribute__ ((packed)) QCQMICTL_MSG, *PQCQMICTL_MSG;
|
||||
|
||||
// TLV Header
|
||||
typedef struct _QCQMICTL_TLV_HDR
|
||||
{
|
||||
UCHAR TLVType;
|
||||
USHORT TLVLength;
|
||||
} __attribute__ ((packed)) QCQMICTL_TLV_HDR, *PQCQMICTL_TLV_HDR;
|
||||
|
||||
#define QCQMICTL_TLV_HDR_SIZE sizeof(QCQMICTL_TLV_HDR)
|
||||
|
||||
// QMICTL Type
|
||||
#define QMICTL_SET_INSTANCE_ID_REQ 0x0020
|
||||
#define QMICTL_SET_INSTANCE_ID_RESP 0x0020
|
||||
#define QMICTL_GET_VERSION_REQ 0x0021
|
||||
#define QMICTL_GET_VERSION_RESP 0x0021
|
||||
#define QMICTL_GET_CLIENT_ID_REQ 0x0022
|
||||
#define QMICTL_GET_CLIENT_ID_RESP 0x0022
|
||||
#define QMICTL_RELEASE_CLIENT_ID_REQ 0x0023
|
||||
#define QMICTL_RELEASE_CLIENT_ID_RESP 0x0023
|
||||
#define QMICTL_REVOKE_CLIENT_ID_IND 0x0024
|
||||
#define QMICTL_INVALID_CLIENT_ID_IND 0x0025
|
||||
#define QMICTL_SET_DATA_FORMAT_REQ 0x0026
|
||||
#define QMICTL_SET_DATA_FORMAT_RESP 0x0026
|
||||
#define QMICTL_SYNC_REQ 0x0027
|
||||
#define QMICTL_SYNC_RESP 0x0027
|
||||
#define QMICTL_SYNC_IND 0x0027
|
||||
#define QMI_MESSAGE_CTL_INTERNAL_PROXY_OPEN 0xFF00
|
||||
|
||||
#define QMICTL_FLAG_REQUEST 0x00
|
||||
#define QMICTL_FLAG_RESPONSE 0x01
|
||||
#define QMICTL_FLAG_INDICATION 0x02
|
||||
|
||||
// QMICTL Message Definitions
|
||||
|
||||
typedef struct _QMICTL_SET_INSTANCE_ID_REQ_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_SET_INSTANCE_ID_REQ
|
||||
USHORT Length; // 4
|
||||
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||
USHORT TLVLength; // 1
|
||||
UCHAR Value; // Host-unique QMI instance for this device driver
|
||||
} __attribute__ ((packed)) QMICTL_SET_INSTANCE_ID_REQ_MSG, *PQMICTL_SET_INSTANCE_ID_REQ_MSG;
|
||||
|
||||
typedef struct _QMICTL_SET_INSTANCE_ID_RESP_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_SET_INSTANCE_ID_RESP
|
||||
USHORT Length;
|
||||
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
|
||||
USHORT TLVLength; // 0x0004
|
||||
USHORT QMIResult;
|
||||
USHORT QMIError;
|
||||
UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||
USHORT TLV2Length; // 0x0002
|
||||
USHORT QMI_ID; // Upper byte is assigned by MSM,
|
||||
// lower assigned by host
|
||||
} __attribute__ ((packed)) QMICTL_SET_INSTANCE_ID_RESP_MSG, *PQMICTL_SET_INSTANCE_ID_RESP_MSG;
|
||||
|
||||
typedef struct _QMICTL_GET_VERSION_REQ_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_GET_VERSION_REQ
|
||||
USHORT Length; // 0
|
||||
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||
USHORT TLVLength; // var
|
||||
UCHAR QMUXTypes; // List of one byte QMUX_TYPE values
|
||||
// 0xFF returns a list of versions for all
|
||||
// QMUX_TYPEs implemented on the device
|
||||
} __attribute__ ((packed)) QMICTL_GET_VERSION_REQ_MSG, *PQMICTL_GET_VERSION_REQ_MSG;
|
||||
|
||||
typedef struct _QMUX_TYPE_VERSION_STRUCT
|
||||
{
|
||||
UCHAR QMUXType;
|
||||
USHORT MajorVersion;
|
||||
USHORT MinorVersion;
|
||||
} __attribute__ ((packed)) QMUX_TYPE_VERSION_STRUCT, *PQMUX_TYPE_VERSION_STRUCT;
|
||||
|
||||
typedef struct _ADDENDUM_VERSION_PREAMBLE
|
||||
{
|
||||
UCHAR LabelLength;
|
||||
UCHAR Label;
|
||||
} __attribute__ ((packed)) ADDENDUM_VERSION_PREAMBLE, *PADDENDUM_VERSION_PREAMBLE;
|
||||
|
||||
#define QMICTL_GET_VERSION_RSP_TLV_TYPE_VERSION 0x01
|
||||
#define QMICTL_GET_VERSION_RSP_TLV_TYPE_ADD_VERSION 0x10
|
||||
|
||||
typedef struct _QMICTL_GET_VERSION_RESP_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_GET_VERSION_RESP
|
||||
USHORT Length;
|
||||
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
|
||||
USHORT TLVLength; // 0x0004
|
||||
USHORT QMIResult;
|
||||
USHORT QMIError;
|
||||
UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||
USHORT TLV2Length; // var
|
||||
UCHAR NumElements; // Num of QMUX_TYPE_VERSION_STRUCT
|
||||
QMUX_TYPE_VERSION_STRUCT TypeVersion[0];
|
||||
} __attribute__ ((packed)) QMICTL_GET_VERSION_RESP_MSG, *PQMICTL_GET_VERSION_RESP_MSG;
|
||||
|
||||
typedef struct _QMICTL_GET_CLIENT_ID_REQ_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_GET_CLIENT_ID_REQ
|
||||
USHORT Length;
|
||||
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||
USHORT TLVLength; // 1
|
||||
UCHAR QMIType; // QMUX type
|
||||
} __attribute__ ((packed)) QMICTL_GET_CLIENT_ID_REQ_MSG, *PQMICTL_GET_CLIENT_ID_REQ_MSG;
|
||||
|
||||
typedef struct _QMICTL_GET_CLIENT_ID_RESP_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_GET_CLIENT_ID_RESP
|
||||
USHORT Length;
|
||||
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
|
||||
USHORT TLVLength; // 0x0004
|
||||
USHORT QMIResult; // result code
|
||||
USHORT QMIError; // error code
|
||||
UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||
USHORT TLV2Length; // 2
|
||||
UCHAR QMIType;
|
||||
UCHAR ClientId;
|
||||
} __attribute__ ((packed)) QMICTL_GET_CLIENT_ID_RESP_MSG, *PQMICTL_GET_CLIENT_ID_RESP_MSG;
|
||||
|
||||
typedef struct _QMICTL_RELEASE_CLIENT_ID_REQ_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_RELEASE_CLIENT_ID_REQ
|
||||
USHORT Length;
|
||||
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||
USHORT TLVLength; // 0x0002
|
||||
UCHAR QMIType;
|
||||
UCHAR ClientId;
|
||||
} __attribute__ ((packed)) QMICTL_RELEASE_CLIENT_ID_REQ_MSG, *PQMICTL_RELEASE_CLIENT_ID_REQ_MSG;
|
||||
|
||||
typedef struct _QMICTL_RELEASE_CLIENT_ID_RESP_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_RELEASE_CLIENT_ID_RESP
|
||||
USHORT Length;
|
||||
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
|
||||
USHORT TLVLength; // 0x0004
|
||||
USHORT QMIResult; // result code
|
||||
USHORT QMIError; // error code
|
||||
UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||
USHORT TLV2Length; // 2
|
||||
UCHAR QMIType;
|
||||
UCHAR ClientId;
|
||||
} __attribute__ ((packed)) QMICTL_RELEASE_CLIENT_ID_RESP_MSG, *PQMICTL_RELEASE_CLIENT_ID_RESP_MSG;
|
||||
|
||||
typedef struct _QMICTL_REVOKE_CLIENT_ID_IND_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_INDICATION
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND
|
||||
USHORT Length;
|
||||
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||
USHORT TLVLength; // 0x0002
|
||||
UCHAR QMIType;
|
||||
UCHAR ClientId;
|
||||
} __attribute__ ((packed)) QMICTL_REVOKE_CLIENT_ID_IND_MSG, *PQMICTL_REVOKE_CLIENT_ID_IND_MSG;
|
||||
|
||||
typedef struct _QMICTL_INVALID_CLIENT_ID_IND_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_INDICATION
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND
|
||||
USHORT Length;
|
||||
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||
USHORT TLVLength; // 0x0002
|
||||
UCHAR QMIType;
|
||||
UCHAR ClientId;
|
||||
} __attribute__ ((packed)) QMICTL_INVALID_CLIENT_ID_IND_MSG, *PQMICTL_INVALID_CLIENT_ID_IND_MSG;
|
||||
|
||||
typedef struct _QMICTL_SET_DATA_FORMAT_REQ_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_SET_DATA_FORMAT_REQ
|
||||
USHORT Length;
|
||||
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||
USHORT TLVLength; // 1
|
||||
UCHAR DataFormat; // 0-default; 1-QoS hdr present
|
||||
} __attribute__ ((packed)) QMICTL_SET_DATA_FORMAT_REQ_MSG, *PQMICTL_SET_DATA_FORMAT_REQ_MSG;
|
||||
|
||||
#ifdef QC_IP_MODE
|
||||
#define SET_DATA_FORMAT_TLV_TYPE_LINK_PROTO 0x10
|
||||
#define SET_DATA_FORMAT_LINK_PROTO_ETH 0x0001
|
||||
#define SET_DATA_FORMAT_LINK_PROTO_IP 0x0002
|
||||
typedef struct _QMICTL_SET_DATA_FORMAT_TLV_LINK_PROT
|
||||
{
|
||||
UCHAR TLVType; // Link-Layer Protocol
|
||||
USHORT TLVLength; // 2
|
||||
USHORT LinkProt; // 0x1: ETH; 0x2: IP
|
||||
} QMICTL_SET_DATA_FORMAT_TLV_LINK_PROT, *PQMICTL_SET_DATA_FORMAT_TLV_LINK_PROT;
|
||||
|
||||
#ifdef QCMP_UL_TLP
|
||||
#define SET_DATA_FORMAT_TLV_TYPE_UL_TLP 0x11
|
||||
typedef struct _QMICTL_SET_DATA_FORMAT_TLV_UL_TLP
|
||||
{
|
||||
UCHAR TLVType; // 0x11, Uplink TLP Setting
|
||||
USHORT TLVLength; // 1
|
||||
UCHAR UlTlpSetting; // 0x0: Disable; 0x01: Enable
|
||||
} QMICTL_SET_DATA_FORMAT_TLV_UL_TLP, *PQMICTL_SET_DATA_FORMAT_TLV_UL_TLP;
|
||||
#endif // QCMP_UL_TLP
|
||||
|
||||
#ifdef QCMP_DL_TLP
|
||||
#define SET_DATA_FORMAT_TLV_TYPE_DL_TLP 0x13
|
||||
typedef struct _QMICTL_SET_DATA_FORMAT_TLV_DL_TLP
|
||||
{
|
||||
UCHAR TLVType; // 0x11, Uplink TLP Setting
|
||||
USHORT TLVLength; // 1
|
||||
UCHAR DlTlpSetting; // 0x0: Disable; 0x01: Enable
|
||||
} QMICTL_SET_DATA_FORMAT_TLV_DL_TLP, *PQMICTL_SET_DATA_FORMAT_TLV_DL_TLP;
|
||||
#endif // QCMP_DL_TLP
|
||||
|
||||
#endif // QC_IP_MODE
|
||||
|
||||
#ifdef MP_QCQOS_ENABLED
|
||||
#define SET_DATA_FORMAT_TLV_TYPE_QOS_SETTING 0x12
|
||||
typedef struct _QMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING
|
||||
{
|
||||
UCHAR TLVType; // 0x12, QoS setting
|
||||
USHORT TLVLength; // 1
|
||||
UCHAR QosSetting; // 0x0: Disable; 0x01: Enable
|
||||
} QMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING, *PQMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING;
|
||||
#endif // MP_QCQOS_ENABLED
|
||||
|
||||
typedef struct _QMICTL_SET_DATA_FORMAT_RESP_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_SET_DATA_FORMAT_RESP
|
||||
USHORT Length;
|
||||
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
|
||||
USHORT TLVLength; // 0x0004
|
||||
USHORT QMIResult; // result code
|
||||
USHORT QMIError; // error code
|
||||
} __attribute__ ((packed)) QMICTL_SET_DATA_FORMAT_RESP_MSG, *PQMICTL_SET_DATA_FORMAT_RESP_MSG;
|
||||
|
||||
typedef struct _QMICTL_SYNC_REQ_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_CTL_SYNC_REQ
|
||||
USHORT Length; // 0
|
||||
} __attribute__ ((packed)) QMICTL_SYNC_REQ_MSG, *PQMICTL_SYNC_REQ_MSG;
|
||||
|
||||
typedef struct _QMICTL_SYNC_RESP_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_CTL_SYNC_RESP
|
||||
USHORT Length;
|
||||
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
|
||||
USHORT TLVLength; // 0x0004
|
||||
USHORT QMIResult;
|
||||
USHORT QMIError;
|
||||
} __attribute__ ((packed)) QMICTL_SYNC_RESP_MSG, *PQMICTL_SYNC_RESP_MSG;
|
||||
|
||||
typedef struct _QMICTL_SYNC_IND_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_INDICATION
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND
|
||||
USHORT Length;
|
||||
} __attribute__ ((packed)) QMICTL_SYNC_IND_MSG, *PQMICTL_SYNC_IND_MSG;
|
||||
|
||||
typedef struct _QMICTL_LIBQMI_PROXY_OPEN_MSG
|
||||
{
|
||||
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
|
||||
UCHAR TransactionId;
|
||||
USHORT QMICTLType; // QMICTL_SET_DATA_FORMAT_RESP
|
||||
USHORT Length;
|
||||
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
|
||||
USHORT TLVLength; // 0x0004
|
||||
char device_path[0]; // result code
|
||||
} __attribute__ ((packed)) QMICTL_LIBQMI_PROXY_OPEN_MSG, *PQMICTL_LIBQMI_PROXY_OPEN_MSG;
|
||||
|
||||
typedef struct _QMICTL_MSG
|
||||
{
|
||||
union
|
||||
{
|
||||
// Message Header
|
||||
QCQMICTL_MSG_HDR QMICTLMsgHdr;
|
||||
QCQMICTL_MSG_HDR_RESP QMICTLMsgHdrRsp;
|
||||
|
||||
// QMICTL Message
|
||||
QMICTL_SET_INSTANCE_ID_REQ_MSG SetInstanceIdReq;
|
||||
QMICTL_SET_INSTANCE_ID_RESP_MSG SetInstanceIdRsp;
|
||||
QMICTL_GET_VERSION_REQ_MSG GetVersionReq;
|
||||
QMICTL_GET_VERSION_RESP_MSG GetVersionRsp;
|
||||
QMICTL_GET_CLIENT_ID_REQ_MSG GetClientIdReq;
|
||||
QMICTL_GET_CLIENT_ID_RESP_MSG GetClientIdRsp;
|
||||
QMICTL_RELEASE_CLIENT_ID_REQ_MSG ReleaseClientIdReq;
|
||||
QMICTL_RELEASE_CLIENT_ID_RESP_MSG ReleaseClientIdRsp;
|
||||
QMICTL_REVOKE_CLIENT_ID_IND_MSG RevokeClientIdInd;
|
||||
QMICTL_INVALID_CLIENT_ID_IND_MSG InvalidClientIdInd;
|
||||
QMICTL_SET_DATA_FORMAT_REQ_MSG SetDataFormatReq;
|
||||
QMICTL_SET_DATA_FORMAT_RESP_MSG SetDataFormatRsp;
|
||||
QMICTL_SYNC_REQ_MSG SyncReq;
|
||||
QMICTL_SYNC_RESP_MSG SyncRsp;
|
||||
QMICTL_SYNC_IND_MSG SyncInd;
|
||||
QMICTL_LIBQMI_PROXY_OPEN_MSG LibQmiProxyOpenReq;
|
||||
};
|
||||
} __attribute__ ((packed)) QMICTL_MSG, *PQMICTL_MSG;
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif // MPQCTL_H
|
||||
325
applications/quectel_cm_5G/src/MPQMI.h
Normal file
325
applications/quectel_cm_5G/src/MPQMI.h
Normal file
@@ -0,0 +1,325 @@
|
||||
/*===========================================================================
|
||||
|
||||
M P Q M I. H
|
||||
DESCRIPTION:
|
||||
|
||||
This module contains forward references to the QMI module.
|
||||
|
||||
INITIALIZATION AND SEQUENCING REQUIREMENTS:
|
||||
|
||||
Copyright (C) 2011 by Qualcomm Technologies, Incorporated. All Rights Reserved.
|
||||
===========================================================================*/
|
||||
/*===========================================================================
|
||||
|
||||
EDIT HISTORY FOR FILE
|
||||
$Header: //depot/QMI/win/qcdrivers/ndis/MPQMI.h#3 $
|
||||
|
||||
when who what, where, why
|
||||
-------- --- ----------------------------------------------------------
|
||||
11/20/04 hg Initial version.
|
||||
===========================================================================*/
|
||||
|
||||
#ifndef USBQMI_H
|
||||
#define USBQMI_H
|
||||
|
||||
typedef uint8_t uint8;
|
||||
typedef int8_t int8;
|
||||
typedef uint16_t uint16;
|
||||
typedef int16_t int16;
|
||||
typedef uint32_t uint32;
|
||||
typedef uint64_t uint64;
|
||||
|
||||
typedef signed char CHAR;
|
||||
typedef unsigned char UCHAR;
|
||||
typedef short SHORT;
|
||||
typedef unsigned short USHORT;
|
||||
typedef int INT;
|
||||
typedef unsigned int UINT;
|
||||
typedef long LONG;
|
||||
typedef unsigned int ULONG;
|
||||
typedef unsigned long long ULONG64;
|
||||
typedef signed char *PCHAR;
|
||||
typedef unsigned char *PUCHAR;
|
||||
typedef int *PINT;
|
||||
typedef int BOOL;
|
||||
|
||||
#define TRUE (1 == 1)
|
||||
#define FALSE (1 != 1)
|
||||
|
||||
#define QMICTL_SUPPORTED_MAJOR_VERSION 1
|
||||
#define QMICTL_SUPPORTED_MINOR_VERSION 0
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
// ========= USB Control Message ==========
|
||||
|
||||
#define USB_CTL_MSG_TYPE_QMI 0x01
|
||||
|
||||
// USB Control Message
|
||||
typedef struct _QCUSB_CTL_MSG_HDR
|
||||
{
|
||||
UCHAR IFType;
|
||||
} __attribute__ ((packed)) QCUSB_CTL_MSG_HDR, *PQCUSB_CTL_MSG_HDR;
|
||||
|
||||
#define QCUSB_CTL_MSG_HDR_SIZE sizeof(QCUSB_CTL_MSG_HDR)
|
||||
|
||||
typedef struct _QCUSB_CTL_MSG
|
||||
{
|
||||
UCHAR IFType;
|
||||
UCHAR Message;
|
||||
} __attribute__ ((packed)) QCUSB_CTL_MSG, *PQCUSB_CTL_MSG;
|
||||
|
||||
#define QCTLV_TYPE_REQUIRED_PARAMETER 0x01
|
||||
#define QCTLV_TYPE_RESULT_CODE 0x02
|
||||
|
||||
// ================= QMI ==================
|
||||
|
||||
// Define QMI Type
|
||||
typedef enum _QMI_SERVICE_TYPE
|
||||
{
|
||||
QMUX_TYPE_CTL = 0x00,
|
||||
QMUX_TYPE_WDS = 0x01,
|
||||
QMUX_TYPE_DMS = 0x02,
|
||||
QMUX_TYPE_NAS = 0x03,
|
||||
QMUX_TYPE_QOS = 0x04,
|
||||
QMUX_TYPE_WMS = 0x05,
|
||||
QMUX_TYPE_PDS = 0x06,
|
||||
QMUX_TYPE_UIM = 0x0B,
|
||||
QMUX_TYPE_WDS_IPV6 = 0x11,
|
||||
QMUX_TYPE_WDS_ADMIN = 0x1A,
|
||||
QMUX_TYPE_COEX = 0x22,
|
||||
QMUX_TYPE_MAX = 0xFF,
|
||||
QMUX_TYPE_ALL = 0xFF
|
||||
} QMI_SERVICE_TYPE;
|
||||
|
||||
typedef enum _QMI_RESULT_CODE_TYPE
|
||||
{
|
||||
QMI_RESULT_SUCCESS = 0x0000,
|
||||
QMI_RESULT_FAILURE = 0x0001
|
||||
} QMI_RESULT_CODE_TYPE;
|
||||
|
||||
typedef enum _QMI_ERROR_CODE_TYPE
|
||||
{
|
||||
QMI_ERR_NONE = 0x0000
|
||||
,QMI_ERR_MALFORMED_MSG = 0x0001
|
||||
,QMI_ERR_NO_MEMORY = 0x0002
|
||||
,QMI_ERR_INTERNAL = 0x0003
|
||||
,QMI_ERR_ABORTED = 0x0004
|
||||
,QMI_ERR_CLIENT_IDS_EXHAUSTED = 0x0005
|
||||
,QMI_ERR_UNABORTABLE_TRANSACTION = 0x0006
|
||||
,QMI_ERR_INVALID_CLIENT_ID = 0x0007
|
||||
,QMI_ERR_NO_THRESHOLDS = 0x0008
|
||||
,QMI_ERR_INVALID_HANDLE = 0x0009
|
||||
,QMI_ERR_INVALID_PROFILE = 0x000A
|
||||
,QMI_ERR_INVALID_PINID = 0x000B
|
||||
,QMI_ERR_INCORRECT_PIN = 0x000C
|
||||
,QMI_ERR_NO_NETWORK_FOUND = 0x000D
|
||||
,QMI_ERR_CALL_FAILED = 0x000E
|
||||
,QMI_ERR_OUT_OF_CALL = 0x000F
|
||||
,QMI_ERR_NOT_PROVISIONED = 0x0010
|
||||
,QMI_ERR_MISSING_ARG = 0x0011
|
||||
,QMI_ERR_ARG_TOO_LONG = 0x0013
|
||||
,QMI_ERR_INVALID_TX_ID = 0x0016
|
||||
,QMI_ERR_DEVICE_IN_USE = 0x0017
|
||||
,QMI_ERR_OP_NETWORK_UNSUPPORTED = 0x0018
|
||||
,QMI_ERR_OP_DEVICE_UNSUPPORTED = 0x0019
|
||||
,QMI_ERR_NO_EFFECT = 0x001A
|
||||
,QMI_ERR_NO_FREE_PROFILE = 0x001B
|
||||
,QMI_ERR_INVALID_PDP_TYPE = 0x001C
|
||||
,QMI_ERR_INVALID_TECH_PREF = 0x001D
|
||||
,QMI_ERR_INVALID_PROFILE_TYPE = 0x001E
|
||||
,QMI_ERR_INVALID_SERVICE_TYPE = 0x001F
|
||||
,QMI_ERR_INVALID_REGISTER_ACTION = 0x0020
|
||||
,QMI_ERR_INVALID_PS_ATTACH_ACTION = 0x0021
|
||||
,QMI_ERR_AUTHENTICATION_FAILED = 0x0022
|
||||
,QMI_ERR_PIN_BLOCKED = 0x0023
|
||||
,QMI_ERR_PIN_PERM_BLOCKED = 0x0024
|
||||
,QMI_ERR_SIM_NOT_INITIALIZED = 0x0025
|
||||
,QMI_ERR_MAX_QOS_REQUESTS_IN_USE = 0x0026
|
||||
,QMI_ERR_INCORRECT_FLOW_FILTER = 0x0027
|
||||
,QMI_ERR_NETWORK_QOS_UNAWARE = 0x0028
|
||||
,QMI_ERR_INVALID_QOS_ID = 0x0029
|
||||
,QMI_ERR_INVALID_ID = 0x0029
|
||||
,QMI_ERR_REQUESTED_NUM_UNSUPPORTED = 0x002A
|
||||
,QMI_ERR_INTERFACE_NOT_FOUND = 0x002B
|
||||
,QMI_ERR_FLOW_SUSPENDED = 0x002C
|
||||
,QMI_ERR_INVALID_DATA_FORMAT = 0x002D
|
||||
,QMI_ERR_GENERAL = 0x002E
|
||||
,QMI_ERR_UNKNOWN = 0x002F
|
||||
,QMI_ERR_INVALID_ARG = 0x0030
|
||||
,QMI_ERR_INVALID_INDEX = 0x0031
|
||||
,QMI_ERR_NO_ENTRY = 0x0032
|
||||
,QMI_ERR_DEVICE_STORAGE_FULL = 0x0033
|
||||
,QMI_ERR_DEVICE_NOT_READY = 0x0034
|
||||
,QMI_ERR_NETWORK_NOT_READY = 0x0035
|
||||
,QMI_ERR_CAUSE_CODE = 0x0036
|
||||
,QMI_ERR_MESSAGE_NOT_SENT = 0x0037
|
||||
,QMI_ERR_MESSAGE_DELIVERY_FAILURE = 0x0038
|
||||
,QMI_ERR_INVALID_MESSAGE_ID = 0x0039
|
||||
,QMI_ERR_ENCODING = 0x003A
|
||||
,QMI_ERR_AUTHENTICATION_LOCK = 0x003B
|
||||
,QMI_ERR_INVALID_TRANSITION = 0x003C
|
||||
,QMI_ERR_NOT_A_MCAST_IFACE = 0x003D
|
||||
,QMI_ERR_MAX_MCAST_REQUESTS_IN_USE = 0x003E
|
||||
,QMI_ERR_INVALID_MCAST_HANDLE = 0x003F
|
||||
,QMI_ERR_INVALID_IP_FAMILY_PREF = 0x0040
|
||||
,QMI_ERR_SESSION_INACTIVE = 0x0041
|
||||
,QMI_ERR_SESSION_INVALID = 0x0042
|
||||
,QMI_ERR_SESSION_OWNERSHIP = 0x0043
|
||||
,QMI_ERR_INSUFFICIENT_RESOURCES = 0x0044
|
||||
,QMI_ERR_DISABLED = 0x0045
|
||||
,QMI_ERR_INVALID_OPERATION = 0x0046
|
||||
,QMI_ERR_INVALID_QMI_CMD = 0x0047
|
||||
,QMI_ERR_TPDU_TYPE = 0x0048
|
||||
,QMI_ERR_SMSC_ADDR = 0x0049
|
||||
,QMI_ERR_INFO_UNAVAILABLE = 0x004A
|
||||
,QMI_ERR_SEGMENT_TOO_LONG = 0x004B
|
||||
,QMI_ERR_SEGMENT_ORDER = 0x004C
|
||||
,QMI_ERR_BUNDLING_NOT_SUPPORTED = 0x004D
|
||||
,QMI_ERR_OP_PARTIAL_FAILURE = 0x004E
|
||||
,QMI_ERR_POLICY_MISMATCH = 0x004F
|
||||
,QMI_ERR_SIM_FILE_NOT_FOUND = 0x0050
|
||||
,QMI_ERR_EXTENDED_INTERNAL = 0x0051
|
||||
,QMI_ERR_ACCESS_DENIED = 0x0052
|
||||
,QMI_ERR_HARDWARE_RESTRICTED = 0x0053
|
||||
,QMI_ERR_ACK_NOT_SENT = 0x0054
|
||||
,QMI_ERR_INJECT_TIMEOUT = 0x0055
|
||||
,QMI_ERR_INCOMPATIBLE_STATE = 0x005A
|
||||
,QMI_ERR_FDN_RESTRICT = 0x005B
|
||||
,QMI_ERR_SUPS_FAILURE_CAUSE = 0x005C
|
||||
,QMI_ERR_NO_RADIO = 0x005D
|
||||
,QMI_ERR_NOT_SUPPORTED = 0x005E
|
||||
,QMI_ERR_NO_SUBSCRIPTION = 0x005F
|
||||
,QMI_ERR_CARD_CALL_CONTROL_FAILED = 0x0060
|
||||
,QMI_ERR_NETWORK_ABORTED = 0x0061
|
||||
,QMI_ERR_MSG_BLOCKED = 0x0062
|
||||
,QMI_ERR_INVALID_SESSION_TYPE = 0x0064
|
||||
,QMI_ERR_INVALID_PB_TYPE = 0x0065
|
||||
,QMI_ERR_NO_SIM = 0x0066
|
||||
,QMI_ERR_PB_NOT_READY = 0x0067
|
||||
,QMI_ERR_PIN_RESTRICTION = 0x0068
|
||||
,QMI_ERR_PIN2_RESTRICTION = 0x0069
|
||||
,QMI_ERR_PUK_RESTRICTION = 0x006A
|
||||
,QMI_ERR_PUK2_RESTRICTION = 0x006B
|
||||
,QMI_ERR_PB_ACCESS_RESTRICTED = 0x006C
|
||||
,QMI_ERR_PB_DELETE_IN_PROG = 0x006D
|
||||
,QMI_ERR_PB_TEXT_TOO_LONG = 0x006E
|
||||
,QMI_ERR_PB_NUMBER_TOO_LONG = 0x006F
|
||||
,QMI_ERR_PB_HIDDEN_KEY_RESTRICTION = 0x0070
|
||||
} QMI_ERROR_CODE_TYPE;
|
||||
|
||||
#define QCQMI_CTL_FLAG_SERVICE 0x80
|
||||
#define QCQMI_CTL_FLAG_CTL_POINT 0x00
|
||||
|
||||
typedef struct _QCQMI_HDR
|
||||
{
|
||||
UCHAR IFType;
|
||||
USHORT Length;
|
||||
UCHAR CtlFlags; // reserved
|
||||
UCHAR QMIType;
|
||||
UCHAR ClientId;
|
||||
} __attribute__ ((packed)) QCQMI_HDR, *PQCQMI_HDR;
|
||||
|
||||
#define QCQMI_HDR_SIZE (sizeof(QCQMI_HDR)-1)
|
||||
|
||||
typedef struct _QCQMI
|
||||
{
|
||||
UCHAR IFType;
|
||||
USHORT Length;
|
||||
UCHAR CtlFlags; // reserved
|
||||
UCHAR QMIType;
|
||||
UCHAR ClientId;
|
||||
UCHAR SDU;
|
||||
} __attribute__ ((packed)) QCQMI, *PQCQMI;
|
||||
|
||||
typedef struct _QMI_SERVICE_VERSION
|
||||
{
|
||||
USHORT Major;
|
||||
USHORT Minor;
|
||||
USHORT AddendumMajor;
|
||||
USHORT AddendumMinor;
|
||||
} __attribute__ ((packed)) QMI_SERVICE_VERSION, *PQMI_SERVICE_VERSION;
|
||||
|
||||
// ================= QMUX ==================
|
||||
|
||||
#define QMUX_MSG_OVERHEAD_BYTES 4 // Type(USHORT) Length(USHORT) -- header
|
||||
|
||||
#define QMUX_BROADCAST_CID 0xFF
|
||||
|
||||
typedef struct _QCQMUX_HDR
|
||||
{
|
||||
UCHAR CtlFlags; // 0: single QMUX Msg; 1:
|
||||
USHORT TransactionId;
|
||||
} __attribute__ ((packed)) QCQMUX_HDR, *PQCQMUX_HDR;
|
||||
|
||||
typedef struct _QCQMUX
|
||||
{
|
||||
UCHAR CtlFlags; // 0: single QMUX Msg; 1:
|
||||
USHORT TransactionId;
|
||||
UCHAR Message; // Type(2), Length(2), Value
|
||||
} __attribute__ ((packed)) QCQMUX, *PQCQMUX;
|
||||
|
||||
#define QCQMUX_HDR_SIZE sizeof(QCQMUX_HDR)
|
||||
|
||||
typedef struct _QCQMUX_MSG_HDR
|
||||
{
|
||||
USHORT Type;
|
||||
USHORT Length;
|
||||
} __attribute__ ((packed)) QCQMUX_MSG_HDR, *PQCQMUX_MSG_HDR;
|
||||
|
||||
#define QCQMUX_MSG_HDR_SIZE sizeof(QCQMUX_MSG_HDR)
|
||||
|
||||
typedef struct _QCQMUX_MSG_HDR_RESP
|
||||
{
|
||||
USHORT Type;
|
||||
USHORT Length;
|
||||
UCHAR TLVType; // 0x02 - result code
|
||||
USHORT TLVLength; // 4
|
||||
USHORT QMUXResult; // QMI_RESULT_SUCCESS
|
||||
// QMI_RESULT_FAILURE
|
||||
USHORT QMUXError; // QMI_ERR_INVALID_ARG
|
||||
// QMI_ERR_NO_MEMORY
|
||||
// QMI_ERR_INTERNAL
|
||||
// QMI_ERR_FAULT
|
||||
} __attribute__ ((packed)) QCQMUX_MSG_HDR_RESP, *PQCQMUX_MSG_HDR_RESP;
|
||||
|
||||
typedef struct _QCQMUX_TLV
|
||||
{
|
||||
UCHAR Type;
|
||||
USHORT Length;
|
||||
UCHAR Value;
|
||||
} __attribute__ ((packed)) QCQMUX_TLV, *PQCQMUX_TLV;
|
||||
|
||||
typedef struct _QMI_TLV_HDR
|
||||
{
|
||||
UCHAR TLVType;
|
||||
USHORT TLVLength;
|
||||
} __attribute__ ((packed)) QMI_TLV_HDR, *PQMI_TLV_HDR;
|
||||
|
||||
typedef struct _QMI_TLV
|
||||
{
|
||||
UCHAR TLVType;
|
||||
USHORT TLVLength;
|
||||
union {
|
||||
int8_t s8;
|
||||
uint8_t u8;
|
||||
int16_t s16;
|
||||
uint16_t u16;
|
||||
int32_t s32;
|
||||
uint32_t u32;
|
||||
uint64_t u64;
|
||||
};
|
||||
} __attribute__ ((packed)) QMI_TLV, *PQMI_TLV;
|
||||
|
||||
// QMUX Message Definitions -- QMI SDU
|
||||
#define QMUX_CTL_FLAG_SINGLE_MSG 0x00
|
||||
#define QMUX_CTL_FLAG_COMPOUND_MSG 0x01
|
||||
#define QMUX_CTL_FLAG_TYPE_CMD 0x00
|
||||
#define QMUX_CTL_FLAG_TYPE_RSP 0x02
|
||||
#define QMUX_CTL_FLAG_TYPE_IND 0x04
|
||||
#define QMUX_CTL_FLAG_MASK_COMPOUND 0x01
|
||||
#define QMUX_CTL_FLAG_MASK_TYPE 0x06 // 00-cmd, 01-rsp, 10-ind
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif // USBQMI_H
|
||||
477
applications/quectel_cm_5G/src/MPQMUX.c
Normal file
477
applications/quectel_cm_5G/src/MPQMUX.c
Normal file
@@ -0,0 +1,477 @@
|
||||
/******************************************************************************
|
||||
@file MPQMUX.c
|
||||
@brief QMI mux.
|
||||
|
||||
DESCRIPTION
|
||||
Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules.
|
||||
|
||||
INITIALIZATION AND SEQUENCING REQUIREMENTS
|
||||
None.
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 2016 - 2020 Quectel Wireless Solution, Co., Ltd. All Rights Reserved.
|
||||
Quectel Wireless Solution Proprietary and Confidential.
|
||||
---------------------------------------------------------------------------
|
||||
******************************************************************************/
|
||||
|
||||
#include "QMIThread.h"
|
||||
static char line[1024];
|
||||
static pthread_mutex_t dumpQMIMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
#undef dbg
|
||||
#define dbg( format, arg... ) do {if (strlen(line) < sizeof(line)) snprintf(&line[strlen(line)], sizeof(line) - strlen(line), format, ## arg);} while (0)
|
||||
|
||||
PQMI_TLV_HDR GetTLV (PQCQMUX_MSG_HDR pQMUXMsgHdr, int TLVType);
|
||||
|
||||
typedef struct {
|
||||
UINT type;
|
||||
const char *name;
|
||||
} QMI_NAME_T;
|
||||
|
||||
#define qmi_name_item(type) {type, #type}
|
||||
|
||||
#if 0
|
||||
static const QMI_NAME_T qmi_IFType[] = {
|
||||
{USB_CTL_MSG_TYPE_QMI, "USB_CTL_MSG_TYPE_QMI"},
|
||||
};
|
||||
|
||||
static const QMI_NAME_T qmi_CtlFlags[] = {
|
||||
qmi_name_item(QMICTL_CTL_FLAG_CMD),
|
||||
qmi_name_item(QCQMI_CTL_FLAG_SERVICE),
|
||||
};
|
||||
|
||||
static const QMI_NAME_T qmi_QMIType[] = {
|
||||
qmi_name_item(QMUX_TYPE_CTL),
|
||||
qmi_name_item(QMUX_TYPE_WDS),
|
||||
qmi_name_item(QMUX_TYPE_DMS),
|
||||
qmi_name_item(QMUX_TYPE_NAS),
|
||||
qmi_name_item(QMUX_TYPE_QOS),
|
||||
qmi_name_item(QMUX_TYPE_WMS),
|
||||
qmi_name_item(QMUX_TYPE_PDS),
|
||||
qmi_name_item(QMUX_TYPE_WDS_ADMIN),
|
||||
qmi_name_item(QMUX_TYPE_COEX),
|
||||
};
|
||||
|
||||
static const QMI_NAME_T qmi_ctl_CtlFlags[] = {
|
||||
qmi_name_item(QMICTL_FLAG_REQUEST),
|
||||
qmi_name_item(QMICTL_FLAG_RESPONSE),
|
||||
qmi_name_item(QMICTL_FLAG_INDICATION),
|
||||
};
|
||||
#endif
|
||||
|
||||
static const QMI_NAME_T qmux_ctl_QMICTLType[] = {
|
||||
// QMICTL Type
|
||||
qmi_name_item(QMICTL_SET_INSTANCE_ID_REQ), // 0x0020
|
||||
qmi_name_item(QMICTL_SET_INSTANCE_ID_RESP), // 0x0020
|
||||
qmi_name_item(QMICTL_GET_VERSION_REQ), // 0x0021
|
||||
qmi_name_item(QMICTL_GET_VERSION_RESP), // 0x0021
|
||||
qmi_name_item(QMICTL_GET_CLIENT_ID_REQ), // 0x0022
|
||||
qmi_name_item(QMICTL_GET_CLIENT_ID_RESP), // 0x0022
|
||||
qmi_name_item(QMICTL_RELEASE_CLIENT_ID_REQ), // 0x0023
|
||||
qmi_name_item(QMICTL_RELEASE_CLIENT_ID_RESP), // 0x0023
|
||||
qmi_name_item(QMICTL_REVOKE_CLIENT_ID_IND), // 0x0024
|
||||
qmi_name_item(QMICTL_INVALID_CLIENT_ID_IND), // 0x0025
|
||||
qmi_name_item(QMICTL_SET_DATA_FORMAT_REQ), // 0x0026
|
||||
qmi_name_item(QMICTL_SET_DATA_FORMAT_RESP), // 0x0026
|
||||
qmi_name_item(QMICTL_SYNC_REQ), // 0x0027
|
||||
qmi_name_item(QMICTL_SYNC_RESP), // 0x0027
|
||||
qmi_name_item(QMICTL_SYNC_IND), // 0x0027
|
||||
};
|
||||
|
||||
static const QMI_NAME_T qmux_CtlFlags[] = {
|
||||
qmi_name_item(QMUX_CTL_FLAG_TYPE_CMD),
|
||||
qmi_name_item(QMUX_CTL_FLAG_TYPE_RSP),
|
||||
qmi_name_item(QMUX_CTL_FLAG_TYPE_IND),
|
||||
};
|
||||
|
||||
|
||||
static const QMI_NAME_T qmux_wds_Type[] = {
|
||||
qmi_name_item(QMIWDS_SET_EVENT_REPORT_REQ), // 0x0001
|
||||
qmi_name_item(QMIWDS_SET_EVENT_REPORT_RESP), // 0x0001
|
||||
qmi_name_item(QMIWDS_EVENT_REPORT_IND), // 0x0001
|
||||
qmi_name_item(QMIWDS_START_NETWORK_INTERFACE_REQ), // 0x0020
|
||||
qmi_name_item(QMIWDS_START_NETWORK_INTERFACE_RESP), // 0x0020
|
||||
qmi_name_item(QMIWDS_STOP_NETWORK_INTERFACE_REQ), // 0x0021
|
||||
qmi_name_item(QMIWDS_STOP_NETWORK_INTERFACE_RESP), // 0x0021
|
||||
qmi_name_item(QMIWDS_GET_PKT_SRVC_STATUS_REQ), // 0x0022
|
||||
qmi_name_item(QMIWDS_GET_PKT_SRVC_STATUS_RESP), // 0x0022
|
||||
qmi_name_item(QMIWDS_GET_PKT_SRVC_STATUS_IND), // 0x0022
|
||||
qmi_name_item(QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ), // 0x0023
|
||||
qmi_name_item(QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP), // 0x0023
|
||||
qmi_name_item(QMIWDS_GET_PKT_STATISTICS_REQ), // 0x0024
|
||||
qmi_name_item(QMIWDS_GET_PKT_STATISTICS_RESP), // 0x0024
|
||||
qmi_name_item(QMIWDS_MODIFY_PROFILE_SETTINGS_REQ), // 0x0028
|
||||
qmi_name_item(QMIWDS_MODIFY_PROFILE_SETTINGS_RESP), // 0x0028
|
||||
qmi_name_item(QMIWDS_GET_PROFILE_SETTINGS_REQ), // 0x002B
|
||||
qmi_name_item(QMIWDS_GET_PROFILE_SETTINGS_RESP), // 0x002BD
|
||||
qmi_name_item(QMIWDS_GET_DEFAULT_SETTINGS_REQ), // 0x002C
|
||||
qmi_name_item(QMIWDS_GET_DEFAULT_SETTINGS_RESP), // 0x002C
|
||||
qmi_name_item(QMIWDS_GET_RUNTIME_SETTINGS_REQ), // 0x002D
|
||||
qmi_name_item(QMIWDS_GET_RUNTIME_SETTINGS_RESP), // 0x002D
|
||||
qmi_name_item(QMIWDS_GET_MIP_MODE_REQ), // 0x002F
|
||||
qmi_name_item(QMIWDS_GET_MIP_MODE_RESP), // 0x002F
|
||||
qmi_name_item(QMIWDS_GET_DATA_BEARER_REQ), // 0x0037
|
||||
qmi_name_item(QMIWDS_GET_DATA_BEARER_RESP), // 0x0037
|
||||
qmi_name_item(QMIWDS_DUN_CALL_INFO_REQ), // 0x0038
|
||||
qmi_name_item(QMIWDS_DUN_CALL_INFO_RESP), // 0x0038
|
||||
qmi_name_item(QMIWDS_DUN_CALL_INFO_IND), // 0x0038
|
||||
qmi_name_item(QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ), // 0x004D
|
||||
qmi_name_item(QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP), // 0x004D
|
||||
qmi_name_item(QMIWDS_SET_AUTO_CONNECT_REQ), // 0x0051
|
||||
qmi_name_item(QMIWDS_SET_AUTO_CONNECT_RESP), // 0x0051
|
||||
qmi_name_item(QMIWDS_BIND_MUX_DATA_PORT_REQ), // 0x00A2
|
||||
qmi_name_item(QMIWDS_BIND_MUX_DATA_PORT_RESP), // 0x00A2
|
||||
};
|
||||
|
||||
static const QMI_NAME_T qmux_dms_Type[] = {
|
||||
// ======================= DMS ==============================
|
||||
qmi_name_item(QMIDMS_SET_EVENT_REPORT_REQ), // 0x0001
|
||||
qmi_name_item(QMIDMS_SET_EVENT_REPORT_RESP), // 0x0001
|
||||
qmi_name_item(QMIDMS_EVENT_REPORT_IND), // 0x0001
|
||||
qmi_name_item(QMIDMS_GET_DEVICE_CAP_REQ), // 0x0020
|
||||
qmi_name_item(QMIDMS_GET_DEVICE_CAP_RESP), // 0x0020
|
||||
qmi_name_item(QMIDMS_GET_DEVICE_MFR_REQ), // 0x0021
|
||||
qmi_name_item(QMIDMS_GET_DEVICE_MFR_RESP), // 0x0021
|
||||
qmi_name_item(QMIDMS_GET_DEVICE_MODEL_ID_REQ), // 0x0022
|
||||
qmi_name_item(QMIDMS_GET_DEVICE_MODEL_ID_RESP), // 0x0022
|
||||
qmi_name_item(QMIDMS_GET_DEVICE_REV_ID_REQ), // 0x0023
|
||||
qmi_name_item(QMIDMS_GET_DEVICE_REV_ID_RESP), // 0x0023
|
||||
qmi_name_item(QMIDMS_GET_MSISDN_REQ), // 0x0024
|
||||
qmi_name_item(QMIDMS_GET_MSISDN_RESP), // 0x0024
|
||||
qmi_name_item(QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ), // 0x0025
|
||||
qmi_name_item(QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP), // 0x0025
|
||||
qmi_name_item(QMIDMS_UIM_SET_PIN_PROTECTION_REQ), // 0x0027
|
||||
qmi_name_item(QMIDMS_UIM_SET_PIN_PROTECTION_RESP), // 0x0027
|
||||
qmi_name_item(QMIDMS_UIM_VERIFY_PIN_REQ), // 0x0028
|
||||
qmi_name_item(QMIDMS_UIM_VERIFY_PIN_RESP), // 0x0028
|
||||
qmi_name_item(QMIDMS_UIM_UNBLOCK_PIN_REQ), // 0x0029
|
||||
qmi_name_item(QMIDMS_UIM_UNBLOCK_PIN_RESP), // 0x0029
|
||||
qmi_name_item(QMIDMS_UIM_CHANGE_PIN_REQ), // 0x002A
|
||||
qmi_name_item(QMIDMS_UIM_CHANGE_PIN_RESP), // 0x002A
|
||||
qmi_name_item(QMIDMS_UIM_GET_PIN_STATUS_REQ), // 0x002B
|
||||
qmi_name_item(QMIDMS_UIM_GET_PIN_STATUS_RESP), // 0x002B
|
||||
qmi_name_item(QMIDMS_GET_DEVICE_HARDWARE_REV_REQ), // 0x002C
|
||||
qmi_name_item(QMIDMS_GET_DEVICE_HARDWARE_REV_RESP), // 0x002C
|
||||
qmi_name_item(QMIDMS_GET_OPERATING_MODE_REQ), // 0x002D
|
||||
qmi_name_item(QMIDMS_GET_OPERATING_MODE_RESP), // 0x002D
|
||||
qmi_name_item(QMIDMS_SET_OPERATING_MODE_REQ), // 0x002E
|
||||
qmi_name_item(QMIDMS_SET_OPERATING_MODE_RESP), // 0x002E
|
||||
qmi_name_item(QMIDMS_GET_ACTIVATED_STATUS_REQ), // 0x0031
|
||||
qmi_name_item(QMIDMS_GET_ACTIVATED_STATUS_RESP), // 0x0031
|
||||
qmi_name_item(QMIDMS_ACTIVATE_AUTOMATIC_REQ), // 0x0032
|
||||
qmi_name_item(QMIDMS_ACTIVATE_AUTOMATIC_RESP), // 0x0032
|
||||
qmi_name_item(QMIDMS_ACTIVATE_MANUAL_REQ), // 0x0033
|
||||
qmi_name_item(QMIDMS_ACTIVATE_MANUAL_RESP), // 0x0033
|
||||
qmi_name_item(QMIDMS_UIM_GET_ICCID_REQ), // 0x003C
|
||||
qmi_name_item(QMIDMS_UIM_GET_ICCID_RESP), // 0x003C
|
||||
qmi_name_item(QMIDMS_UIM_GET_CK_STATUS_REQ), // 0x0040
|
||||
qmi_name_item(QMIDMS_UIM_GET_CK_STATUS_RESP), // 0x0040
|
||||
qmi_name_item(QMIDMS_UIM_SET_CK_PROTECTION_REQ), // 0x0041
|
||||
qmi_name_item(QMIDMS_UIM_SET_CK_PROTECTION_RESP), // 0x0041
|
||||
qmi_name_item(QMIDMS_UIM_UNBLOCK_CK_REQ), // 0x0042
|
||||
qmi_name_item(QMIDMS_UIM_UNBLOCK_CK_RESP), // 0x0042
|
||||
qmi_name_item(QMIDMS_UIM_GET_IMSI_REQ), // 0x0043
|
||||
qmi_name_item(QMIDMS_UIM_GET_IMSI_RESP), // 0x0043
|
||||
qmi_name_item(QMIDMS_UIM_GET_STATE_REQ), // 0x0044
|
||||
qmi_name_item(QMIDMS_UIM_GET_STATE_RESP), // 0x0044
|
||||
qmi_name_item(QMIDMS_GET_BAND_CAP_REQ), // 0x0045
|
||||
qmi_name_item(QMIDMS_GET_BAND_CAP_RESP), // 0x0045
|
||||
};
|
||||
|
||||
static const QMI_NAME_T qmux_qos_Type[] = {
|
||||
qmi_name_item( QMI_QOS_SET_EVENT_REPORT_REQ), // 0x0001
|
||||
qmi_name_item( QMI_QOS_SET_EVENT_REPORT_RESP), // 0x0001
|
||||
qmi_name_item( QMI_QOS_SET_EVENT_REPORT_IND), // 0x0001
|
||||
qmi_name_item( QMI_QOS_BIND_DATA_PORT_REQ), // 0x002B
|
||||
qmi_name_item( QMI_QOS_BIND_DATA_PORT_RESP), // 0x002B
|
||||
qmi_name_item( QMI_QOS_INDICATION_REGISTER_REQ), // 0x002F
|
||||
qmi_name_item( QMI_QOS_INDICATION_REGISTER_RESP), // 0x002F
|
||||
qmi_name_item( QMI_QOS_GLOBAL_QOS_FLOW_IND), // 0x0031
|
||||
qmi_name_item( QMI_QOS_GET_QOS_INFO_REQ), // 0x0033
|
||||
qmi_name_item( QMI_QOS_GET_QOS_INFO_RESP), // 0x0033
|
||||
};
|
||||
|
||||
static const QMI_NAME_T qmux_nas_Type[] = {
|
||||
// ======================= NAS ==============================
|
||||
qmi_name_item(QMINAS_SET_EVENT_REPORT_REQ), // 0x0002
|
||||
qmi_name_item(QMINAS_SET_EVENT_REPORT_RESP), // 0x0002
|
||||
qmi_name_item(QMINAS_EVENT_REPORT_IND), // 0x0002
|
||||
qmi_name_item(QMINAS_GET_SIGNAL_STRENGTH_REQ), // 0x0020
|
||||
qmi_name_item(QMINAS_GET_SIGNAL_STRENGTH_RESP), // 0x0020
|
||||
qmi_name_item(QMINAS_PERFORM_NETWORK_SCAN_REQ), // 0x0021
|
||||
qmi_name_item(QMINAS_PERFORM_NETWORK_SCAN_RESP), // 0x0021
|
||||
qmi_name_item(QMINAS_INITIATE_NW_REGISTER_REQ), // 0x0022
|
||||
qmi_name_item(QMINAS_INITIATE_NW_REGISTER_RESP), // 0x0022
|
||||
qmi_name_item(QMINAS_INITIATE_ATTACH_REQ), // 0x0023
|
||||
qmi_name_item(QMINAS_INITIATE_ATTACH_RESP), // 0x0023
|
||||
qmi_name_item(QMINAS_GET_SERVING_SYSTEM_REQ), // 0x0024
|
||||
qmi_name_item(QMINAS_GET_SERVING_SYSTEM_RESP), // 0x0024
|
||||
qmi_name_item(QMINAS_SERVING_SYSTEM_IND), // 0x0024
|
||||
qmi_name_item(QMINAS_GET_HOME_NETWORK_REQ), // 0x0025
|
||||
qmi_name_item(QMINAS_GET_HOME_NETWORK_RESP), // 0x0025
|
||||
qmi_name_item(QMINAS_GET_PREFERRED_NETWORK_REQ), // 0x0026
|
||||
qmi_name_item(QMINAS_GET_PREFERRED_NETWORK_RESP), // 0x0026
|
||||
qmi_name_item(QMINAS_SET_PREFERRED_NETWORK_REQ), // 0x0027
|
||||
qmi_name_item(QMINAS_SET_PREFERRED_NETWORK_RESP), // 0x0027
|
||||
qmi_name_item(QMINAS_GET_FORBIDDEN_NETWORK_REQ), // 0x0028
|
||||
qmi_name_item(QMINAS_GET_FORBIDDEN_NETWORK_RESP), // 0x0028
|
||||
qmi_name_item(QMINAS_SET_FORBIDDEN_NETWORK_REQ), // 0x0029
|
||||
qmi_name_item(QMINAS_SET_FORBIDDEN_NETWORK_RESP), // 0x0029
|
||||
qmi_name_item(QMINAS_SET_TECHNOLOGY_PREF_REQ), // 0x002A
|
||||
qmi_name_item(QMINAS_SET_TECHNOLOGY_PREF_RESP), // 0x002A
|
||||
qmi_name_item(QMINAS_GET_RF_BAND_INFO_REQ), // 0x0031
|
||||
qmi_name_item(QMINAS_GET_RF_BAND_INFO_RESP), // 0x0031
|
||||
qmi_name_item(QMINAS_GET_CELL_LOCATION_INFO_REQ),
|
||||
qmi_name_item(QMINAS_GET_CELL_LOCATION_INFO_RESP),
|
||||
qmi_name_item(QMINAS_GET_PLMN_NAME_REQ), // 0x0044
|
||||
qmi_name_item(QMINAS_GET_PLMN_NAME_RESP), // 0x0044
|
||||
qmi_name_item(QUECTEL_PACKET_TRANSFER_START_IND), // 0X100
|
||||
qmi_name_item(QUECTEL_PACKET_TRANSFER_END_IND), // 0X101
|
||||
qmi_name_item(QMINAS_GET_SYS_INFO_REQ), // 0x004D
|
||||
qmi_name_item(QMINAS_GET_SYS_INFO_RESP), // 0x004D
|
||||
qmi_name_item(QMINAS_SYS_INFO_IND), // 0x004D
|
||||
qmi_name_item(QMINAS_GET_SIG_INFO_REQ),
|
||||
qmi_name_item(QMINAS_GET_SIG_INFO_RESP),
|
||||
|
||||
};
|
||||
|
||||
static const QMI_NAME_T qmux_wms_Type[] = {
|
||||
// ======================= WMS ==============================
|
||||
qmi_name_item(QMIWMS_SET_EVENT_REPORT_REQ), // 0x0001
|
||||
qmi_name_item(QMIWMS_SET_EVENT_REPORT_RESP), // 0x0001
|
||||
qmi_name_item(QMIWMS_EVENT_REPORT_IND), // 0x0001
|
||||
qmi_name_item(QMIWMS_RAW_SEND_REQ), // 0x0020
|
||||
qmi_name_item(QMIWMS_RAW_SEND_RESP), // 0x0020
|
||||
qmi_name_item(QMIWMS_RAW_WRITE_REQ), // 0x0021
|
||||
qmi_name_item(QMIWMS_RAW_WRITE_RESP), // 0x0021
|
||||
qmi_name_item(QMIWMS_RAW_READ_REQ), // 0x0022
|
||||
qmi_name_item(QMIWMS_RAW_READ_RESP), // 0x0022
|
||||
qmi_name_item(QMIWMS_MODIFY_TAG_REQ), // 0x0023
|
||||
qmi_name_item(QMIWMS_MODIFY_TAG_RESP), // 0x0023
|
||||
qmi_name_item(QMIWMS_DELETE_REQ), // 0x0024
|
||||
qmi_name_item(QMIWMS_DELETE_RESP), // 0x0024
|
||||
qmi_name_item(QMIWMS_GET_MESSAGE_PROTOCOL_REQ), // 0x0030
|
||||
qmi_name_item(QMIWMS_GET_MESSAGE_PROTOCOL_RESP), // 0x0030
|
||||
qmi_name_item(QMIWMS_LIST_MESSAGES_REQ), // 0x0031
|
||||
qmi_name_item(QMIWMS_LIST_MESSAGES_RESP), // 0x0031
|
||||
qmi_name_item(QMIWMS_GET_SMSC_ADDRESS_REQ), // 0x0034
|
||||
qmi_name_item(QMIWMS_GET_SMSC_ADDRESS_RESP), // 0x0034
|
||||
qmi_name_item(QMIWMS_SET_SMSC_ADDRESS_REQ), // 0x0035
|
||||
qmi_name_item(QMIWMS_SET_SMSC_ADDRESS_RESP), // 0x0035
|
||||
qmi_name_item(QMIWMS_GET_STORE_MAX_SIZE_REQ), // 0x0036
|
||||
qmi_name_item(QMIWMS_GET_STORE_MAX_SIZE_RESP), // 0x0036
|
||||
};
|
||||
|
||||
static const QMI_NAME_T qmux_wds_admin_Type[] = {
|
||||
qmi_name_item(QMIWDS_ADMIN_SET_DATA_FORMAT_REQ), // 0x0020
|
||||
qmi_name_item(QMIWDS_ADMIN_SET_DATA_FORMAT_RESP), // 0x0020
|
||||
qmi_name_item(QMIWDS_ADMIN_GET_DATA_FORMAT_REQ), // 0x0021
|
||||
qmi_name_item(QMIWDS_ADMIN_GET_DATA_FORMAT_RESP), // 0x0021
|
||||
qmi_name_item(QMIWDS_ADMIN_SET_QMAP_SETTINGS_REQ), // 0x002B
|
||||
qmi_name_item(QMIWDS_ADMIN_SET_QMAP_SETTINGS_RESP), // 0x002B
|
||||
qmi_name_item(QMIWDS_ADMIN_GET_QMAP_SETTINGS_REQ), // 0x002C
|
||||
qmi_name_item(QMIWDS_ADMIN_GET_QMAP_SETTINGS_RESP), // 0x002C
|
||||
qmi_name_item(QMI_WDA_SET_LOOPBACK_CONFIG_REQ), // 0x002F
|
||||
qmi_name_item(QMI_WDA_SET_LOOPBACK_CONFIG_RESP), // 0x002F
|
||||
qmi_name_item(QMI_WDA_SET_LOOPBACK_CONFIG_IND), // 0x002F
|
||||
};
|
||||
|
||||
static const QMI_NAME_T qmux_uim_Type[] = {
|
||||
qmi_name_item( QMIUIM_READ_TRANSPARENT_REQ), // 0x0020
|
||||
qmi_name_item( QMIUIM_READ_TRANSPARENT_RESP), // 0x0020
|
||||
qmi_name_item( QMIUIM_READ_TRANSPARENT_IND), // 0x0020
|
||||
qmi_name_item( QMIUIM_READ_RECORD_REQ), // 0x0021
|
||||
qmi_name_item( QMIUIM_READ_RECORD_RESP), // 0x0021
|
||||
qmi_name_item( QMIUIM_READ_RECORD_IND), // 0x0021
|
||||
qmi_name_item( QMIUIM_WRITE_TRANSPARENT_REQ), // 0x0022
|
||||
qmi_name_item( QMIUIM_WRITE_TRANSPARENT_RESP), // 0x0022
|
||||
qmi_name_item( QMIUIM_WRITE_TRANSPARENT_IND), // 0x0022
|
||||
qmi_name_item( QMIUIM_WRITE_RECORD_REQ), // 0x0023
|
||||
qmi_name_item( QMIUIM_WRITE_RECORD_RESP), // 0x0023
|
||||
qmi_name_item( QMIUIM_WRITE_RECORD_IND), // 0x0023
|
||||
qmi_name_item( QMIUIM_SET_PIN_PROTECTION_REQ), // 0x0025
|
||||
qmi_name_item( QMIUIM_SET_PIN_PROTECTION_RESP), // 0x0025
|
||||
qmi_name_item( QMIUIM_SET_PIN_PROTECTION_IND), // 0x0025
|
||||
qmi_name_item( QMIUIM_VERIFY_PIN_REQ), // 0x0026
|
||||
qmi_name_item( QMIUIM_VERIFY_PIN_RESP), // 0x0026
|
||||
qmi_name_item( QMIUIM_VERIFY_PIN_IND), // 0x0026
|
||||
qmi_name_item( QMIUIM_UNBLOCK_PIN_REQ), // 0x0027
|
||||
qmi_name_item( QMIUIM_UNBLOCK_PIN_RESP), // 0x0027
|
||||
qmi_name_item( QMIUIM_UNBLOCK_PIN_IND), // 0x0027
|
||||
qmi_name_item( QMIUIM_CHANGE_PIN_REQ), // 0x0028
|
||||
qmi_name_item( QMIUIM_CHANGE_PIN_RESP), // 0x0028
|
||||
qmi_name_item( QMIUIM_CHANGE_PIN_IND), // 0x0028
|
||||
qmi_name_item( QMIUIM_DEPERSONALIZATION_REQ), // 0x0029
|
||||
qmi_name_item( QMIUIM_DEPERSONALIZATION_RESP), // 0x0029
|
||||
qmi_name_item( QMIUIM_EVENT_REG_REQ), // 0x002E
|
||||
qmi_name_item( QMIUIM_EVENT_REG_RESP), // 0x002E
|
||||
qmi_name_item( QMIUIM_GET_CARD_STATUS_REQ), // 0x002F
|
||||
qmi_name_item( QMIUIM_GET_CARD_STATUS_RESP), // 0x002F
|
||||
qmi_name_item( QMIUIM_STATUS_CHANGE_IND), // 0x0032
|
||||
};
|
||||
|
||||
static const QMI_NAME_T qmux_coex_Type[] = {
|
||||
qmi_name_item(QMI_COEX_GET_WWAN_STATE_REQ), // 0x0022
|
||||
qmi_name_item(QMI_COEX_GET_WWAN_STATE_RESP), // 0x0022
|
||||
};
|
||||
|
||||
static const char * qmi_name_get(const QMI_NAME_T *table, size_t size, int type, const char *tag) {
|
||||
static char unknow[40];
|
||||
size_t i;
|
||||
|
||||
if (qmux_CtlFlags == table) {
|
||||
if (!strcmp(tag, "_REQ"))
|
||||
tag = "_CMD";
|
||||
else if (!strcmp(tag, "_RESP"))
|
||||
tag = "_RSP";
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (table[i].type == (UINT)type) {
|
||||
if (!tag || (strstr(table[i].name, tag)))
|
||||
return table[i].name;
|
||||
}
|
||||
}
|
||||
sprintf(unknow, "unknow_%x", type);
|
||||
return unknow;
|
||||
}
|
||||
|
||||
#define QMI_NAME(table, type) qmi_name_get(table, sizeof(table) / sizeof(table[0]), type, 0)
|
||||
#define QMUX_NAME(table, type, tag) qmi_name_get(table, sizeof(table) / sizeof(table[0]), type, tag)
|
||||
|
||||
void dump_tlv(PQCQMUX_MSG_HDR pQMUXMsgHdr) {
|
||||
int TLVFind = 0;
|
||||
int i;
|
||||
//dbg("QCQMUX_TLV-----------------------------------\n");
|
||||
//dbg("{Type,\tLength,\tValue}\n");
|
||||
|
||||
while (1) {
|
||||
PQMI_TLV_HDR TLVHdr = GetTLV(pQMUXMsgHdr, 0x1000 + (++TLVFind));
|
||||
if (TLVHdr == NULL)
|
||||
break;
|
||||
|
||||
//if ((TLVHdr->TLVType == 0x02) && ((USHORT *)(TLVHdr+1))[0])
|
||||
{
|
||||
dbg("{%02x,\t%04x,\t", TLVHdr->TLVType, le16_to_cpu(TLVHdr->TLVLength));
|
||||
for (i = 0; i < le16_to_cpu(TLVHdr->TLVLength); i++) {
|
||||
dbg("%02x ", ((UCHAR *)(TLVHdr+1))[i]);
|
||||
}
|
||||
dbg("}\n");
|
||||
}
|
||||
} // while
|
||||
}
|
||||
|
||||
void dump_ctl(PQCQMICTL_MSG_HDR CTLHdr) {
|
||||
const char *tag;
|
||||
|
||||
//dbg("QCQMICTL_MSG--------------------------------------------\n");
|
||||
//dbg("CtlFlags: %02x\t\t%s\n", CTLHdr->CtlFlags, QMI_NAME(qmi_ctl_CtlFlags, CTLHdr->CtlFlags));
|
||||
dbg("TransactionId: %02x\n", CTLHdr->TransactionId);
|
||||
switch (CTLHdr->CtlFlags) {
|
||||
case QMICTL_FLAG_REQUEST: tag = "_REQ"; break;
|
||||
case QMICTL_FLAG_RESPONSE: tag = "_RESP"; break;
|
||||
case QMICTL_FLAG_INDICATION: tag = "_IND"; break;
|
||||
default: tag = 0; break;
|
||||
}
|
||||
dbg("QMICTLType: %04x\t%s\n", le16_to_cpu(CTLHdr->QMICTLType),
|
||||
QMUX_NAME(qmux_ctl_QMICTLType, le16_to_cpu(CTLHdr->QMICTLType), tag));
|
||||
dbg("Length: %04x\n", le16_to_cpu(CTLHdr->Length));
|
||||
|
||||
dump_tlv((PQCQMUX_MSG_HDR)(&CTLHdr->QMICTLType));
|
||||
}
|
||||
|
||||
int dump_qmux(QMI_SERVICE_TYPE serviceType, PQCQMUX_HDR QMUXHdr) {
|
||||
PQCQMUX_MSG_HDR QMUXMsgHdr = (PQCQMUX_MSG_HDR) (QMUXHdr + 1);
|
||||
const char *tag;
|
||||
|
||||
//dbg("QCQMUX--------------------------------------------\n");
|
||||
switch (QMUXHdr->CtlFlags&QMUX_CTL_FLAG_MASK_TYPE) {
|
||||
case QMUX_CTL_FLAG_TYPE_CMD: tag = "_REQ"; break;
|
||||
case QMUX_CTL_FLAG_TYPE_RSP: tag = "_RESP"; break;
|
||||
case QMUX_CTL_FLAG_TYPE_IND: tag = "_IND"; break;
|
||||
default: tag = 0; break;
|
||||
}
|
||||
//dbg("CtlFlags: %02x\t\t%s\n", QMUXHdr->CtlFlags, QMUX_NAME(qmux_CtlFlags, QMUXHdr->CtlFlags, tag));
|
||||
dbg("TransactionId: %04x\n", le16_to_cpu(QMUXHdr->TransactionId));
|
||||
|
||||
//dbg("QCQMUX_MSG_HDR-----------------------------------\n");
|
||||
switch (serviceType) {
|
||||
case QMUX_TYPE_DMS:
|
||||
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
|
||||
QMUX_NAME(qmux_dms_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
|
||||
break;
|
||||
case QMUX_TYPE_NAS:
|
||||
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
|
||||
QMUX_NAME(qmux_nas_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
|
||||
break;
|
||||
case QMUX_TYPE_WDS:
|
||||
case QMUX_TYPE_WDS_IPV6:
|
||||
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
|
||||
QMUX_NAME(qmux_wds_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
|
||||
break;
|
||||
case QMUX_TYPE_WMS:
|
||||
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
|
||||
QMUX_NAME(qmux_wms_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
|
||||
break;
|
||||
case QMUX_TYPE_WDS_ADMIN:
|
||||
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
|
||||
QMUX_NAME(qmux_wds_admin_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
|
||||
break;
|
||||
case QMUX_TYPE_UIM:
|
||||
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
|
||||
QMUX_NAME(qmux_uim_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
|
||||
break;
|
||||
case QMUX_TYPE_PDS:
|
||||
case QMUX_TYPE_QOS:
|
||||
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
|
||||
QMUX_NAME(qmux_qos_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
|
||||
break;
|
||||
case QMUX_TYPE_COEX:
|
||||
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
|
||||
QMUX_NAME(qmux_coex_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
|
||||
break;
|
||||
case QMUX_TYPE_CTL:
|
||||
default:
|
||||
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), "PDS/QOS/CTL/unknown!");
|
||||
break;
|
||||
}
|
||||
dbg("Length: %04x\n", le16_to_cpu(QMUXMsgHdr->Length));
|
||||
|
||||
dump_tlv(QMUXMsgHdr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dump_qmi(void *dataBuffer, int dataLen)
|
||||
{
|
||||
PQCQMI_HDR QMIHdr = (PQCQMI_HDR)dataBuffer;
|
||||
PQCQMUX_HDR QMUXHdr = (PQCQMUX_HDR) (QMIHdr + 1);
|
||||
PQCQMICTL_MSG_HDR CTLHdr = (PQCQMICTL_MSG_HDR) (QMIHdr + 1);
|
||||
|
||||
int i;
|
||||
|
||||
if (!debug_qmi)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&dumpQMIMutex);
|
||||
line[0] = 0;
|
||||
for (i = 0; i < dataLen; i++) {
|
||||
dbg("%02x ", ((unsigned char *)dataBuffer)[i]);
|
||||
}
|
||||
dbg_time("%s", line);
|
||||
line[0] = 0;
|
||||
|
||||
//dbg("QCQMI_HDR-----------------------------------------");
|
||||
//dbg("IFType: %02x\t\t%s", QMIHdr->IFType, QMI_NAME(qmi_IFType, QMIHdr->IFType));
|
||||
//dbg("Length: %04x", le16_to_cpu(QMIHdr->Length));
|
||||
//dbg("CtlFlags: %02x\t\t%s", QMIHdr->CtlFlags, QMI_NAME(qmi_CtlFlags, QMIHdr->CtlFlags));
|
||||
//dbg("QMIType: %02x\t\t%s", QMIHdr->QMIType, QMI_NAME(qmi_QMIType, QMIHdr->QMIType));
|
||||
//dbg("ClientId: %02x", QMIHdr->ClientId);
|
||||
|
||||
if (QMIHdr->QMIType == QMUX_TYPE_CTL) {
|
||||
dump_ctl(CTLHdr);
|
||||
} else {
|
||||
dump_qmux(QMIHdr->QMIType, QMUXHdr);
|
||||
}
|
||||
dbg_time("%s", line);
|
||||
pthread_mutex_unlock(&dumpQMIMutex);
|
||||
}
|
||||
4273
applications/quectel_cm_5G/src/MPQMUX.h
Normal file
4273
applications/quectel_cm_5G/src/MPQMUX.h
Normal file
File diff suppressed because it is too large
Load Diff
45
applications/quectel_cm_5G/src/Makefile
Normal file
45
applications/quectel_cm_5G/src/Makefile
Normal file
@@ -0,0 +1,45 @@
|
||||
ifneq ($(CROSS_COMPILE),)
|
||||
CROSS-COMPILE:=$(CROSS_COMPILE)
|
||||
endif
|
||||
#CROSS-COMPILE:=/workspace/buildroot/buildroot-qemu_mips_malta_defconfig/output/host/usr/bin/mips-buildroot-linux-uclibc-
|
||||
#CROSS-COMPILE:=/workspace/buildroot/buildroot-qemu_arm_vexpress_defconfig/output/host/usr/bin/arm-buildroot-linux-uclibcgnueabi-
|
||||
#CROSS-COMPILE:=/workspace/buildroot-git/qemu_mips64_malta/output/host/usr/bin/mips-gnu-linux-
|
||||
ifeq ($(CC),cc)
|
||||
CC:=$(CROSS-COMPILE)gcc
|
||||
endif
|
||||
LD:=$(CROSS-COMPILE)ld
|
||||
|
||||
QL_CM_SRC=QmiWwanCM.c GobiNetCM.c main.c MPQMUX.c QMIThread.c util.c qmap_bridge_mode.c mbim-cm.c device.c
|
||||
QL_CM_SRC+=atc.c atchannel.c at_tok.c
|
||||
#QL_CM_SRC+=qrtr.c rmnetctl.c
|
||||
ifeq (1,1)
|
||||
QL_CM_DHCP=udhcpc.c
|
||||
else
|
||||
LIBMNL=libmnl/ifutils.c libmnl/attr.c libmnl/callback.c libmnl/nlmsg.c libmnl/socket.c
|
||||
DHCP=libmnl/dhcp/dhcpclient.c libmnl/dhcp/dhcpmsg.c libmnl/dhcp/packet.c
|
||||
QL_CM_DHCP=udhcpc_netlink.c
|
||||
QL_CM_DHCP+=${LIBMNL}
|
||||
endif
|
||||
|
||||
LDFLAGS += -lpthread -ldl -lrt
|
||||
|
||||
release: clean qmi-proxy mbim-proxy atc-proxy #qrtr-proxy
|
||||
$(CC) ${CFLAGS} ${QL_CM_SRC} ${QL_CM_DHCP} -o quectel-CM ${LDFLAGS}
|
||||
|
||||
debug: clean
|
||||
$(CC) ${CFLAGS} -g -DCM_DEBUG ${QL_CM_SRC} ${QL_CM_DHCP} -o quectel-CM -lpthread -ldl -lrt
|
||||
|
||||
qmi-proxy:
|
||||
$(CC) ${CFLAGS} quectel-qmi-proxy.c -o quectel-qmi-proxy ${LDFLAGS}
|
||||
|
||||
mbim-proxy:
|
||||
$(CC) ${CFLAGS} quectel-mbim-proxy.c -o quectel-mbim-proxy ${LDFLAGS}
|
||||
|
||||
qrtr-proxy:
|
||||
$(CC) ${CFLAGS} quectel-qrtr-proxy.c -o quectel-qrtr-proxy ${LDFLAGS}
|
||||
|
||||
atc-proxy:
|
||||
$(CC) ${CFLAGS} quectel-atc-proxy.c atchannel.c at_tok.c util.c -o quectel-atc-proxy ${LDFLAGS}
|
||||
|
||||
clean:
|
||||
rm -rf *.o libmnl/*.o quectel-CM quectel-qmi-proxy quectel-mbim-proxy quectel-atc-proxy
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user