Tuesday, April 26, 2011

FPGA's rock

So I'm in an R&D organization at work, I lead or am involved in a lot of projects.

One of the projects I'm working on is one where we are streaming multiplexed, phase-synchronized audio over a wifi link to an accessory driving a speaker array hooked up to I2S peripherals.

We're using a FPGA to instantiate all the peripherals, those no simple COTS solutions available that we can use.   So we leveraged some work I did before with a Cyclone-III based design running ThreadX from ExpressLogic (highly recommend, those guys rock and their code is tight).   Our previous design was super clever:  we ran the NIOS at 64MHz, attached it to a Numonyx NOR/PSRAM stacked device, and double-clocked the memory (x16) at 128Mhz.  This gave us an effective x32 interface at the CPU (128MHz x16 = 64MHz x32, the native clock and bandwidth of our avalon architecture).

The previous architecture was super cool and really optimized for our memory part.  But like all things that are optimized, they aren't very flexible.   We needed to achieve 13Mbps across the wifi link, and we just ran out of bandwidth clocked at 64MHz when we shoveled data across the wifi link (too much processing required for moving the data).   We couldn't just up the CPU clock, because it was tightly coupled to the PSRAM memory interface (2x the CPU clock), and if we increased above 64, we'd exceed the spec of the memory of 128MHz.

Taking the easy route, I went brute-force and I changed the design to add a DDR2 400 part, which would allow us to up our CPU to 128MHz (128 MHz on a x16 PSRAM part would've been obviously memory interface bandwidth limited).  I'd have the everything boot from NOR, copy the code to DDR, then execute from DDR.   Run the DDR full rate x16 at 128MHz, so effective x32 data to the CPU at 128MHz.  Cool, memory bandwidth and CPU clock double from the previous design, problem solved.

So I spent the weekend implementing that, and of course, nothing goes well.  Our old design was based on Quartus 9.1, but we had to upgrade to 10.1 to get the latest DDR controller...  And of course, that broke.  Timing broke.   New nets added to Top.   Dogs and Cats started fighting, it was terrible.  Enter Scott & Cal (Altera FAE's), one of the prime reasons why I stick with Altera.

At 10:00 pm, I send Scott an email because I'm failing timing, the PLL's not instantiating correctly, the fitter auto-placed pins at the wrong location, and my thermostat was set wrong.   So Scott set's up a Webex at 10:00, and we work for 1 hour slogging through all this. 

After 1 hour, my pins are placed, I sent the netlist and routing instructions off to the CAD group, and we're back on track. Scott and Cal:  absolutely brilliant.  You want the best, use the guys from Altera, they know what they are doing and their stuff Just Works.  It's brilliant.

In the words of John  "Hannibal" Smith:  I love it when a plan comes together.

Sunday, January 23, 2011

Qualcomm USB Drivers for Windows 7 x64

All drivers on Windows 7 need to be signed. For some reason unknown to man, Qualcomm doesn't have a signed driver for their products, they leave it up to the OEM's.

Fortunately, the INF file of existing drivers for Gobi can be tweaked to make it work with other QC devices.  Here's the results of last weekend's anomolous (and anonymous) hacking...

First, the QC device enumerates as 3 devices, shown below.  With no driver installed, it just says Qualcomm MSM.

So, we'll need to add a driver for each interface of the composite QC device.  Next question is: what drivers?   We can do a little more searching and find some unpublished documents, or better, use a good source like the Linux kernel.


Looking here, we can see it's actually enumerated as 2 serial ports and 1 modem port.  Alright, now we have all the information we need.  Now it's time to plan our hack...  Here's what we're gonna do.
  1. Find some signed Gobi drivers from any laptop OEM, such as Lenovo, that ships serial and modem drivers
  2. Modify the inf's for the modem device and the usb serial device .inf files to add the vid/pids we are interested in for our devices
  3. Point device manager to these drivers

Ok, on to the work...  I spent some time searching for gobi drivers, and the ones I found first were from Lenovo.  First come, first served, so let's use those.  Download the drivers from Lenovo:


Run the extractor, and it will extract them to c:\drivers.   Navigate to this folder

C:\DRIVERS\WIN\WWANQL\Driver\Source\Module Retargetable Folder\QCUSB-Lenovo\DriverPackage\Qualcomm\Win64\AMD64

This is the driver package portion we are interested it.

Looking at the inf files, the ones we are interested in are the usbser (usb serial) and mdm (modem). The net driver (qcnetlno.inf) is the driver that is used for to make the modem show up as a network interface (ala RNDIS), not applicable here...

First let's modify the mdm file to get the modem enumerated.  Look in device manager and edit the properties of one of the USB devices.   Right-click the device, driver, properties, and you'll see this:

So you can see that our PID is 9002.  Depending on the chipset, etc, the PID may be different.  You can check the linux source for examples of other common PID's.  The _00 is the interface number, in the this case 0, or the first interface (USB interface #'s are zero-based indexed).  Other interfaces will be _01, 02, etc..  Now we need to figure out which interface to associate with which driver, and then associate the driver with the VID/PID (vendor ID and product ID) of the modem we are interested in.

Being smart, let's look at what was done with the Gobi driver.  Looking in the modem and ser .inf files, we can see that interface _00 and _01 are the serial interfaces for diag and NMEA, and interface _02 is the modem interface.   So, we need to add the modem interface to the qcmdmlno.inf file, and the serial interfaces for diag and NMEA to qcusbserlno.inf.  IMPORTANT NOTE:  these interfaces numbers can be different, this just happens to work for the chipset used in my device!

Now we modify the .INF's.   Let's do the MDM interface first.   Edit qcmdmlno.inf (with VIM, of course!).  Everywhere we find the PID used, we need to add another entry for our PID.  For example, in my .INF file, because my PID is 9002, I added the following entries (see my diff below...   for those not familiar with diffs, + is an added line)

*** 24,39 ****
--- 24,42 ----
  ExcludeFromSelect = *

  %QUALCOMM92022% = Modem2, USB\VID_05C6&PID_9202&MI_02
+ %QUALCOMM90022% = Modem2, USB\VID_05C6&PID_9002&MI_02

  %QUALCOMM92022% = Modem2, USB\VID_05C6&PID_9202&MI_02
+ %QUALCOMM90022% = Modem2, USB\VID_05C6&PID_9002&MI_02

  %QUALCOMM92022% = Modem2, USB\VID_05C6&PID_9202&MI_02
+ %QUALCOMM90022% = Modem2, USB\VID_05C6&PID_9002&MI_02

  AddReg = All, MfgAddReg, Modem1.AddReg, USB

*** 62,71 ****
--- 65,75 ----
  QCUSBSER = "Lenovo USB Device for Legacy Serial Communication"
  QcomSrcDisk = "Lenovo USB Driver Disk"
  QUALCOMM   = "Qualcomm Incorporated"
  QUALCOMM92022 = "Qualcomm HS-USB Modem 9202"
+ QUALCOMM90022 = "Qualcomm HS-USB Modem 9002"


 So, add the above entries to the qcmdmLno.inf.  Done.   The driver should now load correctly for the modem interface.

Now let's move on to the USB serial ports for Diag and NMEA.   Using the same approach, we will add the _00 and _01 ports the qcserlno.inf file.  Below is what I did:

*** 25,44 ****
--- 25,50 ----

  %QcomDevice92010%  = QportInstall00, USB\VID_05C6&PID_9201
  %QcomDevice92020%  = QportInstall00, USB\VID_05C6&PID_9202&MI_00
  %QcomDevice92021%  = QportInstall00, USB\VID_05C6&PID_9202&MI_01
+ %QcomDevice90020%  = QportInstall00, USB\VID_05C6&PID_9002&MI_00
+ %QcomDevice90021%  = QportInstall00, USB\VID_05C6&PID_9002&MI_01

  %QcomDevice92010%  = QportInstall00, USB\VID_05C6&PID_9201
  %QcomDevice92020%  = QportInstall00, USB\VID_05C6&PID_9202&MI_00
  %QcomDevice92021%  = QportInstall00, USB\VID_05C6&PID_9202&MI_01
+ %QcomDevice90020%  = QportInstall00, USB\VID_05C6&PID_9002&MI_00
+ %QcomDevice90021%  = QportInstall00, USB\VID_05C6&PID_9002&MI_01

  %QcomDevice92010%  = QportInstall00, USB\VID_05C6&PID_9201
  %QcomDevice92020%  = QportInstall00, USB\VID_05C6&PID_9202&MI_00
  %QcomDevice92021%  = QportInstall00, USB\VID_05C6&PID_9202&MI_01
+ %QcomDevice90020%  = QportInstall00, USB\VID_05C6&PID_9002&MI_00
+ %QcomDevice90021%  = QportInstall00, USB\VID_05C6&PID_9002&MI_01

  AddReg = All00, AddReg

*** 70,75 ****
--- 76,83 ----
  QcomSrcDisk = "Lenovo USB Driver Disk"
  QUALCOMM   = "Qualcomm Incorporated"
  QcomDevice92010 = "Qualcomm HS-USB QDLoader 9201"
  QcomDevice92020 = "Qualcomm HS-USB Diagnostics 9202"
  QcomDevice92021 = "Qualcomm HS-USB NMEA 9202"
+ QcomDevice90020 = "Qualcomm HS-USB Diagnostics 9002"
+ QcomDevice90021 = "Qualcomm HS-USB NMEA 9002"

Again, this is for the PID's for my device.  Your device may have a different PID.  Add these to qcserlno.inf (or whichever driver set you hijacked).

Ok, now let's test this.   Go through each interface in the Device Manager and update the driver.  To do that:

  1. start device manager
  2. find the Qualcomm devices listed in the "other devices" box as shown in the screenshot earlier
  3. right-click on each device, and select "Update Driver Software"
  4. the next dialog box will be "Update driver Software", select "Browse my computer for driver software"
  5. Navigate to where you saved the drivers in "update drivers" dialog 

  6. Select "Install this driver anway" when you are prompted about verifying the publisher of the driver
  7. Repeat for each device in the list (3 times, in my case)
Ok, after doing this, you should see all the devices loaded, as shown below

Now let's test it!   Right-click on the modem, and select properties, and select the "Diagnostics" tab.   Press the Query Modem button, wait a few seconds, and you should see a response come back that says "success".   It works!

Here's a link to the drivers + modified INF, zipped up, that some friends were kind enough to host.