Driver adventures for a 1999 webcam

Apr 28 2023
We usually know that after we purchase a chunk of know-how that it’s going to not final eternally, connectors put on out and/or exit of trend. However I believe probably the most irritating cause to should do away with one thing is that drivers cease being made for units.
USB has been a exceptional success. It has been with us for a very long time and has saved a (principally, ignoring USB-C) constant connector. That means that very previous units made for USB 1 are nonetheless usable in techniques which might be being offered as we speak. At the very least this might be the case if the older units nonetheless had drivers for presently related working techniques.
The USB common video, audio and storage courses have offered an ordinary for units to implement to make sure that they’ll work with little customized work on drivers or no additional drivers in any respect, however they nonetheless should have been made in a time the place these requirements existed.
An excellent buddy of mine this week was clearing out stuff and handed me an previous logitech QuickCam Specific webcam, this was really a fairly critical nostalgic second because it occurred to even be the identical mannequin as my first webcam, so pondering it could possibly be humorous at some work conferences to have an “early 2000s” vibe I took it dwelling.
Nevertheless the QuickCam Specific has not had drivers since Home windows XP it appears. I connected it to my Linux machine, and no module was loaded, and once I then connected it to my Home windows 10 VM I used to be introduced with an unknown system. That means I used to be out of luck for official assist for this factor.
This was particularly annoying since I had already taken this factor dwelling. Not wanting to surrender so rapidly I made a decision to go and truly confirm if this webcam nonetheless labored by putting in a replica of Home windows XP to see if it could appropriately perform on a interval appropriate working system.
(As a facet observe I consider it must be on report that putting in Home windows XP on moderately quick fashionable techniques could be very amusing, the setup wizard will say that it has half-hour remaining after which proceed to blow by way of the whole set up in lower than 15 seconds)
After set up, I loaded up Home windows Film Maker to see if the webcam would appropriately work and was delighted to see that it does. (I have to say the standard of webcams has undoubtedly improved since 1999)
So the query was, how are we going to make this webcam that solely has drivers as much as Home windows XP work on a modern-day working system?
In one of my previous blog posts I purchased various “VGA2USB” video seize units for very low-cost and I later understood that these have been very low-cost as a result of in addition they had no modern-day drivers, so I made a decision to appropriate it by writing a consumer area driver (and I now use these units not less than as soon as a month!).
A consumer area driver is a driver that’s embedded inside a program reasonably than a module of code contained in the working system. Which means that the motive force can run usually on completely different variations of working techniques and sometimes on completely different platforms with minimal code adjustments.
To get an thought of what the Home windows XP driver was doing I loaded usbmon into my desktop that was operating the Home windows XP VM after which recorded the USB visitors going between the digital machine and the webcam. That is invaluable in reverse engineering because it permits us to see an actual time “recognized good communication” transcript that we are able to then construct our personal driver from.
Reassuringly it appeared that the communication was fairly primary, involving what regarded like some primary settings bootstrapping after which isochronous knowledge switch of a fairly primary trying knowledge stream.
I then started to go searching to see if anyone had beforehand written a Linux driver for this webcam, and it turned out somebody had within the type of qc-usb. So utilizing that as a base I labored in the direction of getting a really primary setup the place I might stream picture knowledge.
To start out with I used the identical libusb wrapper go-usb and set it as much as search for the QuickCam’s usb VID and PID:
ctx := gousb.NewContext()
defer ctx.Shut()
// idVendor=046d, idProduct=0870,
dev, err := ctx.OpenDeviceWithVIDPID(0x046d, 0x0870)
if err != nil || dev == nil {
log.Fatalf("Couldn't open a tool: %v", err)
}
deviceConfig, err := dev.Config(1)
if err != nil {
log.Fatalf("Didn't get USB config for system: %v", err)
return
}
At that time we are able to seize an interface for the system so we are able to talk with it.
// Config.Interface(num, alt int)
USBInterface, err := deviceConfig.Interface(0, 0)
if err != nil {
log.Fatalf("can't seize management interface %v", err)
}
Beneath the hood, one thing that you simply plug right into a USB port has a tool configuration, that specifies a number of interfaces, and that interface doubtless has a number of endpoints inside. There are a number of endpoints actually because USB units do multiple factor. For instance in a USB sound card there would be the output perform, a microphone, and the buttons on the cardboard to regulate quantity. These would usually be separate endpoints, and for doubtlessly completely different interface configurations for no matter setup the motive force controlling it desires to make use of.
Because the actually attention-grabbing job is the precise webcam picture knowledge, I exported out a CSV from wireshark of the entire management packets to setup the webcam that the VM despatched to the webcam earlier than it outputted a picture, and put that within the consumer area driver to be replayed (with out fairly understanding what they do but).
I then setup the switch half by utilizing the endpoint that used a isochronous knowledge stream (a kind of USB knowledge change that ensures latency and bandwidth on the bus) and located that may lead to:
libusb: invalid param [code -2]
Confused about this, I went to look within the kernel log for extra info since that usually has useful info in these conditions:
kernel: usb 5-1.3: usbfs: usb_submit_urb returned -90
Helpfully, the kernel does have some helpful info! -90 is an effective trace about what is going on. The damaging quantity would point out it’s an error quantity from the kernel. So let’s search for what 90 means as an error quantity.
$ cat /usr/src/linux-headers-`uname -r`/embrace/uapi/asm-generic/errno.h | grep 90
#outline EMSGSIZE 90 /* Message too lengthy */
We are able to see that 90 (or -90) means “Message too lengthy”. This stumped me for some time, till I made a decision to look once more on the USB Machine configuration buildings…
I then noticed that the 0’th interface (the one I had chosen out of behavior) had a MaxPacketSize of 0 for what I assumed was the picture switch endpoint.
Which means that all makes an attempt to get knowledge from it utilizing the primary USB interface would fail. Now you would possibly ask, why does the webcam have an endpoint with a 0 byte MaxPacketSize on its first interface? Who is aware of! However the different out there interface is a mirror of the 0th, however with a MaxPacketSize of 1023. Ok, and very quickly, I had the power to stream knowledge out of the webcam!
We are able to now learn the 1023 byte knowledge chunks from the webcam. however now we have to decode what this stream of bytes actually means!
The webcam seems to have a chunking protocol the place it offers you picture knowledge in 1023 byte chunks. Primarily based on patterns we might assume that 0x02,0x00 is perhaps the beginning of a brand new body. However I made a decision to check out the qc-usb
driver to avoid wasting time and located that 0x02,0x00 is the message ID for a picture chunk: these are supposed to be added to a bigger picture buffer, with the subsequent 16 bits being the size of the chunk. The beginning and finish of frames are encoded utilizing completely different message IDs. Both manner, a fairly simple factor to implement and it didn’t take lengthy till we had full “body” knowledge.
Now comes the tougher job of determining how pictures are encoded. My first try was to take the decision that Home windows XP claimed it was (320×240) and draw that instantly as RGB 24bit color pixels:
Properly, it was value a shot. At this level I did have a suspicion although that we nonetheless had uncooked sensor knowledge with out some other compression concerned. This was as a result of the info popping out is all the time 52096 bytes. If it was compressed you’ll see some sort of variation (even when small).
Nevertheless to substantiate this I merely put my hand in entrance of the webcam and took some extra snapshots and noticed that the lightness of the corrupted mess not less than matched my hand being over the digicam.
So we now know we’re taking a look at uncooked sensor samples not less than. The query is, how are these sensor values packaged?
I investigated the sensor within the webcam and found it was a Photobit PB-100, a sensor that has a decision of 352×288. I then assumed that every pixel needed to be round 4 bits primarily based on the body measurement of 52096 bytes:
(52096*8)/(352*288) = 4.11
So what if we attempt that new decision and assume that every sensor worth is a 4 bit quantity?
With that I had a picture that did really appear to be me (it turned out I additionally had a blanket over me that was very useful as a test pattern!) – however bunched up and with none color.
One of many advantages of engaged on picture processing is that you simply get to see plenty of “Digital sign processing fashionable artwork”. I believe I by accident made a DSP Andy Warhol.
I then realised that I solely have sufficient pixels for a monochrome picture, but when I drew it out incorrectly, I acquired a “full-sized” picture that resembled one thing that regarded like me!
finalImg := picture.NewRGBA(
picture.Rect(0, 0, 400, 400))
xLimit := 352
for idx, _ := vary imgData {
if idx+3 > len(imgData) {
break
}
x := idx % xLimit
y := idx / xLimit
finalImg.SetRGBA(x, y, colour.RGBA{
imgData[idx],
imgData[idx+1],
imgData[idx+2],
255,
})
}
The subsequent hurdle is color. I assumed that this was going to be just like my earlier adventures and I attempted doing a YUV colorspace remodel (Since RGB was clearly not working) with no luck.
So I went again to studying the qc-usb
linux driver supply once more, I found that the uncooked picture knowledge had a Bayer filter on it! So, I must undo the filter myself. The driving force itself had various methods (ranked in what number of cycles it took an Intel Pentium 2 to course of a picture) to do that, nonetheless I struggled to port any of them appropriately to golang. So as a substitute I discovered a TIFF library that had a function for it and used that instead.
And with that, I pointed my webcam at a rainbow lanyard. Switched it to GRBG mode and acquired:
That is the primary color picture I acquired from my driver! I used to be thrilled! Subsequent I setup a the digicam to level at a check card to see how nicely it carried out in opposition to it and whereas it did look amusing:
It does remind me once more that we now have come a good distance with webcams since 1999…
Now to offer the QuickCam some credit score, a number of the disappointing color response is as a result of I’m utilizing parameters that I acquired from a one-off USB packet seize. Since these management packets management issues publicity/brightness, primarily these settings are frozen in time from once I first examined the webcam.
Curiously the white steadiness is required to be processed on the motive force facet, I additionally discovered that the auto brightness perform of the webcam is managed completely on the motive force facet and never contained in the webcam. I suppose this does make sense for 1999 however I think (however I don’t know for certain) that as we speak plenty of these features are managed from contained in the webcams, heck, some of the webcams run Linux now!
Now this isn’t very helpful if I can’t be a part of a Google meet with it, so for that we are able to use V4L2 Loopback to make a pretend video system and permit us to inject our newly decoded webcam pictures again into the kernel so purposes like Google meet can use it!
Doing that is really pretty easy as ffmpeg can do all of the heavy lifting, so all we actually must do is to offer FFMPEG a MJPEG stream and the system.
First we create the V4L2 system:
sudo modprobe v4l2loopback exclusive_caps=1
After which we are able to feed MJPEG in as a webcam utilizing one thing like:
ffmpeg -f mjpeg -i - -pix_fmt yuv420p -f v4l2 /dev/video0
I made my userspace driver do that mechanically for optimum ease, and earlier than I knew it, I might load up google meet and see a webcam from 1999 present my face once more (with some bizarre processing artifacts, no thought on that that one)
Mission Success. We turned what was going to be nostalgic e-waste right into a horrible however functioning webcam, and better of all, I discovered much more concerning the horrors of USB on the best way!
For those who additionally occur to personal one in all these, You could find the code to do all of this right here: benjojo/qc-usb-userspace
If you wish to keep updated with the weblog you should use the RSS feed or you may observe me on Fediverse @benjojo@benjojo.co.uk (or twitter the place I’m now not often posting)
You might have observed I’ve not posted in a very long time! It is because I’ve been working by myself enterprise referred to as bgp.tools. If in case you have any use for BGP monitoring or knowledge evaluation do have a look!
For those who like what I do or assume that you might do with a few of my weird areas of information I’m additionally open for contact work, please contact me over at workwith@benjojo.co.uk!
Till subsequent time!