├── LICENSE ├── Makefile ├── README.md ├── build └── placeholder ├── include ├── reg_field.h ├── rtl-sdr.h ├── rtl-sdr_export.h ├── rtlsdr_i2c.h ├── rtlsdr_rpc.h ├── rtlsdr_rpc_msg.h ├── tuner_e4k.h ├── tuner_fc0012.h ├── tuner_fc0013.h ├── tuner_fc2580.h └── tuner_r82xx.h ├── python └── r820tweak.py ├── screenshot └── ss.png └── src ├── convenience ├── convenience.c └── convenience.h ├── librtlsdr.c ├── rtlsdr_rpc.c ├── rtlsdr_rpc_msg.c ├── tuner_e4k.c ├── tuner_fc0012.c ├── tuner_fc0013.c ├── tuner_fc2580.c └── tuner_r82xx.c /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -fPIC -Wall -O2 -g -Iinclude -I/usr/include/libusb-1.0 -I/usr/include/libusb-1.1 3 | LDFLAGS = -shared -lusb -lpthread 4 | RM = rm -f 5 | TARGET_LIB = build/librtlsdr.so 6 | SRCS = src/librtlsdr.c src/rtlsdr_rpc.c src/rtlsdr_rpc_msg.c src/tuner_e4k.c src/tuner_fc0012.c src/tuner_fc0013.c src/tuner_fc2580.c src/tuner_r82xx.c src/convenience/convenience.c 7 | OBJS = $(SRCS:.c=.o) 8 | 9 | .PHONY: all 10 | 11 | 12 | all: ${TARGET_LIB} 13 | 14 | $(TARGET_LIB): $(OBJS) 15 | $(CC) ${LDFLAGS} -o $@ $^ 16 | 17 | $(SRCS:.c=.d):%.d:%.c 18 | $(CC) $(CFLAGS) -MM $< >$@ 19 | 20 | 21 | include $(SRCS:.c=.d) 22 | 23 | .PHONY: clean 24 | 25 | clean: 26 | -${RM} ${TARGET_LIB} ${OBJS} 27 | 28 | install: 29 | mkdir -p /usr/local/bin 2>/dev/null 30 | mkdir -p /usr/local/share/r820tweak 2>/dev/null 31 | cp build/librtlsdr.so /usr/local/share/r820tweak 32 | cp python/r820tweak.py /usr/local/bin/r820tweak 33 | chmod ugo+x /usr/local/bin/r820tweak -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Screenshot](/screenshot/ss.png?raw=true "r820tweak") 2 | 3 | 4 | r820tweak is a modified RTL-SDR driver that exposes the R820T2 device gain stages and filters and makes them accessible through a GUI app. 5 | 6 | You just need to launch your SDR program (e.g gqrx) like this: 7 | 8 | `r820tweak gqrx` and it will automatically preload the modified driver 9 | 10 | Then you need to run 11 | 12 | `r820tweak` 13 | 14 | which will launch the GUI app that manages RTLSDR settings 15 | 16 | 17 | 18 | ## Why? 19 | 20 | Because it takes eons to get the new features in the gnuradio source and then the software that uses it. 21 | 22 | ## Installation 23 | 24 | `python`, `wxpython`, `libusb-dev` and a gcc compiler are neeeded to build the project, e.g: 25 | 26 | `sudo apt-get install gcc libusb-dev gcc` 27 | `sudo pip install wxpython` 28 | 29 | Building the program is as easy as running make: 30 | 31 | `make && sudo make install` 32 | 33 | 34 | ## Author 35 | 36 | Milen Rangelov 37 | 38 | ## License 39 | 40 | GNU GPL 41 | -------------------------------------------------------------------------------- /build/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gat3way/r820tweak/c0a53a516a3ec8a2aee7118d5effe40e026d7430/build/placeholder -------------------------------------------------------------------------------- /include/reg_field.h: -------------------------------------------------------------------------------- 1 | #ifndef _REG_FIELD_H 2 | #define _REG_FIELD_H 3 | 4 | #include 5 | #include 6 | 7 | enum cmd_op { 8 | CMD_OP_GET = (1 << 0), 9 | CMD_OP_SET = (1 << 1), 10 | CMD_OP_EXEC = (1 << 2), 11 | }; 12 | 13 | enum pstate { 14 | ST_IN_CMD, 15 | ST_IN_ARG, 16 | }; 17 | 18 | struct strbuf { 19 | uint8_t idx; 20 | char buf[32]; 21 | }; 22 | 23 | struct cmd_state { 24 | struct strbuf cmd; 25 | struct strbuf arg; 26 | enum pstate state; 27 | void (*out)(const char *format, va_list ap); 28 | }; 29 | 30 | struct cmd { 31 | const char *cmd; 32 | uint32_t ops; 33 | int (*cb)(struct cmd_state *cs, enum cmd_op op, const char *cmd, 34 | int argc, char **argv); 35 | const char *help; 36 | }; 37 | 38 | /* structure describing a field in a register */ 39 | struct reg_field { 40 | uint8_t reg; 41 | uint8_t shift; 42 | uint8_t width; 43 | }; 44 | 45 | struct reg_field_ops { 46 | const struct reg_field *fields; 47 | const char **field_names; 48 | uint32_t num_fields; 49 | void *data; 50 | int (*write_cb)(void *data, uint32_t reg, uint32_t val); 51 | uint32_t (*read_cb)(void *data, uint32_t reg); 52 | }; 53 | 54 | uint32_t reg_field_read(struct reg_field_ops *ops, struct reg_field *field); 55 | int reg_field_write(struct reg_field_ops *ops, struct reg_field *field, uint32_t val); 56 | int reg_field_cmd(struct cmd_state *cs, enum cmd_op op, 57 | const char *cmd, int argc, char **argv, 58 | struct reg_field_ops *ops); 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /include/rtl-sdr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver 3 | * Copyright (C) 2012-2013 by Steve Markgraf 4 | * Copyright (C) 2012 by Dimitri Stolnikov 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef __RTL_SDR_H 21 | #define __RTL_SDR_H 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | #ifndef WIN32 28 | #define _ENABLE_RPC 29 | #endif 30 | 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | typedef struct rtlsdr_dev rtlsdr_dev_t; 37 | 38 | RTLSDR_API uint32_t rtlsdr_get_device_count(void); 39 | 40 | RTLSDR_API const char* rtlsdr_get_device_name(uint32_t index); 41 | 42 | /*! 43 | * Get USB device strings. 44 | * 45 | * NOTE: The string arguments must provide space for up to 256 bytes. 46 | * 47 | * \param index the device index 48 | * \param manufact manufacturer name, may be NULL 49 | * \param product product name, may be NULL 50 | * \param serial serial number, may be NULL 51 | * \return 0 on success 52 | */ 53 | RTLSDR_API int rtlsdr_get_device_usb_strings(uint32_t index, 54 | char *manufact, 55 | char *product, 56 | char *serial); 57 | 58 | /*! 59 | * Get device index by USB serial string descriptor. 60 | * 61 | * \param serial serial string of the device 62 | * \return device index of first device where the name matched 63 | * \return -1 if name is NULL 64 | * \return -2 if no devices were found at all 65 | * \return -3 if devices were found, but none with matching name 66 | */ 67 | RTLSDR_API int rtlsdr_get_index_by_serial(const char *serial); 68 | 69 | RTLSDR_API int rtlsdr_open(rtlsdr_dev_t **dev, uint32_t index); 70 | 71 | RTLSDR_API int rtlsdr_close(rtlsdr_dev_t *dev); 72 | 73 | /* configuration functions */ 74 | 75 | /*! 76 | * Set crystal oscillator frequencies used for the RTL2832 and the tuner IC. 77 | * 78 | * Usually both ICs use the same clock. Changing the clock may make sense if 79 | * you are applying an external clock to the tuner or to compensate the 80 | * frequency (and samplerate) error caused by the original (cheap) crystal. 81 | * 82 | * NOTE: Call this function only if you fully understand the implications. 83 | * 84 | * \param dev the device handle given by rtlsdr_open() 85 | * \param rtl_freq frequency value used to clock the RTL2832 in Hz 86 | * \param tuner_freq frequency value used to clock the tuner IC in Hz 87 | * \return 0 on success 88 | */ 89 | RTLSDR_API int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq, 90 | uint32_t tuner_freq); 91 | 92 | /*! 93 | * Get crystal oscillator frequencies used for the RTL2832 and the tuner IC. 94 | * 95 | * Usually both ICs use the same clock. 96 | * 97 | * \param dev the device handle given by rtlsdr_open() 98 | * \param rtl_freq frequency value used to clock the RTL2832 in Hz 99 | * \param tuner_freq frequency value used to clock the tuner IC in Hz 100 | * \return 0 on success 101 | */ 102 | RTLSDR_API int rtlsdr_get_xtal_freq(rtlsdr_dev_t *dev, uint32_t *rtl_freq, 103 | uint32_t *tuner_freq); 104 | 105 | /*! 106 | * Get USB device strings. 107 | * 108 | * NOTE: The string arguments must provide space for up to 256 bytes. 109 | * 110 | * \param dev the device handle given by rtlsdr_open() 111 | * \param manufact manufacturer name, may be NULL 112 | * \param product product name, may be NULL 113 | * \param serial serial number, may be NULL 114 | * \return 0 on success 115 | */ 116 | RTLSDR_API int rtlsdr_get_usb_strings(rtlsdr_dev_t *dev, char *manufact, 117 | char *product, char *serial); 118 | 119 | /*! 120 | * Write the device EEPROM 121 | * 122 | * \param dev the device handle given by rtlsdr_open() 123 | * \param data buffer of data to be written 124 | * \param offset address where the data should be written 125 | * \param len length of the data 126 | * \return 0 on success 127 | * \return -1 if device handle is invalid 128 | * \return -2 if EEPROM size is exceeded 129 | * \return -3 if no EEPROM was found 130 | */ 131 | 132 | RTLSDR_API int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data, 133 | uint8_t offset, uint16_t len); 134 | 135 | /*! 136 | * Read the device EEPROM 137 | * 138 | * \param dev the device handle given by rtlsdr_open() 139 | * \param data buffer where the data should be written 140 | * \param offset address where the data should be read from 141 | * \param len length of the data 142 | * \return 0 on success 143 | * \return -1 if device handle is invalid 144 | * \return -2 if EEPROM size is exceeded 145 | * \return -3 if no EEPROM was found 146 | */ 147 | 148 | RTLSDR_API int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, 149 | uint8_t offset, uint16_t len); 150 | 151 | /*! 152 | * Set the frequency the device is tuned to. 153 | * 154 | * \param dev the device handle given by rtlsdr_open() 155 | * \param frequency in Hz 156 | * \return 0 on error, frequency in Hz otherwise 157 | */ 158 | RTLSDR_API int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq); 159 | 160 | /*! 161 | * Get actual frequency the device is tuned to. 162 | * 163 | * \param dev the device handle given by rtlsdr_open() 164 | * \return 0 on error, frequency in Hz otherwise 165 | */ 166 | RTLSDR_API uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev); 167 | 168 | /*! 169 | * Set the frequency correction value for the device. 170 | * 171 | * \param dev the device handle given by rtlsdr_open() 172 | * \param ppm correction value in parts per million (ppm) 173 | * \return 0 on success 174 | */ 175 | RTLSDR_API int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm); 176 | 177 | /*! 178 | * Get actual frequency correction value of the device. 179 | * 180 | * \param dev the device handle given by rtlsdr_open() 181 | * \return correction value in parts per million (ppm) 182 | */ 183 | RTLSDR_API int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev); 184 | 185 | enum rtlsdr_tuner { 186 | RTLSDR_TUNER_UNKNOWN = 0, 187 | RTLSDR_TUNER_E4000, 188 | RTLSDR_TUNER_FC0012, 189 | RTLSDR_TUNER_FC0013, 190 | RTLSDR_TUNER_FC2580, 191 | RTLSDR_TUNER_R820T, 192 | RTLSDR_TUNER_R828D 193 | }; 194 | 195 | /*! 196 | * Get the tuner type. 197 | * 198 | * \param dev the device handle given by rtlsdr_open() 199 | * \return RTLSDR_TUNER_UNKNOWN on error, tuner type otherwise 200 | */ 201 | RTLSDR_API enum rtlsdr_tuner rtlsdr_get_tuner_type(rtlsdr_dev_t *dev); 202 | 203 | /*! 204 | * Get a list of gains supported by the tuner. 205 | * 206 | * NOTE: The gains argument must be preallocated by the caller. If NULL is 207 | * being given instead, the number of available gain values will be returned. 208 | * 209 | * \param dev the device handle given by rtlsdr_open() 210 | * \param gains array of gain values. In tenths of a dB, 115 means 11.5 dB. 211 | * \return <= 0 on error, number of available (returned) gain values otherwise 212 | */ 213 | RTLSDR_API int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains); 214 | 215 | /*! 216 | * Set the gain for the device. 217 | * Manual gain mode must be enabled for this to work. 218 | * 219 | * Valid gain values (in tenths of a dB) for the E4000 tuner: 220 | * -10, 15, 40, 65, 90, 115, 140, 165, 190, 221 | * 215, 240, 290, 340, 420, 430, 450, 470, 490 222 | * 223 | * Valid gain values may be queried with \ref rtlsdr_get_tuner_gains function. 224 | * 225 | * \param dev the device handle given by rtlsdr_open() 226 | * \param gain in tenths of a dB, 115 means 11.5 dB. 227 | * \return 0 on success 228 | */ 229 | RTLSDR_API int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain); 230 | 231 | /*! 232 | * Set the bandwidth for the device. 233 | * 234 | * \param dev the device handle given by rtlsdr_open() 235 | * \param bw bandwidth in Hz. Zero means automatic BW selection. 236 | * \param applied_bw is applied bandwidth in Hz, or 0 if unknown 237 | * \param apply_bw: 1 to really apply configure the tuner chip; 0 for just returning applied_bw 238 | * \return 0 on success 239 | */ 240 | RTLSDR_API int rtlsdr_set_and_get_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw, uint32_t *applied_bw, int apply_bw ); 241 | 242 | RTLSDR_API int rtlsdr_set_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw ); 243 | 244 | 245 | /*! 246 | * Get actual gain the device is configured to. 247 | * 248 | * \param dev the device handle given by rtlsdr_open() 249 | * \return 0 on error, gain in tenths of a dB, 115 means 11.5 dB. 250 | */ 251 | RTLSDR_API int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev); 252 | 253 | /*! 254 | * Set LNA / Mixer / VGA Device Gain for R820T device is configured to. 255 | * 256 | * \param dev the device handle given by rtlsdr_open() 257 | * \param lna_gain in tenths of a dB, -30 means -3.0 dB. 258 | * \param mixer_gain in tenths of a dB, -30 means -3.0 dB. 259 | * \param vga_gain in tenths of a dB, -30 means -3.0 dB. 260 | * \return 0 on success 261 | */ 262 | RTLSDR_API int rtlsdr_set_tuner_gain_ext(rtlsdr_dev_t *dev, int lna_gain, int mixer_gain, int vga_gain); 263 | 264 | /*! 265 | * Set the intermediate frequency gain for the device. 266 | * 267 | * \param dev the device handle given by rtlsdr_open() 268 | * \param stage intermediate frequency gain stage number (1 to 6 for E4000) 269 | * \param gain in tenths of a dB, -30 means -3.0 dB. 270 | * \return 0 on success 271 | */ 272 | RTLSDR_API int rtlsdr_set_tuner_if_gain(rtlsdr_dev_t *dev, int stage, int gain); 273 | 274 | /*! 275 | * Set the gain mode (automatic/manual) for the device. 276 | * Manual gain mode must be enabled for the gain setter function to work. 277 | * 278 | * \param dev the device handle given by rtlsdr_open() 279 | * \param manual gain mode, 1 means manual gain mode shall be enabled. 280 | * \return 0 on success 281 | */ 282 | RTLSDR_API int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual); 283 | 284 | /*! 285 | * Set the sample rate for the device, also selects the baseband filters 286 | * according to the requested sample rate for tuners where this is possible. 287 | * 288 | * \param dev the device handle given by rtlsdr_open() 289 | * \param samp_rate the sample rate to be set, possible values are: 290 | * 225001 - 300000 Hz 291 | * 900001 - 3200000 Hz 292 | * sample loss is to be expected for rates > 2400000 293 | * \return 0 on success, -EINVAL on invalid rate 294 | */ 295 | RTLSDR_API int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate); 296 | 297 | /*! 298 | * Get actual sample rate the device is configured to. 299 | * 300 | * \param dev the device handle given by rtlsdr_open() 301 | * \return 0 on error, sample rate in Hz otherwise 302 | */ 303 | RTLSDR_API uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev); 304 | 305 | /*! 306 | * Enable test mode that returns an 8 bit counter instead of the samples. 307 | * The counter is generated inside the RTL2832. 308 | * 309 | * \param dev the device handle given by rtlsdr_open() 310 | * \param test mode, 1 means enabled, 0 disabled 311 | * \return 0 on success 312 | */ 313 | RTLSDR_API int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on); 314 | 315 | /*! 316 | * Enable or disable the internal digital AGC of the RTL2832. 317 | * 318 | * \param dev the device handle given by rtlsdr_open() 319 | * \param digital AGC mode, 1 means enabled, 0 disabled 320 | * \return 0 on success 321 | */ 322 | RTLSDR_API int rtlsdr_set_agc_mode(rtlsdr_dev_t *dev, int on); 323 | 324 | /*! 325 | * Enable or disable the direct sampling mode. When enabled, the IF mode 326 | * of the RTL2832 is activated, and rtlsdr_set_center_freq() will control 327 | * the IF-frequency of the DDC, which can be used to tune from 0 to 28.8 MHz 328 | * (xtal frequency of the RTL2832). 329 | * 330 | * \param dev the device handle given by rtlsdr_open() 331 | * \param on 0 means disabled, 1 I-ADC input enabled, 2 Q-ADC input enabled 332 | * \return 0 on success 333 | */ 334 | RTLSDR_API int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on); 335 | 336 | /*! 337 | * Get state of the direct sampling mode 338 | * 339 | * \param dev the device handle given by rtlsdr_open() 340 | * \return -1 on error, 0 means disabled, 1 I-ADC input enabled 341 | * 2 Q-ADC input enabled 342 | */ 343 | RTLSDR_API int rtlsdr_get_direct_sampling(rtlsdr_dev_t *dev); 344 | 345 | enum rtlsdr_ds_mode { 346 | RTLSDR_DS_IQ = 0, /* I/Q quadrature sampling of tuner output */ 347 | RTLSDR_DS_I, /* 1: direct sampling on I branch: usually not connected */ 348 | RTLSDR_DS_Q, /* 2: direct sampling on Q branch: HF on rtl-sdr v3 dongle */ 349 | RTLSDR_DS_I_BELOW, /* 3: direct sampling on I branch when frequency below 'DS threshold frequency' */ 350 | RTLSDR_DS_Q_BELOW /* 4: direct sampling on Q branch when frequency below 'DS threshold frequency' */ 351 | }; 352 | 353 | /*! 354 | * Set direct sampling mode with threshold 355 | * 356 | * \param dev the device handle given by rtlsdr_open() 357 | * \param mode static modes 0 .. 2 as in rtlsdr_set_direct_sampling(). other modes do automatic switching 358 | * \param freq_threshold direct sampling is used below this frequency, else quadrature mode through tuner 359 | * set 0 for using default setting per tuner - not fully implemented yet! 360 | * \return negative on error, 0 on success 361 | */ 362 | RTLSDR_API int rtlsdr_set_ds_mode(rtlsdr_dev_t *dev, enum rtlsdr_ds_mode mode, uint32_t freq_threshold); 363 | 364 | /*! 365 | * Enable or disable offset tuning for zero-IF tuners, which allows to avoid 366 | * problems caused by the DC offset of the ADCs and 1/f noise. 367 | * 368 | * \param dev the device handle given by rtlsdr_open() 369 | * \param on 0 means disabled, 1 enabled 370 | * \return 0 on success 371 | */ 372 | RTLSDR_API int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on); 373 | 374 | /*! 375 | * Get state of the offset tuning mode 376 | * 377 | * \param dev the device handle given by rtlsdr_open() 378 | * \return -1 on error, 0 means disabled, 1 enabled 379 | */ 380 | RTLSDR_API int rtlsdr_get_offset_tuning(rtlsdr_dev_t *dev); 381 | 382 | /* streaming functions */ 383 | 384 | RTLSDR_API int rtlsdr_reset_buffer(rtlsdr_dev_t *dev); 385 | 386 | RTLSDR_API int rtlsdr_read_sync(rtlsdr_dev_t *dev, void *buf, int len, int *n_read); 387 | 388 | typedef void(*rtlsdr_read_async_cb_t)(unsigned char *buf, uint32_t len, void *ctx); 389 | 390 | /*! 391 | * Read samples from the device asynchronously. This function will block until 392 | * it is being canceled using rtlsdr_cancel_async() 393 | * 394 | * NOTE: This function is deprecated and is subject for removal. 395 | * 396 | * \param dev the device handle given by rtlsdr_open() 397 | * \param cb callback function to return received samples 398 | * \param ctx user specific context to pass via the callback function 399 | * \return 0 on success 400 | */ 401 | RTLSDR_API int rtlsdr_wait_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx); 402 | 403 | /*! 404 | * Read samples from the device asynchronously. This function will block until 405 | * it is being canceled using rtlsdr_cancel_async() 406 | * 407 | * \param dev the device handle given by rtlsdr_open() 408 | * \param cb callback function to return received samples 409 | * \param ctx user specific context to pass via the callback function 410 | * \param buf_num optional buffer count, buf_num * buf_len = overall buffer size 411 | * set to 0 for default buffer count (15) 412 | * \param buf_len optional buffer length, must be multiple of 512, 413 | * should be a multiple of 16384 (URB size), set to 0 414 | * for default buffer length (16 * 32 * 512) 415 | * \return 0 on success 416 | */ 417 | RTLSDR_API int rtlsdr_read_async(rtlsdr_dev_t *dev, 418 | rtlsdr_read_async_cb_t cb, 419 | void *ctx, 420 | uint32_t buf_num, 421 | uint32_t buf_len); 422 | 423 | /*! 424 | * Cancel all pending asynchronous operations on the device. 425 | * 426 | * \param dev the device handle given by rtlsdr_open() 427 | * \return 0 on success 428 | */ 429 | RTLSDR_API int rtlsdr_cancel_async(rtlsdr_dev_t *dev); 430 | 431 | /*! 432 | * Read from the remote control (RC) infrared (IR) sensor 433 | * 434 | * \param dev the device handle given by rtlsdr_open() 435 | * \param buf buffer to write IR signal (MSB=pulse/space, 7LSB=duration*20usec), recommended 128-bytes 436 | * \param buf_len size of buf 437 | * \return 0 if no signal, >0 number of bytes written into buf, <0 for error 438 | */ 439 | RTLSDR_API int rtlsdr_ir_query(rtlsdr_dev_t *dev, uint8_t *buf, size_t buf_len); 440 | 441 | 442 | /*! 443 | * Enable or disable the bias tee on GPIO PIN 0. (Works for rtl-sdr.com v3 dongles) 444 | * See: http://www.rtl-sdr.com/rtl-sdr-blog-v-3-dongles-user-guide/ 445 | * 446 | * \param dev the device handle given by rtlsdr_open() 447 | * \param on 1 for Bias T on. 0 for Bias T off. 448 | * \return -1 if device is not initialized. 1 otherwise. 449 | */ 450 | RTLSDR_API int rtlsdr_set_bias_tee(rtlsdr_dev_t *dev, int on); 451 | 452 | #ifdef __cplusplus 453 | } 454 | #endif 455 | 456 | #endif /* __RTL_SDR_H */ 457 | -------------------------------------------------------------------------------- /include/rtl-sdr_export.h: -------------------------------------------------------------------------------- 1 | /* 2 | * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver 3 | * Copyright (C) 2012 by Hoernchen 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef RTLSDR_EXPORT_H 20 | #define RTLSDR_EXPORT_H 21 | 22 | #if defined __GNUC__ 23 | # if __GNUC__ >= 4 24 | # define __SDR_EXPORT __attribute__((visibility("default"))) 25 | # define __SDR_IMPORT __attribute__((visibility("default"))) 26 | # else 27 | # define __SDR_EXPORT 28 | # define __SDR_IMPORT 29 | # endif 30 | #elif _MSC_VER 31 | # define __SDR_EXPORT __declspec(dllexport) 32 | # define __SDR_IMPORT __declspec(dllimport) 33 | #else 34 | # define __SDR_EXPORT 35 | # define __SDR_IMPORT 36 | #endif 37 | 38 | #ifndef rtlsdr_STATIC 39 | # ifdef rtlsdr_EXPORTS 40 | # define RTLSDR_API __SDR_EXPORT 41 | # else 42 | # define RTLSDR_API __SDR_IMPORT 43 | # endif 44 | #else 45 | #define RTLSDR_API 46 | #endif 47 | #endif /* RTLSDR_EXPORT_H */ 48 | -------------------------------------------------------------------------------- /include/rtlsdr_i2c.h: -------------------------------------------------------------------------------- 1 | #ifndef __I2C_H 2 | #define __I2C_H 3 | 4 | uint32_t rtlsdr_get_tuner_clock(void *dev); 5 | int rtlsdr_i2c_write_fn(void *dev, uint8_t addr, uint8_t *buf, int len); 6 | int rtlsdr_i2c_read_fn(void *dev, uint8_t addr, uint8_t *buf, int len); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /include/rtlsdr_rpc.h: -------------------------------------------------------------------------------- 1 | #ifndef RTLSDR_RPC_H_INCLUDED 2 | #define RTLSDR_RPC_H_INCLUDED 3 | 4 | 5 | #include 6 | 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | typedef void (*rtlsdr_rpc_read_async_cb_t) 13 | (unsigned char*, uint32_t, void*); 14 | 15 | uint32_t rtlsdr_rpc_get_device_count(void); 16 | 17 | const char* rtlsdr_rpc_get_device_name 18 | (uint32_t nidex); 19 | 20 | int rtlsdr_rpc_get_device_usb_strings 21 | (uint32_t index, char* manufact, char* product, char* serial); 22 | 23 | int rtlsdr_rpc_get_index_by_serial 24 | (const char* serial); 25 | 26 | int rtlsdr_rpc_open 27 | (void** dev, uint32_t index); 28 | 29 | int rtlsdr_rpc_close 30 | (void* dev); 31 | 32 | int rtlsdr_rpc_set_xtal_freq 33 | (void* dev, uint32_t rtl_freq, uint32_t tuner_freq); 34 | 35 | int rtlsdr_rpc_get_xtal_freq 36 | (void* dev, uint32_t* rtl_freq, uint32_t* tuner_freq); 37 | 38 | int rtlsdr_rpc_get_usb_strings 39 | (void* dev, char* manufact, char* product, char* serial); 40 | 41 | int rtlsdr_rpc_write_eeprom 42 | (void* dev, uint8_t* data, uint8_t offset, uint16_t len); 43 | 44 | int rtlsdr_rpc_read_eeprom 45 | (void* dev, uint8_t* data, uint8_t offset, uint16_t len); 46 | 47 | int rtlsdr_rpc_set_center_freq 48 | (void* dev, uint32_t freq); 49 | 50 | uint32_t rtlsdr_rpc_get_center_freq 51 | (void* dev); 52 | 53 | int rtlsdr_rpc_set_freq_correction 54 | (void* dev, int ppm); 55 | 56 | int rtlsdr_rpc_get_freq_correction 57 | (void *dev); 58 | 59 | int rtlsdr_rpc_get_tuner_type 60 | (void* dev); 61 | 62 | int rtlsdr_rpc_get_tuner_gains 63 | (void* dev, int* gainsp); 64 | 65 | int rtlsdr_rpc_set_tuner_gain 66 | (void *dev, int gain); 67 | 68 | int rtlsdr_rpc_get_tuner_gain 69 | (void* dev); 70 | 71 | int rtlsdr_rpc_set_tuner_if_gain 72 | (void* dev, int stage, int gain); 73 | 74 | int rtlsdr_rpc_set_tuner_gain_mode 75 | (void* dev, int manual); 76 | 77 | int rtlsdr_rpc_set_sample_rate 78 | (void* dev, uint32_t rate); 79 | 80 | uint32_t rtlsdr_rpc_get_sample_rate 81 | (void* dev); 82 | 83 | int rtlsdr_rpc_set_testmode 84 | (void* dev, int on); 85 | 86 | int rtlsdr_rpc_set_agc_mode 87 | (void* dev, int on); 88 | 89 | int rtlsdr_rpc_set_direct_sampling 90 | (void* dev, int on); 91 | 92 | int rtlsdr_rpc_get_direct_sampling 93 | (void* dev); 94 | 95 | int rtlsdr_rpc_set_offset_tuning 96 | (void* dev, int on); 97 | 98 | int rtlsdr_rpc_get_offset_tuning 99 | (void* dev); 100 | 101 | int rtlsdr_rpc_reset_buffer 102 | (void* dev); 103 | 104 | int rtlsdr_rpc_read_sync 105 | (void* dev, void* buf, int len, int* n_read); 106 | 107 | int rtlsdr_rpc_wait_async 108 | (void* dev, rtlsdr_rpc_read_async_cb_t cb, void* ctx); 109 | 110 | int rtlsdr_rpc_read_async 111 | (void* dev, rtlsdr_rpc_read_async_cb_t cb, void* ctx, uint32_t buf_num, uint32_t buf_len); 112 | 113 | int rtlsdr_rpc_cancel_async 114 | (void* dev); 115 | 116 | unsigned int rtlsdr_rpc_is_enabled(void); 117 | 118 | #ifdef __cplusplus 119 | } 120 | #endif 121 | 122 | 123 | #endif /* RTLSDR_RPC_H_INCLUDED */ 124 | -------------------------------------------------------------------------------- /include/rtlsdr_rpc_msg.h: -------------------------------------------------------------------------------- 1 | #ifndef RTLSDR_RPC_MSG_H_INCLUDED 2 | #define RTLSDR_RPC_MSG_H_INCLUDED 3 | 4 | 5 | #include 6 | #include 7 | 8 | typedef enum 9 | { 10 | RTLSDR_RPC_OP_GET_DEVICE_COUNT = 0, 11 | RTLSDR_RPC_OP_GET_DEVICE_NAME, 12 | RTLSDR_RPC_OP_GET_DEVICE_USB_STRINGS, 13 | RTLSDR_RPC_OP_GET_INDEX_BY_SERIAL, 14 | RTLSDR_RPC_OP_OPEN, 15 | RTLSDR_RPC_OP_CLOSE, 16 | RTLSDR_RPC_OP_SET_XTAL_FREQ, 17 | RTLSDR_RPC_OP_GET_XTAL_FREQ, 18 | RTLSDR_RPC_OP_GET_USB_STRINGS, 19 | RTLSDR_RPC_OP_WRITE_EEPROM, 20 | RTLSDR_RPC_OP_READ_EEPROM, 21 | RTLSDR_RPC_OP_SET_CENTER_FREQ, 22 | RTLSDR_RPC_OP_GET_CENTER_FREQ, 23 | RTLSDR_RPC_OP_SET_FREQ_CORRECTION, 24 | RTLSDR_RPC_OP_GET_FREQ_CORRECTION, 25 | RTLSDR_RPC_OP_GET_TUNER_TYPE, 26 | RTLSDR_RPC_OP_GET_TUNER_GAINS, 27 | RTLSDR_RPC_OP_SET_TUNER_GAIN, 28 | RTLSDR_RPC_OP_GET_TUNER_GAIN, 29 | RTLSDR_RPC_OP_SET_TUNER_IF_GAIN, 30 | RTLSDR_RPC_OP_SET_TUNER_GAIN_MODE, 31 | RTLSDR_RPC_OP_SET_SAMPLE_RATE, 32 | RTLSDR_RPC_OP_GET_SAMPLE_RATE, 33 | RTLSDR_RPC_OP_SET_TESTMODE, 34 | RTLSDR_RPC_OP_SET_AGC_MODE, 35 | RTLSDR_RPC_OP_SET_DIRECT_SAMPLING, 36 | RTLSDR_RPC_OP_GET_DIRECT_SAMPLING, 37 | RTLSDR_RPC_OP_SET_OFFSET_TUNING, 38 | RTLSDR_RPC_OP_GET_OFFSET_TUNING, 39 | RTLSDR_RPC_OP_RESET_BUFFER, 40 | RTLSDR_RPC_OP_READ_SYNC, 41 | RTLSDR_RPC_OP_WAIT_ASYNC, 42 | RTLSDR_RPC_OP_READ_ASYNC, 43 | RTLSDR_RPC_OP_CANCEL_ASYNC, 44 | 45 | /* non api operations */ 46 | RTLSDR_RPC_OP_EVENT_STATE, 47 | 48 | RTLSDR_RPC_OP_INVALID 49 | } rtlsdr_rpc_op_t; 50 | 51 | typedef struct 52 | { 53 | /* raw network format */ 54 | uint32_t size; 55 | uint8_t op; 56 | uint8_t id; 57 | uint32_t err; 58 | uint8_t data[1]; 59 | } __attribute__((packed)) rtlsdr_rpc_fmt_t; 60 | 61 | typedef struct 62 | { 63 | size_t off; 64 | size_t size; 65 | uint8_t* fmt; 66 | } rtlsdr_rpc_msg_t; 67 | 68 | int rtlsdr_rpc_msg_init(rtlsdr_rpc_msg_t*, size_t); 69 | int rtlsdr_rpc_msg_fini(rtlsdr_rpc_msg_t*); 70 | void rtlsdr_rpc_msg_reset(rtlsdr_rpc_msg_t*); 71 | int rtlsdr_rpc_msg_realloc(rtlsdr_rpc_msg_t*, size_t); 72 | 73 | void rtlsdr_rpc_msg_set_size(rtlsdr_rpc_msg_t*, size_t); 74 | size_t rtlsdr_rpc_msg_get_size(const rtlsdr_rpc_msg_t*); 75 | void rtlsdr_rpc_msg_set_op(rtlsdr_rpc_msg_t*, rtlsdr_rpc_op_t); 76 | rtlsdr_rpc_op_t rtlsdr_rpc_msg_get_op(const rtlsdr_rpc_msg_t*); 77 | void rtlsdr_rpc_msg_set_id(rtlsdr_rpc_msg_t*, uint8_t); 78 | uint8_t rtlsdr_rpc_msg_get_id(const rtlsdr_rpc_msg_t*); 79 | void rtlsdr_rpc_msg_set_err(rtlsdr_rpc_msg_t*, int); 80 | int rtlsdr_rpc_msg_get_err(const rtlsdr_rpc_msg_t*); 81 | 82 | int rtlsdr_rpc_msg_push_int32(rtlsdr_rpc_msg_t*, int32_t); 83 | int rtlsdr_rpc_msg_push_uint32(rtlsdr_rpc_msg_t*, uint32_t); 84 | void rtlsdr_rpc_msg_push_uint32_safe(rtlsdr_rpc_msg_t*, uint32_t); 85 | int rtlsdr_rpc_msg_push_str(rtlsdr_rpc_msg_t*, const char*); 86 | int rtlsdr_rpc_msg_push_buf(rtlsdr_rpc_msg_t*, const uint8_t*, size_t); 87 | void rtlsdr_rpc_msg_skip_safe(rtlsdr_rpc_msg_t*, size_t); 88 | int rtlsdr_rpc_msg_pop_int32(rtlsdr_rpc_msg_t*, int32_t*); 89 | int rtlsdr_rpc_msg_pop_uint32(rtlsdr_rpc_msg_t*, uint32_t*); 90 | int rtlsdr_rpc_msg_pop_str(rtlsdr_rpc_msg_t*, const char**); 91 | int rtlsdr_rpc_msg_pop_buf(rtlsdr_rpc_msg_t*, const uint8_t**, size_t*); 92 | 93 | 94 | #endif /* RTLSDR_RPC_MSG_H_INCLUDED */ 95 | -------------------------------------------------------------------------------- /include/tuner_e4k.h: -------------------------------------------------------------------------------- 1 | #ifndef _E4K_TUNER_H 2 | #define _E4K_TUNER_H 3 | 4 | /* 5 | * Elonics E4000 tuner driver 6 | * 7 | * (C) 2011-2012 by Harald Welte 8 | * (C) 2012 by Sylvain Munaut 9 | * (C) 2012 by Hoernchen 10 | * 11 | * All Rights Reserved 12 | * 13 | * This program is free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 2 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU General Public License 24 | * along with this program. If not, see . 25 | */ 26 | 27 | #define E4K_I2C_ADDR 0xc8 28 | #define E4K_CHECK_ADDR 0x02 29 | #define E4K_CHECK_VAL 0x40 30 | 31 | enum e4k_reg { 32 | E4K_REG_MASTER1 = 0x00, 33 | E4K_REG_MASTER2 = 0x01, 34 | E4K_REG_MASTER3 = 0x02, 35 | E4K_REG_MASTER4 = 0x03, 36 | E4K_REG_MASTER5 = 0x04, 37 | E4K_REG_CLK_INP = 0x05, 38 | E4K_REG_REF_CLK = 0x06, 39 | E4K_REG_SYNTH1 = 0x07, 40 | E4K_REG_SYNTH2 = 0x08, 41 | E4K_REG_SYNTH3 = 0x09, 42 | E4K_REG_SYNTH4 = 0x0a, 43 | E4K_REG_SYNTH5 = 0x0b, 44 | E4K_REG_SYNTH6 = 0x0c, 45 | E4K_REG_SYNTH7 = 0x0d, 46 | E4K_REG_SYNTH8 = 0x0e, 47 | E4K_REG_SYNTH9 = 0x0f, 48 | E4K_REG_FILT1 = 0x10, 49 | E4K_REG_FILT2 = 0x11, 50 | E4K_REG_FILT3 = 0x12, 51 | // gap 52 | E4K_REG_GAIN1 = 0x14, 53 | E4K_REG_GAIN2 = 0x15, 54 | E4K_REG_GAIN3 = 0x16, 55 | E4K_REG_GAIN4 = 0x17, 56 | // gap 57 | E4K_REG_AGC1 = 0x1a, 58 | E4K_REG_AGC2 = 0x1b, 59 | E4K_REG_AGC3 = 0x1c, 60 | E4K_REG_AGC4 = 0x1d, 61 | E4K_REG_AGC5 = 0x1e, 62 | E4K_REG_AGC6 = 0x1f, 63 | E4K_REG_AGC7 = 0x20, 64 | E4K_REG_AGC8 = 0x21, 65 | // gap 66 | E4K_REG_AGC11 = 0x24, 67 | E4K_REG_AGC12 = 0x25, 68 | // gap 69 | E4K_REG_DC1 = 0x29, 70 | E4K_REG_DC2 = 0x2a, 71 | E4K_REG_DC3 = 0x2b, 72 | E4K_REG_DC4 = 0x2c, 73 | E4K_REG_DC5 = 0x2d, 74 | E4K_REG_DC6 = 0x2e, 75 | E4K_REG_DC7 = 0x2f, 76 | E4K_REG_DC8 = 0x30, 77 | // gap 78 | E4K_REG_QLUT0 = 0x50, 79 | E4K_REG_QLUT1 = 0x51, 80 | E4K_REG_QLUT2 = 0x52, 81 | E4K_REG_QLUT3 = 0x53, 82 | // gap 83 | E4K_REG_ILUT0 = 0x60, 84 | E4K_REG_ILUT1 = 0x61, 85 | E4K_REG_ILUT2 = 0x62, 86 | E4K_REG_ILUT3 = 0x63, 87 | // gap 88 | E4K_REG_DCTIME1 = 0x70, 89 | E4K_REG_DCTIME2 = 0x71, 90 | E4K_REG_DCTIME3 = 0x72, 91 | E4K_REG_DCTIME4 = 0x73, 92 | E4K_REG_PWM1 = 0x74, 93 | E4K_REG_PWM2 = 0x75, 94 | E4K_REG_PWM3 = 0x76, 95 | E4K_REG_PWM4 = 0x77, 96 | E4K_REG_BIAS = 0x78, 97 | E4K_REG_CLKOUT_PWDN = 0x7a, 98 | E4K_REG_CHFILT_CALIB = 0x7b, 99 | E4K_REG_I2C_REG_ADDR = 0x7d, 100 | // FIXME 101 | }; 102 | 103 | #define E4K_MASTER1_RESET (1 << 0) 104 | #define E4K_MASTER1_NORM_STBY (1 << 1) 105 | #define E4K_MASTER1_POR_DET (1 << 2) 106 | 107 | #define E4K_SYNTH1_PLL_LOCK (1 << 0) 108 | #define E4K_SYNTH1_BAND_SHIF 1 109 | 110 | #define E4K_SYNTH7_3PHASE_EN (1 << 3) 111 | 112 | #define E4K_SYNTH8_VCOCAL_UPD (1 << 2) 113 | 114 | #define E4K_FILT3_DISABLE (1 << 5) 115 | 116 | #define E4K_AGC1_LIN_MODE (1 << 4) 117 | #define E4K_AGC1_LNA_UPDATE (1 << 5) 118 | #define E4K_AGC1_LNA_G_LOW (1 << 6) 119 | #define E4K_AGC1_LNA_G_HIGH (1 << 7) 120 | 121 | #define E4K_AGC6_LNA_CAL_REQ (1 << 4) 122 | 123 | #define E4K_AGC7_MIX_GAIN_AUTO (1 << 0) 124 | #define E4K_AGC7_GAIN_STEP_5dB (1 << 5) 125 | 126 | #define E4K_AGC8_SENS_LIN_AUTO (1 << 0) 127 | 128 | #define E4K_AGC11_LNA_GAIN_ENH (1 << 0) 129 | 130 | #define E4K_DC1_CAL_REQ (1 << 0) 131 | 132 | #define E4K_DC5_I_LUT_EN (1 << 0) 133 | #define E4K_DC5_Q_LUT_EN (1 << 1) 134 | #define E4K_DC5_RANGE_DET_EN (1 << 2) 135 | #define E4K_DC5_RANGE_EN (1 << 3) 136 | #define E4K_DC5_TIMEVAR_EN (1 << 4) 137 | 138 | #define E4K_CLKOUT_DISABLE 0x96 139 | 140 | #define E4K_CHFCALIB_CMD (1 << 0) 141 | 142 | #define E4K_AGC1_MOD_MASK 0xF 143 | 144 | enum e4k_agc_mode { 145 | E4K_AGC_MOD_SERIAL = 0x0, 146 | E4K_AGC_MOD_IF_PWM_LNA_SERIAL = 0x1, 147 | E4K_AGC_MOD_IF_PWM_LNA_AUTONL = 0x2, 148 | E4K_AGC_MOD_IF_PWM_LNA_SUPERV = 0x3, 149 | E4K_AGC_MOD_IF_SERIAL_LNA_PWM = 0x4, 150 | E4K_AGC_MOD_IF_PWM_LNA_PWM = 0x5, 151 | E4K_AGC_MOD_IF_DIG_LNA_SERIAL = 0x6, 152 | E4K_AGC_MOD_IF_DIG_LNA_AUTON = 0x7, 153 | E4K_AGC_MOD_IF_DIG_LNA_SUPERV = 0x8, 154 | E4K_AGC_MOD_IF_SERIAL_LNA_AUTON = 0x9, 155 | E4K_AGC_MOD_IF_SERIAL_LNA_SUPERV = 0xa, 156 | }; 157 | 158 | enum e4k_band { 159 | E4K_BAND_VHF2 = 0, 160 | E4K_BAND_VHF3 = 1, 161 | E4K_BAND_UHF = 2, 162 | E4K_BAND_L = 3, 163 | }; 164 | 165 | enum e4k_mixer_filter_bw { 166 | E4K_F_MIX_BW_27M = 0, 167 | E4K_F_MIX_BW_4M6 = 8, 168 | E4K_F_MIX_BW_4M2 = 9, 169 | E4K_F_MIX_BW_3M8 = 10, 170 | E4K_F_MIX_BW_3M4 = 11, 171 | E4K_F_MIX_BW_3M = 12, 172 | E4K_F_MIX_BW_2M7 = 13, 173 | E4K_F_MIX_BW_2M3 = 14, 174 | E4K_F_MIX_BW_1M9 = 15, 175 | }; 176 | 177 | enum e4k_if_filter { 178 | E4K_IF_FILTER_MIX, 179 | E4K_IF_FILTER_CHAN, 180 | E4K_IF_FILTER_RC 181 | }; 182 | struct e4k_pll_params { 183 | uint32_t fosc; 184 | uint32_t intended_flo; 185 | uint32_t flo; 186 | uint16_t x; 187 | uint8_t z; 188 | uint8_t r; 189 | uint8_t r_idx; 190 | uint8_t threephase; 191 | }; 192 | 193 | struct e4k_state { 194 | void *i2c_dev; 195 | uint8_t i2c_addr; 196 | enum e4k_band band; 197 | struct e4k_pll_params vco; 198 | void *rtl_dev; 199 | }; 200 | 201 | int e4k_init(struct e4k_state *e4k); 202 | int e4k_standby(struct e4k_state *e4k, int enable); 203 | int e4k_if_gain_set(struct e4k_state *e4k, uint8_t stage, int8_t value); 204 | int e4k_mixer_gain_set(struct e4k_state *e4k, int8_t value); 205 | int e4k_commonmode_set(struct e4k_state *e4k, int8_t value); 206 | int e4k_tune_freq(struct e4k_state *e4k, uint32_t freq); 207 | int e4k_tune_params(struct e4k_state *e4k, struct e4k_pll_params *p); 208 | uint32_t e4k_compute_pll_params(struct e4k_pll_params *oscp, uint32_t fosc, uint32_t intended_flo); 209 | int e4k_if_filter_bw_get(struct e4k_state *e4k, enum e4k_if_filter filter); 210 | int e4k_if_filter_bw_set(struct e4k_state *e4k, enum e4k_if_filter filter, 211 | uint32_t bandwidth); 212 | int e4k_if_filter_chan_enable(struct e4k_state *e4k, int on); 213 | int e4k_rf_filter_set(struct e4k_state *e4k); 214 | 215 | int e4k_manual_dc_offset(struct e4k_state *e4k, int8_t iofs, int8_t irange, int8_t qofs, int8_t qrange); 216 | int e4k_dc_offset_calibrate(struct e4k_state *e4k); 217 | int e4k_dc_offset_gen_table(struct e4k_state *e4k); 218 | 219 | int e4k_set_lna_gain(struct e4k_state *e4k, int32_t gain); 220 | int e4k_enable_manual_gain(struct e4k_state *e4k, uint8_t manual); 221 | int e4k_set_enh_gain(struct e4k_state *e4k, int32_t gain); 222 | #endif /* _E4K_TUNER_H */ 223 | -------------------------------------------------------------------------------- /include/tuner_fc0012.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Fitipower FC0012 tuner driver 3 | * 4 | * Copyright (C) 2012 Hans-Frieder Vogt 5 | * 6 | * modified for use in librtlsdr 7 | * Copyright (C) 2012 Steve Markgraf 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 2 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program; if not, write to the Free Software 21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 | * 23 | */ 24 | 25 | #ifndef _FC0012_H_ 26 | #define _FC0012_H_ 27 | 28 | #define FC0012_I2C_ADDR 0xc6 29 | #define FC0012_CHECK_ADDR 0x00 30 | #define FC0012_CHECK_VAL 0xa1 31 | 32 | int fc0012_init(void *dev); 33 | int fc0012_set_params(void *dev, uint32_t freq, uint32_t bandwidth); 34 | int fc0012_set_gain(void *dev, int gain); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /include/tuner_fc0013.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Fitipower FC0013 tuner driver 3 | * 4 | * Copyright (C) 2012 Hans-Frieder Vogt 5 | * 6 | * modified for use in librtlsdr 7 | * Copyright (C) 2012 Steve Markgraf 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 2 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program; if not, write to the Free Software 21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 | * 23 | */ 24 | 25 | #ifndef _FC0013_H_ 26 | #define _FC0013_H_ 27 | 28 | #define FC0013_I2C_ADDR 0xc6 29 | #define FC0013_CHECK_ADDR 0x00 30 | #define FC0013_CHECK_VAL 0xa3 31 | 32 | int fc0013_init(void *dev); 33 | int fc0013_set_params(void *dev, uint32_t freq, uint32_t bandwidth); 34 | int fc0013_set_gain_mode(void *dev, int manual); 35 | int fc0013_set_lna_gain(void *dev, int gain); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /include/tuner_fc2580.h: -------------------------------------------------------------------------------- 1 | #ifndef __TUNER_FC2580_H 2 | #define __TUNER_FC2580_H 3 | 4 | #define BORDER_FREQ 2600000 /* 2.6GHz : The border frequency which determines whether Low VCO or High VCO is used */ 5 | #define USE_EXT_CLK 0 /* 0 : Use internal XTAL Oscillator / 1 : Use External Clock input */ 6 | #define OFS_RSSI 57 7 | 8 | #define FC2580_I2C_ADDR 0xac 9 | #define FC2580_CHECK_ADDR 0x01 10 | #define FC2580_CHECK_VAL 0x56 11 | 12 | typedef enum { 13 | FC2580_UHF_BAND, 14 | FC2580_L_BAND, 15 | FC2580_VHF_BAND, 16 | FC2580_NO_BAND 17 | } fc2580_band_type; 18 | 19 | typedef enum { 20 | FC2580_FCI_FAIL, 21 | FC2580_FCI_SUCCESS 22 | } fc2580_fci_result_type; 23 | 24 | enum FUNCTION_STATUS 25 | { 26 | FUNCTION_SUCCESS, 27 | FUNCTION_ERROR, 28 | }; 29 | 30 | extern void fc2580_wait_msec(void *pTuner, int a); 31 | 32 | fc2580_fci_result_type fc2580_i2c_write(void *pTuner, unsigned char reg, unsigned char val); 33 | fc2580_fci_result_type fc2580_i2c_read(void *pTuner, unsigned char reg, unsigned char *read_data); 34 | 35 | /*============================================================================== 36 | fc2580 initial setting 37 | 38 | This function is a generic function which gets called to initialize 39 | 40 | fc2580 in DVB-H mode or L-Band TDMB mode 41 | 42 | 43 | 44 | ifagc_mode 45 | type : integer 46 | 1 : Internal AGC 47 | 2 : Voltage Control Mode 48 | 49 | ==============================================================================*/ 50 | fc2580_fci_result_type fc2580_set_init(void *pTuner, int ifagc_mode, unsigned int freq_xtal ); 51 | 52 | /*============================================================================== 53 | fc2580 frequency setting 54 | 55 | This function is a generic function which gets called to change LO Frequency 56 | 57 | of fc2580 in DVB-H mode or L-Band TDMB mode 58 | 59 | 60 | 61 | f_lo 62 | Value of target LO Frequency in 'kHz' unit 63 | ex) 2.6GHz = 2600000 64 | 65 | ==============================================================================*/ 66 | fc2580_fci_result_type fc2580_set_freq(void *pTuner, unsigned int f_lo, unsigned int freq_xtal ); 67 | 68 | 69 | /*============================================================================== 70 | fc2580 filter BW setting 71 | 72 | This function is a generic function which gets called to change Bandwidth 73 | 74 | frequency of fc2580's channel selection filter 75 | 76 | 77 | 78 | filter_bw 79 | 1 : 1.53MHz(TDMB) 80 | 6 : 6MHz 81 | 7 : 7MHz 82 | 8 : 7.8MHz 83 | 84 | 85 | ==============================================================================*/ 86 | fc2580_fci_result_type fc2580_set_filter( void *pTuner, unsigned char filter_bw, unsigned int freq_xtal ); 87 | 88 | // The following context is FC2580 tuner API source code 89 | // Definitions 90 | 91 | // AGC mode 92 | enum FC2580_AGC_MODE 93 | { 94 | FC2580_AGC_INTERNAL = 1, 95 | FC2580_AGC_EXTERNAL = 2, 96 | }; 97 | 98 | 99 | // Bandwidth mode 100 | enum FC2580_BANDWIDTH_MODE 101 | { 102 | FC2580_BANDWIDTH_1530000HZ = 1, 103 | FC2580_BANDWIDTH_6000000HZ = 6, 104 | FC2580_BANDWIDTH_7000000HZ = 7, 105 | FC2580_BANDWIDTH_8000000HZ = 8, 106 | }; 107 | 108 | // Manipulaing functions 109 | int 110 | fc2580_Initialize( 111 | void *pTuner 112 | ); 113 | 114 | int 115 | fc2580_SetRfFreqHz( 116 | void *pTuner, 117 | unsigned long RfFreqHz 118 | ); 119 | 120 | // Extra manipulaing functions 121 | int 122 | fc2580_SetBandwidthMode( 123 | void *pTuner, 124 | int BandwidthMode 125 | ); 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /include/tuner_r82xx.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Rafael Micro R820T/R828D driver 3 | * 4 | * Copyright (C) 2013 Mauro Carvalho Chehab 5 | * Copyright (C) 2013 Steve Markgraf 6 | * 7 | * This driver is a heavily modified version of the driver found in the 8 | * Linux kernel: 9 | * http://git.linuxtv.org/linux-2.6.git/history/HEAD:/drivers/media/tuners/r820t.c 10 | * 11 | * This program is free software: you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation, either version 2 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License 22 | * along with this program. If not, see . 23 | */ 24 | 25 | #ifndef R82XX_H 26 | #define R82XX_H 27 | 28 | #define R820T_I2C_ADDR 0x34 29 | #define R828D_I2C_ADDR 0x74 30 | #define R828D_XTAL_FREQ 16000000 31 | 32 | #define R82XX_CHECK_ADDR 0x00 33 | #define R82XX_CHECK_VAL 0x69 34 | 35 | #define R82XX_IF_FREQ 3570000 36 | 37 | #define REG_SHADOW_START 5 38 | #define NUM_REGS 30 39 | #define NUM_IMR 5 40 | #define IMR_TRIAL 9 41 | 42 | #define VER_NUM 49 43 | 44 | enum r82xx_chip { 45 | CHIP_R820T, 46 | CHIP_R620D, 47 | CHIP_R828D, 48 | CHIP_R828, 49 | CHIP_R828S, 50 | CHIP_R820C, 51 | }; 52 | 53 | enum r82xx_tuner_type { 54 | TUNER_RADIO = 1, 55 | TUNER_ANALOG_TV, 56 | TUNER_DIGITAL_TV 57 | }; 58 | 59 | enum r82xx_xtal_cap_value { 60 | XTAL_LOW_CAP_30P = 0, 61 | XTAL_LOW_CAP_20P, 62 | XTAL_LOW_CAP_10P, 63 | XTAL_LOW_CAP_0P, 64 | XTAL_HIGH_CAP_0P 65 | }; 66 | 67 | struct r82xx_config { 68 | uint8_t i2c_addr; 69 | uint32_t xtal; 70 | enum r82xx_chip rafael_chip; 71 | unsigned int max_i2c_msg_len; 72 | int use_predetect; 73 | }; 74 | 75 | struct r82xx_priv { 76 | struct r82xx_config *cfg; 77 | 78 | uint8_t regs[NUM_REGS]; 79 | uint8_t buf[NUM_REGS + 1]; 80 | enum r82xx_xtal_cap_value xtal_cap_sel; 81 | uint16_t pll; /* kHz */ 82 | uint32_t int_freq; 83 | uint8_t fil_cal_code; 84 | uint8_t input; 85 | int has_lock; 86 | int init_done; 87 | 88 | /* Store current mode */ 89 | uint32_t delsys; 90 | enum r82xx_tuner_type type; 91 | uint32_t bw; /* in MHz */ 92 | void *rtl_dev; 93 | }; 94 | 95 | struct r82xx_freq_range { 96 | uint32_t freq; 97 | uint8_t open_d; 98 | uint8_t rf_mux_ploy; 99 | uint8_t tf_c; 100 | uint8_t xtal_cap20p; 101 | uint8_t xtal_cap10p; 102 | uint8_t xtal_cap0p; 103 | }; 104 | 105 | enum r82xx_delivery_system { 106 | SYS_UNDEFINED, 107 | SYS_DVBT, 108 | SYS_DVBT2, 109 | SYS_ISDBT, 110 | }; 111 | 112 | int r82xx_standby(struct r82xx_priv *priv); 113 | int r82xx_init(struct r82xx_priv *priv); 114 | int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq); 115 | //int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain); 116 | int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain, int extended_mode, int lna_gain, int mixer_gain, int vga_gain); 117 | 118 | int r82xx_set_bandwidth(struct r82xx_priv *priv, int bandwidth, uint32_t rate, uint32_t * applied_bw, int apply); 119 | int r82xx_read_cache_reg(struct r82xx_priv *priv, int reg); 120 | int r82xx_write_reg_mask(struct r82xx_priv *priv, uint8_t reg, uint8_t val,uint8_t bit_mask); 121 | 122 | #endif 123 | -------------------------------------------------------------------------------- /python/r820tweak.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import wx 4 | import socket 5 | import sys 6 | import os 7 | 8 | 9 | def get_lna_gain(sock): 10 | message = "g 5\n" 11 | sock.sendall(message) 12 | data = sock.recv(32) 13 | message = "s 6 "+str(1<<6)+" "+str(1<<6)+"\n" 14 | sock.sendall(message) 15 | data2 = sock.recv(32) 16 | return int(data[2:])&15 17 | 18 | def get_mix_gain(sock): 19 | message = "g 7\n" 20 | sock.sendall(message) 21 | data = sock.recv(32) 22 | return int(data[2:])&15 23 | 24 | def get_vga_gain(sock): 25 | message = "g 12\n" 26 | sock.sendall(message) 27 | data = sock.recv(32) 28 | return int(data[2:])&15 29 | 30 | def get_hpf(sock): 31 | message = "g 27\n" 32 | sock.sendall(message) 33 | data = sock.recv(32) 34 | return 15-(int(data[2:])&15) 35 | 36 | def set_hpf(sock,width): 37 | message = "s 27 "+str(15-width)+" 15\n" 38 | sock.sendall(message) 39 | data = sock.recv(32) 40 | 41 | def get_lpnf(sock): 42 | message = "g 27\n" 43 | sock.sendall(message) 44 | data = sock.recv(32) 45 | return 15-(int(data[2:])&(15>>4)) 46 | 47 | def set_lpnf(sock,width): 48 | message = "s 27 "+str((15-width)<<4)+" "+str(15<<4)+"\n" 49 | sock.sendall(message) 50 | data = sock.recv(32) 51 | 52 | 53 | def get_lpf(sock): 54 | message = "g 11\n" 55 | sock.sendall(message) 56 | data = sock.recv(32) 57 | return (int(data[2:])&15) 58 | 59 | def set_lpf(sock,width): 60 | message = "s 11 "+str(width)+" 15\n" 61 | sock.sendall(message) 62 | data = sock.recv(32) 63 | 64 | def get_filt(sock): 65 | message = "g 10\n" 66 | sock.sendall(message) 67 | data = sock.recv(32) 68 | return 15-(int(data[2:])&15) 69 | 70 | def set_filt(sock,width): 71 | message = "s 10 "+str(15-width)+" 15\n" 72 | sock.sendall(message) 73 | data = sock.recv(32) 74 | 75 | def set_lna_gain(sock,gain): 76 | message = "s 5 "+str(gain)+" 15\n" 77 | sock.sendall(message) 78 | data = sock.recv(32) 79 | 80 | 81 | def set_mix_gain(sock, gain): 82 | message = "s 7 "+str(gain)+" 15\n" 83 | sock.sendall(message) 84 | data = sock.recv(32) 85 | 86 | 87 | def set_vga_gain(sock, gain): 88 | message = "s 12 "+str(gain)+" 15\n" 89 | sock.sendall(message) 90 | data = sock.recv(32) 91 | 92 | 93 | 94 | 95 | 96 | class MyPanel(wx.Panel): 97 | 98 | def scan_device(self): 99 | self.lna_gain = get_lna_gain(self.sock) 100 | self.mix_gain = get_mix_gain(self.sock) 101 | self.vga_gain = get_vga_gain(self.sock) 102 | self.lpf = get_lpf(self.sock) 103 | self.lpnf = get_lpnf(self.sock) 104 | self.hpf = get_hpf(self.sock) 105 | self.filt = get_filt(self.sock) 106 | 107 | 108 | self.slider_gain_lna.SetValue(self.lna_gain) 109 | self.slider_gain_mix.SetValue(self.mix_gain) 110 | self.slider_gain_vga.SetValue(self.vga_gain) 111 | self.slider_lpf.SetValue(self.lpf) 112 | self.slider_lpnf.SetValue(self.lpnf) 113 | self.slider_hpf.SetValue(self.hpf) 114 | self.slider_filt.SetValue(self.filt) 115 | 116 | 117 | def connect(self, dev): 118 | self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 119 | server_address = '/var/tmp/rtlsdr' + str(dev) 120 | try: 121 | self.sock.connect(server_address) 122 | except socket.error: 123 | print >>sys.stderr 124 | #sys.exit(1) 125 | # TODO: proper warning 126 | 127 | 128 | def scan_devices(self): 129 | self.device_list = [] 130 | self.device_nodes = [] 131 | for a in range(16): 132 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 133 | server_address = '/var/tmp/rtlsdr' + str(a) 134 | try: 135 | sock.connect(server_address) 136 | except socket.error: 137 | pass 138 | else: 139 | self.device_list.append("R820T2 device: #" + str(a)) 140 | self.device_nodes.append(a) 141 | 142 | 143 | 144 | def __init__(self, parent, id): 145 | self.scan_devices() 146 | 147 | wx.Panel.__init__(self, parent, id) 148 | self.SetBackgroundColour("white") 149 | 150 | wx.StaticText(self, -1, 'LNA Gain', (10,45)) 151 | self.slider_gain_lna = wx.Slider(self, -1, 0, 0, 15, (10, 50), (272, 40), wx.SL_HORIZONTAL | wx.SL_AUTOTICKS, name="LNA Gain") 152 | wx.StaticText(self, -1, 'Mixer Gain', (10,80)) 153 | self.slider_gain_mix = wx.Slider(self, -1, 0, 0, 15, (10, 85), (272, 40), wx.SL_HORIZONTAL | wx.SL_AUTOTICKS, name="Mixer Gain") 154 | wx.StaticText(self, -1, 'VGA Gain', (10,115)) 155 | self.slider_gain_vga = wx.Slider(self, -1, 0, 0, 15, (10, 120), (272, 40), wx.SL_HORIZONTAL | wx.SL_AUTOTICKS, name="VGA Gain") 156 | 157 | wx.StaticText(self, -1, 'LPF Cutoff', (10,170)) 158 | self.slider_lpf = wx.Slider(self, -1, 0, 0, 15, (10, 175), (272, 40), wx.SL_HORIZONTAL | wx.SL_AUTOTICKS, name="LPF Cutoff") 159 | wx.StaticText(self, -1, 'LPNF Cutoff', (10,205)) 160 | self.slider_lpnf = wx.Slider(self, -1, 0, 0, 15, (10, 210), (272, 40), wx.SL_HORIZONTAL | wx.SL_AUTOTICKS, name="LNPF Cutoff") 161 | wx.StaticText(self, -1, 'HPF Cutoff', (10,240)) 162 | self.slider_hpf = wx.Slider(self, -1, 0, 0, 15, (10, 245), (272, 40), wx.SL_HORIZONTAL | wx.SL_AUTOTICKS, name="HPF Cutoff") 163 | wx.StaticText(self, -1, 'Filter BW', (10,275)) 164 | self.slider_filt = wx.Slider(self, -1, 0, 0, 15, (10, 280), (272, 40), wx.SL_HORIZONTAL | wx.SL_AUTOTICKS, name="Filter BW") 165 | 166 | 167 | self.cb = wx.ComboBox(self, 168 | id=0, 169 | value=self.device_list[0], 170 | pos=(10,10), 171 | size=(180,25), 172 | choices=self.device_list, 173 | style=wx.CB_READONLY|wx.CB_DROPDOWN) 174 | 175 | self.button = wx.Button(self, id=wx.ID_ANY, pos=(210,10), size=(70,25), label="Rescan") 176 | self.button.Bind(wx.EVT_BUTTON, self.onButton) 177 | self.cb.Bind(wx.EVT_COMBOBOX, self.onCBChange) 178 | 179 | 180 | if len(self.device_list): 181 | self.connect(self.device_nodes[0]) 182 | self.scan_device() 183 | 184 | self.Bind(wx.EVT_SLIDER, self.sliderUpdate) 185 | 186 | 187 | def onCBChange(self, event): 188 | item = self.cb.GetValue() 189 | num = self.device_list.index(item) 190 | self.connect(num) 191 | self.scan_device() 192 | 193 | 194 | def onButton(self, event): 195 | self.scan_devices() 196 | if len(self.device_list): 197 | self.connect(self.device_nodes[0]) 198 | self.scan_device() 199 | self.cb = wx.ComboBox(self, 200 | id=0, 201 | value=self.device_list[0], 202 | pos=(10,10), 203 | size=(180,25), 204 | choices=self.device_list, 205 | style=wx.CB_READONLY|wx.CB_DROPDOWN) 206 | 207 | 208 | 209 | def sliderUpdate(self, event): 210 | try: 211 | if self.lna_gain != self.slider_gain_lna.GetValue(): 212 | self.lna_gain = self.slider_gain_lna.GetValue() 213 | set_lna_gain(self.sock,self.lna_gain) 214 | 215 | if self.mix_gain != self.slider_gain_mix.GetValue(): 216 | self.mix_gain = self.slider_gain_mix.GetValue() 217 | set_mix_gain(self.sock,self.mix_gain) 218 | 219 | if self.vga_gain != self.slider_gain_vga.GetValue(): 220 | self.vga_gain = self.slider_gain_vga.GetValue() 221 | set_vga_gain(self.sock,self.vga_gain) 222 | 223 | if self.lpf != self.slider_lpf.GetValue(): 224 | self.lpf = self.slider_lpf.GetValue() 225 | set_lpf(self.sock,self.lpf) 226 | 227 | if self.lpnf != self.slider_lpnf.GetValue(): 228 | self.lpnf = self.slider_lpnf.GetValue() 229 | set_lpnf(self.sock,self.lpnf) 230 | 231 | if self.hpf != self.slider_hpf.GetValue(): 232 | self.hpf = self.slider_hpf.GetValue() 233 | set_hpf(self.sock,self.hpf) 234 | 235 | if self.filt != self.slider_filt.GetValue(): 236 | self.filt = self.slider_filt.GetValue() 237 | set_filt(self.sock,self.filt) 238 | except Exception: 239 | self.connect(0) 240 | 241 | 242 | def usage(): 243 | print sys.argv[1],"[program_to_run]" 244 | print "\n\n" 245 | print "When used without argument, the r820tweak control panel will launch" 246 | print "When [program_to_run] is provided, it starts the SDR program with the modified RTLSDR driver" 247 | 248 | 249 | def main(): 250 | if len(sys.argv)>1: 251 | if sys.argv[1]!="-h" or sys.argv[1]!="-?": 252 | os.system("LD_PRELOAD=/usr/local/share/r820tweak/librtlsdr.so " + sys.argv[1]) 253 | return 254 | else: 255 | usage() 256 | return 257 | 258 | app = wx.PySimpleApp() 259 | frame = wx.Frame(None, -1, "r820tweak", size = (290, 340)) 260 | MyPanel(frame,-1) 261 | frame.Show(True) 262 | app.MainLoop() 263 | 264 | 265 | if __name__ == '__main__': 266 | main() -------------------------------------------------------------------------------- /screenshot/ss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gat3way/r820tweak/c0a53a516a3ec8a2aee7118d5effe40e026d7430/screenshot/ss.png -------------------------------------------------------------------------------- /src/convenience/convenience.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 by Kyle Keen 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /* a collection of user friendly tools 19 | * todo: use strtol for more flexible int parsing 20 | * */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #ifndef _WIN32 28 | #include 29 | #else 30 | #include 31 | #include 32 | #include 33 | #include 34 | #define _USE_MATH_DEFINES 35 | #endif 36 | 37 | #include 38 | 39 | #include "rtl-sdr.h" 40 | 41 | double atofs(char *s) 42 | /* standard suffixes */ 43 | { 44 | char last; 45 | int len; 46 | double suff = 1.0; 47 | len = strlen(s); 48 | /* allow formatting spaces from .csv command file */ 49 | while ( len > 1 && isspace(s[len-1]) ) --len; 50 | last = s[len-1]; 51 | s[len-1] = '\0'; 52 | switch (last) { 53 | case 'g': 54 | case 'G': 55 | suff *= 1e3; 56 | case 'm': 57 | case 'M': 58 | suff *= 1e3; 59 | case 'k': 60 | case 'K': 61 | suff *= 1e3; 62 | suff *= atof(s); 63 | s[len-1] = last; 64 | return suff; 65 | } 66 | s[len-1] = last; 67 | return atof(s); 68 | } 69 | 70 | double atoft(char *s) 71 | /* time suffixes, returns seconds */ 72 | { 73 | char last; 74 | int len; 75 | double suff = 1.0; 76 | len = strlen(s); 77 | last = s[len-1]; 78 | s[len-1] = '\0'; 79 | switch (last) { 80 | case 'h': 81 | case 'H': 82 | suff *= 60; 83 | case 'm': 84 | case 'M': 85 | suff *= 60; 86 | case 's': 87 | case 'S': 88 | suff *= atof(s); 89 | s[len-1] = last; 90 | return suff; 91 | } 92 | s[len-1] = last; 93 | return atof(s); 94 | } 95 | 96 | double atofp(char *s) 97 | /* percent suffixes */ 98 | { 99 | char last; 100 | int len; 101 | double suff = 1.0; 102 | len = strlen(s); 103 | last = s[len-1]; 104 | s[len-1] = '\0'; 105 | switch (last) { 106 | case '%': 107 | suff *= 0.01; 108 | suff *= atof(s); 109 | s[len-1] = last; 110 | return suff; 111 | } 112 | s[len-1] = last; 113 | return atof(s); 114 | } 115 | 116 | int nearest_gain(rtlsdr_dev_t *dev, int target_gain) 117 | { 118 | int i, r, err1, err2, count, nearest; 119 | int* gains; 120 | r = rtlsdr_set_tuner_gain_mode(dev, 1); 121 | if (r < 0) { 122 | fprintf(stderr, "WARNING: Failed to enable manual gain.\n"); 123 | return r; 124 | } 125 | count = rtlsdr_get_tuner_gains(dev, NULL); 126 | if (count <= 0) { 127 | return 0; 128 | } 129 | gains = malloc(sizeof(int) * count); 130 | count = rtlsdr_get_tuner_gains(dev, gains); 131 | nearest = gains[0]; 132 | for (i=0; i 0) { 176 | if (applied_bw) 177 | fprintf(stderr, "Bandwidth parameter %u Hz resulted in %u Hz.\n", bandwidth, applied_bw); 178 | else 179 | fprintf(stderr, "Set bandwidth parameter %u Hz.\n", bandwidth); 180 | } else { 181 | fprintf(stderr, "Bandwidth set to automatic resulted in %u Hz.\n", applied_bw); 182 | } 183 | return r; 184 | } 185 | 186 | int verbose_direct_sampling(rtlsdr_dev_t *dev, int on) 187 | { 188 | int r; 189 | r = rtlsdr_set_direct_sampling(dev, on); 190 | if (r != 0) { 191 | fprintf(stderr, "WARNING: Failed to set direct sampling mode.\n"); 192 | return r; 193 | } 194 | if (on == 0) { 195 | fprintf(stderr, "Direct sampling mode disabled.\n");} 196 | if (on == 1) { 197 | fprintf(stderr, "Enabled direct sampling mode, input 1/I.\n");} 198 | if (on == 2) { 199 | fprintf(stderr, "Enabled direct sampling mode, input 2/Q.\n");} 200 | return r; 201 | } 202 | 203 | int verbose_offset_tuning(rtlsdr_dev_t *dev) 204 | { 205 | int r; 206 | r = rtlsdr_set_offset_tuning(dev, 1); 207 | if (r != 0) { 208 | if ( r == -2 ) 209 | fprintf(stderr, "WARNING: Failed to set offset tuning: tuner doesn't support offset tuning!\n"); 210 | else if ( r == -3 ) 211 | fprintf(stderr, "WARNING: Failed to set offset tuning: direct sampling not combinable with offset tuning!\n"); 212 | else 213 | fprintf(stderr, "WARNING: Failed to set offset tuning.\n"); 214 | } else { 215 | fprintf(stderr, "Offset tuning mode enabled.\n"); 216 | } 217 | return r; 218 | } 219 | 220 | int verbose_auto_gain(rtlsdr_dev_t *dev) 221 | { 222 | int r; 223 | r = rtlsdr_set_tuner_gain_mode(dev, 0); 224 | if (r != 0) { 225 | fprintf(stderr, "WARNING: Failed to set tuner gain.\n"); 226 | } else { 227 | fprintf(stderr, "Tuner gain set to automatic.\n"); 228 | } 229 | return r; 230 | } 231 | 232 | int verbose_gain_set(rtlsdr_dev_t *dev, int gain) 233 | { 234 | int r; 235 | r = rtlsdr_set_tuner_gain_mode(dev, 1); 236 | if (r < 0) { 237 | fprintf(stderr, "WARNING: Failed to enable manual gain.\n"); 238 | return r; 239 | } 240 | r = rtlsdr_set_tuner_gain(dev, gain); 241 | if (r != 0) { 242 | fprintf(stderr, "WARNING: Failed to set tuner gain.\n"); 243 | } else { 244 | fprintf(stderr, "Tuner gain set to %0.2f dB.\n", gain/10.0); 245 | } 246 | return r; 247 | } 248 | 249 | int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error) 250 | { 251 | int r; 252 | if (ppm_error == 0) { 253 | return 0;} 254 | r = rtlsdr_set_freq_correction(dev, ppm_error); 255 | if (r < 0) { 256 | fprintf(stderr, "WARNING: Failed to set ppm error.\n"); 257 | } else { 258 | fprintf(stderr, "Tuner error set to %i ppm.\n", ppm_error); 259 | } 260 | return r; 261 | } 262 | 263 | int verbose_reset_buffer(rtlsdr_dev_t *dev) 264 | { 265 | int r; 266 | r = rtlsdr_reset_buffer(dev); 267 | if (r < 0) { 268 | fprintf(stderr, "WARNING: Failed to reset buffers.\n");} 269 | return r; 270 | } 271 | 272 | int verbose_device_search(char *s) 273 | { 274 | int i, device_count, device, offset; 275 | char *s2; 276 | char vendor[256], product[256], serial[256]; 277 | device_count = rtlsdr_get_device_count(); 278 | if (!device_count) { 279 | fprintf(stderr, "No supported devices found.\n"); 280 | return -1; 281 | } 282 | fprintf(stderr, "Found %d device(s):\n", device_count); 283 | for (i = 0; i < device_count; i++) { 284 | rtlsdr_get_device_usb_strings(i, vendor, product, serial); 285 | fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial); 286 | } 287 | fprintf(stderr, "\n"); 288 | /* does string look like raw id number */ 289 | device = (int)strtol(s, &s2, 0); 290 | if (s2[0] == '\0' && device >= 0 && device < device_count) { 291 | fprintf(stderr, "Using device %d: %s\n", 292 | device, rtlsdr_get_device_name((uint32_t)device)); 293 | return device; 294 | } 295 | /* does string exact match a serial */ 296 | for (i = 0; i < device_count; i++) { 297 | rtlsdr_get_device_usb_strings(i, vendor, product, serial); 298 | if (strcmp(s, serial) != 0) { 299 | continue;} 300 | device = i; 301 | fprintf(stderr, "Using device %d: %s\n", 302 | device, rtlsdr_get_device_name((uint32_t)device)); 303 | return device; 304 | } 305 | /* does string prefix match a serial */ 306 | for (i = 0; i < device_count; i++) { 307 | rtlsdr_get_device_usb_strings(i, vendor, product, serial); 308 | if (strncmp(s, serial, strlen(s)) != 0) { 309 | continue;} 310 | device = i; 311 | fprintf(stderr, "Using device %d: %s\n", 312 | device, rtlsdr_get_device_name((uint32_t)device)); 313 | return device; 314 | } 315 | /* does string suffix match a serial */ 316 | for (i = 0; i < device_count; i++) { 317 | rtlsdr_get_device_usb_strings(i, vendor, product, serial); 318 | offset = strlen(serial) - strlen(s); 319 | if (offset < 0) { 320 | continue;} 321 | if (strncmp(s, serial+offset, strlen(s)) != 0) { 322 | continue;} 323 | device = i; 324 | fprintf(stderr, "Using device %d: %s\n", 325 | device, rtlsdr_get_device_name((uint32_t)device)); 326 | return device; 327 | } 328 | fprintf(stderr, "No matching devices found.\n"); 329 | return -1; 330 | } 331 | 332 | #ifndef _WIN32 333 | 334 | void executeInBackground( char * file, char * args, char * searchStr[], char * replaceStr[] ) 335 | { 336 | pid_t pid; 337 | char * argv[256] = { NULL }; 338 | int k, argc = 0; 339 | argv[argc++] = file; 340 | if (args) { 341 | argv[argc] = strtok(args, " "); 342 | while (argc < 256 && argv[argc]) { 343 | argv[++argc] = strtok(NULL, " "); 344 | for (k=0; argv[argc] && searchStr && replaceStr && searchStr[k] && replaceStr[k]; k++) { 345 | if (!strcmp(argv[argc], searchStr[k])) { 346 | argv[argc] = replaceStr[k]; 347 | break; 348 | } 349 | } 350 | } 351 | } 352 | 353 | pid = fork(); 354 | switch (pid) 355 | { 356 | case -1: 357 | /* Fork() has failed */ 358 | fprintf(stderr, "error: fork for '%s' failed!\n", file); 359 | break; 360 | case 0: 361 | execvp(file, argv); 362 | fprintf(stderr, "error: execv of '%s' from within fork failed!\n", file); 363 | exit(10); 364 | break; 365 | default: 366 | /* This is processed by the parent */ 367 | break; 368 | } 369 | } 370 | 371 | #else 372 | 373 | void executeInBackground( char * file, char * args, char * searchStr[], char * replaceStr[] ) 374 | { 375 | char * argv[256] = { NULL }; 376 | int k, argc = 0; 377 | argv[argc++] = file; 378 | if (args) { 379 | argv[argc] = strtok(args, " \t"); 380 | while (argc < 256 && argv[argc]) { 381 | argv[++argc] = strtok(NULL, " \t"); 382 | for (k=0; argv[argc] && searchStr && replaceStr && searchStr[k] && replaceStr[k]; k++) { 383 | if (!strcmp(argv[argc], searchStr[k])) { 384 | argv[argc] = replaceStr[k]; 385 | break; 386 | } 387 | } 388 | } 389 | } 390 | 391 | spawnvp(P_NOWAIT, file, argv); 392 | } 393 | 394 | #endif 395 | 396 | // vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab 397 | -------------------------------------------------------------------------------- /src/convenience/convenience.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 by Kyle Keen 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | #ifndef __CONVENIENCE_H 18 | #define __CONVENIENCE_H 19 | 20 | 21 | /* a collection of user friendly tools */ 22 | 23 | /*! 24 | * Convert standard suffixes (k, M, G) to double 25 | * 26 | * \param s a string to be parsed 27 | * \return double 28 | */ 29 | 30 | double atofs(char *s); 31 | 32 | /*! 33 | * Convert time suffixes (s, m, h) to double 34 | * 35 | * \param s a string to be parsed 36 | * \return seconds as double 37 | */ 38 | 39 | double atoft(char *s); 40 | 41 | /*! 42 | * Convert percent suffixe (%) to double 43 | * 44 | * \param s a string to be parsed 45 | * \return double 46 | */ 47 | 48 | double atofp(char *s); 49 | 50 | /*! 51 | * Find nearest supported gain 52 | * 53 | * \param dev the device handle given by rtlsdr_open() 54 | * \param target_gain in tenths of a dB 55 | * \return 0 on success 56 | */ 57 | 58 | int nearest_gain(rtlsdr_dev_t *dev, int target_gain); 59 | 60 | /*! 61 | * Set device frequency and report status on stderr 62 | * 63 | * \param dev the device handle given by rtlsdr_open() 64 | * \param frequency in Hz 65 | * \return 0 on success 66 | */ 67 | 68 | int verbose_set_frequency(rtlsdr_dev_t *dev, uint32_t frequency); 69 | 70 | /*! 71 | * Set device sample rate and report status on stderr 72 | * 73 | * \param dev the device handle given by rtlsdr_open() 74 | * \param samp_rate in samples/second 75 | * \return 0 on success 76 | */ 77 | 78 | int verbose_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate); 79 | 80 | /*! 81 | * Set device bandwidth and report status on stderr 82 | * 83 | * \param dev the device handle given by rtlsdr_open() 84 | * \param frequency in Hz 85 | * \return 0 on success 86 | */ 87 | 88 | int verbose_set_bandwidth(rtlsdr_dev_t *dev, uint32_t bandwidth); 89 | 90 | 91 | /*! 92 | * Enable or disable the direct sampling mode and report status on stderr 93 | * 94 | * \param dev the device handle given by rtlsdr_open() 95 | * \param on 0 means disabled, 1 I-ADC input enabled, 2 Q-ADC input enabled 96 | * \return 0 on success 97 | */ 98 | 99 | int verbose_direct_sampling(rtlsdr_dev_t *dev, int on); 100 | 101 | /*! 102 | * Enable offset tuning and report status on stderr 103 | * 104 | * \param dev the device handle given by rtlsdr_open() 105 | * \return 0 on success 106 | */ 107 | 108 | int verbose_offset_tuning(rtlsdr_dev_t *dev); 109 | 110 | /*! 111 | * Enable auto gain and report status on stderr 112 | * 113 | * \param dev the device handle given by rtlsdr_open() 114 | * \return 0 on success 115 | */ 116 | 117 | int verbose_auto_gain(rtlsdr_dev_t *dev); 118 | 119 | /*! 120 | * Set tuner gain and report status on stderr 121 | * 122 | * \param dev the device handle given by rtlsdr_open() 123 | * \param gain in tenths of a dB 124 | * \return 0 on success 125 | */ 126 | 127 | int verbose_gain_set(rtlsdr_dev_t *dev, int gain); 128 | 129 | /*! 130 | * Set the frequency correction value for the device and report status on stderr. 131 | * 132 | * \param dev the device handle given by rtlsdr_open() 133 | * \param ppm_error correction value in parts per million (ppm) 134 | * \return 0 on success 135 | */ 136 | 137 | int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error); 138 | 139 | /*! 140 | * Reset buffer 141 | * 142 | * \param dev the device handle given by rtlsdr_open() 143 | * \return 0 on success 144 | */ 145 | 146 | int verbose_reset_buffer(rtlsdr_dev_t *dev); 147 | 148 | /*! 149 | * Find the closest matching device. 150 | * 151 | * \param s a string to be parsed 152 | * \return dev_index int, -1 on error 153 | */ 154 | 155 | int verbose_device_search(char *s); 156 | 157 | 158 | void executeInBackground( char * file, char * args, char * searchStr[], char * replaceStr[] ); 159 | 160 | #endif /*__CONVENIENCE_H*/ 161 | -------------------------------------------------------------------------------- /src/rtlsdr_rpc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "rtlsdr_rpc_msg.h" 18 | 19 | 20 | #if 1 21 | #include 22 | #define PRINTF(__s, ...) fprintf(stderr, __s, ##__VA_ARGS__) 23 | #define TRACE() PRINTF("[t] %s,%u\n", __FILE__, __LINE__) 24 | #define ERROR() PRINTF("[e] %s,%u\n", __FILE__, __LINE__) 25 | #define UNIMPL() PRINTF("[u] %s,%u\n", __FILE__, __LINE__) 26 | #else 27 | #define PRINTF(...) 28 | #define TRACE() 29 | #define ERROR() 30 | #define UNIMPL() 31 | #endif 32 | 33 | 34 | typedef struct 35 | { 36 | volatile unsigned int is_locked; 37 | volatile unsigned int is_init; 38 | int sock; 39 | 40 | #define QR_COUNT 32 41 | pthread_mutex_t qr_lock; 42 | volatile uint32_t qr_mask; 43 | volatile uint32_t qr_box; 44 | rtlsdr_rpc_msg_t query[QR_COUNT]; 45 | rtlsdr_rpc_msg_t reply[QR_COUNT]; 46 | 47 | pthread_mutex_t send_lock; 48 | pthread_mutex_t recv_lock; 49 | 50 | volatile unsigned int is_async_cancel; 51 | 52 | } rtlsdr_rpc_cli_t; 53 | 54 | static rtlsdr_rpc_cli_t rtlsdr_rpc_cli = { 0, }; 55 | 56 | typedef void (*rtlsdr_rpc_read_async_cb_t) 57 | (unsigned char*, uint32_t, void*); 58 | 59 | typedef struct rtlsdr_rpc_dev 60 | { 61 | uint32_t index; 62 | size_t gain_count; 63 | rtlsdr_rpc_cli_t* cli; 64 | } rtlsdr_rpc_dev_t; 65 | 66 | static int resolve_ip_addr 67 | ( 68 | struct sockaddr_storage saddr_both[2], size_t size_both[2], 69 | const char* addr, const char* port 70 | ) 71 | { 72 | struct addrinfo ai; 73 | struct addrinfo* aip = NULL; 74 | int err = -1; 75 | size_t i; 76 | 77 | memset(&ai, 0, sizeof(ai)); 78 | ai.ai_family = AF_UNSPEC; 79 | ai.ai_socktype = SOCK_STREAM; 80 | ai.ai_flags = AI_PASSIVE; 81 | 82 | if (getaddrinfo(addr, port, &ai, &aip)) goto on_error; 83 | 84 | size_both[0] = 0; 85 | size_both[1] = 0; 86 | i = 0; 87 | for (; (i != 2) && (aip != NULL); aip = aip->ai_next) 88 | { 89 | if ((aip->ai_family != AF_INET) && (aip->ai_family != AF_INET6)) continue ; 90 | if (aip->ai_addrlen == 0) continue ; 91 | memcpy(&saddr_both[i], aip->ai_addr, aip->ai_addrlen); 92 | size_both[i] = aip->ai_addrlen; 93 | ++i; 94 | } 95 | 96 | if (i == 0) goto on_error; 97 | 98 | err = 0; 99 | on_error: 100 | if (aip != NULL) freeaddrinfo(aip); 101 | return err; 102 | } 103 | 104 | static int open_socket 105 | ( 106 | struct sockaddr_storage saddr_both[2], size_t size_both[2], 107 | int type, int proto, 108 | struct sockaddr_storage** saddr_used, size_t* size_used 109 | ) 110 | { 111 | size_t i; 112 | int fd; 113 | 114 | for (i = 0; (i != 2) && (size_both[i]); ++i) 115 | { 116 | const struct sockaddr* const sa = (const struct sockaddr*)&saddr_both[i]; 117 | fd = socket(sa->sa_family, type, proto); 118 | if (fd != -1) break ; 119 | } 120 | 121 | if ((i == 2) || (size_both[i] == 0)) return -1; 122 | 123 | *saddr_used = &saddr_both[i]; 124 | *size_used = size_both[i]; 125 | 126 | return fd; 127 | } 128 | 129 | static int init_cli(rtlsdr_rpc_cli_t* cli) 130 | { 131 | struct sockaddr_storage saddrs[2]; 132 | struct sockaddr_storage* saddr; 133 | size_t sizes[2]; 134 | size_t size; 135 | const char* addr; 136 | const char* port; 137 | size_t i; 138 | size_t j; 139 | int err = -1; 140 | 141 | /* no better way in this case ... */ 142 | while (cli->is_locked) usleep(10000); 143 | cli->is_locked = 1; 144 | 145 | if (cli->is_init) goto on_success; 146 | 147 | addr = getenv("RTLSDR_RPC_SERV_ADDR"); 148 | if (addr == NULL) addr = "127.0.0.1"; 149 | 150 | port = getenv("RTLSDR_RPC_SERV_PORT"); 151 | if (port == NULL) port = "40000"; 152 | 153 | if (resolve_ip_addr(saddrs, sizes, addr, port)) 154 | { 155 | ERROR(); 156 | goto on_error_0; 157 | } 158 | 159 | cli->sock = open_socket 160 | (saddrs, sizes, SOCK_STREAM, IPPROTO_TCP, &saddr, &size); 161 | if (cli->sock == -1) 162 | { 163 | ERROR(); 164 | goto on_error_0; 165 | } 166 | 167 | if (connect(cli->sock, (const struct sockaddr*)saddr, (socklen_t)size)) 168 | { 169 | ERROR(); 170 | goto on_error_1; 171 | } 172 | 173 | if (fcntl(cli->sock, F_SETFL, O_NONBLOCK)) 174 | { 175 | ERROR(); 176 | goto on_error_1; 177 | } 178 | 179 | for (i = 0; i != QR_COUNT; ++i) 180 | { 181 | rtlsdr_rpc_msg_t* const q = &cli->query[i]; 182 | rtlsdr_rpc_msg_t* const r = &cli->reply[i]; 183 | 184 | if (rtlsdr_rpc_msg_init(q, 0)) 185 | goto on_error_2; 186 | 187 | if (rtlsdr_rpc_msg_init(r, 0)) 188 | { 189 | rtlsdr_rpc_msg_fini(q); 190 | goto on_error_2; 191 | } 192 | } 193 | pthread_mutex_init(&cli->qr_lock, NULL); 194 | cli->qr_mask = 0; 195 | cli->qr_box = 0; 196 | 197 | pthread_mutex_init(&cli->send_lock, NULL); 198 | pthread_mutex_init(&cli->recv_lock, NULL); 199 | 200 | cli->is_init = 1; 201 | 202 | on_success: 203 | err = 0; 204 | goto on_error_0; 205 | 206 | on_error_2: 207 | for (j = 0; j != i; ++j) 208 | { 209 | rtlsdr_rpc_msg_fini(&cli->query[j]); 210 | rtlsdr_rpc_msg_fini(&cli->reply[j]); 211 | } 212 | 213 | on_error_1: 214 | shutdown(cli->sock, SHUT_RDWR); 215 | close(cli->sock); 216 | 217 | on_error_0: 218 | cli->is_locked = 0; 219 | return err; 220 | } 221 | 222 | __attribute__((unused)) 223 | static int fini_cli(rtlsdr_rpc_cli_t* cli) 224 | { 225 | size_t i; 226 | 227 | for (i = 0; i != QR_COUNT; ++i) 228 | { 229 | rtlsdr_rpc_msg_fini(&cli->query[i]); 230 | rtlsdr_rpc_msg_fini(&cli->reply[i]); 231 | } 232 | pthread_mutex_destroy(&cli->qr_lock); 233 | 234 | pthread_mutex_destroy(&cli->send_lock); 235 | pthread_mutex_destroy(&cli->recv_lock); 236 | 237 | shutdown(cli->sock, SHUT_RDWR); 238 | close(cli->sock); 239 | 240 | return 0; 241 | } 242 | 243 | static int alloc_qr 244 | (rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t** q, rtlsdr_rpc_msg_t** r) 245 | { 246 | size_t i; 247 | 248 | pthread_mutex_lock(&cli->qr_lock); 249 | for (i = 0; i != QR_COUNT; ++i) 250 | { 251 | const uint32_t m = 1 << i; 252 | if ((cli->qr_mask & m) == 0) 253 | { 254 | cli->qr_mask |= m; 255 | break ; 256 | } 257 | } 258 | pthread_mutex_unlock(&cli->qr_lock); 259 | 260 | if (i == QR_COUNT) return -1; 261 | 262 | *q = &cli->query[i]; 263 | *r = &cli->reply[i]; 264 | 265 | /* set the query id */ 266 | rtlsdr_rpc_msg_reset(*q); 267 | rtlsdr_rpc_msg_set_id(*q, (uint8_t)i); 268 | 269 | return 0; 270 | } 271 | 272 | static void free_qr 273 | (rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* q, rtlsdr_rpc_msg_t* r) 274 | { 275 | const uint32_t m = 1 << (uint32_t)rtlsdr_rpc_msg_get_id(q); 276 | pthread_mutex_lock(&cli->qr_lock); 277 | cli->qr_mask &= ~m; 278 | pthread_mutex_unlock(&cli->qr_lock); 279 | } 280 | 281 | static int recv_all(int fd, uint8_t* buf, size_t size) 282 | { 283 | ssize_t n; 284 | fd_set rset; 285 | 286 | while (1) 287 | { 288 | errno = 0; 289 | n = recv(fd, buf, size, 0); 290 | if (n <= 0) 291 | { 292 | if ((errno != EWOULDBLOCK) || (errno != EAGAIN)) 293 | return -1; 294 | } 295 | else 296 | { 297 | size -= (size_t)n; 298 | buf += (size_t)n; 299 | if (size == 0) break ; 300 | } 301 | 302 | FD_ZERO(&rset); 303 | FD_SET(fd, &rset); 304 | if (select(fd + 1, &rset, NULL, NULL, NULL) <= 0) 305 | { 306 | return -1; 307 | } 308 | } 309 | 310 | return 0; 311 | } 312 | 313 | static int send_all(int fd, const uint8_t* buf, size_t size) 314 | { 315 | ssize_t n; 316 | fd_set wset; 317 | 318 | while (1) 319 | { 320 | errno = 0; 321 | n = send(fd, buf, size, 0); 322 | if (n <= 0) 323 | { 324 | if ((errno != EWOULDBLOCK) || (errno != EAGAIN)) 325 | return -1; 326 | } 327 | else 328 | { 329 | size -= (size_t)n; 330 | buf += (size_t)n; 331 | if (size == 0) break ; 332 | } 333 | 334 | FD_ZERO(&wset); 335 | FD_SET(fd, &wset); 336 | if (select(fd + 1, NULL, &wset, NULL, NULL) <= 0) 337 | { 338 | return -1; 339 | } 340 | } 341 | 342 | return 0; 343 | } 344 | 345 | static int recv_msg 346 | (rtlsdr_rpc_cli_t* cli, uint8_t id, rtlsdr_rpc_msg_t* m) 347 | { 348 | static const size_t fmt_size = offsetof(rtlsdr_rpc_fmt_t, data); 349 | const uint32_t mask = 1 << (uint32_t)id; 350 | const int fd = cli->sock; 351 | uint32_t size; 352 | uint8_t to_id; 353 | int err = -1; 354 | rtlsdr_rpc_msg_t* to_m; 355 | 356 | pthread_mutex_lock(&cli->recv_lock); 357 | 358 | if (cli->qr_box & mask) 359 | { 360 | cli->qr_box &= ~mask; 361 | goto on_success; 362 | } 363 | 364 | while (1) 365 | { 366 | /* receive next message */ 367 | if (recv_all(fd, m->fmt, fmt_size)) goto on_error; 368 | 369 | /* get destination message by id */ 370 | to_id = rtlsdr_rpc_msg_get_id(m); 371 | 372 | if (to_id >= QR_COUNT) goto on_error; 373 | to_m = &cli->reply[to_id]; 374 | if (to_id != id) memcpy(to_m->fmt, m->fmt, fmt_size); 375 | 376 | size = rtlsdr_rpc_msg_get_size(to_m); 377 | if (size < fmt_size) goto on_error; 378 | 379 | if (rtlsdr_rpc_msg_realloc(to_m, size)) goto on_error; 380 | 381 | size -= fmt_size; 382 | if (size) 383 | { 384 | if (recv_all(fd, to_m->fmt + fmt_size, size)) goto on_error; 385 | } 386 | 387 | if (to_id == id) goto on_success; 388 | 389 | /* message not for this query, forward */ 390 | cli->qr_box |= 1 << (uint32_t)to_id; 391 | } 392 | 393 | on_success: 394 | err = 0; 395 | on_error: 396 | pthread_mutex_unlock(&cli->recv_lock); 397 | return err; 398 | } 399 | 400 | static int send_msg(rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* m) 401 | { 402 | int err; 403 | 404 | rtlsdr_rpc_msg_set_size(m, (uint32_t)m->off); 405 | 406 | pthread_mutex_lock(&cli->send_lock); 407 | err = send_all(cli->sock, m->fmt, m->off); 408 | pthread_mutex_unlock(&cli->send_lock); 409 | 410 | return err; 411 | } 412 | 413 | static int send_recv_msg 414 | (rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* q, rtlsdr_rpc_msg_t* r) 415 | { 416 | const uint8_t id = rtlsdr_rpc_msg_get_id(q); 417 | 418 | if (send_msg(cli, q)) return -1; 419 | if (recv_msg(cli, id, r)) return -1; 420 | rtlsdr_rpc_msg_reset(r); 421 | 422 | return 0; 423 | } 424 | 425 | static int send_flush_msgs 426 | (rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* q) 427 | { 428 | struct timeval tm; 429 | fd_set rset; 430 | uint8_t buf[256]; 431 | 432 | if (send_msg(cli, q)) return -1; 433 | 434 | pthread_mutex_lock(&cli->recv_lock); 435 | 436 | while (1) 437 | { 438 | FD_ZERO(&rset); 439 | FD_SET(cli->sock, &rset); 440 | tm.tv_sec = 0; 441 | tm.tv_usec = 200000; 442 | if (select(cli->sock + 1, &rset, NULL, NULL, &tm) < 0) break ; 443 | if (recv(cli->sock, buf, sizeof(buf), 0) <= 0) break ; 444 | } 445 | 446 | pthread_mutex_unlock(&cli->recv_lock); 447 | 448 | return 0; 449 | } 450 | 451 | uint32_t rtlsdr_rpc_get_device_count(void) 452 | { 453 | rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; 454 | rtlsdr_rpc_msg_t* q; 455 | rtlsdr_rpc_msg_t* r; 456 | uint32_t n = 0; 457 | 458 | if (init_cli(cli)) goto on_error_0; 459 | 460 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 461 | 462 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DEVICE_COUNT); 463 | 464 | if (send_recv_msg(cli, q, r)) goto on_error_1; 465 | 466 | if (rtlsdr_rpc_msg_pop_uint32(r, &n)) goto on_error_1; 467 | 468 | on_error_1: 469 | free_qr(cli, q, r); 470 | on_error_0: 471 | return n; 472 | } 473 | 474 | const char* rtlsdr_rpc_get_device_name 475 | ( 476 | uint32_t index 477 | ) 478 | { 479 | rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; 480 | rtlsdr_rpc_msg_t* q; 481 | rtlsdr_rpc_msg_t* r; 482 | const char* s = NULL; 483 | 484 | if (init_cli(cli)) goto on_error_0; 485 | 486 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 487 | 488 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DEVICE_NAME); 489 | if (rtlsdr_rpc_msg_push_uint32(q, index)) goto on_error_1; 490 | 491 | if (send_recv_msg(cli, q, r)) goto on_error_1; 492 | 493 | if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; 494 | /* TODO: memory leak here */ 495 | s = strdup(s); 496 | 497 | on_error_1: 498 | free_qr(cli, q, r); 499 | on_error_0: 500 | return s; 501 | } 502 | 503 | int rtlsdr_rpc_get_device_usb_strings 504 | (uint32_t index, char* manufact, char* product, char* serial) 505 | { 506 | rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; 507 | rtlsdr_rpc_msg_t* q; 508 | rtlsdr_rpc_msg_t* r; 509 | const char* s; 510 | int err = -1; 511 | 512 | if (init_cli(cli)) goto on_error_0; 513 | 514 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 515 | 516 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DEVICE_USB_STRINGS); 517 | if (rtlsdr_rpc_msg_push_uint32(q, index)) goto on_error_1; 518 | 519 | if (send_recv_msg(cli, q, r)) goto on_error_1; 520 | 521 | err = rtlsdr_rpc_msg_get_err(r); 522 | if (err) goto on_error_1; 523 | 524 | if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; 525 | strcpy(manufact, s); 526 | if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; 527 | strcpy(product, s); 528 | if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; 529 | strcpy(serial, s); 530 | 531 | on_error_1: 532 | free_qr(cli, q, r); 533 | on_error_0: 534 | return err; 535 | } 536 | 537 | int rtlsdr_rpc_get_index_by_serial(const char* serial) 538 | { 539 | rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; 540 | rtlsdr_rpc_msg_t* q; 541 | rtlsdr_rpc_msg_t* r; 542 | int err = -1; 543 | 544 | if (init_cli(cli)) goto on_error_0; 545 | 546 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 547 | 548 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_INDEX_BY_SERIAL); 549 | if (rtlsdr_rpc_msg_push_str(q, serial)) goto on_error_1; 550 | 551 | if (send_recv_msg(cli, q, r)) goto on_error_1; 552 | 553 | err = rtlsdr_rpc_msg_get_err(r); 554 | 555 | on_error_1: 556 | free_qr(cli, q, r); 557 | on_error_0: 558 | return err; 559 | } 560 | 561 | int rtlsdr_rpc_open(void** devp, uint32_t index) 562 | { 563 | rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; 564 | rtlsdr_rpc_msg_t* q; 565 | rtlsdr_rpc_msg_t* r; 566 | rtlsdr_rpc_dev_t* dev; 567 | int err = -1; 568 | 569 | *devp = NULL; 570 | 571 | if (init_cli(cli)) goto on_error_0; 572 | 573 | dev = malloc(sizeof(rtlsdr_rpc_dev_t)); 574 | if (dev == NULL) goto on_error_0; 575 | 576 | if (alloc_qr(cli, &q, &r)) goto on_error_1; 577 | 578 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_OPEN); 579 | if (rtlsdr_rpc_msg_push_uint32(q, index)) goto on_error_2; 580 | 581 | if (send_recv_msg(cli, q, r)) goto on_error_2; 582 | 583 | err = rtlsdr_rpc_msg_get_err(r); 584 | if (err) goto on_error_2; 585 | 586 | dev->index = index; 587 | dev->gain_count = 32; 588 | dev->cli = cli; 589 | *devp = dev; 590 | 591 | on_error_2: 592 | free_qr(cli, q, r); 593 | on_error_1: 594 | if (err) free(dev); 595 | on_error_0: 596 | return err; 597 | } 598 | 599 | int rtlsdr_rpc_close(void* devp) 600 | { 601 | rtlsdr_rpc_dev_t* const dev = devp; 602 | rtlsdr_rpc_cli_t* const cli = dev->cli; 603 | rtlsdr_rpc_msg_t* q; 604 | rtlsdr_rpc_msg_t* r; 605 | int err = -1; 606 | 607 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 608 | 609 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_CLOSE); 610 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 611 | 612 | if (send_recv_msg(cli, q, r)) goto on_error_1; 613 | 614 | err = rtlsdr_rpc_msg_get_err(r); 615 | if (err) goto on_error_1; 616 | 617 | on_error_1: 618 | free_qr(cli, q, r); 619 | on_error_0: 620 | free(dev); 621 | return err; 622 | } 623 | 624 | int rtlsdr_rpc_set_xtal_freq 625 | (void* devp, uint32_t rtl_freq, uint32_t tuner_freq) 626 | { 627 | rtlsdr_rpc_dev_t* const dev = devp; 628 | rtlsdr_rpc_cli_t* const cli = dev->cli; 629 | rtlsdr_rpc_msg_t* q; 630 | rtlsdr_rpc_msg_t* r; 631 | int err = -1; 632 | 633 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 634 | 635 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_XTAL_FREQ); 636 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 637 | if (rtlsdr_rpc_msg_push_uint32(q, rtl_freq)) goto on_error_1; 638 | if (rtlsdr_rpc_msg_push_uint32(q, tuner_freq)) goto on_error_1; 639 | 640 | if (send_recv_msg(cli, q, r)) goto on_error_1; 641 | 642 | err = rtlsdr_rpc_msg_get_err(r); 643 | if (err) goto on_error_1; 644 | 645 | on_error_1: 646 | free_qr(cli, q, r); 647 | on_error_0: 648 | return err; 649 | } 650 | 651 | int rtlsdr_rpc_get_xtal_freq 652 | (void* devp, uint32_t* rtl_freq, uint32_t* tuner_freq) 653 | { 654 | rtlsdr_rpc_dev_t* const dev = devp; 655 | rtlsdr_rpc_cli_t* const cli = dev->cli; 656 | rtlsdr_rpc_msg_t* q; 657 | rtlsdr_rpc_msg_t* r; 658 | int err = -1; 659 | 660 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 661 | 662 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_XTAL_FREQ); 663 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 664 | 665 | if (send_recv_msg(cli, q, r)) goto on_error_1; 666 | 667 | err = rtlsdr_rpc_msg_get_err(r); 668 | if (err) goto on_error_1; 669 | 670 | if (rtlsdr_rpc_msg_pop_uint32(r, rtl_freq)) 671 | { 672 | err = -1; 673 | goto on_error_1; 674 | } 675 | 676 | if (rtlsdr_rpc_msg_pop_uint32(r, tuner_freq)) 677 | { 678 | err = -1; 679 | goto on_error_1; 680 | } 681 | 682 | on_error_1: 683 | free_qr(cli, q, r); 684 | on_error_0: 685 | return err; 686 | } 687 | 688 | int rtlsdr_rpc_get_usb_strings 689 | (void* devp, char* manufact, char* product, char* serial) 690 | { 691 | rtlsdr_rpc_dev_t* const dev = devp; 692 | rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; 693 | rtlsdr_rpc_msg_t* q; 694 | rtlsdr_rpc_msg_t* r; 695 | const char* s; 696 | int err = -1; 697 | 698 | if (init_cli(cli)) goto on_error_0; 699 | 700 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 701 | 702 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_USB_STRINGS); 703 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 704 | 705 | if (send_recv_msg(cli, q, r)) goto on_error_1; 706 | 707 | err = rtlsdr_rpc_msg_get_err(r); 708 | if (err) goto on_error_1; 709 | 710 | if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; 711 | strcpy(manufact, s); 712 | if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; 713 | strcpy(product, s); 714 | if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; 715 | strcpy(serial, s); 716 | 717 | on_error_1: 718 | free_qr(cli, q, r); 719 | on_error_0: 720 | return err; 721 | } 722 | 723 | int rtlsdr_rpc_write_eeprom 724 | (void* dev, uint8_t* data, uint8_t offset, uint16_t len) 725 | { 726 | UNIMPL(); 727 | return -1; 728 | } 729 | 730 | int rtlsdr_rpc_read_eeprom 731 | (void* dev, uint8_t* data, uint8_t offset, uint16_t len) 732 | { 733 | UNIMPL(); 734 | return -1; 735 | } 736 | 737 | int rtlsdr_rpc_set_center_freq(void* devp, uint32_t freq) 738 | { 739 | rtlsdr_rpc_dev_t* const dev = devp; 740 | rtlsdr_rpc_cli_t* const cli = dev->cli; 741 | rtlsdr_rpc_msg_t* q; 742 | rtlsdr_rpc_msg_t* r; 743 | int err = -1; 744 | 745 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 746 | 747 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_CENTER_FREQ); 748 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 749 | if (rtlsdr_rpc_msg_push_uint32(q, freq)) goto on_error_1; 750 | 751 | if (send_recv_msg(cli, q, r)) goto on_error_1; 752 | 753 | err = rtlsdr_rpc_msg_get_err(r); 754 | if (err) goto on_error_1; 755 | 756 | on_error_1: 757 | free_qr(cli, q, r); 758 | on_error_0: 759 | return err; 760 | } 761 | 762 | uint32_t rtlsdr_rpc_get_center_freq(void* devp) 763 | { 764 | rtlsdr_rpc_dev_t* const dev = devp; 765 | rtlsdr_rpc_cli_t* const cli = dev->cli; 766 | rtlsdr_rpc_msg_t* q; 767 | rtlsdr_rpc_msg_t* r; 768 | uint32_t freq = 0; 769 | 770 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 771 | 772 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_CENTER_FREQ); 773 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 774 | 775 | if (send_recv_msg(cli, q, r)) goto on_error_1; 776 | 777 | if (rtlsdr_rpc_msg_get_err(r)) goto on_error_1; 778 | if (rtlsdr_rpc_msg_pop_uint32(r, &freq)) goto on_error_1; 779 | 780 | on_error_1: 781 | free_qr(cli, q, r); 782 | on_error_0: 783 | return freq; 784 | } 785 | 786 | int rtlsdr_rpc_set_freq_correction(void* devp, int ppm) 787 | { 788 | rtlsdr_rpc_dev_t* const dev = devp; 789 | rtlsdr_rpc_cli_t* const cli = dev->cli; 790 | rtlsdr_rpc_msg_t* q; 791 | rtlsdr_rpc_msg_t* r; 792 | int err = -1; 793 | 794 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 795 | 796 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_FREQ_CORRECTION); 797 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 798 | if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)ppm)) goto on_error_1; 799 | 800 | if (send_recv_msg(cli, q, r)) goto on_error_1; 801 | 802 | err = rtlsdr_rpc_msg_get_err(r); 803 | if (err) goto on_error_1; 804 | 805 | on_error_1: 806 | free_qr(cli, q, r); 807 | on_error_0: 808 | return err; 809 | } 810 | 811 | int rtlsdr_rpc_get_freq_correction(void* devp) 812 | { 813 | rtlsdr_rpc_dev_t* const dev = devp; 814 | rtlsdr_rpc_cli_t* const cli = dev->cli; 815 | rtlsdr_rpc_msg_t* q; 816 | rtlsdr_rpc_msg_t* r; 817 | int err = -1; 818 | 819 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 820 | 821 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_FREQ_CORRECTION); 822 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 823 | 824 | if (send_recv_msg(cli, q, r)) goto on_error_1; 825 | 826 | err = rtlsdr_rpc_msg_get_err(r); 827 | 828 | on_error_1: 829 | free_qr(cli, q, r); 830 | on_error_0: 831 | return err; 832 | } 833 | 834 | int rtlsdr_rpc_get_tuner_type(void* devp) 835 | { 836 | rtlsdr_rpc_dev_t* const dev = devp; 837 | rtlsdr_rpc_cli_t* const cli = dev->cli; 838 | rtlsdr_rpc_msg_t* q; 839 | rtlsdr_rpc_msg_t* r; 840 | int err = -1; 841 | 842 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 843 | 844 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_TUNER_TYPE); 845 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 846 | 847 | if (send_recv_msg(cli, q, r)) goto on_error_1; 848 | 849 | err = rtlsdr_rpc_msg_get_err(r); 850 | 851 | on_error_1: 852 | free_qr(cli, q, r); 853 | on_error_0: 854 | return err; 855 | } 856 | 857 | int rtlsdr_rpc_get_tuner_gains(void* devp, int* gainsp) 858 | { 859 | rtlsdr_rpc_dev_t* const dev = devp; 860 | rtlsdr_rpc_cli_t* const cli = dev->cli; 861 | rtlsdr_rpc_msg_t* q; 862 | rtlsdr_rpc_msg_t* r; 863 | const uint32_t is_null = (gainsp == NULL); 864 | const uint32_t gain_size = (uint32_t)dev->gain_count * sizeof(int); 865 | const uint8_t* tmp; 866 | size_t size; 867 | int err = 0; 868 | 869 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 870 | 871 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_TUNER_GAINS); 872 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 873 | if (rtlsdr_rpc_msg_push_uint32(q, is_null)) goto on_error_1; 874 | if (rtlsdr_rpc_msg_push_uint32(q, gain_size)) goto on_error_1; 875 | 876 | if (send_recv_msg(cli, q, r)) goto on_error_1; 877 | 878 | err = rtlsdr_rpc_msg_get_err(r); 879 | 880 | if (err <= 0) goto on_error_1; 881 | 882 | dev->gain_count = (size_t)err; 883 | 884 | if (is_null == 0) 885 | { 886 | if (rtlsdr_rpc_msg_pop_buf(r, &tmp, &size)) 887 | { 888 | err = 0; 889 | goto on_error_1; 890 | } 891 | 892 | /* TODO: endianess */ 893 | memcpy(gainsp, tmp, size); 894 | } 895 | 896 | on_error_1: 897 | free_qr(cli, q, r); 898 | on_error_0: 899 | return err; 900 | } 901 | 902 | int rtlsdr_rpc_set_tuner_gain(void* devp, int gain) 903 | { 904 | rtlsdr_rpc_dev_t* const dev = devp; 905 | rtlsdr_rpc_cli_t* const cli = dev->cli; 906 | rtlsdr_rpc_msg_t* q; 907 | rtlsdr_rpc_msg_t* r; 908 | int err = -1; 909 | 910 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 911 | 912 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TUNER_GAIN); 913 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 914 | if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)gain)) goto on_error_1; 915 | 916 | if (send_recv_msg(cli, q, r)) goto on_error_1; 917 | 918 | err = rtlsdr_rpc_msg_get_err(r); 919 | if (err) goto on_error_1; 920 | 921 | on_error_1: 922 | free_qr(cli, q, r); 923 | on_error_0: 924 | return err; 925 | } 926 | 927 | int rtlsdr_rpc_get_tuner_gain(void* devp) 928 | { 929 | rtlsdr_rpc_dev_t* const dev = devp; 930 | rtlsdr_rpc_cli_t* const cli = dev->cli; 931 | rtlsdr_rpc_msg_t* q; 932 | rtlsdr_rpc_msg_t* r; 933 | int err = -1; 934 | 935 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 936 | 937 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_TUNER_GAIN); 938 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 939 | 940 | if (send_recv_msg(cli, q, r)) goto on_error_1; 941 | 942 | err = rtlsdr_rpc_msg_get_err(r); 943 | 944 | on_error_1: 945 | free_qr(cli, q, r); 946 | on_error_0: 947 | return err; 948 | } 949 | 950 | int rtlsdr_rpc_set_tuner_if_gain(void* devp, int stage, int gain) 951 | { 952 | rtlsdr_rpc_dev_t* const dev = devp; 953 | rtlsdr_rpc_cli_t* const cli = dev->cli; 954 | rtlsdr_rpc_msg_t* q; 955 | rtlsdr_rpc_msg_t* r; 956 | int err = -1; 957 | 958 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 959 | 960 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TUNER_IF_GAIN); 961 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 962 | if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)stage)) goto on_error_1; 963 | if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)gain)) goto on_error_1; 964 | 965 | if (send_recv_msg(cli, q, r)) goto on_error_1; 966 | 967 | err = rtlsdr_rpc_msg_get_err(r); 968 | if (err) goto on_error_1; 969 | 970 | on_error_1: 971 | free_qr(cli, q, r); 972 | on_error_0: 973 | return err; 974 | } 975 | 976 | int rtlsdr_rpc_set_tuner_gain_mode(void* devp, int manual) 977 | { 978 | rtlsdr_rpc_dev_t* const dev = devp; 979 | rtlsdr_rpc_cli_t* const cli = dev->cli; 980 | rtlsdr_rpc_msg_t* q; 981 | rtlsdr_rpc_msg_t* r; 982 | int err = -1; 983 | 984 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 985 | 986 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TUNER_GAIN_MODE); 987 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 988 | if (rtlsdr_rpc_msg_push_uint32(q, manual)) goto on_error_1; 989 | 990 | if (send_recv_msg(cli, q, r)) goto on_error_1; 991 | 992 | err = rtlsdr_rpc_msg_get_err(r); 993 | if (err) goto on_error_1; 994 | 995 | on_error_1: 996 | free_qr(cli, q, r); 997 | on_error_0: 998 | return err; 999 | } 1000 | 1001 | int rtlsdr_rpc_set_sample_rate(void* devp, uint32_t rate) 1002 | { 1003 | rtlsdr_rpc_dev_t* const dev = devp; 1004 | rtlsdr_rpc_cli_t* const cli = dev->cli; 1005 | rtlsdr_rpc_msg_t* q; 1006 | rtlsdr_rpc_msg_t* r; 1007 | int err = -1; 1008 | 1009 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 1010 | 1011 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_SAMPLE_RATE); 1012 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 1013 | if (rtlsdr_rpc_msg_push_uint32(q, rate)) goto on_error_1; 1014 | 1015 | if (send_recv_msg(cli, q, r)) goto on_error_1; 1016 | 1017 | err = rtlsdr_rpc_msg_get_err(r); 1018 | if (err) goto on_error_1; 1019 | 1020 | on_error_1: 1021 | free_qr(cli, q, r); 1022 | on_error_0: 1023 | return err; 1024 | } 1025 | 1026 | uint32_t rtlsdr_rpc_get_sample_rate(void* devp) 1027 | { 1028 | rtlsdr_rpc_dev_t* const dev = devp; 1029 | rtlsdr_rpc_cli_t* const cli = dev->cli; 1030 | rtlsdr_rpc_msg_t* q; 1031 | rtlsdr_rpc_msg_t* r; 1032 | uint32_t rate = 0; 1033 | 1034 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 1035 | 1036 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_SAMPLE_RATE); 1037 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 1038 | 1039 | if (send_recv_msg(cli, q, r)) goto on_error_1; 1040 | 1041 | if (rtlsdr_rpc_msg_get_err(r)) goto on_error_1; 1042 | if (rtlsdr_rpc_msg_pop_uint32(r, &rate)) goto on_error_1; 1043 | 1044 | on_error_1: 1045 | free_qr(cli, q, r); 1046 | on_error_0: 1047 | return rate; 1048 | } 1049 | 1050 | int rtlsdr_rpc_set_testmode(void* devp, int on) 1051 | { 1052 | rtlsdr_rpc_dev_t* const dev = devp; 1053 | rtlsdr_rpc_cli_t* const cli = dev->cli; 1054 | rtlsdr_rpc_msg_t* q; 1055 | rtlsdr_rpc_msg_t* r; 1056 | int err = -1; 1057 | 1058 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 1059 | 1060 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TESTMODE); 1061 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 1062 | if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; 1063 | 1064 | if (send_recv_msg(cli, q, r)) goto on_error_1; 1065 | 1066 | err = rtlsdr_rpc_msg_get_err(r); 1067 | if (err) goto on_error_1; 1068 | 1069 | on_error_1: 1070 | free_qr(cli, q, r); 1071 | on_error_0: 1072 | return err; 1073 | } 1074 | 1075 | int rtlsdr_rpc_set_agc_mode(void* devp, int on) 1076 | { 1077 | rtlsdr_rpc_dev_t* const dev = devp; 1078 | rtlsdr_rpc_cli_t* const cli = dev->cli; 1079 | rtlsdr_rpc_msg_t* q; 1080 | rtlsdr_rpc_msg_t* r; 1081 | int err = -1; 1082 | 1083 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 1084 | 1085 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_AGC_MODE); 1086 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 1087 | if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; 1088 | 1089 | if (send_recv_msg(cli, q, r)) goto on_error_1; 1090 | 1091 | err = rtlsdr_rpc_msg_get_err(r); 1092 | if (err) goto on_error_1; 1093 | 1094 | on_error_1: 1095 | free_qr(cli, q, r); 1096 | on_error_0: 1097 | return err; 1098 | } 1099 | 1100 | int rtlsdr_rpc_set_direct_sampling(void* devp, int on) 1101 | { 1102 | rtlsdr_rpc_dev_t* const dev = devp; 1103 | rtlsdr_rpc_cli_t* const cli = dev->cli; 1104 | rtlsdr_rpc_msg_t* q; 1105 | rtlsdr_rpc_msg_t* r; 1106 | int err = -1; 1107 | 1108 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 1109 | 1110 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_DIRECT_SAMPLING); 1111 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 1112 | if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; 1113 | 1114 | if (send_recv_msg(cli, q, r)) goto on_error_1; 1115 | 1116 | err = rtlsdr_rpc_msg_get_err(r); 1117 | if (err) goto on_error_1; 1118 | 1119 | on_error_1: 1120 | free_qr(cli, q, r); 1121 | on_error_0: 1122 | return err; 1123 | } 1124 | 1125 | int rtlsdr_rpc_get_direct_sampling(void* devp) 1126 | { 1127 | rtlsdr_rpc_dev_t* const dev = devp; 1128 | rtlsdr_rpc_cli_t* const cli = dev->cli; 1129 | rtlsdr_rpc_msg_t* q; 1130 | rtlsdr_rpc_msg_t* r; 1131 | int err = -1; 1132 | 1133 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 1134 | 1135 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DIRECT_SAMPLING); 1136 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 1137 | 1138 | if (send_recv_msg(cli, q, r)) goto on_error_1; 1139 | 1140 | err = rtlsdr_rpc_msg_get_err(r); 1141 | 1142 | on_error_1: 1143 | free_qr(cli, q, r); 1144 | on_error_0: 1145 | return err; 1146 | } 1147 | 1148 | int rtlsdr_rpc_set_offset_tuning(void* devp, int on) 1149 | { 1150 | rtlsdr_rpc_dev_t* const dev = devp; 1151 | rtlsdr_rpc_cli_t* const cli = dev->cli; 1152 | rtlsdr_rpc_msg_t* q; 1153 | rtlsdr_rpc_msg_t* r; 1154 | int err = -1; 1155 | 1156 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 1157 | 1158 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_OFFSET_TUNING); 1159 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 1160 | if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; 1161 | 1162 | if (send_recv_msg(cli, q, r)) goto on_error_1; 1163 | 1164 | err = rtlsdr_rpc_msg_get_err(r); 1165 | if (err) goto on_error_1; 1166 | 1167 | on_error_1: 1168 | free_qr(cli, q, r); 1169 | on_error_0: 1170 | return err; 1171 | } 1172 | 1173 | int rtlsdr_rpc_get_offset_tuning(void* devp) 1174 | { 1175 | rtlsdr_rpc_dev_t* const dev = devp; 1176 | rtlsdr_rpc_cli_t* const cli = dev->cli; 1177 | rtlsdr_rpc_msg_t* q; 1178 | rtlsdr_rpc_msg_t* r; 1179 | int err = -1; 1180 | 1181 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 1182 | 1183 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_OFFSET_TUNING); 1184 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 1185 | 1186 | if (send_recv_msg(cli, q, r)) goto on_error_1; 1187 | 1188 | err = rtlsdr_rpc_msg_get_err(r); 1189 | 1190 | on_error_1: 1191 | free_qr(cli, q, r); 1192 | on_error_0: 1193 | return err; 1194 | } 1195 | 1196 | int rtlsdr_rpc_reset_buffer(void* devp) 1197 | { 1198 | rtlsdr_rpc_dev_t* const dev = devp; 1199 | rtlsdr_rpc_cli_t* const cli = dev->cli; 1200 | rtlsdr_rpc_msg_t* q; 1201 | rtlsdr_rpc_msg_t* r; 1202 | int err = -1; 1203 | 1204 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 1205 | 1206 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_RESET_BUFFER); 1207 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 1208 | 1209 | if (send_recv_msg(cli, q, r)) goto on_error_1; 1210 | 1211 | err = rtlsdr_rpc_msg_get_err(r); 1212 | if (err) goto on_error_1; 1213 | 1214 | on_error_1: 1215 | free_qr(cli, q, r); 1216 | on_error_0: 1217 | return err; 1218 | } 1219 | 1220 | int rtlsdr_rpc_read_sync 1221 | (void* devp, void* buf, int len, int* n_read) 1222 | { 1223 | rtlsdr_rpc_dev_t* const dev = devp; 1224 | rtlsdr_rpc_cli_t* const cli = dev->cli; 1225 | rtlsdr_rpc_msg_t* q; 1226 | rtlsdr_rpc_msg_t* r; 1227 | const uint8_t* tmp; 1228 | size_t size; 1229 | int err = -1; 1230 | 1231 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 1232 | 1233 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_READ_SYNC); 1234 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 1235 | if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)len)) goto on_error_1; 1236 | 1237 | if (send_recv_msg(cli, q, r)) goto on_error_1; 1238 | 1239 | err = rtlsdr_rpc_msg_get_err(r); 1240 | if (err) goto on_error_1; 1241 | 1242 | if (rtlsdr_rpc_msg_pop_buf(r, &tmp, &size)) 1243 | { 1244 | err = -1; 1245 | goto on_error_1; 1246 | } 1247 | 1248 | if (size > (size_t)len) size = len; 1249 | 1250 | memcpy(buf, tmp, size); 1251 | 1252 | *n_read = (int)size; 1253 | 1254 | on_error_1: 1255 | free_qr(cli, q, r); 1256 | on_error_0: 1257 | return err; 1258 | } 1259 | 1260 | 1261 | static volatile unsigned int is_cancel; 1262 | int rtlsdr_rpc_read_async 1263 | ( 1264 | void* devp, 1265 | rtlsdr_rpc_read_async_cb_t cb, void* ctx, 1266 | uint32_t buf_num, uint32_t buf_len 1267 | ) 1268 | { 1269 | rtlsdr_rpc_dev_t* const dev = devp; 1270 | rtlsdr_rpc_cli_t* const cli = dev->cli; 1271 | rtlsdr_rpc_msg_t* q; 1272 | rtlsdr_rpc_msg_t* r; 1273 | uint8_t id; 1274 | size_t size; 1275 | int err = -1; 1276 | 1277 | if (alloc_qr(cli, &q, &r)) goto on_error_0; 1278 | 1279 | id = rtlsdr_rpc_msg_get_id(q); 1280 | 1281 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_READ_ASYNC); 1282 | if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; 1283 | if (rtlsdr_rpc_msg_push_uint32(q, buf_num)) goto on_error_1; 1284 | if (rtlsdr_rpc_msg_push_uint32(q, buf_len)) goto on_error_1; 1285 | 1286 | if (send_recv_msg(cli, q, r)) goto on_error_1; 1287 | 1288 | err = rtlsdr_rpc_msg_get_err(r); 1289 | if (err) goto on_error_1; 1290 | 1291 | cli->is_async_cancel = 0; 1292 | while (cli->is_async_cancel == 0) 1293 | { 1294 | static const size_t off = offsetof(rtlsdr_rpc_fmt_t, data); 1295 | 1296 | if (recv_msg(cli, id, r)) 1297 | { 1298 | err = -1; 1299 | goto on_error_1; 1300 | } 1301 | 1302 | size = rtlsdr_rpc_msg_get_size(r); 1303 | cb(r->fmt + off, size - off, ctx); 1304 | } 1305 | 1306 | rtlsdr_rpc_msg_reset(q); 1307 | rtlsdr_rpc_msg_set_id(q, id); 1308 | rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_CANCEL_ASYNC); 1309 | rtlsdr_rpc_msg_push_uint32(q, dev->index); 1310 | send_flush_msgs(cli, q); 1311 | 1312 | on_error_1: 1313 | free_qr(cli, q, r); 1314 | on_error_0: 1315 | return err; 1316 | } 1317 | 1318 | int rtlsdr_rpc_wait_async 1319 | ( 1320 | void* dev, 1321 | rtlsdr_rpc_read_async_cb_t cb, void* ctx 1322 | ) 1323 | { 1324 | return rtlsdr_rpc_read_async(dev, cb, ctx, 0, 0); 1325 | } 1326 | 1327 | int rtlsdr_rpc_cancel_async(void* devp) 1328 | { 1329 | rtlsdr_rpc_dev_t* const dev = devp; 1330 | rtlsdr_rpc_cli_t* const cli = dev->cli; 1331 | cli->is_async_cancel = 1; 1332 | return 0; 1333 | } 1334 | 1335 | unsigned int rtlsdr_rpc_is_enabled(void) 1336 | { 1337 | static unsigned int is_enabled = (unsigned int)-1; 1338 | if (is_enabled == (unsigned int)-1) 1339 | is_enabled = (getenv("RTLSDR_RPC_IS_ENABLED") != NULL); 1340 | return is_enabled; 1341 | } 1342 | -------------------------------------------------------------------------------- /src/rtlsdr_rpc_msg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "rtlsdr_rpc_msg.h" 6 | 7 | #if 1 8 | #include 9 | #define PRINTF(__s, ...) fprintf(stderr, __s, ##__VA_ARGS__) 10 | #define TRACE() PRINTF("[t] %s,%u\n", __FILE__, __LINE__) 11 | #define ERROR() PRINTF("[e] %s,%u\n", __FILE__, __LINE__) 12 | #else 13 | #define TRACE() 14 | #define ERROR() 15 | #define PRINTF(...) 16 | #endif 17 | 18 | 19 | int rtlsdr_rpc_msg_init(rtlsdr_rpc_msg_t* msg, size_t data_size) 20 | { 21 | size_t fmt_size; 22 | 23 | if (data_size == 0) data_size = 64; 24 | 25 | fmt_size = offsetof(rtlsdr_rpc_fmt_t, data) + data_size; 26 | msg->fmt = malloc(fmt_size); 27 | if (msg->fmt == NULL) return -1; 28 | 29 | msg->off = offsetof(rtlsdr_rpc_fmt_t, data); 30 | msg->size = fmt_size; 31 | 32 | return 0; 33 | } 34 | 35 | int rtlsdr_rpc_msg_fini(rtlsdr_rpc_msg_t* msg) 36 | { 37 | free(msg->fmt); 38 | return 0; 39 | } 40 | 41 | void rtlsdr_rpc_msg_reset(rtlsdr_rpc_msg_t* msg) 42 | { 43 | msg->off = offsetof(rtlsdr_rpc_fmt_t, data); 44 | } 45 | 46 | int rtlsdr_rpc_msg_realloc(rtlsdr_rpc_msg_t* msg, size_t size) 47 | { 48 | uint8_t* new_fmt; 49 | 50 | if (msg->size >= size) return 0; 51 | 52 | new_fmt = malloc(size); 53 | if (new_fmt == NULL) return -1; 54 | 55 | memcpy(new_fmt, msg->fmt, msg->off); 56 | free(msg->fmt); 57 | msg->fmt = new_fmt; 58 | msg->size = size; 59 | 60 | return 0; 61 | } 62 | 63 | static int check_size(const rtlsdr_rpc_msg_t* msg, size_t size) 64 | { 65 | if ((msg->off + size) > msg->size) return -1; 66 | return 0; 67 | } 68 | 69 | static int check_size_or_realloc(rtlsdr_rpc_msg_t* msg, size_t size) 70 | { 71 | uint8_t* new_fmt; 72 | size_t new_size; 73 | 74 | if (check_size(msg, size) == 0) return 0; 75 | 76 | new_size = (msg->off + size + 256) & ~(256 - 1); 77 | new_fmt = malloc(new_size); 78 | if (new_fmt == NULL) return -1; 79 | 80 | memcpy(new_fmt, msg->fmt, msg->off); 81 | free(msg->fmt); 82 | 83 | msg->fmt = new_fmt; 84 | msg->size = new_size; 85 | 86 | return 0; 87 | } 88 | 89 | static int pop_uint32(rtlsdr_rpc_msg_t* msg, uint32_t* x) 90 | { 91 | #if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) 92 | #error "unsupported endianness" 93 | #endif 94 | 95 | if (check_size(msg, sizeof(uint32_t))) return -1; 96 | *x = *(const uint32_t*)(msg->fmt + msg->off); 97 | msg->off += sizeof(uint32_t); 98 | return 0; 99 | } 100 | 101 | static void push_mem_safe(rtlsdr_rpc_msg_t* msg, const uint8_t* x, size_t n) 102 | { 103 | memcpy(msg->fmt + msg->off, x, n); 104 | msg->off += n; 105 | } 106 | 107 | static void push_uint32_safe(rtlsdr_rpc_msg_t* msg, uint32_t x) 108 | { 109 | #if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) 110 | #error "unsupported endianness" 111 | #endif 112 | 113 | push_mem_safe(msg, (const uint8_t*)&x, sizeof(uint32_t)); 114 | } 115 | 116 | int rtlsdr_rpc_msg_push_int32(rtlsdr_rpc_msg_t* msg, int x) 117 | { 118 | if (check_size_or_realloc(msg, sizeof(x))) return -1; 119 | push_uint32_safe(msg, (uint32_t)x); 120 | return 0; 121 | } 122 | 123 | int rtlsdr_rpc_msg_push_uint32(rtlsdr_rpc_msg_t* msg, uint32_t x) 124 | { 125 | if (check_size_or_realloc(msg, sizeof(x))) return -1; 126 | push_uint32_safe(msg, x); 127 | return 0; 128 | } 129 | 130 | void rtlsdr_rpc_msg_push_uint32_safe(rtlsdr_rpc_msg_t* msg, uint32_t x) 131 | { 132 | push_uint32_safe(msg, x); 133 | } 134 | 135 | int rtlsdr_rpc_msg_push_str(rtlsdr_rpc_msg_t* msg, const char* s) 136 | { 137 | if (check_size_or_realloc(msg, strlen(s) + 1)) return -1; 138 | push_mem_safe(msg, (const uint8_t*)s, strlen(s) + 1); 139 | return 0; 140 | } 141 | 142 | int rtlsdr_rpc_msg_push_buf(rtlsdr_rpc_msg_t* msg, const uint8_t* buf, size_t size) 143 | { 144 | size_t total_size = sizeof(uint32_t) + size; 145 | if (check_size_or_realloc(msg, total_size)) return -1; 146 | push_uint32_safe(msg, (uint32_t)size); 147 | push_mem_safe(msg, buf, size); 148 | return 0; 149 | } 150 | 151 | void rtlsdr_rpc_msg_skip_safe(rtlsdr_rpc_msg_t* msg, size_t size) 152 | { 153 | msg->off += size; 154 | } 155 | 156 | int rtlsdr_rpc_msg_pop_int(rtlsdr_rpc_msg_t* msg, int* x) 157 | { 158 | return pop_uint32(msg, (uint32_t*)x); 159 | } 160 | 161 | int rtlsdr_rpc_msg_pop_uint32(rtlsdr_rpc_msg_t* msg, uint32_t* x) 162 | { 163 | return pop_uint32(msg, x); 164 | } 165 | 166 | int rtlsdr_rpc_msg_pop_str(rtlsdr_rpc_msg_t* msg, const char** s) 167 | { 168 | size_t i; 169 | 170 | *s = (const char*)(msg->fmt + msg->off); 171 | 172 | for (i = msg->off; i != msg->size; ++i) 173 | { 174 | if (msg->fmt[i] == 0) 175 | { 176 | msg->off = i + 1; 177 | return 0; 178 | } 179 | } 180 | 181 | return -1; 182 | } 183 | 184 | int rtlsdr_rpc_msg_pop_buf 185 | (rtlsdr_rpc_msg_t* msg, const uint8_t** buf, size_t* size) 186 | { 187 | uint32_t x; 188 | 189 | if (pop_uint32(msg, &x)) return -1; 190 | if ((msg->off + x) > msg->size) return -1; 191 | 192 | *buf = (const uint8_t*)(msg->fmt + msg->off); 193 | msg->off += x; 194 | 195 | *size = (size_t)x; 196 | 197 | return 0; 198 | } 199 | 200 | static void put_uint8(void* p, uint8_t x) 201 | { 202 | memcpy(p, (const void*)&x, sizeof(x)); 203 | } 204 | 205 | static void put_uint16(void* p, uint16_t x) 206 | { 207 | #if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) 208 | #error "unsupported endianness" 209 | #endif 210 | 211 | memcpy(p, (const void*)&x, sizeof(x)); 212 | } 213 | 214 | static void put_uint32(void* p, uint32_t x) 215 | { 216 | #if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) 217 | #error "unsupported endianness" 218 | #endif 219 | 220 | memcpy(p, (const void*)&x, sizeof(x)); 221 | } 222 | 223 | static uint8_t get_uint8(const void* p) 224 | { 225 | uint8_t x; 226 | memcpy((void*)&x, p, sizeof(x)); 227 | return x; 228 | } 229 | 230 | /* 231 | static uint16_t get_uint16(const void* p) 232 | { 233 | #if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) 234 | #error "unsupported endianness" 235 | #endif 236 | 237 | uint16_t x; 238 | memcpy((void*)&x, p, sizeof(x)); 239 | return x; 240 | } 241 | */ 242 | 243 | static uint32_t get_uint32(const void* p) 244 | { 245 | #if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) 246 | #error "unsupported endianness" 247 | #endif 248 | 249 | uint32_t x; 250 | memcpy((void*)&x, p, sizeof(x)); 251 | return x; 252 | } 253 | 254 | void rtlsdr_rpc_msg_set_size(rtlsdr_rpc_msg_t* msg, size_t size) 255 | { 256 | rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; 257 | put_uint32(&fmt->size, (uint32_t)size); 258 | } 259 | 260 | size_t rtlsdr_rpc_msg_get_size(const rtlsdr_rpc_msg_t* msg) 261 | { 262 | const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; 263 | return (size_t)get_uint32(&fmt->size); 264 | } 265 | 266 | void rtlsdr_rpc_msg_set_op(rtlsdr_rpc_msg_t* msg, rtlsdr_rpc_op_t op) 267 | { 268 | rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; 269 | put_uint8(&fmt->op, (uint8_t)op); 270 | } 271 | 272 | rtlsdr_rpc_op_t rtlsdr_rpc_msg_get_op(const rtlsdr_rpc_msg_t* msg) 273 | { 274 | const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; 275 | return (rtlsdr_rpc_op_t)get_uint8(&fmt->op); 276 | } 277 | 278 | void rtlsdr_rpc_msg_set_id(rtlsdr_rpc_msg_t* msg, uint8_t id) 279 | { 280 | rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; 281 | put_uint16(&fmt->id, id); 282 | } 283 | 284 | uint8_t rtlsdr_rpc_msg_get_id(const rtlsdr_rpc_msg_t* msg) 285 | { 286 | const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; 287 | return get_uint8(&fmt->id); 288 | } 289 | 290 | void rtlsdr_rpc_msg_set_err(rtlsdr_rpc_msg_t* msg, int err) 291 | { 292 | rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; 293 | put_uint32(&fmt->err, (uint32_t)err); 294 | } 295 | 296 | int rtlsdr_rpc_msg_get_err(const rtlsdr_rpc_msg_t* msg) 297 | { 298 | const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; 299 | return (int)get_uint32(&fmt->err); 300 | } 301 | -------------------------------------------------------------------------------- /src/tuner_e4k.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Elonics E4000 tuner driver 3 | * 4 | * (C) 2011-2012 by Harald Welte 5 | * (C) 2012 by Sylvain Munaut 6 | * (C) 2012 by Hoernchen 7 | * 8 | * All Rights Reserved 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 35 | 36 | /* If this is defined, the limits are somewhat relaxed compared to what the 37 | * vendor claims is possible */ 38 | #define OUT_OF_SPEC 39 | 40 | #define MHZ(x) ((x)*1000*1000) 41 | #define KHZ(x) ((x)*1000) 42 | 43 | uint32_t unsigned_delta(uint32_t a, uint32_t b) 44 | { 45 | if (a > b) 46 | return a - b; 47 | else 48 | return b - a; 49 | } 50 | 51 | /* look-up table bit-width -> mask */ 52 | static const uint8_t width2mask[] = { 53 | 0, 1, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0xff 54 | }; 55 | 56 | /*********************************************************************** 57 | * Register Access */ 58 | 59 | /*! \brief Write a register of the tuner chip 60 | * \param[in] e4k reference to the tuner 61 | * \param[in] reg number of the register 62 | * \param[in] val value to be written 63 | * \returns 0 on success, negative in case of error 64 | */ 65 | static int e4k_reg_write(struct e4k_state *e4k, uint8_t reg, uint8_t val) 66 | { 67 | int r; 68 | uint8_t data[2]; 69 | data[0] = reg; 70 | data[1] = val; 71 | 72 | r = rtlsdr_i2c_write_fn(e4k->rtl_dev, e4k->i2c_addr, data, 2); 73 | return r == 2 ? 0 : -1; 74 | } 75 | 76 | /*! \brief Read a register of the tuner chip 77 | * \param[in] e4k reference to the tuner 78 | * \param[in] reg number of the register 79 | * \returns positive 8bit register contents on success, negative in case of error 80 | */ 81 | static int e4k_reg_read(struct e4k_state *e4k, uint8_t reg) 82 | { 83 | uint8_t data = reg; 84 | 85 | if (rtlsdr_i2c_write_fn(e4k->rtl_dev, e4k->i2c_addr, &data, 1) < 1) 86 | return -1; 87 | 88 | if (rtlsdr_i2c_read_fn(e4k->rtl_dev, e4k->i2c_addr, &data, 1) < 1) 89 | return -1; 90 | 91 | return data; 92 | } 93 | 94 | /*! \brief Set or clear some (masked) bits inside a register 95 | * \param[in] e4k reference to the tuner 96 | * \param[in] reg number of the register 97 | * \param[in] mask bit-mask of the value 98 | * \param[in] val data value to be written to register 99 | * \returns 0 on success, negative in case of error 100 | */ 101 | static int e4k_reg_set_mask(struct e4k_state *e4k, uint8_t reg, 102 | uint8_t mask, uint8_t val) 103 | { 104 | uint8_t tmp = e4k_reg_read(e4k, reg); 105 | 106 | if ((tmp & mask) == val) 107 | return 0; 108 | 109 | return e4k_reg_write(e4k, reg, (tmp & ~mask) | (val & mask)); 110 | } 111 | 112 | /*! \brief Write a given field inside a register 113 | * \param[in] e4k reference to the tuner 114 | * \param[in] field structure describing the field 115 | * \param[in] val value to be written 116 | * \returns 0 on success, negative in case of error 117 | */ 118 | static int e4k_field_write(struct e4k_state *e4k, const struct reg_field *field, uint8_t val) 119 | { 120 | int rc; 121 | uint8_t mask; 122 | 123 | rc = e4k_reg_read(e4k, field->reg); 124 | if (rc < 0) 125 | return rc; 126 | 127 | mask = width2mask[field->width] << field->shift; 128 | 129 | return e4k_reg_set_mask(e4k, field->reg, mask, val << field->shift); 130 | } 131 | 132 | /*! \brief Read a given field inside a register 133 | * \param[in] e4k reference to the tuner 134 | * \param[in] field structure describing the field 135 | * \returns positive value of the field, negative in case of error 136 | */ 137 | static int e4k_field_read(struct e4k_state *e4k, const struct reg_field *field) 138 | { 139 | int rc; 140 | 141 | rc = e4k_reg_read(e4k, field->reg); 142 | if (rc < 0) 143 | return rc; 144 | 145 | rc = (rc >> field->shift) & width2mask[field->width]; 146 | 147 | return rc; 148 | } 149 | 150 | /*********************************************************************** 151 | * Filter Control */ 152 | 153 | static const uint32_t rf_filt_center_uhf[] = { 154 | MHZ(360), MHZ(380), MHZ(405), MHZ(425), 155 | MHZ(450), MHZ(475), MHZ(505), MHZ(540), 156 | MHZ(575), MHZ(615), MHZ(670), MHZ(720), 157 | MHZ(760), MHZ(840), MHZ(890), MHZ(970) 158 | }; 159 | 160 | static const uint32_t rf_filt_center_l[] = { 161 | MHZ(1300), MHZ(1320), MHZ(1360), MHZ(1410), 162 | MHZ(1445), MHZ(1460), MHZ(1490), MHZ(1530), 163 | MHZ(1560), MHZ(1590), MHZ(1640), MHZ(1660), 164 | MHZ(1680), MHZ(1700), MHZ(1720), MHZ(1750) 165 | }; 166 | 167 | static int closest_arr_idx(const uint32_t *arr, unsigned int arr_size, uint32_t freq) 168 | { 169 | unsigned int i, bi = 0; 170 | uint32_t best_delta = 0xffffffff; 171 | 172 | /* iterate over the array containing a list of the center 173 | * frequencies, selecting the closest one */ 174 | for (i = 0; i < arr_size; i++) { 175 | uint32_t delta = unsigned_delta(freq, arr[i]); 176 | if (delta < best_delta) { 177 | best_delta = delta; 178 | bi = i; 179 | } 180 | } 181 | 182 | return bi; 183 | } 184 | 185 | /* return 4-bit index as to which RF filter to select */ 186 | static int choose_rf_filter(enum e4k_band band, uint32_t freq) 187 | { 188 | int rc; 189 | 190 | switch (band) { 191 | case E4K_BAND_VHF2: 192 | case E4K_BAND_VHF3: 193 | rc = 0; 194 | break; 195 | case E4K_BAND_UHF: 196 | rc = closest_arr_idx(rf_filt_center_uhf, 197 | ARRAY_SIZE(rf_filt_center_uhf), 198 | freq); 199 | break; 200 | case E4K_BAND_L: 201 | rc = closest_arr_idx(rf_filt_center_l, 202 | ARRAY_SIZE(rf_filt_center_l), 203 | freq); 204 | break; 205 | default: 206 | rc = -EINVAL; 207 | break; 208 | } 209 | 210 | return rc; 211 | } 212 | 213 | /* \brief Automatically select apropriate RF filter based on e4k state */ 214 | int e4k_rf_filter_set(struct e4k_state *e4k) 215 | { 216 | int rc; 217 | 218 | rc = choose_rf_filter(e4k->band, e4k->vco.flo); 219 | if (rc < 0) 220 | return rc; 221 | 222 | return e4k_reg_set_mask(e4k, E4K_REG_FILT1, 0xF, rc); 223 | } 224 | 225 | /* Mixer Filter */ 226 | static const uint32_t mix_filter_bw[] = { 227 | KHZ(27000), KHZ(27000), KHZ(27000), KHZ(27000), 228 | KHZ(27000), KHZ(27000), KHZ(27000), KHZ(27000), 229 | KHZ(4600), KHZ(4200), KHZ(3800), KHZ(3400), 230 | KHZ(3300), KHZ(2700), KHZ(2300), KHZ(1900) 231 | }; 232 | 233 | /* IF RC Filter */ 234 | static const uint32_t ifrc_filter_bw[] = { 235 | KHZ(21400), KHZ(21000), KHZ(17600), KHZ(14700), 236 | KHZ(12400), KHZ(10600), KHZ(9000), KHZ(7700), 237 | KHZ(6400), KHZ(5300), KHZ(4400), KHZ(3400), 238 | KHZ(2600), KHZ(1800), KHZ(1200), KHZ(1000) 239 | }; 240 | 241 | /* IF Channel Filter */ 242 | static const uint32_t ifch_filter_bw[] = { 243 | KHZ(5500), KHZ(5300), KHZ(5000), KHZ(4800), 244 | KHZ(4600), KHZ(4400), KHZ(4300), KHZ(4100), 245 | KHZ(3900), KHZ(3800), KHZ(3700), KHZ(3600), 246 | KHZ(3400), KHZ(3300), KHZ(3200), KHZ(3100), 247 | KHZ(3000), KHZ(2950), KHZ(2900), KHZ(2800), 248 | KHZ(2750), KHZ(2700), KHZ(2600), KHZ(2550), 249 | KHZ(2500), KHZ(2450), KHZ(2400), KHZ(2300), 250 | KHZ(2280), KHZ(2240), KHZ(2200), KHZ(2150) 251 | }; 252 | 253 | static const uint32_t *if_filter_bw[] = { 254 | mix_filter_bw, 255 | ifch_filter_bw, 256 | ifrc_filter_bw, 257 | }; 258 | 259 | static const uint32_t if_filter_bw_len[] = { 260 | ARRAY_SIZE(mix_filter_bw), 261 | ARRAY_SIZE(ifch_filter_bw), 262 | ARRAY_SIZE(ifrc_filter_bw), 263 | }; 264 | 265 | static const struct reg_field if_filter_fields[] = { 266 | { 267 | E4K_REG_FILT2, 4, 4, 268 | }, 269 | { 270 | E4K_REG_FILT3, 0, 5, 271 | }, 272 | { 273 | E4K_REG_FILT2, 0, 4, 274 | } 275 | }; 276 | 277 | static int find_if_bw(enum e4k_if_filter filter, uint32_t bw) 278 | { 279 | if (filter >= ARRAY_SIZE(if_filter_bw)) 280 | return -EINVAL; 281 | 282 | return closest_arr_idx(if_filter_bw[filter], 283 | if_filter_bw_len[filter], bw); 284 | } 285 | 286 | /*! \brief Set the filter band-width of any of the IF filters 287 | * \param[in] e4k reference to the tuner chip 288 | * \param[in] filter filter to be configured 289 | * \param[in] bandwidth bandwidth to be configured 290 | * \returns positive actual filter band-width, negative in case of error 291 | */ 292 | int e4k_if_filter_bw_set(struct e4k_state *e4k, enum e4k_if_filter filter, 293 | uint32_t bandwidth) 294 | { 295 | int bw_idx; 296 | const struct reg_field *field; 297 | 298 | if (filter >= ARRAY_SIZE(if_filter_bw)) 299 | return -EINVAL; 300 | 301 | bw_idx = find_if_bw(filter, bandwidth); 302 | 303 | field = &if_filter_fields[filter]; 304 | 305 | return e4k_field_write(e4k, field, bw_idx); 306 | } 307 | 308 | /*! \brief Enables / Disables the channel filter 309 | * \param[in] e4k reference to the tuner chip 310 | * \param[in] on 1=filter enabled, 0=filter disabled 311 | * \returns 0 success, negative errors 312 | */ 313 | int e4k_if_filter_chan_enable(struct e4k_state *e4k, int on) 314 | { 315 | return e4k_reg_set_mask(e4k, E4K_REG_FILT3, E4K_FILT3_DISABLE, 316 | on ? 0 : E4K_FILT3_DISABLE); 317 | } 318 | 319 | int e4k_if_filter_bw_get(struct e4k_state *e4k, enum e4k_if_filter filter) 320 | { 321 | const uint32_t *arr; 322 | int rc; 323 | const struct reg_field *field; 324 | 325 | if (filter >= ARRAY_SIZE(if_filter_bw)) 326 | return -EINVAL; 327 | 328 | field = &if_filter_fields[filter]; 329 | 330 | rc = e4k_field_read(e4k, field); 331 | if (rc < 0) 332 | return rc; 333 | 334 | arr = if_filter_bw[filter]; 335 | 336 | return arr[rc]; 337 | } 338 | 339 | 340 | /*********************************************************************** 341 | * Frequency Control */ 342 | 343 | #define E4K_FVCO_MIN_KHZ 2600000 /* 2.6 GHz */ 344 | #define E4K_FVCO_MAX_KHZ 3900000 /* 3.9 GHz */ 345 | #define E4K_PLL_Y 65536 346 | 347 | #ifdef OUT_OF_SPEC 348 | #define E4K_FLO_MIN_MHZ 50 349 | #define E4K_FLO_MAX_MHZ 2200UL 350 | #else 351 | #define E4K_FLO_MIN_MHZ 64 352 | #define E4K_FLO_MAX_MHZ 1700 353 | #endif 354 | 355 | struct pll_settings { 356 | uint32_t freq; 357 | uint8_t reg_synth7; 358 | uint8_t mult; 359 | }; 360 | 361 | static const struct pll_settings pll_vars[] = { 362 | {KHZ(72400), (1 << 3) | 7, 48}, 363 | {KHZ(81200), (1 << 3) | 6, 40}, 364 | {KHZ(108300), (1 << 3) | 5, 32}, 365 | {KHZ(162500), (1 << 3) | 4, 24}, 366 | {KHZ(216600), (1 << 3) | 3, 16}, 367 | {KHZ(325000), (1 << 3) | 2, 12}, 368 | {KHZ(350000), (1 << 3) | 1, 8}, 369 | {KHZ(432000), (0 << 3) | 3, 8}, 370 | {KHZ(667000), (0 << 3) | 2, 6}, 371 | {KHZ(1200000), (0 << 3) | 1, 4} 372 | }; 373 | /* 374 | static int is_fvco_valid(uint32_t fvco_z) 375 | { 376 | if (fvco_z/1000 < E4K_FVCO_MIN_KHZ || 377 | fvco_z/1000 > E4K_FVCO_MAX_KHZ) { 378 | fprintf(stderr, "[E4K] Fvco %u invalid\n", fvco_z); 379 | return 0; 380 | } 381 | 382 | return 1; 383 | } 384 | */ 385 | static int is_fosc_valid(uint32_t fosc) 386 | { 387 | if (fosc < MHZ(16) || fosc > MHZ(30)) { 388 | fprintf(stderr, "[E4K] Fosc %u invalid\n", fosc); 389 | return 0; 390 | } 391 | 392 | return 1; 393 | } 394 | /* 395 | static int is_z_valid(uint32_t z) 396 | { 397 | if (z > 255) { 398 | fprintf(stderr, "[E4K] Z %u invalid\n", z); 399 | return 0; 400 | } 401 | 402 | return 1; 403 | } 404 | */ 405 | /*! \brief Determine if 3-phase mixing shall be used or not */ 406 | /* 407 | static int use_3ph_mixing(uint32_t flo) 408 | { 409 | if (flo < MHZ(350)) 410 | return 1; 411 | 412 | return 0; 413 | } 414 | */ 415 | /* \brief compute Fvco based on Fosc, Z and X 416 | * \returns positive value (Fvco in Hz), 0 in case of error */ 417 | static uint64_t compute_fvco(uint32_t f_osc, uint8_t z, uint16_t x) 418 | { 419 | uint64_t fvco_z, fvco_x, fvco; 420 | 421 | /* We use the following transformation in order to 422 | * handle the fractional part with integer arithmetic: 423 | * Fvco = Fosc * (Z + X/Y) <=> Fvco = Fosc * Z + (Fosc * X)/Y 424 | * This avoids X/Y = 0. However, then we would overflow a 32bit 425 | * integer, as we cannot hold e.g. 26 MHz * 65536 either. 426 | */ 427 | fvco_z = (uint64_t)f_osc * z; 428 | 429 | #if 0 430 | if (!is_fvco_valid(fvco_z)) 431 | return 0; 432 | #endif 433 | 434 | fvco_x = ((uint64_t)f_osc * x) / E4K_PLL_Y; 435 | 436 | fvco = fvco_z + fvco_x; 437 | 438 | return fvco; 439 | } 440 | 441 | static uint32_t compute_flo(uint32_t f_osc, uint8_t z, uint16_t x, uint8_t r) 442 | { 443 | uint64_t fvco = compute_fvco(f_osc, z, x); 444 | if (fvco == 0) 445 | return -EINVAL; 446 | 447 | return fvco / r; 448 | } 449 | 450 | static int e4k_band_set(struct e4k_state *e4k, enum e4k_band band) 451 | { 452 | int rc; 453 | 454 | switch (band) { 455 | case E4K_BAND_VHF2: 456 | case E4K_BAND_VHF3: 457 | case E4K_BAND_UHF: 458 | e4k_reg_write(e4k, E4K_REG_BIAS, 3); 459 | break; 460 | case E4K_BAND_L: 461 | e4k_reg_write(e4k, E4K_REG_BIAS, 0); 462 | break; 463 | } 464 | 465 | /* workaround: if we don't reset this register before writing to it, 466 | * we get a gap between 325-350 MHz */ 467 | rc = e4k_reg_set_mask(e4k, E4K_REG_SYNTH1, 0x06, 0); 468 | rc = e4k_reg_set_mask(e4k, E4K_REG_SYNTH1, 0x06, band << 1); 469 | if (rc >= 0) 470 | e4k->band = band; 471 | 472 | return rc; 473 | } 474 | 475 | /*! \brief Compute PLL parameters for givent target frequency 476 | * \param[out] oscp Oscillator parameters, if computation successful 477 | * \param[in] fosc Clock input frequency applied to the chip (Hz) 478 | * \param[in] intended_flo target tuning frequency (Hz) 479 | * \returns actual PLL frequency, as close as possible to intended_flo, 480 | * 0 in case of error 481 | */ 482 | uint32_t e4k_compute_pll_params(struct e4k_pll_params *oscp, uint32_t fosc, uint32_t intended_flo) 483 | { 484 | uint32_t i; 485 | uint8_t r = 2; 486 | uint64_t intended_fvco, remainder; 487 | uint64_t z = 0; 488 | uint32_t x; 489 | int flo; 490 | int three_phase_mixing = 0; 491 | oscp->r_idx = 0; 492 | 493 | if (!is_fosc_valid(fosc)) 494 | return 0; 495 | 496 | for(i = 0; i < ARRAY_SIZE(pll_vars); ++i) { 497 | if(intended_flo < pll_vars[i].freq) { 498 | three_phase_mixing = (pll_vars[i].reg_synth7 & 0x08) ? 1 : 0; 499 | oscp->r_idx = pll_vars[i].reg_synth7; 500 | r = pll_vars[i].mult; 501 | break; 502 | } 503 | } 504 | 505 | //fprintf(stderr, "[E4K] Fint=%u, R=%u\n", intended_flo, r); 506 | 507 | /* flo(max) = 1700MHz, R(max) = 48, we need 64bit! */ 508 | intended_fvco = (uint64_t)intended_flo * r; 509 | 510 | /* compute integral component of multiplier */ 511 | z = intended_fvco / fosc; 512 | 513 | /* compute fractional part. this will not overflow, 514 | * as fosc(max) = 30MHz and z(max) = 255 */ 515 | remainder = intended_fvco - (fosc * z); 516 | /* remainder(max) = 30MHz, E4K_PLL_Y = 65536 -> 64bit! */ 517 | x = (remainder * E4K_PLL_Y) / fosc; 518 | /* x(max) as result of this computation is 65536 */ 519 | 520 | flo = compute_flo(fosc, z, x, r); 521 | 522 | oscp->fosc = fosc; 523 | oscp->flo = flo; 524 | oscp->intended_flo = intended_flo; 525 | oscp->r = r; 526 | // oscp->r_idx = pll_vars[i].reg_synth7 & 0x0; 527 | oscp->threephase = three_phase_mixing; 528 | oscp->x = x; 529 | oscp->z = z; 530 | 531 | return flo; 532 | } 533 | 534 | int e4k_tune_params(struct e4k_state *e4k, struct e4k_pll_params *p) 535 | { 536 | /* program R + 3phase/2phase */ 537 | e4k_reg_write(e4k, E4K_REG_SYNTH7, p->r_idx); 538 | /* program Z */ 539 | e4k_reg_write(e4k, E4K_REG_SYNTH3, p->z); 540 | /* program X */ 541 | e4k_reg_write(e4k, E4K_REG_SYNTH4, p->x & 0xff); 542 | e4k_reg_write(e4k, E4K_REG_SYNTH5, p->x >> 8); 543 | 544 | /* we're in auto calibration mode, so there's no need to trigger it */ 545 | 546 | memcpy(&e4k->vco, p, sizeof(e4k->vco)); 547 | 548 | /* set the band */ 549 | if (e4k->vco.flo < MHZ(140)) 550 | e4k_band_set(e4k, E4K_BAND_VHF2); 551 | else if (e4k->vco.flo < MHZ(350)) 552 | e4k_band_set(e4k, E4K_BAND_VHF3); 553 | else if (e4k->vco.flo < MHZ(1135)) 554 | e4k_band_set(e4k, E4K_BAND_UHF); 555 | else 556 | e4k_band_set(e4k, E4K_BAND_L); 557 | 558 | /* select and set proper RF filter */ 559 | e4k_rf_filter_set(e4k); 560 | 561 | return e4k->vco.flo; 562 | } 563 | 564 | /*! \brief High-level tuning API, just specify frquency 565 | * 566 | * This function will compute matching PLL parameters, program them into the 567 | * hardware and set the band as well as RF filter. 568 | * 569 | * \param[in] e4k reference to tuner 570 | * \param[in] freq frequency in Hz 571 | * \returns actual tuned frequency, negative in case of error 572 | */ 573 | int e4k_tune_freq(struct e4k_state *e4k, uint32_t freq) 574 | { 575 | uint32_t rc; 576 | struct e4k_pll_params p; 577 | 578 | /* determine PLL parameters */ 579 | rc = e4k_compute_pll_params(&p, e4k->vco.fosc, freq); 580 | if (!rc) 581 | return -EINVAL; 582 | 583 | /* actually tune to those parameters */ 584 | rc = e4k_tune_params(e4k, &p); 585 | 586 | /* check PLL lock */ 587 | rc = e4k_reg_read(e4k, E4K_REG_SYNTH1); 588 | if (!(rc & 0x01)) { 589 | fprintf(stderr, "[E4K] PLL not locked for %u Hz!\n", freq); 590 | return -1; 591 | } 592 | 593 | return 0; 594 | } 595 | 596 | /*********************************************************************** 597 | * Gain Control */ 598 | 599 | static const int8_t if_stage1_gain[] = { 600 | -3, 6 601 | }; 602 | 603 | static const int8_t if_stage23_gain[] = { 604 | 0, 3, 6, 9 605 | }; 606 | 607 | static const int8_t if_stage4_gain[] = { 608 | 0, 1, 2, 2 609 | }; 610 | 611 | static const int8_t if_stage56_gain[] = { 612 | 3, 6, 9, 12, 15, 15, 15, 15 613 | }; 614 | 615 | static const int8_t *if_stage_gain[] = { 616 | 0, 617 | if_stage1_gain, 618 | if_stage23_gain, 619 | if_stage23_gain, 620 | if_stage4_gain, 621 | if_stage56_gain, 622 | if_stage56_gain 623 | }; 624 | 625 | static const uint8_t if_stage_gain_len[] = { 626 | 0, 627 | ARRAY_SIZE(if_stage1_gain), 628 | ARRAY_SIZE(if_stage23_gain), 629 | ARRAY_SIZE(if_stage23_gain), 630 | ARRAY_SIZE(if_stage4_gain), 631 | ARRAY_SIZE(if_stage56_gain), 632 | ARRAY_SIZE(if_stage56_gain) 633 | }; 634 | 635 | static const struct reg_field if_stage_gain_regs[] = { 636 | { 0, 0, 0 }, 637 | { E4K_REG_GAIN3, 0, 1 }, 638 | { E4K_REG_GAIN3, 1, 2 }, 639 | { E4K_REG_GAIN3, 3, 2 }, 640 | { E4K_REG_GAIN3, 5, 2 }, 641 | { E4K_REG_GAIN4, 0, 3 }, 642 | { E4K_REG_GAIN4, 3, 3 } 643 | }; 644 | 645 | static const int32_t lnagain[] = { 646 | -50, 0, 647 | -25, 1, 648 | 0, 4, 649 | 25, 5, 650 | 50, 6, 651 | 75, 7, 652 | 100, 8, 653 | 125, 9, 654 | 150, 10, 655 | 175, 11, 656 | 200, 12, 657 | 250, 13, 658 | 300, 14, 659 | }; 660 | 661 | static const int32_t enhgain[] = { 662 | 10, 30, 50, 70 663 | }; 664 | 665 | int e4k_set_lna_gain(struct e4k_state *e4k, int32_t gain) 666 | { 667 | uint32_t i; 668 | for(i = 0; i < ARRAY_SIZE(lnagain)/2; ++i) { 669 | if(lnagain[i*2] == gain) { 670 | e4k_reg_set_mask(e4k, E4K_REG_GAIN1, 0xf, lnagain[i*2+1]); 671 | return gain; 672 | } 673 | } 674 | return -EINVAL; 675 | } 676 | 677 | int e4k_set_enh_gain(struct e4k_state *e4k, int32_t gain) 678 | { 679 | uint32_t i; 680 | for(i = 0; i < ARRAY_SIZE(enhgain); ++i) { 681 | if(enhgain[i] == gain) { 682 | e4k_reg_set_mask(e4k, E4K_REG_AGC11, 0x7, E4K_AGC11_LNA_GAIN_ENH | (i << 1)); 683 | return gain; 684 | } 685 | } 686 | e4k_reg_set_mask(e4k, E4K_REG_AGC11, 0x7, 0); 687 | 688 | /* special case: 0 = off*/ 689 | if(0 == gain) 690 | return 0; 691 | else 692 | return -EINVAL; 693 | } 694 | 695 | int e4k_enable_manual_gain(struct e4k_state *e4k, uint8_t manual) 696 | { 697 | if (manual) { 698 | /* Set LNA mode to manual */ 699 | e4k_reg_set_mask(e4k, E4K_REG_AGC1, E4K_AGC1_MOD_MASK, E4K_AGC_MOD_SERIAL); 700 | 701 | /* Set Mixer Gain Control to manual */ 702 | e4k_reg_set_mask(e4k, E4K_REG_AGC7, E4K_AGC7_MIX_GAIN_AUTO, 0); 703 | } else { 704 | /* Set LNA mode to auto */ 705 | e4k_reg_set_mask(e4k, E4K_REG_AGC1, E4K_AGC1_MOD_MASK, E4K_AGC_MOD_IF_SERIAL_LNA_AUTON); 706 | /* Set Mixer Gain Control to auto */ 707 | e4k_reg_set_mask(e4k, E4K_REG_AGC7, E4K_AGC7_MIX_GAIN_AUTO, 1); 708 | 709 | e4k_reg_set_mask(e4k, E4K_REG_AGC11, 0x7, 0); 710 | } 711 | 712 | return 0; 713 | } 714 | 715 | static int find_stage_gain(uint8_t stage, int8_t val) 716 | { 717 | const int8_t *arr; 718 | int i; 719 | 720 | if (stage >= ARRAY_SIZE(if_stage_gain)) 721 | return -EINVAL; 722 | 723 | arr = if_stage_gain[stage]; 724 | 725 | for (i = 0; i < if_stage_gain_len[stage]; i++) { 726 | if (arr[i] == val) 727 | return i; 728 | } 729 | return -EINVAL; 730 | } 731 | 732 | /*! \brief Set the gain of one of the IF gain stages 733 | * \param [e4k] handle to the tuner chip 734 | * \param [stage] number of the stage (1..6) 735 | * \param [value] gain value in dB 736 | * \returns 0 on success, negative in case of error 737 | */ 738 | int e4k_if_gain_set(struct e4k_state *e4k, uint8_t stage, int8_t value) 739 | { 740 | int rc; 741 | uint8_t mask; 742 | const struct reg_field *field; 743 | 744 | rc = find_stage_gain(stage, value); 745 | if (rc < 0) 746 | return rc; 747 | 748 | /* compute the bit-mask for the given gain field */ 749 | field = &if_stage_gain_regs[stage]; 750 | mask = width2mask[field->width] << field->shift; 751 | 752 | return e4k_reg_set_mask(e4k, field->reg, mask, rc << field->shift); 753 | } 754 | 755 | int e4k_mixer_gain_set(struct e4k_state *e4k, int8_t value) 756 | { 757 | uint8_t bit; 758 | 759 | switch (value) { 760 | case 4: 761 | bit = 0; 762 | break; 763 | case 12: 764 | bit = 1; 765 | break; 766 | default: 767 | return -EINVAL; 768 | } 769 | 770 | return e4k_reg_set_mask(e4k, E4K_REG_GAIN2, 1, bit); 771 | } 772 | 773 | int e4k_commonmode_set(struct e4k_state *e4k, int8_t value) 774 | { 775 | if(value < 0) 776 | return -EINVAL; 777 | else if(value > 7) 778 | return -EINVAL; 779 | 780 | return e4k_reg_set_mask(e4k, E4K_REG_DC7, 7, value); 781 | } 782 | 783 | /*********************************************************************** 784 | * DC Offset */ 785 | 786 | int e4k_manual_dc_offset(struct e4k_state *e4k, int8_t iofs, int8_t irange, int8_t qofs, int8_t qrange) 787 | { 788 | int res; 789 | 790 | if((iofs < 0x00) || (iofs > 0x3f)) 791 | return -EINVAL; 792 | if((irange < 0x00) || (irange > 0x03)) 793 | return -EINVAL; 794 | if((qofs < 0x00) || (qofs > 0x3f)) 795 | return -EINVAL; 796 | if((qrange < 0x00) || (qrange > 0x03)) 797 | return -EINVAL; 798 | 799 | res = e4k_reg_set_mask(e4k, E4K_REG_DC2, 0x3f, iofs); 800 | if(res < 0) 801 | return res; 802 | 803 | res = e4k_reg_set_mask(e4k, E4K_REG_DC3, 0x3f, qofs); 804 | if(res < 0) 805 | return res; 806 | 807 | res = e4k_reg_set_mask(e4k, E4K_REG_DC4, 0x33, (qrange << 4) | irange); 808 | return res; 809 | } 810 | 811 | /*! \brief Perform a DC offset calibration right now 812 | * \param [e4k] handle to the tuner chip 813 | */ 814 | int e4k_dc_offset_calibrate(struct e4k_state *e4k) 815 | { 816 | /* make sure the DC range detector is enabled */ 817 | e4k_reg_set_mask(e4k, E4K_REG_DC5, E4K_DC5_RANGE_DET_EN, E4K_DC5_RANGE_DET_EN); 818 | 819 | return e4k_reg_write(e4k, E4K_REG_DC1, 0x01); 820 | } 821 | 822 | 823 | static const int8_t if_gains_max[] = { 824 | 0, 6, 9, 9, 2, 15, 15 825 | }; 826 | 827 | struct gain_comb { 828 | int8_t mixer_gain; 829 | int8_t if1_gain; 830 | uint8_t reg; 831 | }; 832 | 833 | static const struct gain_comb dc_gain_comb[] = { 834 | { 4, -3, 0x50 }, 835 | { 4, 6, 0x51 }, 836 | { 12, -3, 0x52 }, 837 | { 12, 6, 0x53 }, 838 | }; 839 | 840 | #define TO_LUT(offset, range) (offset | (range << 6)) 841 | 842 | int e4k_dc_offset_gen_table(struct e4k_state *e4k) 843 | { 844 | uint32_t i; 845 | 846 | /* FIXME: read ont current gain values and write them back 847 | * before returning to the caller */ 848 | 849 | /* disable auto mixer gain */ 850 | e4k_reg_set_mask(e4k, E4K_REG_AGC7, E4K_AGC7_MIX_GAIN_AUTO, 0); 851 | 852 | /* set LNA/IF gain to full manual */ 853 | e4k_reg_set_mask(e4k, E4K_REG_AGC1, E4K_AGC1_MOD_MASK, 854 | E4K_AGC_MOD_SERIAL); 855 | 856 | /* set all 'other' gains to maximum */ 857 | for (i = 2; i <= 6; i++) 858 | e4k_if_gain_set(e4k, i, if_gains_max[i]); 859 | 860 | /* iterate over all mixer + if_stage_1 gain combinations */ 861 | for (i = 0; i < ARRAY_SIZE(dc_gain_comb); i++) { 862 | uint8_t offs_i, offs_q, range, range_i, range_q; 863 | 864 | /* set the combination of mixer / if1 gain */ 865 | e4k_mixer_gain_set(e4k, dc_gain_comb[i].mixer_gain); 866 | e4k_if_gain_set(e4k, 1, dc_gain_comb[i].if1_gain); 867 | 868 | /* perform actual calibration */ 869 | e4k_dc_offset_calibrate(e4k); 870 | 871 | /* extract I/Q offset and range values */ 872 | offs_i = e4k_reg_read(e4k, E4K_REG_DC2) & 0x3f; 873 | offs_q = e4k_reg_read(e4k, E4K_REG_DC3) & 0x3f; 874 | range = e4k_reg_read(e4k, E4K_REG_DC4); 875 | range_i = range & 0x3; 876 | range_q = (range >> 4) & 0x3; 877 | 878 | fprintf(stderr, "[E4K] Table %u I=%u/%u, Q=%u/%u\n", 879 | i, range_i, offs_i, range_q, offs_q); 880 | 881 | /* write into the table */ 882 | e4k_reg_write(e4k, dc_gain_comb[i].reg, 883 | TO_LUT(offs_q, range_q)); 884 | e4k_reg_write(e4k, dc_gain_comb[i].reg + 0x10, 885 | TO_LUT(offs_i, range_i)); 886 | } 887 | 888 | return 0; 889 | } 890 | 891 | /*********************************************************************** 892 | * Standby */ 893 | 894 | /*! \brief Enable/disable standby mode 895 | */ 896 | int e4k_standby(struct e4k_state *e4k, int enable) 897 | { 898 | e4k_reg_set_mask(e4k, E4K_REG_MASTER1, E4K_MASTER1_NORM_STBY, 899 | enable ? 0 : E4K_MASTER1_NORM_STBY); 900 | 901 | return 0; 902 | } 903 | 904 | /*********************************************************************** 905 | * Initialization */ 906 | 907 | static int magic_init(struct e4k_state *e4k) 908 | { 909 | e4k_reg_write(e4k, 0x7e, 0x01); 910 | e4k_reg_write(e4k, 0x7f, 0xfe); 911 | e4k_reg_write(e4k, 0x82, 0x00); 912 | e4k_reg_write(e4k, 0x86, 0x50); /* polarity A */ 913 | e4k_reg_write(e4k, 0x87, 0x20); 914 | e4k_reg_write(e4k, 0x88, 0x01); 915 | e4k_reg_write(e4k, 0x9f, 0x7f); 916 | e4k_reg_write(e4k, 0xa0, 0x07); 917 | 918 | return 0; 919 | } 920 | 921 | /*! \brief Initialize the E4K tuner 922 | */ 923 | int e4k_init(struct e4k_state *e4k) 924 | { 925 | /* make a dummy i2c read or write command, will not be ACKed! */ 926 | e4k_reg_read(e4k, 0); 927 | 928 | /* Make sure we reset everything and clear POR indicator */ 929 | e4k_reg_write(e4k, E4K_REG_MASTER1, 930 | E4K_MASTER1_RESET | 931 | E4K_MASTER1_NORM_STBY | 932 | E4K_MASTER1_POR_DET 933 | ); 934 | 935 | /* Configure clock input */ 936 | e4k_reg_write(e4k, E4K_REG_CLK_INP, 0x00); 937 | 938 | /* Disable clock output */ 939 | e4k_reg_write(e4k, E4K_REG_REF_CLK, 0x00); 940 | e4k_reg_write(e4k, E4K_REG_CLKOUT_PWDN, 0x96); 941 | 942 | /* Write some magic values into registers */ 943 | magic_init(e4k); 944 | #if 0 945 | /* Set common mode voltage a bit higher for more margin 850 mv */ 946 | e4k_commonmode_set(e4k, 4); 947 | 948 | /* Initialize DC offset lookup tables */ 949 | e4k_dc_offset_gen_table(e4k); 950 | 951 | /* Enable time variant DC correction */ 952 | e4k_reg_write(e4k, E4K_REG_DCTIME1, 0x01); 953 | e4k_reg_write(e4k, E4K_REG_DCTIME2, 0x01); 954 | #endif 955 | 956 | /* Set LNA mode to manual */ 957 | e4k_reg_write(e4k, E4K_REG_AGC4, 0x10); /* High threshold */ 958 | e4k_reg_write(e4k, E4K_REG_AGC5, 0x04); /* Low threshold */ 959 | e4k_reg_write(e4k, E4K_REG_AGC6, 0x1a); /* LNA calib + loop rate */ 960 | 961 | e4k_reg_set_mask(e4k, E4K_REG_AGC1, E4K_AGC1_MOD_MASK, 962 | E4K_AGC_MOD_SERIAL); 963 | 964 | /* Set Mixer Gain Control to manual */ 965 | e4k_reg_set_mask(e4k, E4K_REG_AGC7, E4K_AGC7_MIX_GAIN_AUTO, 0); 966 | 967 | #if 0 968 | /* Enable LNA Gain enhancement */ 969 | e4k_reg_set_mask(e4k, E4K_REG_AGC11, 0x7, 970 | E4K_AGC11_LNA_GAIN_ENH | (2 << 1)); 971 | 972 | /* Enable automatic IF gain mode switching */ 973 | e4k_reg_set_mask(e4k, E4K_REG_AGC8, 0x1, E4K_AGC8_SENS_LIN_AUTO); 974 | #endif 975 | 976 | /* Use auto-gain as default */ 977 | e4k_enable_manual_gain(e4k, 0); 978 | 979 | /* Select moderate gain levels */ 980 | e4k_if_gain_set(e4k, 1, 6); 981 | e4k_if_gain_set(e4k, 2, 0); 982 | e4k_if_gain_set(e4k, 3, 0); 983 | e4k_if_gain_set(e4k, 4, 0); 984 | e4k_if_gain_set(e4k, 5, 9); 985 | e4k_if_gain_set(e4k, 6, 9); 986 | 987 | /* Set the most narrow filter we can possibly use */ 988 | e4k_if_filter_bw_set(e4k, E4K_IF_FILTER_MIX, KHZ(1900)); 989 | e4k_if_filter_bw_set(e4k, E4K_IF_FILTER_RC, KHZ(1000)); 990 | e4k_if_filter_bw_set(e4k, E4K_IF_FILTER_CHAN, KHZ(2150)); 991 | e4k_if_filter_chan_enable(e4k, 1); 992 | 993 | /* Disable time variant DC correction and LUT */ 994 | e4k_reg_set_mask(e4k, E4K_REG_DC5, 0x03, 0); 995 | e4k_reg_set_mask(e4k, E4K_REG_DCTIME1, 0x03, 0); 996 | e4k_reg_set_mask(e4k, E4K_REG_DCTIME2, 0x03, 0); 997 | 998 | return 0; 999 | } 1000 | -------------------------------------------------------------------------------- /src/tuner_fc0012.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Fitipower FC0012 tuner driver 3 | * 4 | * Copyright (C) 2012 Hans-Frieder Vogt 5 | * 6 | * modified for use in librtlsdr 7 | * Copyright (C) 2012 Steve Markgraf 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 2 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program; if not, write to the Free Software 21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 | */ 23 | 24 | #include 25 | #include 26 | 27 | #include "rtlsdr_i2c.h" 28 | #include "tuner_fc0012.h" 29 | 30 | static int fc0012_writereg(void *dev, uint8_t reg, uint8_t val) 31 | { 32 | uint8_t data[2]; 33 | data[0] = reg; 34 | data[1] = val; 35 | 36 | if (rtlsdr_i2c_write_fn(dev, FC0012_I2C_ADDR, data, 2) < 0) 37 | return -1; 38 | 39 | return 0; 40 | } 41 | 42 | static int fc0012_readreg(void *dev, uint8_t reg, uint8_t *val) 43 | { 44 | uint8_t data = reg; 45 | 46 | if (rtlsdr_i2c_write_fn(dev, FC0012_I2C_ADDR, &data, 1) < 0) 47 | return -1; 48 | 49 | if (rtlsdr_i2c_read_fn(dev, FC0012_I2C_ADDR, &data, 1) < 0) 50 | return -1; 51 | 52 | *val = data; 53 | 54 | return 0; 55 | } 56 | 57 | /* Incomplete list of register settings: 58 | * 59 | * Name Reg Bits Desc 60 | * CHIP_ID 0x00 0-7 Chip ID (constant 0xA1) 61 | * RF_A 0x01 0-3 Number of count-to-9 cycles in RF 62 | * divider (suggested: 2..9) 63 | * RF_M 0x02 0-7 Total number of cycles (to-8 and to-9) 64 | * in RF divider 65 | * RF_K_HIGH 0x03 0-6 Bits 8..14 of fractional divider 66 | * RF_K_LOW 0x04 0-7 Bits 0..7 of fractional RF divider 67 | * RF_OUTDIV_A 0x05 3-7 Power of two required? 68 | * LNA_POWER_DOWN 0x06 0 Set to 1 to switch off low noise amp 69 | * RF_OUTDIV_B 0x06 1 Set to select 3 instead of 2 for the 70 | * RF output divider 71 | * VCO_SPEED 0x06 3 Select tuning range of VCO: 72 | * 0 = Low range, (ca. 1.1 - 1.5GHz) 73 | * 1 = High range (ca. 1.4 - 1.8GHz) 74 | * BANDWIDTH 0x06 6-7 Set bandwidth. 6MHz = 0x80, 7MHz=0x40 75 | * 8MHz=0x00 76 | * XTAL_SPEED 0x07 5 Set to 1 for 28.8MHz Crystal input 77 | * or 0 for 36MHz 78 | * 0x08 0-7 79 | * EN_CAL_RSSI 0x09 4 Enable calibrate RSSI 80 | * (Receive Signal Strength Indicator) 81 | * LNA_FORCE 0x0d 0 82 | * AGC_FORCE 0x0d ? 83 | * LNA_GAIN 0x13 3-4 Low noise amp gain 84 | * LNA_COMPS 0x15 3 ? 85 | * VCO_CALIB 0x0e 7 Set high then low to calibrate VCO 86 | * (fast lock?) 87 | * VCO_VOLTAGE 0x0e 0-6 Read Control voltage of VCO 88 | * (big value -> low freq) 89 | */ 90 | 91 | int fc0012_init(void *dev) 92 | { 93 | int ret = 0; 94 | unsigned int i; 95 | uint8_t reg[] = { 96 | 0x00, /* dummy reg. 0 */ 97 | 0x05, /* reg. 0x01 */ 98 | 0x10, /* reg. 0x02 */ 99 | 0x00, /* reg. 0x03 */ 100 | 0x00, /* reg. 0x04 */ 101 | 0x0f, /* reg. 0x05: may also be 0x0a */ 102 | 0x00, /* reg. 0x06: divider 2, VCO slow */ 103 | 0x00, /* reg. 0x07: may also be 0x0f */ 104 | 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, 105 | Loop Bw 1/8 */ 106 | 0x6e, /* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */ 107 | 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ 108 | 0x82, /* reg. 0x0b: Output Clock is same as clock frequency, 109 | may also be 0x83 */ 110 | 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ 111 | 0x02, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, 0x02 for DVB-T */ 112 | 0x00, /* reg. 0x0e */ 113 | 0x00, /* reg. 0x0f */ 114 | 0x00, /* reg. 0x10: may also be 0x0d */ 115 | 0x00, /* reg. 0x11 */ 116 | 0x1f, /* reg. 0x12: Set to maximum gain */ 117 | 0x08, /* reg. 0x13: Set to Middle Gain: 0x08, 118 | Low Gain: 0x00, High Gain: 0x10, enable IX2: 0x80 */ 119 | 0x00, /* reg. 0x14 */ 120 | 0x04, /* reg. 0x15: Enable LNA COMPS */ 121 | }; 122 | 123 | #if 0 124 | switch (rtlsdr_get_tuner_clock(dev)) { 125 | case FC_XTAL_27_MHZ: 126 | case FC_XTAL_28_8_MHZ: 127 | reg[0x07] |= 0x20; 128 | break; 129 | case FC_XTAL_36_MHZ: 130 | default: 131 | break; 132 | } 133 | #endif 134 | reg[0x07] |= 0x20; 135 | 136 | // if (priv->dual_master) 137 | reg[0x0c] |= 0x02; 138 | 139 | for (i = 1; i < sizeof(reg); i++) { 140 | ret = fc0012_writereg(dev, i, reg[i]); 141 | if (ret) 142 | break; 143 | } 144 | 145 | return ret; 146 | } 147 | 148 | int fc0012_set_params(void *dev, uint32_t freq, uint32_t bandwidth) 149 | { 150 | int i, ret = 0; 151 | uint8_t reg[7], am, pm, multi, tmp; 152 | uint64_t f_vco; 153 | uint32_t xtal_freq_div_2; 154 | uint16_t xin, xdiv; 155 | int vco_select = 0; 156 | 157 | xtal_freq_div_2 = rtlsdr_get_tuner_clock(dev) / 2; 158 | 159 | /* select frequency divider and the frequency of VCO */ 160 | if (freq < 37084000) { /* freq * 96 < 3560000000 */ 161 | multi = 96; 162 | reg[5] = 0x82; 163 | reg[6] = 0x00; 164 | } else if (freq < 55625000) { /* freq * 64 < 3560000000 */ 165 | multi = 64; 166 | reg[5] = 0x82; 167 | reg[6] = 0x02; 168 | } else if (freq < 74167000) { /* freq * 48 < 3560000000 */ 169 | multi = 48; 170 | reg[5] = 0x42; 171 | reg[6] = 0x00; 172 | } else if (freq < 111250000) { /* freq * 32 < 3560000000 */ 173 | multi = 32; 174 | reg[5] = 0x42; 175 | reg[6] = 0x02; 176 | } else if (freq < 148334000) { /* freq * 24 < 3560000000 */ 177 | multi = 24; 178 | reg[5] = 0x22; 179 | reg[6] = 0x00; 180 | } else if (freq < 222500000) { /* freq * 16 < 3560000000 */ 181 | multi = 16; 182 | reg[5] = 0x22; 183 | reg[6] = 0x02; 184 | } else if (freq < 296667000) { /* freq * 12 < 3560000000 */ 185 | multi = 12; 186 | reg[5] = 0x12; 187 | reg[6] = 0x00; 188 | } else if (freq < 445000000) { /* freq * 8 < 3560000000 */ 189 | multi = 8; 190 | reg[5] = 0x12; 191 | reg[6] = 0x02; 192 | } else if (freq < 593334000) { /* freq * 6 < 3560000000 */ 193 | multi = 6; 194 | reg[5] = 0x0a; 195 | reg[6] = 0x00; 196 | } else { 197 | multi = 4; 198 | reg[5] = 0x0a; 199 | reg[6] = 0x02; 200 | } 201 | 202 | f_vco = freq * multi; 203 | 204 | if (f_vco >= 3060000000U) { 205 | reg[6] |= 0x08; 206 | vco_select = 1; 207 | } 208 | 209 | /* From divided value (XDIV) determined the FA and FP value */ 210 | xdiv = (uint16_t)(f_vco / xtal_freq_div_2); 211 | if ((f_vco - xdiv * xtal_freq_div_2) >= (xtal_freq_div_2 / 2)) 212 | xdiv++; 213 | 214 | pm = (uint8_t)(xdiv / 8); 215 | am = (uint8_t)(xdiv - (8 * pm)); 216 | 217 | if (am < 2) { 218 | am += 8; 219 | pm--; 220 | } 221 | 222 | if (pm > 31) { 223 | reg[1] = am + (8 * (pm - 31)); 224 | reg[2] = 31; 225 | } else { 226 | reg[1] = am; 227 | reg[2] = pm; 228 | } 229 | 230 | if ((reg[1] > 15) || (reg[2] < 0x0b)) { 231 | fprintf(stderr, "[FC0012] no valid PLL combination " 232 | "found for %u Hz!\n", freq); 233 | return -1; 234 | } 235 | 236 | /* fix clock out */ 237 | reg[6] |= 0x20; 238 | 239 | /* From VCO frequency determines the XIN ( fractional part of Delta 240 | Sigma PLL) and divided value (XDIV) */ 241 | xin = (uint16_t)((f_vco - (f_vco / xtal_freq_div_2) * xtal_freq_div_2) / 1000); 242 | xin = (xin << 15) / (xtal_freq_div_2 / 1000); 243 | if (xin >= 16384) 244 | xin += 32768; 245 | 246 | reg[3] = xin >> 8; /* xin with 9 bit resolution */ 247 | reg[4] = xin & 0xff; 248 | 249 | reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ 250 | switch (bandwidth) { 251 | case 6000000: 252 | reg[6] |= 0x80; 253 | break; 254 | case 7000000: 255 | reg[6] |= 0x40; 256 | break; 257 | case 8000000: 258 | default: 259 | break; 260 | } 261 | 262 | /* modified for Realtek demod */ 263 | reg[5] |= 0x07; 264 | 265 | for (i = 1; i <= 6; i++) { 266 | ret = fc0012_writereg(dev, i, reg[i]); 267 | if (ret) 268 | goto exit; 269 | } 270 | 271 | /* VCO Calibration */ 272 | ret = fc0012_writereg(dev, 0x0e, 0x80); 273 | if (!ret) 274 | ret = fc0012_writereg(dev, 0x0e, 0x00); 275 | 276 | /* VCO Re-Calibration if needed */ 277 | if (!ret) 278 | ret = fc0012_writereg(dev, 0x0e, 0x00); 279 | 280 | if (!ret) { 281 | // msleep(10); 282 | ret = fc0012_readreg(dev, 0x0e, &tmp); 283 | } 284 | if (ret) 285 | goto exit; 286 | 287 | /* vco selection */ 288 | tmp &= 0x3f; 289 | 290 | if (vco_select) { 291 | if (tmp > 0x3c) { 292 | reg[6] &= ~0x08; 293 | ret = fc0012_writereg(dev, 0x06, reg[6]); 294 | if (!ret) 295 | ret = fc0012_writereg(dev, 0x0e, 0x80); 296 | if (!ret) 297 | ret = fc0012_writereg(dev, 0x0e, 0x00); 298 | } 299 | } else { 300 | if (tmp < 0x02) { 301 | reg[6] |= 0x08; 302 | ret = fc0012_writereg(dev, 0x06, reg[6]); 303 | if (!ret) 304 | ret = fc0012_writereg(dev, 0x0e, 0x80); 305 | if (!ret) 306 | ret = fc0012_writereg(dev, 0x0e, 0x00); 307 | } 308 | } 309 | 310 | exit: 311 | return ret; 312 | } 313 | 314 | int fc0012_set_gain(void *dev, int gain) 315 | { 316 | int ret; 317 | uint8_t tmp = 0; 318 | 319 | ret = fc0012_readreg(dev, 0x13, &tmp); 320 | 321 | /* mask bits off */ 322 | tmp &= 0xe0; 323 | 324 | switch (gain) { 325 | case -99: /* -9.9 dB */ 326 | tmp |= 0x02; 327 | break; 328 | case -40: /* -4 dB */ 329 | break; 330 | case 71: 331 | tmp |= 0x08; /* 7.1 dB */ 332 | break; 333 | case 179: 334 | tmp |= 0x17; /* 17.9 dB */ 335 | break; 336 | case 192: 337 | default: 338 | tmp |= 0x10; /* 19.2 dB */ 339 | break; 340 | } 341 | 342 | ret = fc0012_writereg(dev, 0x13, tmp); 343 | 344 | return ret; 345 | } 346 | -------------------------------------------------------------------------------- /src/tuner_fc0013.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Fitipower FC0013 tuner driver 3 | * 4 | * Copyright (C) 2012 Hans-Frieder Vogt 5 | * partially based on driver code from Fitipower 6 | * Copyright (C) 2010 Fitipower Integrated Technology Inc 7 | * 8 | * modified for use in librtlsdr 9 | * Copyright (C) 2012 Steve Markgraf 10 | * 11 | * This program is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation; either version 2 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License 22 | * along with this program; if not, write to the Free Software 23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 24 | * 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | #include "rtlsdr_i2c.h" 31 | #include "tuner_fc0013.h" 32 | 33 | static int fc0013_writereg(void *dev, uint8_t reg, uint8_t val) 34 | { 35 | uint8_t data[2]; 36 | data[0] = reg; 37 | data[1] = val; 38 | 39 | if (rtlsdr_i2c_write_fn(dev, FC0013_I2C_ADDR, data, 2) < 0) 40 | return -1; 41 | 42 | return 0; 43 | } 44 | 45 | static int fc0013_readreg(void *dev, uint8_t reg, uint8_t *val) 46 | { 47 | uint8_t data = reg; 48 | 49 | if (rtlsdr_i2c_write_fn(dev, FC0013_I2C_ADDR, &data, 1) < 0) 50 | return -1; 51 | 52 | if (rtlsdr_i2c_read_fn(dev, FC0013_I2C_ADDR, &data, 1) < 0) 53 | return -1; 54 | 55 | *val = data; 56 | 57 | return 0; 58 | } 59 | 60 | int fc0013_init(void *dev) 61 | { 62 | int ret = 0; 63 | unsigned int i; 64 | uint8_t reg[] = { 65 | 0x00, /* reg. 0x00: dummy */ 66 | 0x09, /* reg. 0x01 */ 67 | 0x16, /* reg. 0x02 */ 68 | 0x00, /* reg. 0x03 */ 69 | 0x00, /* reg. 0x04 */ 70 | 0x17, /* reg. 0x05 */ 71 | 0x02, /* reg. 0x06: LPF bandwidth */ 72 | 0x0a, /* reg. 0x07: CHECK */ 73 | 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, 74 | Loop Bw 1/8 */ 75 | 0x6e, /* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */ 76 | 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ 77 | 0x82, /* reg. 0x0b: CHECK */ 78 | 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ 79 | 0x01, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */ 80 | 0x00, /* reg. 0x0e */ 81 | 0x00, /* reg. 0x0f */ 82 | 0x00, /* reg. 0x10 */ 83 | 0x00, /* reg. 0x11 */ 84 | 0x00, /* reg. 0x12 */ 85 | 0x00, /* reg. 0x13 */ 86 | 0x50, /* reg. 0x14: DVB-t High Gain, UHF. 87 | Middle Gain: 0x48, Low Gain: 0x40 */ 88 | 0x01, /* reg. 0x15 */ 89 | }; 90 | #if 0 91 | switch (rtlsdr_get_tuner_clock(dev)) { 92 | case FC_XTAL_27_MHZ: 93 | case FC_XTAL_28_8_MHZ: 94 | reg[0x07] |= 0x20; 95 | break; 96 | case FC_XTAL_36_MHZ: 97 | default: 98 | break; 99 | } 100 | #endif 101 | reg[0x07] |= 0x20; 102 | 103 | // if (dev->dual_master) 104 | reg[0x0c] |= 0x02; 105 | 106 | for (i = 1; i < sizeof(reg); i++) { 107 | ret = fc0013_writereg(dev, i, reg[i]); 108 | if (ret < 0) 109 | break; 110 | } 111 | 112 | return ret; 113 | } 114 | 115 | int fc0013_rc_cal_add(void *dev, int rc_val) 116 | { 117 | int ret; 118 | uint8_t rc_cal; 119 | int val; 120 | 121 | /* push rc_cal value, get rc_cal value */ 122 | ret = fc0013_writereg(dev, 0x10, 0x00); 123 | if (ret) 124 | goto error_out; 125 | 126 | /* get rc_cal value */ 127 | ret = fc0013_readreg(dev, 0x10, &rc_cal); 128 | if (ret) 129 | goto error_out; 130 | 131 | rc_cal &= 0x0f; 132 | 133 | val = (int)rc_cal + rc_val; 134 | 135 | /* forcing rc_cal */ 136 | ret = fc0013_writereg(dev, 0x0d, 0x11); 137 | if (ret) 138 | goto error_out; 139 | 140 | /* modify rc_cal value */ 141 | if (val > 15) 142 | ret = fc0013_writereg(dev, 0x10, 0x0f); 143 | else if (val < 0) 144 | ret = fc0013_writereg(dev, 0x10, 0x00); 145 | else 146 | ret = fc0013_writereg(dev, 0x10, (uint8_t)val); 147 | 148 | error_out: 149 | return ret; 150 | } 151 | 152 | int fc0013_rc_cal_reset(void *dev) 153 | { 154 | int ret; 155 | 156 | ret = fc0013_writereg(dev, 0x0d, 0x01); 157 | if (!ret) 158 | ret = fc0013_writereg(dev, 0x10, 0x00); 159 | 160 | return ret; 161 | } 162 | 163 | static int fc0013_set_vhf_track(void *dev, uint32_t freq) 164 | { 165 | int ret; 166 | uint8_t tmp; 167 | 168 | ret = fc0013_readreg(dev, 0x1d, &tmp); 169 | if (ret) 170 | goto error_out; 171 | tmp &= 0xe3; 172 | if (freq <= 177500000) { /* VHF Track: 7 */ 173 | ret = fc0013_writereg(dev, 0x1d, tmp | 0x1c); 174 | } else if (freq <= 184500000) { /* VHF Track: 6 */ 175 | ret = fc0013_writereg(dev, 0x1d, tmp | 0x18); 176 | } else if (freq <= 191500000) { /* VHF Track: 5 */ 177 | ret = fc0013_writereg(dev, 0x1d, tmp | 0x14); 178 | } else if (freq <= 198500000) { /* VHF Track: 4 */ 179 | ret = fc0013_writereg(dev, 0x1d, tmp | 0x10); 180 | } else if (freq <= 205500000) { /* VHF Track: 3 */ 181 | ret = fc0013_writereg(dev, 0x1d, tmp | 0x0c); 182 | } else if (freq <= 219500000) { /* VHF Track: 2 */ 183 | ret = fc0013_writereg(dev, 0x1d, tmp | 0x08); 184 | } else if (freq < 300000000) { /* VHF Track: 1 */ 185 | ret = fc0013_writereg(dev, 0x1d, tmp | 0x04); 186 | } else { /* UHF and GPS */ 187 | ret = fc0013_writereg(dev, 0x1d, tmp | 0x1c); 188 | } 189 | 190 | error_out: 191 | return ret; 192 | } 193 | 194 | int fc0013_set_params(void *dev, uint32_t freq, uint32_t bandwidth) 195 | { 196 | int i, ret = 0; 197 | uint8_t reg[7], am, pm, multi, tmp; 198 | uint64_t f_vco; 199 | uint32_t xtal_freq_div_2; 200 | uint16_t xin, xdiv; 201 | int vco_select = 0; 202 | 203 | xtal_freq_div_2 = rtlsdr_get_tuner_clock(dev) / 2; 204 | 205 | /* set VHF track */ 206 | ret = fc0013_set_vhf_track(dev, freq); 207 | if (ret) 208 | goto exit; 209 | 210 | if (freq < 300000000) { 211 | /* enable VHF filter */ 212 | ret = fc0013_readreg(dev, 0x07, &tmp); 213 | if (ret) 214 | goto exit; 215 | ret = fc0013_writereg(dev, 0x07, tmp | 0x10); 216 | if (ret) 217 | goto exit; 218 | 219 | /* disable UHF & disable GPS */ 220 | ret = fc0013_readreg(dev, 0x14, &tmp); 221 | if (ret) 222 | goto exit; 223 | ret = fc0013_writereg(dev, 0x14, tmp & 0x1f); 224 | if (ret) 225 | goto exit; 226 | } else if (freq <= 862000000) { 227 | /* disable VHF filter */ 228 | ret = fc0013_readreg(dev, 0x07, &tmp); 229 | if (ret) 230 | goto exit; 231 | ret = fc0013_writereg(dev, 0x07, tmp & 0xef); 232 | if (ret) 233 | goto exit; 234 | 235 | /* enable UHF & disable GPS */ 236 | ret = fc0013_readreg(dev, 0x14, &tmp); 237 | if (ret) 238 | goto exit; 239 | ret = fc0013_writereg(dev, 0x14, (tmp & 0x1f) | 0x40); 240 | if (ret) 241 | goto exit; 242 | } else { 243 | /* disable VHF filter */ 244 | ret = fc0013_readreg(dev, 0x07, &tmp); 245 | if (ret) 246 | goto exit; 247 | ret = fc0013_writereg(dev, 0x07, tmp & 0xef); 248 | if (ret) 249 | goto exit; 250 | 251 | /* disable UHF & enable GPS */ 252 | ret = fc0013_readreg(dev, 0x14, &tmp); 253 | if (ret) 254 | goto exit; 255 | ret = fc0013_writereg(dev, 0x14, (tmp & 0x1f) | 0x20); 256 | if (ret) 257 | goto exit; 258 | } 259 | 260 | /* select frequency divider and the frequency of VCO */ 261 | if (freq < 37084000) { /* freq * 96 < 3560000000 */ 262 | multi = 96; 263 | reg[5] = 0x82; 264 | reg[6] = 0x00; 265 | } else if (freq < 55625000) { /* freq * 64 < 3560000000 */ 266 | multi = 64; 267 | reg[5] = 0x02; 268 | reg[6] = 0x02; 269 | } else if (freq < 74167000) { /* freq * 48 < 3560000000 */ 270 | multi = 48; 271 | reg[5] = 0x42; 272 | reg[6] = 0x00; 273 | } else if (freq < 111250000) { /* freq * 32 < 3560000000 */ 274 | multi = 32; 275 | reg[5] = 0x82; 276 | reg[6] = 0x02; 277 | } else if (freq < 148334000) { /* freq * 24 < 3560000000 */ 278 | multi = 24; 279 | reg[5] = 0x22; 280 | reg[6] = 0x00; 281 | } else if (freq < 222500000) { /* freq * 16 < 3560000000 */ 282 | multi = 16; 283 | reg[5] = 0x42; 284 | reg[6] = 0x02; 285 | } else if (freq < 296667000) { /* freq * 12 < 3560000000 */ 286 | multi = 12; 287 | reg[5] = 0x12; 288 | reg[6] = 0x00; 289 | } else if (freq < 445000000) { /* freq * 8 < 3560000000 */ 290 | multi = 8; 291 | reg[5] = 0x22; 292 | reg[6] = 0x02; 293 | } else if (freq < 593334000) { /* freq * 6 < 3560000000 */ 294 | multi = 6; 295 | reg[5] = 0x0a; 296 | reg[6] = 0x00; 297 | } else if (freq < 950000000) { /* freq * 4 < 3800000000 */ 298 | multi = 4; 299 | reg[5] = 0x12; 300 | reg[6] = 0x02; 301 | } else { 302 | multi = 2; 303 | reg[5] = 0x0a; 304 | reg[6] = 0x02; 305 | } 306 | 307 | f_vco = freq * multi; 308 | 309 | if (f_vco >= 3060000000U) { 310 | reg[6] |= 0x08; 311 | vco_select = 1; 312 | } 313 | 314 | /* From divided value (XDIV) determined the FA and FP value */ 315 | xdiv = (uint16_t)(f_vco / xtal_freq_div_2); 316 | if ((f_vco - xdiv * xtal_freq_div_2) >= (xtal_freq_div_2 / 2)) 317 | xdiv++; 318 | 319 | pm = (uint8_t)(xdiv / 8); 320 | am = (uint8_t)(xdiv - (8 * pm)); 321 | 322 | if (am < 2) { 323 | am += 8; 324 | pm--; 325 | } 326 | 327 | if (pm > 31) { 328 | reg[1] = am + (8 * (pm - 31)); 329 | reg[2] = 31; 330 | } else { 331 | reg[1] = am; 332 | reg[2] = pm; 333 | } 334 | 335 | if ((reg[1] > 15) || (reg[2] < 0x0b)) { 336 | fprintf(stderr, "[FC0013] no valid PLL combination " 337 | "found for %u Hz!\n", freq); 338 | return -1; 339 | } 340 | 341 | /* fix clock out */ 342 | reg[6] |= 0x20; 343 | 344 | /* From VCO frequency determines the XIN ( fractional part of Delta 345 | Sigma PLL) and divided value (XDIV) */ 346 | xin = (uint16_t)((f_vco - (f_vco / xtal_freq_div_2) * xtal_freq_div_2) / 1000); 347 | xin = (xin << 15) / (xtal_freq_div_2 / 1000); 348 | if (xin >= 16384) 349 | xin += 32768; 350 | 351 | reg[3] = xin >> 8; 352 | reg[4] = xin & 0xff; 353 | 354 | reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ 355 | switch (bandwidth) { 356 | case 6000000: 357 | reg[6] |= 0x80; 358 | break; 359 | case 7000000: 360 | reg[6] |= 0x40; 361 | break; 362 | case 8000000: 363 | default: 364 | break; 365 | } 366 | 367 | /* modified for Realtek demod */ 368 | reg[5] |= 0x07; 369 | 370 | for (i = 1; i <= 6; i++) { 371 | ret = fc0013_writereg(dev, i, reg[i]); 372 | if (ret) 373 | goto exit; 374 | } 375 | 376 | ret = fc0013_readreg(dev, 0x11, &tmp); 377 | if (ret) 378 | goto exit; 379 | if (multi == 64) 380 | ret = fc0013_writereg(dev, 0x11, tmp | 0x04); 381 | else 382 | ret = fc0013_writereg(dev, 0x11, tmp & 0xfb); 383 | if (ret) 384 | goto exit; 385 | 386 | /* VCO Calibration */ 387 | ret = fc0013_writereg(dev, 0x0e, 0x80); 388 | if (!ret) 389 | ret = fc0013_writereg(dev, 0x0e, 0x00); 390 | 391 | /* VCO Re-Calibration if needed */ 392 | if (!ret) 393 | ret = fc0013_writereg(dev, 0x0e, 0x00); 394 | 395 | if (!ret) { 396 | // msleep(10); 397 | ret = fc0013_readreg(dev, 0x0e, &tmp); 398 | } 399 | if (ret) 400 | goto exit; 401 | 402 | /* vco selection */ 403 | tmp &= 0x3f; 404 | 405 | if (vco_select) { 406 | if (tmp > 0x3c) { 407 | reg[6] &= ~0x08; 408 | ret = fc0013_writereg(dev, 0x06, reg[6]); 409 | if (!ret) 410 | ret = fc0013_writereg(dev, 0x0e, 0x80); 411 | if (!ret) 412 | ret = fc0013_writereg(dev, 0x0e, 0x00); 413 | } 414 | } else { 415 | if (tmp < 0x02) { 416 | reg[6] |= 0x08; 417 | ret = fc0013_writereg(dev, 0x06, reg[6]); 418 | if (!ret) 419 | ret = fc0013_writereg(dev, 0x0e, 0x80); 420 | if (!ret) 421 | ret = fc0013_writereg(dev, 0x0e, 0x00); 422 | } 423 | } 424 | 425 | exit: 426 | return ret; 427 | } 428 | 429 | int fc0013_set_gain_mode(void *dev, int manual) 430 | { 431 | int ret = 0; 432 | uint8_t tmp = 0; 433 | 434 | ret |= fc0013_readreg(dev, 0x0d, &tmp); 435 | 436 | if (manual) 437 | tmp |= (1 << 3); 438 | else 439 | tmp &= ~(1 << 3); 440 | 441 | ret |= fc0013_writereg(dev, 0x0d, tmp); 442 | 443 | /* set a fixed IF-gain for now */ 444 | ret |= fc0013_writereg(dev, 0x13, 0x0a); 445 | 446 | return ret; 447 | } 448 | 449 | int fc0013_lna_gains[] ={ 450 | -99, 0x02, 451 | -73, 0x03, 452 | -65, 0x05, 453 | -63, 0x04, 454 | -63, 0x00, 455 | -60, 0x07, 456 | -58, 0x01, 457 | -54, 0x06, 458 | 58, 0x0f, 459 | 61, 0x0e, 460 | 63, 0x0d, 461 | 65, 0x0c, 462 | 67, 0x0b, 463 | 68, 0x0a, 464 | 70, 0x09, 465 | 71, 0x08, 466 | 179, 0x17, 467 | 181, 0x16, 468 | 182, 0x15, 469 | 184, 0x14, 470 | 186, 0x13, 471 | 188, 0x12, 472 | 191, 0x11, 473 | 197, 0x10 474 | }; 475 | 476 | #define GAIN_CNT (sizeof(fc0013_lna_gains) / sizeof(int) / 2) 477 | 478 | int fc0013_set_lna_gain(void *dev, int gain) 479 | { 480 | int ret = 0; 481 | unsigned int i; 482 | uint8_t tmp = 0; 483 | 484 | ret |= fc0013_readreg(dev, 0x14, &tmp); 485 | 486 | /* mask bits off */ 487 | tmp &= 0xe0; 488 | 489 | for (i = 0; i < GAIN_CNT; i++) { 490 | if ((fc0013_lna_gains[i*2] >= gain) || (i+1 == GAIN_CNT)) { 491 | tmp |= fc0013_lna_gains[i*2 + 1]; 492 | break; 493 | } 494 | } 495 | 496 | /* set gain */ 497 | ret |= fc0013_writereg(dev, 0x14, tmp); 498 | 499 | return ret; 500 | } 501 | -------------------------------------------------------------------------------- /src/tuner_fc2580.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FCI FC2580 tuner driver, taken from the kernel driver that can be found 3 | * on http://linux.terratec.de/tv_en.html 4 | * 5 | * This driver is a mess, and should be cleaned up/rewritten. 6 | * 7 | */ 8 | 9 | #include 10 | 11 | #include "rtlsdr_i2c.h" 12 | #include "tuner_fc2580.h" 13 | 14 | /* 16.384 MHz (at least on the Logilink VG0002A) */ 15 | #define CRYSTAL_FREQ 16384000 16 | 17 | /* glue functions to rtl-sdr code */ 18 | 19 | fc2580_fci_result_type fc2580_i2c_write(void *pTuner, unsigned char reg, unsigned char val) 20 | { 21 | uint8_t data[2]; 22 | 23 | data[0] = reg; 24 | data[1] = val; 25 | 26 | if (rtlsdr_i2c_write_fn(pTuner, FC2580_I2C_ADDR, data, 2) < 0) 27 | return FC2580_FCI_FAIL; 28 | 29 | return FC2580_FCI_SUCCESS; 30 | } 31 | 32 | fc2580_fci_result_type fc2580_i2c_read(void *pTuner, unsigned char reg, unsigned char *read_data) 33 | { 34 | uint8_t data = reg; 35 | 36 | if (rtlsdr_i2c_write_fn(pTuner, FC2580_I2C_ADDR, &data, 1) < 0) 37 | return FC2580_FCI_FAIL; 38 | 39 | if (rtlsdr_i2c_read_fn(pTuner, FC2580_I2C_ADDR, &data, 1) < 0) 40 | return FC2580_FCI_FAIL; 41 | 42 | *read_data = data; 43 | 44 | return FC2580_FCI_SUCCESS; 45 | } 46 | 47 | int fc2580_Initialize(void *pTuner) 48 | { 49 | int AgcMode; 50 | unsigned int CrystalFreqKhz; 51 | 52 | //TODO set AGC mode 53 | AgcMode = FC2580_AGC_EXTERNAL; 54 | 55 | // Initialize tuner with AGC mode. 56 | // Note: CrystalFreqKhz = round(CrystalFreqHz / 1000) 57 | CrystalFreqKhz = (unsigned int)((CRYSTAL_FREQ + 500) / 1000); 58 | 59 | if(fc2580_set_init(pTuner, AgcMode, CrystalFreqKhz) != FC2580_FCI_SUCCESS) 60 | goto error_status_initialize_tuner; 61 | 62 | 63 | return FUNCTION_SUCCESS; 64 | 65 | 66 | error_status_initialize_tuner: 67 | return FUNCTION_ERROR; 68 | } 69 | 70 | int fc2580_SetRfFreqHz(void *pTuner, unsigned long RfFreqHz) 71 | { 72 | unsigned int RfFreqKhz; 73 | unsigned int CrystalFreqKhz; 74 | 75 | // Set tuner RF frequency in KHz. 76 | // Note: RfFreqKhz = round(RfFreqHz / 1000) 77 | // CrystalFreqKhz = round(CrystalFreqHz / 1000) 78 | RfFreqKhz = (unsigned int)((RfFreqHz + 500) / 1000); 79 | CrystalFreqKhz = (unsigned int)((CRYSTAL_FREQ + 500) / 1000); 80 | 81 | if(fc2580_set_freq(pTuner, RfFreqKhz, CrystalFreqKhz) != FC2580_FCI_SUCCESS) 82 | goto error_status_set_tuner_rf_frequency; 83 | 84 | return FUNCTION_SUCCESS; 85 | 86 | error_status_set_tuner_rf_frequency: 87 | return FUNCTION_ERROR; 88 | } 89 | 90 | /** 91 | 92 | @brief Set FC2580 tuner bandwidth mode. 93 | 94 | */ 95 | int fc2580_SetBandwidthMode(void *pTuner, int BandwidthMode) 96 | { 97 | unsigned int CrystalFreqKhz; 98 | 99 | // Set tuner bandwidth mode. 100 | // Note: CrystalFreqKhz = round(CrystalFreqHz / 1000) 101 | CrystalFreqKhz = (unsigned int)((CRYSTAL_FREQ + 500) / 1000); 102 | 103 | if(fc2580_set_filter(pTuner, (unsigned char)BandwidthMode, CrystalFreqKhz) != FC2580_FCI_SUCCESS) 104 | goto error_status_set_tuner_bandwidth_mode; 105 | 106 | return FUNCTION_SUCCESS; 107 | 108 | 109 | error_status_set_tuner_bandwidth_mode: 110 | return FUNCTION_ERROR; 111 | } 112 | 113 | void fc2580_wait_msec(void *pTuner, int a) 114 | { 115 | /* USB latency is enough for now ;) */ 116 | // usleep(a * 1000); 117 | return; 118 | } 119 | 120 | /*============================================================================== 121 | fc2580 initial setting 122 | 123 | This function is a generic function which gets called to initialize 124 | 125 | fc2580 in DVB-H mode or L-Band TDMB mode 126 | 127 | 128 | 129 | ifagc_mode 130 | type : integer 131 | 1 : Internal AGC 132 | 2 : Voltage Control Mode 133 | 134 | ==============================================================================*/ 135 | fc2580_fci_result_type fc2580_set_init(void *pTuner, int ifagc_mode, unsigned int freq_xtal) 136 | { 137 | fc2580_fci_result_type result = FC2580_FCI_SUCCESS; 138 | 139 | result &= fc2580_i2c_write(pTuner, 0x00, 0x00); /*** Confidential ***/ 140 | result &= fc2580_i2c_write(pTuner, 0x12, 0x86); 141 | result &= fc2580_i2c_write(pTuner, 0x14, 0x5C); 142 | result &= fc2580_i2c_write(pTuner, 0x16, 0x3C); 143 | result &= fc2580_i2c_write(pTuner, 0x1F, 0xD2); 144 | result &= fc2580_i2c_write(pTuner, 0x09, 0xD7); 145 | result &= fc2580_i2c_write(pTuner, 0x0B, 0xD5); 146 | result &= fc2580_i2c_write(pTuner, 0x0C, 0x32); 147 | result &= fc2580_i2c_write(pTuner, 0x0E, 0x43); 148 | result &= fc2580_i2c_write(pTuner, 0x21, 0x0A); 149 | result &= fc2580_i2c_write(pTuner, 0x22, 0x82); 150 | if( ifagc_mode == 1 ) 151 | { 152 | result &= fc2580_i2c_write(pTuner, 0x45, 0x10); //internal AGC 153 | result &= fc2580_i2c_write(pTuner, 0x4C, 0x00); //HOLD_AGC polarity 154 | } 155 | else if( ifagc_mode == 2 ) 156 | { 157 | result &= fc2580_i2c_write(pTuner, 0x45, 0x20); //Voltage Control Mode 158 | result &= fc2580_i2c_write(pTuner, 0x4C, 0x02); //HOLD_AGC polarity 159 | } 160 | result &= fc2580_i2c_write(pTuner, 0x3F, 0x88); 161 | result &= fc2580_i2c_write(pTuner, 0x02, 0x0E); 162 | result &= fc2580_i2c_write(pTuner, 0x58, 0x14); 163 | result &= fc2580_set_filter(pTuner, 8, freq_xtal); //BW = 7.8MHz 164 | 165 | return result; 166 | } 167 | 168 | 169 | /*============================================================================== 170 | fc2580 frequency setting 171 | 172 | This function is a generic function which gets called to change LO Frequency 173 | 174 | of fc2580 in DVB-H mode or L-Band TDMB mode 175 | 176 | 177 | freq_xtal: kHz 178 | 179 | f_lo 180 | Value of target LO Frequency in 'kHz' unit 181 | ex) 2.6GHz = 2600000 182 | 183 | ==============================================================================*/ 184 | fc2580_fci_result_type fc2580_set_freq(void *pTuner, unsigned int f_lo, unsigned int freq_xtal) 185 | { 186 | unsigned int f_diff, f_diff_shifted, n_val, k_val; 187 | unsigned int f_vco, r_val, f_comp; 188 | unsigned char pre_shift_bits = 4;// number of preshift to prevent overflow in shifting f_diff to f_diff_shifted 189 | unsigned char data_0x18; 190 | unsigned char data_0x02 = (USE_EXT_CLK<<5)|0x0E; 191 | 192 | fc2580_band_type band = ( f_lo > 1000000 )? FC2580_L_BAND : ( f_lo > 400000 )? FC2580_UHF_BAND : FC2580_VHF_BAND; 193 | 194 | fc2580_fci_result_type result = FC2580_FCI_SUCCESS; 195 | 196 | f_vco = ( band == FC2580_UHF_BAND )? f_lo * 4 : (( band == FC2580_L_BAND )? f_lo * 2 : f_lo * 12); 197 | r_val = ( f_vco >= 2*76*freq_xtal )? 1 : ( f_vco >= 76*freq_xtal )? 2 : 4; 198 | f_comp = freq_xtal/r_val; 199 | n_val = ( f_vco / 2 ) / f_comp; 200 | 201 | f_diff = f_vco - 2* f_comp * n_val; 202 | f_diff_shifted = f_diff << ( 20 - pre_shift_bits ); 203 | k_val = f_diff_shifted / ( ( 2* f_comp ) >> pre_shift_bits ); 204 | 205 | if( f_diff_shifted - k_val * ( ( 2* f_comp ) >> pre_shift_bits ) >= ( f_comp >> pre_shift_bits ) ) 206 | k_val = k_val + 1; 207 | 208 | if( f_vco >= BORDER_FREQ ) //Select VCO Band 209 | data_0x02 = data_0x02 | 0x08; //0x02[3] = 1; 210 | else 211 | data_0x02 = data_0x02 & 0xF7; //0x02[3] = 0; 212 | 213 | // if( band != curr_band ) { 214 | switch(band) 215 | { 216 | case FC2580_UHF_BAND: 217 | data_0x02 = (data_0x02 & 0x3F); 218 | 219 | result &= fc2580_i2c_write(pTuner, 0x25, 0xF0); 220 | result &= fc2580_i2c_write(pTuner, 0x27, 0x77); 221 | result &= fc2580_i2c_write(pTuner, 0x28, 0x53); 222 | result &= fc2580_i2c_write(pTuner, 0x29, 0x60); 223 | result &= fc2580_i2c_write(pTuner, 0x30, 0x09); 224 | result &= fc2580_i2c_write(pTuner, 0x50, 0x8C); 225 | result &= fc2580_i2c_write(pTuner, 0x53, 0x50); 226 | 227 | if( f_lo < 538000 ) 228 | result &= fc2580_i2c_write(pTuner, 0x5F, 0x13); 229 | else 230 | result &= fc2580_i2c_write(pTuner, 0x5F, 0x15); 231 | 232 | if( f_lo < 538000 ) 233 | { 234 | result &= fc2580_i2c_write(pTuner, 0x61, 0x07); 235 | result &= fc2580_i2c_write(pTuner, 0x62, 0x06); 236 | result &= fc2580_i2c_write(pTuner, 0x67, 0x06); 237 | result &= fc2580_i2c_write(pTuner, 0x68, 0x08); 238 | result &= fc2580_i2c_write(pTuner, 0x69, 0x10); 239 | result &= fc2580_i2c_write(pTuner, 0x6A, 0x12); 240 | } 241 | else if( f_lo < 794000 ) 242 | { 243 | result &= fc2580_i2c_write(pTuner, 0x61, 0x03); 244 | result &= fc2580_i2c_write(pTuner, 0x62, 0x03); 245 | result &= fc2580_i2c_write(pTuner, 0x67, 0x03); //ACI improve 246 | result &= fc2580_i2c_write(pTuner, 0x68, 0x05); //ACI improve 247 | result &= fc2580_i2c_write(pTuner, 0x69, 0x0C); 248 | result &= fc2580_i2c_write(pTuner, 0x6A, 0x0E); 249 | } 250 | else 251 | { 252 | result &= fc2580_i2c_write(pTuner, 0x61, 0x07); 253 | result &= fc2580_i2c_write(pTuner, 0x62, 0x06); 254 | result &= fc2580_i2c_write(pTuner, 0x67, 0x07); 255 | result &= fc2580_i2c_write(pTuner, 0x68, 0x09); 256 | result &= fc2580_i2c_write(pTuner, 0x69, 0x10); 257 | result &= fc2580_i2c_write(pTuner, 0x6A, 0x12); 258 | } 259 | 260 | result &= fc2580_i2c_write(pTuner, 0x63, 0x15); 261 | 262 | result &= fc2580_i2c_write(pTuner, 0x6B, 0x0B); 263 | result &= fc2580_i2c_write(pTuner, 0x6C, 0x0C); 264 | result &= fc2580_i2c_write(pTuner, 0x6D, 0x78); 265 | result &= fc2580_i2c_write(pTuner, 0x6E, 0x32); 266 | result &= fc2580_i2c_write(pTuner, 0x6F, 0x14); 267 | result &= fc2580_set_filter(pTuner, 8, freq_xtal); //BW = 7.8MHz 268 | break; 269 | case FC2580_VHF_BAND: 270 | data_0x02 = (data_0x02 & 0x3F) | 0x80; 271 | result &= fc2580_i2c_write(pTuner, 0x27, 0x77); 272 | result &= fc2580_i2c_write(pTuner, 0x28, 0x33); 273 | result &= fc2580_i2c_write(pTuner, 0x29, 0x40); 274 | result &= fc2580_i2c_write(pTuner, 0x30, 0x09); 275 | result &= fc2580_i2c_write(pTuner, 0x50, 0x8C); 276 | result &= fc2580_i2c_write(pTuner, 0x53, 0x50); 277 | result &= fc2580_i2c_write(pTuner, 0x5F, 0x0F); 278 | result &= fc2580_i2c_write(pTuner, 0x61, 0x07); 279 | result &= fc2580_i2c_write(pTuner, 0x62, 0x00); 280 | result &= fc2580_i2c_write(pTuner, 0x63, 0x15); 281 | result &= fc2580_i2c_write(pTuner, 0x67, 0x03); 282 | result &= fc2580_i2c_write(pTuner, 0x68, 0x05); 283 | result &= fc2580_i2c_write(pTuner, 0x69, 0x10); 284 | result &= fc2580_i2c_write(pTuner, 0x6A, 0x12); 285 | result &= fc2580_i2c_write(pTuner, 0x6B, 0x08); 286 | result &= fc2580_i2c_write(pTuner, 0x6C, 0x0A); 287 | result &= fc2580_i2c_write(pTuner, 0x6D, 0x78); 288 | result &= fc2580_i2c_write(pTuner, 0x6E, 0x32); 289 | result &= fc2580_i2c_write(pTuner, 0x6F, 0x54); 290 | result &= fc2580_set_filter(pTuner, 7, freq_xtal); //BW = 6.8MHz 291 | break; 292 | case FC2580_L_BAND: 293 | data_0x02 = (data_0x02 & 0x3F) | 0x40; 294 | result &= fc2580_i2c_write(pTuner, 0x2B, 0x70); 295 | result &= fc2580_i2c_write(pTuner, 0x2C, 0x37); 296 | result &= fc2580_i2c_write(pTuner, 0x2D, 0xE7); 297 | result &= fc2580_i2c_write(pTuner, 0x30, 0x09); 298 | result &= fc2580_i2c_write(pTuner, 0x44, 0x20); 299 | result &= fc2580_i2c_write(pTuner, 0x50, 0x8C); 300 | result &= fc2580_i2c_write(pTuner, 0x53, 0x50); 301 | result &= fc2580_i2c_write(pTuner, 0x5F, 0x0F); 302 | result &= fc2580_i2c_write(pTuner, 0x61, 0x0F); 303 | result &= fc2580_i2c_write(pTuner, 0x62, 0x00); 304 | result &= fc2580_i2c_write(pTuner, 0x63, 0x13); 305 | result &= fc2580_i2c_write(pTuner, 0x67, 0x00); 306 | result &= fc2580_i2c_write(pTuner, 0x68, 0x02); 307 | result &= fc2580_i2c_write(pTuner, 0x69, 0x0C); 308 | result &= fc2580_i2c_write(pTuner, 0x6A, 0x0E); 309 | result &= fc2580_i2c_write(pTuner, 0x6B, 0x08); 310 | result &= fc2580_i2c_write(pTuner, 0x6C, 0x0A); 311 | result &= fc2580_i2c_write(pTuner, 0x6D, 0xA0); 312 | result &= fc2580_i2c_write(pTuner, 0x6E, 0x50); 313 | result &= fc2580_i2c_write(pTuner, 0x6F, 0x14); 314 | result &= fc2580_set_filter(pTuner, 1, freq_xtal); //BW = 1.53MHz 315 | break; 316 | default: 317 | break; 318 | } 319 | // curr_band = band; 320 | // } 321 | 322 | //A command about AGC clock's pre-divide ratio 323 | if( freq_xtal >= 28000 ) 324 | result &= fc2580_i2c_write(pTuner, 0x4B, 0x22 ); 325 | 326 | //Commands about VCO Band and PLL setting. 327 | result &= fc2580_i2c_write(pTuner, 0x02, data_0x02); 328 | data_0x18 = ( ( r_val == 1 )? 0x00 : ( ( r_val == 2 )? 0x10 : 0x20 ) ) + (unsigned char)(k_val >> 16); 329 | result &= fc2580_i2c_write(pTuner, 0x18, data_0x18); //Load 'R' value and high part of 'K' values 330 | result &= fc2580_i2c_write(pTuner, 0x1A, (unsigned char)( k_val >> 8 ) ); //Load middle part of 'K' value 331 | result &= fc2580_i2c_write(pTuner, 0x1B, (unsigned char)( k_val ) ); //Load lower part of 'K' value 332 | result &= fc2580_i2c_write(pTuner, 0x1C, (unsigned char)( n_val ) ); //Load 'N' value 333 | 334 | //A command about UHF LNA Load Cap 335 | if( band == FC2580_UHF_BAND ) 336 | result &= fc2580_i2c_write(pTuner, 0x2D, ( f_lo <= (unsigned int)794000 )? 0x9F : 0x8F ); //LNA_OUT_CAP 337 | 338 | 339 | return result; 340 | } 341 | 342 | 343 | /*============================================================================== 344 | fc2580 filter BW setting 345 | 346 | This function is a generic function which gets called to change Bandwidth 347 | 348 | frequency of fc2580's channel selection filter 349 | 350 | 351 | freq_xtal: kHz 352 | 353 | filter_bw 354 | 1 : 1.53MHz(TDMB) 355 | 6 : 6MHz (Bandwidth 6MHz) 356 | 7 : 6.8MHz (Bandwidth 7MHz) 357 | 8 : 7.8MHz (Bandwidth 8MHz) 358 | 359 | 360 | ==============================================================================*/ 361 | fc2580_fci_result_type fc2580_set_filter(void *pTuner, unsigned char filter_bw, unsigned int freq_xtal) 362 | { 363 | unsigned char cal_mon = 0, i; 364 | fc2580_fci_result_type result = FC2580_FCI_SUCCESS; 365 | 366 | if(filter_bw == 1) 367 | { 368 | result &= fc2580_i2c_write(pTuner, 0x36, 0x1C); 369 | result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(4151*freq_xtal/1000000) ); 370 | result &= fc2580_i2c_write(pTuner, 0x39, 0x00); 371 | result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); 372 | } 373 | if(filter_bw == 6) 374 | { 375 | result &= fc2580_i2c_write(pTuner, 0x36, 0x18); 376 | result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(4400*freq_xtal/1000000) ); 377 | result &= fc2580_i2c_write(pTuner, 0x39, 0x00); 378 | result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); 379 | } 380 | else if(filter_bw == 7) 381 | { 382 | result &= fc2580_i2c_write(pTuner, 0x36, 0x18); 383 | result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(3910*freq_xtal/1000000) ); 384 | result &= fc2580_i2c_write(pTuner, 0x39, 0x80); 385 | result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); 386 | } 387 | else if(filter_bw == 8) 388 | { 389 | result &= fc2580_i2c_write(pTuner, 0x36, 0x18); 390 | result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(3300*freq_xtal/1000000) ); 391 | result &= fc2580_i2c_write(pTuner, 0x39, 0x80); 392 | result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); 393 | } 394 | 395 | 396 | for(i=0; i<5; i++) 397 | { 398 | fc2580_wait_msec(pTuner, 5);//wait 5ms 399 | result &= fc2580_i2c_read(pTuner, 0x2F, &cal_mon); 400 | if( (cal_mon & 0xC0) != 0xC0) 401 | { 402 | result &= fc2580_i2c_write(pTuner, 0x2E, 0x01); 403 | result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); 404 | } 405 | else 406 | break; 407 | } 408 | 409 | result &= fc2580_i2c_write(pTuner, 0x2E, 0x01); 410 | 411 | return result; 412 | } 413 | 414 | /*============================================================================== 415 | fc2580 RSSI function 416 | 417 | This function is a generic function which returns fc2580's 418 | 419 | current RSSI value. 420 | 421 | 422 | none 423 | 424 | 425 | int 426 | rssi : estimated input power. 427 | 428 | ==============================================================================*/ 429 | //int fc2580_get_rssi(void) { 430 | // 431 | // unsigned char s_lna, s_rfvga, s_cfs, s_ifvga; 432 | // int ofs_lna, ofs_rfvga, ofs_csf, ofs_ifvga, rssi; 433 | // 434 | // fc2580_i2c_read(0x71, &s_lna ); 435 | // fc2580_i2c_read(0x72, &s_rfvga ); 436 | // fc2580_i2c_read(0x73, &s_cfs ); 437 | // fc2580_i2c_read(0x74, &s_ifvga ); 438 | // 439 | // 440 | // ofs_lna = 441 | // (curr_band==FC2580_UHF_BAND)? 442 | // (s_lna==0)? 0 : 443 | // (s_lna==1)? -6 : 444 | // (s_lna==2)? -17 : 445 | // (s_lna==3)? -22 : -30 : 446 | // (curr_band==FC2580_VHF_BAND)? 447 | // (s_lna==0)? 0 : 448 | // (s_lna==1)? -6 : 449 | // (s_lna==2)? -19 : 450 | // (s_lna==3)? -24 : -32 : 451 | // (curr_band==FC2580_L_BAND)? 452 | // (s_lna==0)? 0 : 453 | // (s_lna==1)? -6 : 454 | // (s_lna==2)? -11 : 455 | // (s_lna==3)? -16 : -34 : 456 | // 0;//FC2580_NO_BAND 457 | // ofs_rfvga = -s_rfvga+((s_rfvga>=11)? 1 : 0) + ((s_rfvga>=18)? 1 : 0); 458 | // ofs_csf = -6*s_cfs; 459 | // ofs_ifvga = s_ifvga/4; 460 | // 461 | // return rssi = ofs_lna+ofs_rfvga+ofs_csf+ofs_ifvga+OFS_RSSI; 462 | // 463 | //} 464 | 465 | /*============================================================================== 466 | fc2580 Xtal frequency Setting 467 | 468 | This function is a generic function which sets 469 | 470 | the frequency of xtal. 471 | 472 | 473 | 474 | frequency 475 | frequency value of internal(external) Xtal(clock) in kHz unit. 476 | 477 | ==============================================================================*/ 478 | //void fc2580_set_freq_xtal(unsigned int frequency) { 479 | // 480 | // freq_xtal = frequency; 481 | // 482 | //} 483 | 484 | --------------------------------------------------------------------------------