USB - TFE

advertisement
USB 2.0
Datum: 2003-10-13
Handledare/Lärare:
Ulf Brydsten
Björne Lindberg
Lars Karlsson
Johannes Karlsson
Av: Mikael Backlund
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
2
Sammanfattning
USB 2.0 är en standard för seriell dataöverföring som utvecklades för att tillgodose
marknadens nya krav på högre bandbredd.
I denna rapport går jag igenom vilka hastigheter USB 2.0 stödjer, hur en USB buss är
uppbyggd (dess topologi), vilka hårdvarukomponenter en USB buss innehåller och
hur de fungerar. Går även in lite närmare på själva överföringen och dataflöde.
Några mer allmänna saker som kompatibilitet, användningsområde och USB On The
Go tas även det upp.
2
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
3
Innehållsförteckning
INLEDNING .......................................................................................................................................... 4
ARKITEKTUR ...................................................................................................................................... 5
TOPOLOGI ............................................................................................................................................ 5
TYPER AV USB .................................................................................................................................... 6
HOT PLUG ............................................................................................................................................ 6
HÅRDVARA .......................................................................................................................................... 7
KOMPONENTER .................................................................................................................................... 7
USB Host ........................................................................................................................................ 7
Hub ................................................................................................................................................. 8
USB Enhet ...................................................................................................................................... 8
Kablage .......................................................................................................................................... 8
STRÖMFÖRSÖRJNING.......................................................................................................................... 10
ÖVERFÖRING .................................................................................................................................... 10
PROTOKOLLAGRET............................................................................................................................. 10
Tokenpaket ................................................................................................................................... 10
Datapaket ..................................................................................................................................... 11
Handskakningspaket ..................................................................................................................... 11
CRC .............................................................................................................................................. 12
Data toggle synchronisation......................................................................................................... 12
Bus Turn-around Timing .............................................................................................................. 13
DATAFLÖDE ....................................................................................................................................... 14
Typer av dataöverföring ............................................................................................................... 14
NRZI-KODNING OCH BITSTUFFING..................................................................................................... 15
ALLMÄNT ........................................................................................................................................... 16
MÅL MED STANDARDEN..................................................................................................................... 16
USB ON THE GO ................................................................................................................................. 16
KOMPATIBILITET ............................................................................................................................... 17
ANVÄNDNINGS OMRÅDE .................................................................................................................... 17
IMPLEMENTATION ......................................................................................................................... 18
PRAKTISK TILLÄMPNING ............................................................................................................. 19
USB DRIVRUTIN FÖR PHILIPS WEBKAMERA ....................................................................................... 19
BILAGA 1: KÄLLKOD ...................................................................................................................... 21
3
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
4
Inledning
Under mitten av 90-talet påbörjade 7 företag utvecklingen av en ny standard för
seriell överföring. Standarden fick namnet USB(Universal Serial Bus) och företagen
var Compaq, Hewlett-Packard, Intel, Lucent, Microsoft, NEC och Philips.
Den nya standarden skulle uppfylla tre krav som företagen ansåg vara kritiska, lätt att
använda med stöd för plug and play, billig men ändå stor bandbredd och att med hjälp
av USB kunna koppla ihop datorn med kommunikations apparater t. ex telefon.
Dagens USB version som släpptes i april år 2000 går under namnet 2.0. USB 2.0
stödjer tre hastigheter: 1.5Mbit/s, 12Mbit/s och 480Mbit/s. Den senaste går under
namnet High-speed USB och utvecklades speciellt för videoöverföring, lagring,
bildmanipulering och bredband.
USB är med sin billiga produktionskostnad och enkelhet idag ledande inom sin
marknad för periferienheter (tangentbord, mus, spelkontroll, skrivare, bärbar
lagringsmedia m. m). Samt att många andra applikationer för USB har tagits fram så
som laddare till mobiltelefoner, lampor m. m.
4
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
5
Arkitektur
Topologi
Nätverket för en USB buss är uppbyggt som ett stjärnnätverk med distinkta nivåer.
För varje steg som nätet tar ifrån sin host bildas en ny nivå med en eller flera stjärnor
och i centrum av dess stjärnor återfinns alltid en hub. Det maximala antalet nivåer en
USB buss kan ha är sju, detta beror på att responstiden försämras i kablar och hubbar.
En sammansatt enhet (compound device) består av både en hub och en enhet och tar
således upp två nivåer och kan därför ej kopplas in som sjunde led i nätet.
Bilden nedan visar en typiskt USB buss uppkoppling som illustrerar tre nivåer och en
sammansatt enhet.
5
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
6
Typer av USB
Det finns tre versioner av USB specificerade i 2.0 standarden.
Version
Low speed
Full speed
Bandbredd
1.5Mbit/s
12Mbit/s
High speed
480Mbit/s
High-speed versionen utvecklades fram till USB 2.0 speciellt för enheter som kräver
stor bandbredd så som videoöverföring, lagring m.m.
De två andra versionerna används på mer normala enheter så som möss, tangentbord,
skrivare m.m.
Men oftast på dagens USB bussar används hög hastighetsversionen mellan nätets host
och hubbar samt mellan hubbar och hubbar, låg och full hastigheterna används oftast
endast mellan hubbar och enheter. Detta minskar chanserna att en långsammare enhet
fördröjer en snabbare enhet.
Hot plug
USB utnyttjar hot-plug teknologi som gör det möjligt att koppla till eller ifrån enheter
medan system är igång.
När en enhet kopplas in eller ifrån noterar hubben det och lagrar förändringen i sin
status. Hubbens host frågar regelbundet om några förändringar skett och förändringar
skett utför lämpliga åtgärder. Portar på hubben öppnas eller stängs, adresser tilldelas
eller tas bort o.s.v.
Om enheten som togs bort eller las till är en hub så upprepas detta för alla enheter
som den hade uppkopplat till sig. När en enhet läggs till får dess inbyggda mjukvara
ta hand om sin initieringen.
6
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
7
Hårdvara
Komponenter
En USB-buss byggs upp av en del olika fysiska delar, dessa är:




USB Host
Hub
USB enhet
Kablage
De flesta moderna datorsystem har en Host och en hub inbyggd, så allt som behövs
för att få en fungerande USB enhet är den aktuella enheten och kabeln mellan enheten
och hubben. Men de existerande USB systemen kan enkelt byggas ut om behovet
finns. Utbyggnad sker enklast genom att hubbar kopplas ihop med andra hubbar och
på så sätt görs en port om till flera. Enheten i sig kan också ha en inbyggd hub och
därigenom bidra till utbyggnad av systemet.
USB Host
Består logiskt av tre separata delar.
 USB Host kontroller
 Systemets mjukvara (drivrutiner m. m)
 Klientens mjukvara
Host:en är den fysiska del som koordinerar dataflödet till enheterna på sitt USB nät.
Om en enhet vill använda bussen för t. ex en överföring måste den först få tillstånd av
sin host. Om flera enheter samtidigt vill ha tillgång så har nätets host specifika
ansvarsförhållanden med varje enskild enhet och kan därigenom särställa vilken enhet
som får gå först.
Varje host har också i uppgift att övervaka topologin för sin buss, alltså måste den
hålla reda på hur nätet runt den ser ut.
Inne i varje host finns en inbyggd hub kallad root hub.
7
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
8
Hub
Abstrakt är en hub som ett nav i ett hjul, den tillåter flera enheter/hubbar att få
kontakt med en host. Men under ytan utför huben en mängd viktiga funktioner åt sitt
nät. Det är här som hastigheten på enheterna avgörs, om en enhet kopplas till eller
från är det här som det upptäcks, strömförsörjning till enheterna sköts också här.
En USB hub byggs upp av tre komponenter, hub repeater, hub controller och en hub
translator.
Hub repeatern hanterar händelser där en enhet läggs till eller tas bort samt att den
hanterar vissa undantagstillstånd.
Hub controllern sköter kommunikation mellan host och hub.
Hub translatorn konverterar high-speed överföringar ämnade för enheter som ej klarar
den hastigheten till full-/low-speed.
USB Enhet
Logisk utseende för en USB enhet:
 Buss interface
 Logisk enhet
 Funktion
En fysisk USB enhet tillför funktionalitet till sin host, en funktionalitet som kan
variera stort mellan olika enheter. Men även om funktionaliteten är väldigt olika
mellan två enheter används samma interface emot bussen för båda. Detta medför att
en host på ett nät med mängder av olika enheter endast behöver kommunicera med
dem på ett sätt. Ungefär som att säga att alla enheter pratar samma språk men ändå är
totalt olika.
Varje enskild enhet innehåller och rapporterar sin egen konfigurationsinformation till
sin host vid kontakt för att nätets host skall kunna identifiera enheten.
Konfigurationsinformationen innehåller även specifik information rörande enhetens
funktion.
Kablage
En USB kabel består av 4 ledare, 2 dataledare (D- och D+) och två matningsledare
(Vbuss och GND).
Kablarna kan vara uppbyggda olika för låg hastighet mot full och hög hastighet.
Kablar för låg hastighets USB rekommenderar men kräver inte tvinnade dataledare
eller ett flätat yttre skyddslager medan de högre hastigheterna kräver det.
8
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
9
Bilden nedan visar en genomskärning av en USB kabel:
USB kablar kan ha tre olika kontakter:
Modell
A
Riktning (dataflöde)
Upstream
Kopplas mot
Host/hub
B
Downstream
Enhet
Mini-B
Downstream
Enhet
Bilden nedan visar modell A och modell B kontakter:
Så en USB kabel har alltid en A kontakt och antingen en B eller mini-B kontakt.
Mini-B kontakten togs speciellt fram för att användas på små enheter som t. ex
digitalkameror.
9
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
10
Strömförsörjning
En USB enhet kan antingen ha en egen strömkälla eller använda bussen som
strömkälla. Men blanda med försiktighet, använda mer än en strömkälla kan medföra
problem.
USB bussen levererar en spänning på mellan 4.4V och 5.25V och en maximal ström
på 500mA, vilket räcker och blir över till små enheter som möss, tangentbord etc.
Överföring
Protokollagret
Protokollet för USB talar om hur bitarna i paketen som skickas på USB bussen är
uppbyggda.
All överföring sker med att LSB (least significant bit) skickas först och MSB (most
significant bit) sist.
Det finns fyra olika typer av paket inom USB protokollet.
 Token
 Data
 Handskakning
 Special
Gemensamt för alla typer av paket är synkroniseringsfältet (SYNC) som används för
att få inkommande data synkroniserat med systemklockan samt
paketidentifieringsfältet (PID) som kommer efter SYNC fältet, PID fältet anger vilket
typ av paket, vilket format samt vilken typ av felkontroll som paketet utnyttjar. På
slutet av alla paket utom handskakningspaketen återfinns CRC fältet för felkontroller.
Tokenpaket
PID-fältet talar om vad för typ av paket det är. IN, OUT eller SETUP är de tre olika
typer av paket ett tokenpaket kan vara. För OUT och SETUP paket talar ADDR-fältet
och ENDP-fältet om vilken ändpunkt som skall ta emot det följande datapaketet.
För IN paket talar de om vilken ändpunkt som skickar det kommande paketet. CRCfältet har till uppgift att skydda ADDR och ENDP fälten.
Det finns ett till speciellt definierat tokenpaket kallat SPLIT och som används vid
delade överföringar t. ex när en host kommunicerar med en hub med hög hastighet
10
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
11
och med en på full eller låg hastighet. Denna token är 1 byte längre än den normala
tokenstandarden och de ytterligare 4 bitarna möjliggör ytterligare typer och mer
specifik överföringsinformation.
Det finns två definierade SPLIT överföringsmetoder:
 SSPLIT – start-split transaction
 CSPLIT – complete-split transaction
Ovan: Denna bild illustrerar ett token paket och dess fält.
Datapaket
PID fältet följs av ett 0-8192 bitar långt data fält samt en 16 bitars CRC fält.
Fyra olika sorters datapaket finns: DATA0, DATA1, DATA2
och MDATA. Två av dessa (DATA0, DATA1) är designade att stödja data toggle
synchronisation. Alla fyra kan användas av ändpunkter som använder högfart och
kräver mycket bandbredd. MDATA, DATA0 och DATA1 används även inom delade
överföringar.
Storleken på datafältet får ej överstiga 8 bytes vid låghastighetsöverföring, 1023 bytes
vid fullfarts och 1024 bytes vid höghastighetsöverföring.
Paketet ovan är ett datapaket.
Handskakningspaket
Det består av ett PID fält och inget mer. Handskakningspaket används för att hålla
koll på statusen av en överföring. En handskakning kan returnera om det gått bra, om
det blev fel, om kommandot mottagits eller förkastats, flödeskontroll och om en
situation uppstår där nåt vill stoppa överföringenn. Det finns 5 typer av
handskakningsmeddelanden.
ACK som säger att allt är ok.
NAK skickas när det inte finns någon data att skicka eller när data från nätets host
inte kunde tas emot.
STALL används när en enhet är upptagen och ej kan ta emot eller skicka data.
11
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
12
NYET är en hög hastighets handskakning som endast används i två fall, som en del
av ett PING protokoll eller som ett svar på en överföring mellan två delar i nätet som
kör på olika hastigheter och där låg eller full farts överföringen inte hunnit klart ännu.
ERR är ett felmeddelande som används som svar när en höghastighets hub vill
anmäla ett fel på en låg eller full fart hub.
Handskakningspaketet som bilden ovan visar är det enklaste paketet inom USB
protokollet.
CRC
Cyclic Redundancy Checks används för att skydda alla fält utom PID fältet i tokenoch datapaket. CRC kodning genereras hos sändaren och avkodas hos mottagaren.
Om CRC kollen inte godkänns tas åtminstone det fält som är korrupt bort men oftast
kastas hela paketet. Kodningssystemet som används är att för varje bit som skickas
eller tas emot körs en bitvis XOR mot den nuvarande summans högsta del, efteråt
skiftas summans ett steg vänster och summans låga del nollställs. När proceduren
gjorts för hela paketet och summan är ett så XOR:as summan med kodgeneratorns
polynom.
När sista tecknet som kollats skickats inverteras generatorns CRC och skickas till
kontrollanten MSb först. När sista tecknet mottagits av kontrollanten och inga fel
upptäckts så ska summan vara lika med den överblivna polynomet.
Eftersom USB utnyttjar bitstuffing (förklaras längre fram i kapitlet) så måste givitvis
dessa krav uppfyllas i CRC generering/kontroll. Alltså om CRC koden innehåller sex
raka logiska ettor måste det sättas in en logisk nolla.
Tokenpaket använder ett 5-bitars fält för CRC. Detta fält skyddar adress- och
ändpunktsfälten. Detta pakets polynom är på binär form: 00101B.
Om alla bitars tas emot korrekt så blir det överblivna polynomet hos mottagaren:
01100B.
Datapaket har ett 16-bitar långt CRC fält som har till uppgift att skydda datafältet.
Polynomets binära mönster: 1000000000000101B.
Överblivna polynomet: 1000000000001101B.
Data toggle synchronisation
USB har en inbyggd mekanism för att garantera att data sekvenserna mellan sändare
och mottagare är synkroniserade. Med denna funktion kan man vara säker på att
12
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
13
handskakningar mellan sändare och mottagare är korrekta. Synkronisering uppnås
med hjälp av PID fältet som anger DATA0 och DATA1 som typ av datapaket. Dessa
paket har separata sekvensbitar för sändare respektive mottagare. Sekvensbitarna hos
mottagaren togglas när den är redo att motta ett nytt paket och får ett korrekt
datapaket med rätt information i paketets PID-fält. Sändaren togglar bitarna när den
får en korrekt ACK handskakning. För att en överföring skall bli korrekt måste båda
sidor vara synkroniserade i början av den överföringen. Denna mekanism fungerar
endast vid synkrona överföringar.
Bilderna nedan illustrerar hur sekvensbitarna (siffran i parentesen) togglar i två olika
fall. Först två raka korrekta överföringar sen under den en felaktig sen en korrekt.
----------------------------------------------------------
Bus Turn-around Timing
Eftersom varken en host eller en enhet skickar nån form av meddelande om nåt går
fel så måste ett brist på svar att det gått bra ses som att det fel. För att få det att
fungera måste därför både enheten och dess host hålla reda på hur mycket tid som
gått sedan sändaren skickade ett paket. Tiden mellan när sändaren skcikat och när den
börjar ta emot svarspaketet kallas bus turn-around time.
En enhet sätter sin bus turn-around time till den värsta tur-retur fördröjningen plus
enhetens maximala svarsdelay.
13
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
14
Dataflöde
USB erbjuder kommunikation mellan enheten och mjukvaran hos dess host. Olika
enheter kan ha olika krav på kommunikationen och dess dataflöde. USB separerar
därför på sina olika kommunikationer för att bättre utnyttja bussen.
Varje kommunikation får ett rör (pipe) att kommunicera igenom. På den sidan dit
datat skall skickas skapas en ändpunkt (endpoint) som har hand om diverse
information om kommunikationen, hastighet, överföringstyp och riktning bland
annat.
Ändpunkten är en del av enheten och bussen och varje enhet måste minst ha en
ändpunkt (ändpunkt 0) men kan ha flera. Ändpunkt 0 består av två separata
ändpunkter, en för indata och en för utdata och används mestadels för konfiguration
och initiering. Varje enskild ändpunkt kan bli adresserad med hjälp av sitt
ändpunktsnummer, riktning på överföring samt enhetens adress.
Ett rör är en association mellan ändpunkten på en enhet och mjukvaran på en host.
Rören använder en minnesbuffer vid dessa överföringar.
Bilden nedan demonstrerar logiskt hur rör och ändpunkter fungerar.
Typer av dataöverföring
I USB standarden finns fyra olika typer av dataöverföring specificerade.
 Kontroll: snabba, ej periodiska, begäran och svars kommunikation som oftast
används vid kommando och status operationer.
 Isokronisk: Periodisk fortlöpande kommunikation mellan host och enhet,
används för det mesta till tidsrelaterade operationer.
 Avbrott: Överföringar som inte händer så ofta och är bundna till något
speciellt.
 Volym: Denna typ är avsedd för dataöverföringar, ej periodisk, stora paket,
klarar alla hastigheter och kan vänta tills rätt bandbredd finns tillgänglig.
För att få dessa överföringar att bli korrekta krävs att de körs i rätt sekvens vilket man
löst med tillståndsmaskiner i båda ändarna.
14
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
15
NRZI-kodning och bitstuffing
USB utnyttjar NRZI kodning vid överföring. Det innebär att en logisk ”1” är när det
inte är någon spänningsändring och logisk ”0” är vid spänningsändring.
Bilden nedan demonstrerar NRZI kodning.
För att försäkra godtagbara signalöverföringar har man även implementerat
bitstuffing, vilket innebär att efter 6 stycken raka ”1” sätter man in en ”0”. Alltså
ligger spänningen oförändrad sex bitar på raken ändrar man spänningen. Detta gör att
en ändring i spänningen sker minst var sjunde bit vilket försäkrar att synkning och
klockning stämmer.
15
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
16
Allmänt
Mål med standarden
I början hade man tre stora anledningar till att man ville utveckla en ny seriell
standard.
Telefoni marknaden hade oanade framtida behov och en standard som medförde PC
till telekommunikation sågs som ett stort steg i rätt riktning.
Datorn var fortfarande inte så användarvänlig som den kunde vara, speciellt parallell
portarna och serieportarna eftersom de inte stödde plug and play. Så en standard för
att ersätta dessa portar samtidigt som den stödde plug and play vore väldigt bra.
Expansionssvårigheter med dåvarande portar, en port som var enkel att utvidga och
samtidigt kunna klara av nog höga hastigheter för avancerad utrustning skulle
medföra ytterligare möjligheter för datorn och föra utvecklingen åt rätt håll.
USB 2.0 standarden har en stor skillnad från tidigare versioner av USB, den klarar
480 Mbit/s överföring. Detta var det stora målet för den nya USB standarden när
utvecklingen av 2.0 påbörjades 1998-99. Marknaden krävde högre hastigheter med
alla digitala utrustningar som utvecklades till datorn. Speciellt inom video, bild och
lagringsmedia.
USB on the go
On The Go är namnet på ett tillägg i USB 2.0 standarden som möjliggör att två eller
flera USB enheter skall kunna kommunicera direkt med varandra utan att en
dator/kontroller behövs.
En del saker i USB standarden gör detta mycket svårt för kommunikation kan endast
ske mellan en host och en enhet med en hub som mellanhand, och även om vissa
enheter har inbyggda hubbar så finns det inte någon host i någon enskild enhet.
Enheten måste även kunna lagra data i form av drivrutiner och få ström på något sätt.
För att få detta att fungera utvecklades ett tillägg till USB 2.0 som kallas USB OnThe-Go. Det möjliggör att en enhet skall kunna fungera i en roll som en begränsad
host.
16
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
17
Kompatibilitet
Eftersom USB standarden har utvecklas genom åren krävs det någon form av
kompatibilitet.
I USB 2.0 är det full kompatibilitet bakåt. Utrustning som utvecklades och använder
USB 1.1 kan köras utan bekymmer på en dator som har USB 2.0.
Användnings område
USB är ett seriellt dataöverföringsmedium som är lätt att använda och passar därför
perfekt som medium för utrustning anpassad för privatpersonen t. ex digitalkameror,
spelkontroller och videkameror. Eftersom USB stödjer plug and play och smidig att
använda är den även utmärkt för standardutrustning så som möss, skrivare,
tangentbord.
Enkelheten att implementera USB i inbyggda lösningar öppnar även marknaden för
såväl proffsutvecklare som amatörerna.
17
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
18
Implementation
USB går förmodligen att implementera på alla dagens mikrokontrollers/processorer
och datasystem men undersök alltid om den aktuella processorn/kontroller klarar det.
I vårt projekt använder vi ett utvecklingskort från Axis. På det kortet sitter en Etrax
100LX processor och på deras hemsida finns instruktioner om hur parallellporten på
utvecklingskortet enkelt kan konstrueras om till en USB port.
Det finns färdiga USB-interface kretsar att köpa som möjliggör USB kommunikation
mellan mikroprocessorer eller enchipsdatorer och vanliga PC system. Dessa kretsar
använder antingen parallellt interface eller seriellt interface men resultatet blir
detsamma.
Efter att konstruktionen/ombyggnaden är klar behövs mjukvara i form av drivrutiner
och program för att hantera all data.
18
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
19
Praktisk tillämpning
USB drivrutin för Philips webkamera
Detta är en drivrutin utvecklad för Philips webkameror som utnyttjar V4L (video for
linux).
Den består av två enheter, en videoenhet och en USB enhet.
När modulen laddas körs initieringen för USB enheten och ger videoenheten en
device node samt registrerar USB enheten till USB kärnan.
Sedan när en enhet pluggas in i USB porten eller när USB kärnan ladas körs probe
kommandot till USB-enheten.
Probe kommandot kollar vad för sorts kamera som sitter på porten, sen initieras
enhetsstrukturen, diverse pekare och videoenheten länkas till USB-enheten.
Mer inställningar utföres, spinlock låses upp, initierar en kö och sätter ägaren till
modulen är några.
Efter det sökes en lista med nodnummer igenom efter en ledig plats och när den hittas
försöker drivrutinen registrera en videoenhet.
Drivrutinen har även en disconnect drivrutin som exekveras vid händelse att enheten
kopplas ur.
Det första den funktionen utför är en funktion som låser kärnan. Efter det kollas om
nåt fel uppstod i.s.f lås upp kärnan och hoppa ur funktionen.
Om det inte var nåt fel utan enheten blev urkopplad kolla först om den användes vid
urkoppling. Efter det stäng videoenheten och avregistrera den samt frigör resurser
som allokerats.
På slutet av denna funktion låses kärnan upp.
Källkod medföljer som bilaga.
19
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
20
Källförteckning
USB 2.0 specifikation
http://www.usb.org/developers/docs/usb_20.zip
Axis utvecklingskort byggs om för USB
http://developer.axis.com/doc/hardware/devboard_lx/USB_Devboard_LX.pdf
USB drivrutin
http://www.smcc.demon.nl/webcam/pwc-8.12.tar.gz
20
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
21
Bilaga 1: Källkod
/* Linux driver for Philips webcam
USB and Video4Linux interface part.
(C) 1999-2003 Nemosoft Unv.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
*/
/*
This code forms the interface between the USB layers and the Philips
specific stuff. Some adanved stuff of the driver falls under an
NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and
is thus not distributed in source form. The binary pwcx.o module
contains the code that falls under the NDA.
In case you're wondering: 'pwc' stands for "Philips WebCam", but
I really didn't want to type 'philips_web_cam' every time (I'm lazy as
any Linux kernel hacker, but I don't like uncomprehensible abbreviations
without explanation).
Oh yes, convention: to disctinguish between all the various pointers to
device-structures, I use these names for the pointer variables:
udev: struct usb_device *
vdev: struct video_device *
pdev: struct pwc_devive *
*/
/* Contributors:
- Alvarado: adding whitebalance code
- Alistar Moire: QuickCam 3000 Pro device/product ID
- Tony Hoyle: Creative Labs Webcam 5 device/product ID
- Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
- Jk Fang: Sotec Afina Eye ID
- Xavier Roche: QuickCam Pro 4000 ID
- Jens Knudsen: QuickCam Zoom ID
- J. Debert: QuickCam for Notebooks ID
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
<linux/errno.h>
<linux/init.h>
<linux/mm.h>
<linux/module.h>
<linux/poll.h>
<linux/slab.h>
<linux/vmalloc.h>
<linux/wrapper.h>
<asm/io.h>
#include "pwc.h"
#include "pwc-ioctl.h"
#include "pwc-uncompress.h"
/* Function prototypes and driver templates */
21
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
22
/* hotplug device table support */
static struct usb_device_id pwc_device_table [] = {
{ USB_DEVICE(0x0471, 0x0302) }, /* Philips models */
{ USB_DEVICE(0x0471, 0x0303) },
{ USB_DEVICE(0x0471, 0x0304) },
{ USB_DEVICE(0x0471, 0x0307) },
{ USB_DEVICE(0x0471, 0x0308) },
{ USB_DEVICE(0x0471, 0x030C) },
{ USB_DEVICE(0x0471, 0x0310) },
{ USB_DEVICE(0x0471, 0x0311) },
{ USB_DEVICE(0x0471, 0x0312) },
{ USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */
{ USB_DEVICE(0x069A, 0x0001) }, /* Askey */
{ USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */
{ USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */
{ USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */
{ USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */
{ USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */
{ USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */
{ USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */
{ USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */
{ USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */
{ USB_DEVICE(0x055D, 0x9000) }, /* Samsung */
{ USB_DEVICE(0x055D, 0x9001) },
{ USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
{ USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */
{ USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */
{ USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */
{ USB_DEVICE(0x0d81, 0x1900) },
{ }
};
MODULE_DEVICE_TABLE(usb, pwc_device_table);
static void *usb_pwc_probe(struct usb_device *udev, unsigned int ifnum, const struct
usb_device_id *id);
static void usb_pwc_disconnect(struct usb_device *udev, void *ptr);
static struct usb_driver pwc_driver =
{
name:
"Philips webcam",
id_table:
pwc_device_table,
probe:
usb_pwc_probe,
disconnect:
usb_pwc_disconnect,
};
#define MAX_DEV_HINTS 20
#define MAX_ISOC_ERRORS
/* name */
/* probe() */
/* disconnect() */
20
static
static
static
static
int default_size = PSZ_QCIF;
int default_fps = 10;
int default_fbufs = 3;
/* Default number of frame buffers */
int default_mbufs = 2; /* Default number of mmap() buffers */
int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX;
static int power_save = 0;
static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */
int pwc_preferred_compression = 2; /* 0..3 = uncompressed..high */
static struct {
int type;
char serial_number[30];
int device_node;
struct pwc_device *pdev;
} device_hint[MAX_DEV_HINTS];
/***/
static int pwc_video_open(struct video_device *vdev, int mode);
static void pwc_video_close(struct video_device *vdev);
static long pwc_video_read(struct video_device *vdev, char *buf, unsigned long count,
int noblock);
static long pwc_video_write(struct video_device *vdev, const char *buf, unsigned long
count, int noblock);
22
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
23
static unsigned int pwc_video_poll(struct video_device *vdev, struct file *file,
poll_table *wait);
static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *arg);
static int pwc_video_mmap(struct video_device *vdev, const char *adr, unsigned long
size);
static struct video_device pwc_template = {
owner:
THIS_MODULE,
name:
"Philips Webcam",
type:
VID_TYPE_CAPTURE,
hardware:
VID_HARDWARE_PWC,
open:
pwc_video_open,
close:
pwc_video_close,
read:
pwc_video_read,
write:
pwc_video_write,
poll:
pwc_video_poll,
ioctl:
pwc_video_ioctl,
mmap:
pwc_video_mmap,
initialize:
NULL,
minor:
0
};
/* Filled in later */
/* initialize */
/* minor */
/***************************************************************************/
/* Okay, this is some magic that I worked out and the reasoning behind it...
The biggest problem with any USB device is of course: "what to do
when the user unplugs the device while it is in use by an application?"
We have several options:
1) Curse them with the 7 plagues when they do (requires divine intervention)
2) Tell them not to (won't work: they'll do it anyway)
3) Oops the kernel (this will have a negative effect on a user's uptime)
4) Do something sensible.
Of course, we go for option 4.
It happens that this device will be linked to two times, once from
usb_device and once from the video_device in their respective 'private'
pointers. This is done when the device is probed() and all initialization
succeeded. The pwc_device struct links back to both structures.
When a device is unplugged while in use it will be removed from the
list of known USB devices; I also de-register it as a V4L device, but
unfortunately I can't free the memory since the struct is still in use
by the file descriptor. This free-ing is then deferend until the first
opportunity. Crude, but it works.
A small 'advantage' is that if a user unplugs the cam and plugs it back
in, it should get assigned the same video device minor, but unfortunately
it's non-trivial to re-link the cam back to the video device... (that
would surely be magic! :))
*/
/***************************************************************************/
/* Private functions */
/* Here we want the physical address of the memory.
* This is used when initializing the contents of the area.
*/
static inline unsigned long kvirt_to_pa(unsigned long adr)
{
unsigned long kva, ret;
kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
kva |= adr & (PAGE_SIZE-1); /* restore the offset */
ret = __pa(kva);
return ret;
}
static void * rvmalloc(unsigned long size)
{
23
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
24
void * mem;
unsigned long adr;
size=PAGE_ALIGN(size);
mem=vmalloc_32(size);
if (mem)
{
memset(mem, 0, size); /* Clear the ram out, no junk to the user */
adr=(unsigned long) mem;
while (size > 0)
{
mem_map_reserve(vmalloc_to_page((void *)adr));
adr+=PAGE_SIZE;
size-=PAGE_SIZE;
}
}
return mem;
}
static void rvfree(void * mem, unsigned long size)
{
unsigned long adr;
if (mem)
{
adr=(unsigned long) mem;
while ((long) size > 0)
{
mem_map_unreserve(vmalloc_to_page((void *)adr));
adr+=PAGE_SIZE;
size-=PAGE_SIZE;
}
vfree(mem);
}
}
static int pwc_allocate_buffers(struct pwc_device *pdev)
{
int i;
void *kbuf;
Trace(TRACE_MEMORY, ">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev);
if (pdev == NULL)
return -ENXIO;
#ifdef PWC_MAGIC
if (pdev->magic != PWC_MAGIC) {
Err("allocate_buffers(): magic failed.\n");
return -ENXIO;
}
#endif
/* Allocate Isochronuous pipe buffers */
for (i = 0; i < MAX_ISO_BUFS; i++) {
if (pdev->sbuf[i].data == NULL) {
kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL);
if (kbuf == NULL) {
Err("Failed to allocate iso buffer %d.\n", i);
return -ENOMEM;
}
Trace(TRACE_MEMORY, "Allocated iso buffer at %p.\n", kbuf);
pdev->sbuf[i].data = kbuf;
memset(kbuf, 0, ISO_BUFFER_SIZE);
}
}
/* Allocate frame buffer structure */
if (pdev->fbuf == NULL) {
24
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
25
kbuf = kmalloc(default_fbufs * sizeof(struct pwc_frame_buf),
GFP_KERNEL);
if (kbuf == NULL) {
Err("Failed to allocate frame buffer structure.\n");
return -ENOMEM;
}
Trace(TRACE_MEMORY, "Allocated frame buffer structure at %p.\n", kbuf);
pdev->fbuf = kbuf;
memset(kbuf, 0, default_fbufs * sizeof(struct pwc_frame_buf));
}
/* create frame buffers, and make circular ring */
for (i = 0; i < default_fbufs; i++) {
if (pdev->fbuf[i].data == NULL) {
kbuf = vmalloc(PWC_FRAME_SIZE); /* need vmalloc since frame
buffer > 128K */
if (kbuf == NULL) {
Err("Failed to allocate frame buffer %d.\n", i);
return -ENOMEM;
}
Trace(TRACE_MEMORY, "Allocated frame buffer %d at %p.\n", i,
kbuf);
pdev->fbuf[i].data = kbuf;
memset(kbuf, 128, PWC_FRAME_SIZE);
}
}
/* Allocate decompressor table space */
kbuf = NULL;
if (pdev->decompressor != NULL) {
kbuf = kmalloc(pdev->decompressor->table_size, GFP_KERNEL);
if (kbuf == NULL) {
Err("Failed to allocate decompress table.\n");
return -ENOMEM;
}
Trace(TRACE_MEMORY, "Allocated decompress table %p.\n", kbuf);
}
pdev->decompress_data = kbuf;
/* Allocate image buffer; double buffer for mmap() */
kbuf = rvmalloc(default_mbufs * pdev->len_per_image);
if (kbuf == NULL) {
Err("Failed to allocate image buffer(s).\n");
return -ENOMEM;
}
Trace(TRACE_MEMORY, "Allocated image buffer at %p.\n", kbuf);
pdev->image_data = kbuf;
for (i = 0; i < default_mbufs; i++)
pdev->image_ptr[i] = kbuf + i * pdev->len_per_image;
for (; i < MAX_IMAGES; i++)
pdev->image_ptr[i] = NULL;
kbuf = NULL;
Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n");
return 0;
}
static void pwc_free_buffers(struct pwc_device *pdev)
{
int i;
Trace(TRACE_MEMORY, "Entering free_buffers(%p).\n", pdev);
if (pdev == NULL)
return;
#ifdef PWC_MAGIC
if (pdev->magic != PWC_MAGIC) {
Err("free_buffers(): magic failed.\n");
return;
}
#endif
25
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
26
/* Release Iso-pipe buffers */
for (i = 0; i < MAX_ISO_BUFS; i++)
if (pdev->sbuf[i].data != NULL) {
Trace(TRACE_MEMORY, "Freeing ISO buffer at %p.\n", pdev>sbuf[i].data);
kfree(pdev->sbuf[i].data);
pdev->sbuf[i].data = NULL;
}
/* The same for frame buffers */
if (pdev->fbuf != NULL) {
for (i = 0; i < default_fbufs; i++) {
if (pdev->fbuf[i].data != NULL) {
Trace(TRACE_MEMORY, "Freeing frame buffer %d at %p.\n",
i, pdev->fbuf[i].data);
vfree(pdev->fbuf[i].data);
pdev->fbuf[i].data = NULL;
}
}
kfree(pdev->fbuf);
pdev->fbuf = NULL;
}
/* Intermediate decompression buffer & tables */
if (pdev->decompress_data != NULL) {
Trace(TRACE_MEMORY, "Freeing decompression buffer at %p.\n", pdev>decompress_data);
kfree(pdev->decompress_data);
pdev->decompress_data = NULL;
}
pdev->decompressor = NULL;
/* Release image buffers */
if (pdev->image_data != NULL) {
Trace(TRACE_MEMORY, "Freeing image buffer at %p.\n", pdev->image_data);
rvfree(pdev->image_data, default_mbufs * pdev->len_per_image);
}
pdev->image_data = NULL;
Trace(TRACE_MEMORY, "Leaving free_buffers().\n");
}
/* The frame & image buffer mess.
Yes, this is a mess. Well, it used to be simple, but alas... In this
module, 3 buffers schemes are used to get the data from the USB bus to
the user program. The first scheme involves the ISO buffers (called thus
since they transport ISO data from the USB controller), and not really
interesting. Suffices to say the data from this buffer is quickly
gathered in an interrupt handler (pwc_isoc_handler) and placed into the
frame buffer.
The frame buffer is the second scheme, and is the central element here.
It collects the data from a single frame from the camera (hence, the
name). Frames are delimited by the USB camera with a short USB packet,
so that's easy to detect. The frame buffers form a list that is filled
by the camera+USB controller and drained by the user process through
either read() or mmap().
The image buffer is the third scheme, in which frames are decompressed
and converted into planar format. For mmap() there is more than
one image buffer available.
The frame buffers provide the image buffering. In case the user process
is a bit slow, this introduces lag and some undesired side-effects.
The problem arises when the frame buffer is full. I used to drop the last
frame, which makes the data in the queue stale very quickly. But dropping
the frame at the head of the queue proved to be a litte bit more difficult.
I tried a circular linked scheme, but this introduced more problems than
it solved.
26
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
27
Because filling and draining are completely asynchronous processes, this
requires some fiddling with pointers and mutexes.
Eventually, I came up with a system with 2 lists: an 'empty' frame list
and a 'full' frame list:
* Initially, all frame buffers but one are on the 'empty' list; the one
remaining buffer is our initial fill frame.
* If a frame is needed for filling, we try to take it from the 'empty'
list, unless that list is empty, in which case we take the buffer at
the head of the 'full' list.
* When our fill buffer has been filled, it is appended to the 'full'
list.
* If a frame is needed by read() or mmap(), it is taken from the head of
the 'full' list, handled, and then appended to the 'empty' list. If no
buffer is present on the 'full' list, we wait.
The advantage is that the buffer that is currently being decompressed/
converted, is on neither list, and thus not in our way (any other scheme
I tried had the problem of old data lingering in the queue).
Whatever strategy you choose, it always remains a tradeoff: with more
frame buffers the chances of a missed frame are reduced. On the other
hand, on slower machines it introduces lag because the queue will
always be full.
*/
/**
\brief Find next frame buffer to fill. Take from empty or full list, whichever comes
first.
*/
static inline int pwc_next_fill_frame(struct pwc_device *pdev)
{
int ret;
unsigned long flags;
ret = 0;
spin_lock_irqsave(&pdev->ptrlock, flags);
if (pdev->fill_frame != NULL) {
/* append to 'full' list */
if (pdev->full_frames == NULL) {
pdev->full_frames = pdev->fill_frame;
pdev->full_frames_tail = pdev->full_frames;
}
else {
pdev->full_frames_tail->next = pdev->fill_frame;
pdev->full_frames_tail = pdev->fill_frame;
}
}
if (pdev->empty_frames != NULL) {
/* We have empty frames available. That's easy */
pdev->fill_frame = pdev->empty_frames;
pdev->empty_frames = pdev->empty_frames->next;
}
else {
/* Hmm. Take it from the full list */
#if PWC_DEBUG
/* sanity check */
if (pdev->full_frames == NULL) {
Err("Neither empty or full frames available!\n");
spin_unlock_irqrestore(&pdev->ptrlock, flags);
return -EINVAL;
}
#endif
pdev->fill_frame = pdev->full_frames;
pdev->full_frames = pdev->full_frames->next;
ret = 1;
}
pdev->fill_frame->next = NULL;
#if PWC_DEBUG
Trace(TRACE_SEQUENCE, "Assigning sequence number %d.\n", pdev->sequence);
pdev->fill_frame->sequence = pdev->sequence++;
27
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
28
#endif
spin_unlock_irqrestore(&pdev->ptrlock, flags);
return ret;
}
/**
\brief Reset all buffers, pointers and lists, except for the image_used[] buffer.
If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble.
*/
static void pwc_reset_buffers(struct pwc_device *pdev)
{
int i;
unsigned long flags;
spin_lock_irqsave(&pdev->ptrlock, flags);
pdev->full_frames = NULL;
pdev->full_frames_tail = NULL;
for (i = 0; i < default_fbufs; i++) {
pdev->fbuf[i].filled = 0;
if (i > 0)
pdev->fbuf[i].next = &pdev->fbuf[i - 1];
else
pdev->fbuf->next = NULL;
}
pdev->empty_frames = &pdev->fbuf[default_fbufs - 1];
pdev->empty_frames_tail = pdev->fbuf;
pdev->read_frame = NULL;
pdev->fill_frame = pdev->empty_frames;
pdev->empty_frames = pdev->empty_frames->next;
pdev->image_read_pos = 0;
pdev->fill_image = 0;
spin_unlock_irqrestore(&pdev->ptrlock, flags);
}
/**
\brief Do all the handling for getting one frame: get pointer, decompress, advance
pointers.
*/
static int pwc_handle_frame(struct pwc_device *pdev)
{
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&pdev->ptrlock, flags);
/* First grab our read_frame; this is removed from all lists, so
we can release the lock after this without problems */
if (pdev->read_frame != NULL) {
/* This can't theoretically happen */
Err("Huh? Read frame still in use?\n");
}
else {
if (pdev->full_frames == NULL) {
Err("Woops. No frames ready.\n");
}
else {
pdev->read_frame = pdev->full_frames;
pdev->full_frames = pdev->full_frames->next;
pdev->read_frame->next = NULL;
}
if (pdev->read_frame != NULL) {
#if PWC_DEBUG
Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev>read_frame->sequence);
#endif
/* Decompression is a lenghty process, so it's outside of the
lock.
28
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
29
This gives the isoc_handler the opportunity to fill more
frames
in the mean time.
*/
spin_unlock_irqrestore(&pdev->ptrlock, flags);
ret = pwc_decompress(pdev);
spin_lock_irqsave(&pdev->ptrlock, flags);
/* We're done with read_buffer, tack it to the end of the empty
buffer list */
if (pdev->empty_frames == NULL) {
pdev->empty_frames = pdev->read_frame;
pdev->empty_frames_tail = pdev->empty_frames;
}
else {
pdev->empty_frames_tail->next = pdev->read_frame;
pdev->empty_frames_tail = pdev->read_frame;
}
pdev->read_frame = NULL;
}
}
spin_unlock_irqrestore(&pdev->ptrlock, flags);
return ret;
}
/**
\brief Advance pointers of image buffer (after each user request)
*/
static inline void pwc_next_image(struct pwc_device *pdev)
{
pdev->image_used[pdev->fill_image] = 0;
pdev->fill_image = (pdev->fill_image + 1) % default_mbufs;
}
/* This gets called for the Isochronous pipe (video). This is done in
* interrupt time, so it has to be fast, not crash, and not stall. Neat.
*/
static void pwc_isoc_handler(struct urb *urb)
{
struct pwc_device *pdev;
int i, fst, flen;
int awake;
struct pwc_frame_buf *fbuf;
unsigned char *fillptr = 0, *iso_buf = 0;
awake = 0;
pdev = (struct pwc_device *)urb->context;
if (pdev == NULL) {
Err("isoc_handler() called with NULL device?!\n");
return;
}
#ifdef PWC_MAGIC
if (pdev->magic != PWC_MAGIC) {
Err("isoc_handler() called with bad magic!\n");
return;
}
#endif
if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
Trace(TRACE_OPEN, "pwc_isoc_handler(): URB (%p) unlinked
%ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
return;
}
if (urb->status != -EINPROGRESS && urb->status != 0) {
const char *errmsg;
errmsg = "Unknown";
switch(urb->status) {
case -ENOSR:
errmsg = "Buffer error (overrun)"; break;
29
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
30
case -EPIPE:
errmsg = "Stalled (device not
case -EOVERFLOW:
case -EPROTO:
errmsg = "Babble (bad cable?)"; break;
errmsg = "Bit-stuff error (bad cable?)";
case -EILSEQ:
errmsg = "CRC/Timeout (could be
case -ETIMEDOUT:
errmsg = "NAK (device does not respond)";
responding)"; break;
break;
anything)"; break;
break;
}
Trace(TRACE_FLOW, "pwc_isoc_handler() called with status %d [%s].\n",
urb->status, errmsg);
/* Give up after a number of contiguous errors on the USB bus.
Appearantly something is wrong so we simulate an unplug event.
*/
if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
{
Info("Too many ISOC errors, bailing out.\n");
pdev->error_status = EIO;
awake = 1;
}
else
return; // better luck next time
}
fbuf = pdev->fill_frame;
if (fbuf == NULL) {
Err("pwc_isoc_handler without valid fill frame.\n");
awake = 1;
}
else {
fillptr = fbuf->data + fbuf->filled;
}
/* Premature wakeup */
if (awake) {
wake_up_interruptible(&pdev->frameq);
return;
}
/* Reset ISOC error counter. We did get here, after all. */
pdev->visoc_errors = 0;
/* vsync: 0 = don't copy data
1 = sync-hunt
2 = synched
*/
/* Compact data */
for (i = 0; i < urb->number_of_packets; i++) {
fst = urb->iso_frame_desc[i].status;
flen = urb->iso_frame_desc[i].actual_length;
iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
if (fst == 0) {
if (flen > 0) { /* if valid data... */
if (pdev->vsync > 0) { /* ...and we are not synchunting... */
pdev->vsync = 2;
/* ...copy data to frame buffer, if possible */
if (flen + fbuf->filled > pdev->frame_size) {
Trace(TRACE_FLOW, "Frame buffer overflow
(flen = %d, frame_size = %d).\n", flen, pdev->frame_size);
pdev->vsync = 0; /* Hmm, let's wait for
an EOF (end-of-frame) */
pdev->vframes_error++;
}
else {
memmove(fillptr, iso_buf, flen);
fillptr += flen;
}
}
fbuf->filled += flen;
30
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
31
} /* ..flen > 0 */
if (flen < pdev->vlast_packet_size) {
/* Shorter packet... We probably have the end of an
image-frame;
wake up read() process and let select()/poll() do
something.
Decompression is done in user time over there.
*/
if (pdev->vsync == 2) {
/* The ToUCam Fun CMOS sensor causes the
firmware to send 2 or 3 bogus
frames on the USB wire after an exposure
change. This conditition is
however detected
in the cam and a bit is set
in the header.
*/
if (pdev->type == 730) {
unsigned char *ptr = (unsigned char
*)fbuf->data;
if (ptr[1] == 1 && ptr[0] & 0x10) {
#if PWC_DEBUG
Debug("Hyundai CMOS sensor bug.
Dropping frame %d.\n", fbuf->sequence);
#endif
pdev->drop_frames += 2;
pdev->vframes_error++;
}
if ((ptr[0] ^ pdev->vmirror) & 0x01) {
if (ptr[0] & 0x01)
Info("Snapshot button
pressed.\n");
else
Info("Snapshot button
released.\n");
}
if ((ptr[0] ^ pdev->vmirror) & 0x02) {
if (ptr[0] & 0x02)
Info("Image is
mirrored.\n");
else
Info("Image is
normal.\n");
}
pdev->vmirror = ptr[0] & 0x03;
/* Sometimes the trailer of the 730 is
still sent as a 4 byte packet
after a short frame; this condition is
filtered out specifically. A 4 byte
frame doesn't make sense anyway.
So we get either this sequence:
drop_bit set -> 4 byte frame ->
short frame -> good frame
Or this one:
drop_bit set -> short frame ->
good frame
So we drop either 3 or 2 frames in
all!
*/
if (fbuf->filled == 4)
pdev->drop_frames++;
}
/* In case we were instructed to drop the frame,
do so silently.
The buffer pointers are not updated either
(but the counters are reset below).
*/
if (pdev->drop_frames > 0)
pdev->drop_frames--;
31
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
32
else {
/* Check for underflow first */
if (fbuf->filled < pdev->frame_size) {
Trace(TRACE_FLOW, "Frame buffer
underflow (%d bytes); discarded.\n", fbuf->filled);
pdev->vframes_error++;
}
else {
/* Send only once per EOF */
awake = 1; /* delay wake_ups */
/* Find our next frame to fill.
This will always succeed, since we
* nick a frame from either empty
or full list, but if we had to
* take it from the full list, it
means a frame got dropped.
*/
if (pwc_next_fill_frame(pdev)) {
pdev->vframes_dumped++;
if ((pdev->vframe_count >
FRAME_LOWMARK) && (pwc_trace & TRACE_FLOW)) {
if (pdev>vframes_dumped < 20)
Trace(TRACE_FLOW, "Dumping frame %d.\n", pdev->vframe_count);
if (pdev>vframes_dumped == 20)
Trace(TRACE_FLOW, "Dumping frame %d (last message).\n", pdev->vframe_count);
}
}
fbuf = pdev->fill_frame;
}
} /* !drop_frames */
pdev->vframe_count++;
}
fbuf->filled = 0;
fillptr = fbuf->data;
pdev->vsync = 1;
} /* .. flen < last_packet_size */
pdev->vlast_packet_size = flen;
} /* ..status == 0 */
#if PWC_DEBUG
/* This is normally not interesting to the user, unless you are really
debugging something */
else {
static int iso_error = 0;
iso_error++;
if (iso_error < 20)
Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n",
i, fst);
}
#endif
}
if (awake)
wake_up_interruptible(&pdev->frameq);
}
static int pwc_isoc_init(struct pwc_device *pdev)
{
struct usb_device *udev;
struct urb *urb;
int i, j, ret;
struct usb_interface_descriptor *idesc;
if (pdev == NULL)
return -EFAULT;
if (pdev->iso_init)
32
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
33
return 0;
pdev->vsync = 0;
udev = pdev->udev;
/* Get the current alternate interface, adjust packet size */
if (!udev->actconfig)
return -EFAULT;
idesc = &udev->actconfig->interface[0].altsetting[pdev->valternate];
if (!idesc)
return -EFAULT;
/* Search video endpoint */
pdev->vmax_packet_size = -1;
for (i = 0; i < idesc->bNumEndpoints; i++)
if ((idesc->endpoint[i].bEndpointAddress & 0xF) == pdev->vendpoint) {
pdev->vmax_packet_size = idesc->endpoint[i].wMaxPacketSize;
break;
}
if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE)
{
Err("Failed to find packet size for video endpoint in current alternate
setting.\n");
return -ENFILE; /* Odd error, that should be noticable */
}
/* Set alternate interface */
ret = 0;
Trace(TRACE_OPEN, "Setting alternate interface %d\n", pdev->valternate);
ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
if (ret < 0)
return ret;
for (i = 0; i < MAX_ISO_BUFS; i++) {
urb = usb_alloc_urb(ISO_FRAMES_PER_DESC);
if (urb == NULL) {
Err("Failed to allocate urb %d\n", i);
ret = -ENOMEM;
break;
}
pdev->sbuf[i].urb = urb;
Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb);
}
if (ret) {
/* De-allocate in reverse order */
while (i >= 0) {
if (pdev->sbuf[i].urb != NULL)
usb_free_urb(pdev->sbuf[i].urb);
pdev->sbuf[i].urb = NULL;
i--;
}
return ret;
}
/* init URB structure */
for (i = 0; i < MAX_ISO_BUFS; i++) {
urb = pdev->sbuf[i].urb;
urb->next = pdev->sbuf[(i + 1) % MAX_ISO_BUFS].urb;
urb->dev = udev;
urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint);
urb->transfer_flags = USB_ISO_ASAP;
urb->transfer_buffer = pdev->sbuf[i].data;
urb->transfer_buffer_length = ISO_BUFFER_SIZE;
urb->complete = pwc_isoc_handler;
urb->context = pdev;
urb->start_frame = 0;
urb->number_of_packets = ISO_FRAMES_PER_DESC;
for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
urb->iso_frame_desc[j].length = pdev->vmax_packet_size;
33
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
34
}
}
/* link */
for (i = 0; i < MAX_ISO_BUFS; i++) {
ret = usb_submit_urb(pdev->sbuf[i].urb);
if (ret)
Err("isoc_init() submit_urb %d failed with error %d\n", i,
ret);
else
Trace(TRACE_OPEN, "URB 0x%p submitted.\n", pdev->sbuf[i].urb);
}
/* All is done... */
pdev->iso_init = 1;
Trace(TRACE_OPEN, "<< pwc_isoc_init()\n");
return 0;
}
static void pwc_isoc_cleanup(struct pwc_device *pdev)
{
int i;
Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n");
if (pdev == NULL)
return;
/* Unlinking ISOC buffers one by one */
for (i = 0; i < MAX_ISO_BUFS; i++) {
struct urb *urb;
urb = pdev->sbuf[i].urb;
if (urb != 0) {
if (pdev->iso_init) {
Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb);
usb_unlink_urb(urb);
}
Trace(TRACE_MEMORY, "Freeing URB\n");
usb_free_urb(urb);
pdev->sbuf[i].urb = NULL;
}
}
/* Stop camera, but only if we are sure the camera is still there (unplug
is signalled by EPIPE)
*/
if (pdev->error_status && pdev->error_status != EPIPE) {
Trace(TRACE_OPEN, "Setting alternate interface 0.\n");
usb_set_interface(pdev->udev, 0, 0);
}
pdev->iso_init = 0;
Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n");
}
int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps,
int new_compression, int new_snapshot)
{
int ret, start;
/* Stop isoc stuff */
pwc_isoc_cleanup(pdev);
/* Reset parameters */
pwc_reset_buffers(pdev);
/* Try to set video mode... */
start = ret = pwc_set_video_mode(pdev, width, height, new_fps,
new_compression, new_snapshot);
if (ret) {
Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n");
/* That failed... restore old mode (we know that worked) */
34
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
35
start = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev>vframes, pdev->vcompression, pdev->vsnapshot);
if (start) {
Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n");
}
}
if (start == 0) {
if (pwc_isoc_init(pdev) < 0)
{
Info("Failed to restart ISOC transfers in
pwc_try_video_mode.\n");
ret = -EAGAIN; /* let's try again, who knows if it works a
second time */
}
}
pdev->drop_frames++; /* try to avoid garbage during switch */
return ret; /* Return original error code */
}
/***************************************************************************/
/* Video4Linux functions */
static int pwc_video_open(struct video_device *vdev, int mode)
{
int i;
struct pwc_device *pdev;
Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev);
if (vdev == NULL)
BUG();
pdev = (struct pwc_device *)vdev->priv;
if (pdev == NULL)
BUG();
if (pdev->vopen)
return -EBUSY;
down(&pdev->modlock);
if (!pdev->usb_init) {
Trace(TRACE_OPEN, "Doing first time initialization.\n");
pdev->usb_init = 1;
{
/* Query sensor type */
const char *sensor_type = NULL;
i = pwc_get_cmos_sensor(pdev);
switch(i) {
case -1: /* Unknown, show nothing */; break;
case 0x00: sensor_type = "Hyundai CMOS sensor"; break;
case 0x20: sensor_type = "Sony CCD sensor + TDA8787"; break;
case 0x2E: sensor_type = "Sony CCD sensor + Exas 98L59";
break;
case 0x2F:
case 0x30:
case 0x3E:
sensor_type = "Sony CCD sensor + ADI 9804"; break;
sensor_type = "Sharp CCD sensor + TDA8787"; break;
sensor_type = "Sharp CCD sensor + Exas 98L59";
break;
case 0x3F: sensor_type = "Sharp CCD sensor + ADI 9804"; break;
case 0x40: sensor_type = "UPA 1021 sensor"; break;
case 0x100: sensor_type = "VGA sensor"; break;
case 0x101: sensor_type = "PAL MR sensor"; break;
default:
sensor_type = "unknown type of sensor"; break;
}
if (sensor_type != NULL)
Info("This %s camera is equipped with a %s (%d).\n",
pdev->vdev.name, sensor_type, i);
}
}
35
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
36
/* Turn on camera */
if (power_save) {
i = pwc_camera_power(pdev, 1);
if (i < 0)
Info("Failed to restore power to the camera! (%d)\n", i);
}
/* Set LED on/off time */
if (pwc_set_leds(pdev, led_on, led_off) < 0)
Info("Failed to set LED on/off time.\n");
/* Find our decompressor, if any */
pdev->decompressor = pwc_find_decompressor(pdev->type);
#if PWC_DEBUG
Debug("Found decompressor for %d at 0x%p\n", pdev->type, pdev->decompressor);
#endif
pwc_construct(pdev); /* set min/max sizes correct */
/* So far, so good. Allocate memory. */
i = pwc_allocate_buffers(pdev);
if (i < 0) {
Trace(TRACE_OPEN, "Failed to allocate buffer memory.\n");
up(&pdev->modlock);
return i;
}
/* Reset buffers & parameters */
pwc_reset_buffers(pdev);
for (i = 0; i < default_mbufs; i++)
pdev->image_used[i] = 0;
pdev->vframe_count = 0;
pdev->vframes_dumped = 0;
pdev->vframes_error = 0;
pdev->visoc_errors = 0;
pdev->error_status = 0;
#if PWC_DEBUG
pdev->sequence = 0;
#endif
/* Set some defaults */
pdev->vsnapshot = 0;
/* Start iso pipe for video; first try the last used video size
(or the default one); if that fails try QCIF/10 or QSIF/10;
it that fails too, give up.
*/
i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x,
pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0);
if (i) {
Trace(TRACE_OPEN, "First attempt at set_video_mode failed.\n");
if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750)
i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QSIF].x,
pwc_image_sizes[PSZ_QSIF].y, 10, pdev->vcompression, 0);
else
i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QCIF].x,
pwc_image_sizes[PSZ_QCIF].y, 10, pdev->vcompression, 0);
}
if (i) {
Trace(TRACE_OPEN, "Second attempt at set_video_mode failed.\n");
up(&pdev->modlock);
return i;
}
i = pwc_isoc_init(pdev);
if (i) {
Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i);
up(&pdev->modlock);
return i;
}
pdev->vopen++;
/* lock decompressor; this has a small race condition, since we
36
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
37
could in theory unload pwcx.o between pwc_find_decompressor()
above and this call. I doubt it's ever going to be a problem.
*/
if (pdev->decompressor != NULL)
pdev->decompressor->lock();
up(&pdev->modlock);
Trace(TRACE_OPEN, "<< video_open() returns 0.\n");
return 0;
}
/* Note that all cleanup is done in the reverse order as in _open */
static void pwc_video_close(struct video_device *vdev)
{
struct pwc_device *pdev;
int i;
Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev);
pdev = (struct pwc_device *)vdev->priv;
if (pdev->vopen == 0)
Info("video_close() called on closed device?\n");
/* Dump statistics, but only if a reasonable amount of frames were
processed (to prevent endless log-entries in case of snap-shot
programs)
*/
if (pdev->vframe_count > 20)
Info("Closing video device: %d frames received, dumped %d frames, %d
frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev>vframes_error);
if (pdev->decompressor != NULL) {
pdev->decompressor->exit();
pdev->decompressor->unlock();
pdev->decompressor = NULL;
}
pwc_isoc_cleanup(pdev);
pwc_free_buffers(pdev);
/* Turn off LEDS and power down camera, but only when not unplugged */
if (pdev->error_status != EPIPE) {
if (pwc_set_leds(pdev, 0, 0) < 0)
Info("Failed to set LED on/off time.\n");
if (power_save) {
i = pwc_camera_power(pdev, 0);
if (i < 0)
Err("Failed to power down camera (%d)\n", i);
}
}
pdev->vopen = 0;
Trace(TRACE_OPEN, "<< video_close()\n");
}
/*
*
*
FIXME: what about two parallel reads ????
ANSWER: Not supported. You can't open the device more than once,
despite what the V4L1 interface says. First, I don't see
the need, second there's no mechanism of alerting the
2nd/3rd/... process of events like changing image size.
And I don't see the point of blocking that for the
2nd/3rd/... process.
In multi-threaded environments reading parallel from any
device is tricky anyhow.
*/
static long pwc_video_read(struct video_device *vdev, char *buf, unsigned long count,
int noblock)
{
struct pwc_device *pdev;
DECLARE_WAITQUEUE(wait, current);
37
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
38
Trace(TRACE_READ, "video_read(0x%p, %p, %ld, %d) called.\n", vdev, buf, count,
noblock);
if (vdev == NULL)
return -EFAULT;
pdev = vdev->priv;
if (pdev == NULL)
return -EFAULT;
if (pdev->error_status)
return -pdev->error_status; /* Something happened, report what. */
/* In case we're doing partial reads, we don't have to wait for a frame */
if (pdev->image_read_pos == 0) {
/* Do wait queueing according to the (doc)book */
add_wait_queue(&pdev->frameq, &wait);
while (pdev->full_frames == NULL) {
/* Check for unplugged/etc. here */
if (pdev->error_status) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
return -pdev->error_status ;
}
if (noblock) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
return -EWOULDBLOCK;
}
if (signal_pending(current)) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
return -ERESTARTSYS;
}
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
/* Decompress and release frame */
if (pwc_handle_frame(pdev))
return -EFAULT;
}
Trace(TRACE_READ, "Copying data to user space.\n");
/* copy bytes to user space; we allow for partial reads */
if (count + pdev->image_read_pos > pdev->view.size)
count = pdev->view.size - pdev->image_read_pos;
if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev>image_read_pos, count))
return -EFAULT;
pdev->image_read_pos += count;
if (pdev->image_read_pos >= pdev->view.size) { /* All data has been read */
pdev->image_read_pos = 0;
pwc_next_image(pdev);
}
return count;
}
static long pwc_video_write(struct video_device *vdev, const char *buf, unsigned long
count, int noblock)
{
return -EINVAL;
}
static unsigned int pwc_video_poll(struct video_device *vdev, struct file *file,
poll_table *wait)
{
struct pwc_device *pdev;
if (vdev == NULL)
38
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
39
return -EFAULT;
pdev = vdev->priv;
if (pdev == NULL)
return -EFAULT;
poll_wait(file, &pdev->frameq, wait);
if (pdev->error_status)
return POLLERR;
if (pdev->full_frames != NULL) /* we have frames waiting */
return (POLLIN | POLLRDNORM);
return 0;
}
static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
{
struct pwc_device *pdev;
DECLARE_WAITQUEUE(wait, current);
if (vdev == NULL)
return -EFAULT;
pdev = vdev->priv;
if (pdev == NULL)
return -EFAULT;
switch (cmd) {
/* Query cabapilities */
case VIDIOCGCAP:
{
struct video_capability caps;
strcpy(caps.name, vdev->name);
caps.type = VID_TYPE_CAPTURE;
caps.channels = 1;
caps.audios = 1;
caps.minwidth = pdev->view_min.x;
caps.minheight = pdev->view_min.y;
caps.maxwidth = pdev->view_max.x;
caps.maxheight = pdev->view_max.y;
if (copy_to_user(arg, &caps, sizeof(caps)))
return -EFAULT;
break;
}
/* Channel functions (simulate 1 channel) */
case VIDIOCGCHAN:
{
struct video_channel v;
if (copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
if (v.channel != 0)
return -EINVAL;
v.flags = 0;
v.tuners = 0;
v.type = VIDEO_TYPE_CAMERA;
strcpy(v.name, "Webcam");
if (copy_to_user(arg, &v, sizeof(v)))
return -EFAULT;
return 0;
}
case VIDIOCSCHAN:
{
/* The spec says the argument is an integer, but
the bttv driver uses a video_channel arg, which
makes sense becasue it also has the norm flag.
*/
39
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
40
struct video_channel v;
if (copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
if (v.channel != 0)
return -EINVAL;
return 0;
}
/* Picture functions; contrast etc. */
case VIDIOCGPICT:
{
struct video_picture p;
int val;
p.colour = 0x8000;
p.hue = 0x8000;
val = pwc_get_brightness(pdev);
if (val >= 0)
p.brightness = val;
else
p.brightness = 0xffff;
val = pwc_get_contrast(pdev);
if (val >= 0)
p.contrast = val;
else
p.contrast = 0xffff;
/* Gamma, Whiteness, what's the difference? :) */
val = pwc_get_gamma(pdev);
if (val >= 0)
p.whiteness = val;
else
p.whiteness = 0xffff;
val = pwc_get_saturation(pdev);
if (val >= 0)
p.colour = val;
else
p.colour = 0xffff;
p.depth = 24;
p.palette = VIDEO_PALETTE_YUV420P;
p.hue = 0xFFFF; /* N/A */
if (copy_to_user(arg, &p, sizeof(p)))
return -EFAULT;
break;
}
case VIDIOCSPICT:
{
struct video_picture p;
if (copy_from_user(&p, arg, sizeof(p)))
return -EFAULT;
/*
*
FIXME: Suppose we are mid read
ANSWER: No problem: the firmware of the camera
can handle brightness/contrast/etc
changes at _any_ time, and the palette
is used exactly once in the uncompress
routine.
*/
pwc_set_brightness(pdev, p.brightness);
pwc_set_contrast(pdev, p.contrast);
pwc_set_gamma(pdev, p.whiteness);
pwc_set_saturation(pdev, p.colour);
if (p.palette && p.palette != VIDEO_PALETTE_YUV420P) {
return -EINVAL;
40
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
41
}
break;
}
/* Window/size parameters */
case VIDIOCGWIN:
{
struct video_window vw;
vw.x = 0;
vw.y = 0;
vw.width = pdev->view.x;
vw.height = pdev->view.y;
vw.chromakey = 0;
vw.flags = (pdev->vframes << PWC_FPS_SHIFT) |
(pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0);
if (copy_to_user(arg, &vw, sizeof(vw)))
return -EFAULT;
break;
}
case VIDIOCSWIN:
{
struct video_window vw;
int fps, snapshot, ret;
if (copy_from_user(&vw, arg, sizeof(vw)))
return -EFAULT;
fps = (vw.flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT;
snapshot = vw.flags & PWC_FPS_SNAPSHOT;
if (fps == 0)
fps = pdev->vframes;
if (pdev->view.x == vw.width && pdev->view.y && fps == pdev>vframes && snapshot == pdev->vsnapshot)
return 0;
ret = pwc_try_video_mode(pdev, vw.width, vw.height, fps, pdev>vcompression, snapshot);
if (ret)
return ret;
break;
}
/* We don't have overlay support (yet) */
case VIDIOCGFBUF:
{
struct video_buffer vb;
vb.base = NULL;
vb.height = 0;
vb.width = 0;
vb.depth = 0;
vb.bytesperline = 0;
if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
return -EFAULT;
break;
}
/* mmap() functions */
case VIDIOCGMBUF:
{
/* Tell the user program how much memory is needed for a mmap()
*/
struct video_mbuf vm;
int i;
memset(&vm, 0, sizeof(vm));
vm.size = default_mbufs * pdev->len_per_image;
41
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
42
vm.frames = default_mbufs; /* double buffering should be enough
for most applications */
for (i = 0; i < default_mbufs; i++)
vm.offsets[i] = i * pdev->len_per_image;
if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
return -EFAULT;
break;
}
case VIDIOCMCAPTURE:
{
/* Start capture into a given image buffer (called 'frame' in
video_mmap structure) */
struct video_mmap vm;
if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
return -EFAULT;
Trace(TRACE_READ, "VIDIOCMCAPTURE: %dx%d, frame %d, format
%d\n", vm.width, vm.height, vm.frame, vm.format);
if (vm.frame < 0 || vm.frame >= default_mbufs)
return -EINVAL;
/* xawtv is nasty. It probes the available palettes
by setting a very small image size and trying
various palettes... The driver doesn't support
such small images, so I'm working around it.
*/
if (vm.format && vm.format != VIDEO_PALETTE_YUV420P)
return -EINVAL;
if ((vm.width != pdev->view.x || vm.height != pdev->view.y) &&
(vm.width >= pdev->view_min.x && vm.height >= pdev>view_min.y)) {
int ret;
Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to
please xawtv :-(.\n");
ret = pwc_try_video_mode(pdev, vm.width, vm.height,
pdev->vframes, pdev->vcompression, pdev->vsnapshot);
if (ret)
return ret;
} /* ... size mismatch */
/* FIXME: should we lock here? */
if (pdev->image_used[vm.frame])
return -EBUSY; /* buffer wasn't available. Bummer */
pdev->image_used[vm.frame] = 1;
/* Okay, we're done here. In the SYNC call we wait until a
frame comes available, then expand image into the given
buffer.
In contrast to the CPiA cam the Philips cams deliver a
constant stream, almost like a grabber card. Also,
we have separate buffers for the rawdata and the image,
meaning we can nearly always expand into the requested
buffer.
*/
Trace(TRACE_READ, "VIDIOCMCAPTURE done.\n");
break;
}
case VIDIOCSYNC:
{
/* The doc says: "Whenever a buffer is used it should
call VIDIOCSYNC to free this frame up and continue."
The only odd thing about this whole procedure is
that MCAPTURE flags the buffer as "in use", and
SYNC immediately unmarks it, while it isn't
after SYNC that you know that the buffer actually
42
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
43
got filled! So you better not start a CAPTURE in
the same frame immediately (use double buffering).
This is not a problem for this cam, since it has
extra intermediate buffers, but a hardware
grabber card will then overwrite the buffer
you're working on.
*/
int mbuf, ret;
if (copy_from_user((void *)&mbuf, arg, sizeof(int)))
return -EFAULT;
Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", mbuf);
/* bounds check */
if (mbuf < 0 || mbuf >= default_mbufs)
return -EINVAL;
/* check if this buffer was requested anyway */
if (pdev->image_used[mbuf] == 0)
return -EINVAL;
/* Add ourselves to the frame wait-queue.
In the loop, check for error conditions and signals.
FIXME: needs auditing for safety.
QUESTION: In what respect? I think that using the
frameq is safe now.
*/
add_wait_queue(&pdev->frameq, &wait);
while (pdev->full_frames == NULL) {
if (pdev->error_status) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
return -pdev->error_status;
}
if (signal_pending(current)) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
return -ERESTARTSYS;
}
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
/* The frame is ready. Expand in the image buffer
requested by the user. I don't care if you
mmap() 5 buffers and request data in this order:
buffer 4 2 3 0 1 2 3 0 4 3 1 . . .
Grabber hardware may not be so forgiving.
*/
Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n");
pdev->fill_image = mbuf; /* tell in which buffer we want the
image to be expanded */
/* Decompress, etc */
ret = pwc_handle_frame(pdev);
pdev->image_used[mbuf] = 0;
if (ret)
return -EFAULT;
break;
}
case VIDIOCGAUDIO:
{
struct video_audio v;
strcpy(v.name, "Microphone");
v.audio = -1; /* unknown audio minor */
v.flags = 0;
v.mode = VIDEO_SOUND_MONO;
43
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
44
v.volume = 0;
v.bass = 0;
v.treble = 0;
v.balance = 0x8000;
v.step = 1;
if (copy_to_user(arg, &v, sizeof(v)))
return -EFAULT;
break;
}
case VIDIOCSAUDIO:
{
struct video_audio v;
if (copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
/* Dummy: nothing can be set */
break;
}
case VIDIOCGUNIT:
{
struct video_unit vu;
vu.video = pdev->vdev.minor & 0x3F;
vu.audio = -1; /* not known yet */
vu.vbi = -1;
vu.radio = -1;
vu.teletext = -1;
if (copy_to_user(arg, &vu, sizeof(vu)))
return -EFAULT;
break;
}
default:
return pwc_ioctl(pdev, cmd, arg);
} /* ..switch */
return 0;
}
static int pwc_video_mmap(struct video_device *vdev, const char *adr, unsigned long
size)
{
struct pwc_device *pdev;
unsigned long start = (unsigned long)adr;
unsigned long page, pos;
Trace(TRACE_MEMORY, "mmap(0x%p, 0x%p, %lu) called.\n", vdev, adr, size);
pdev = vdev->priv;
pos = (unsigned long)pdev->image_data;
while (size > 0) {
page = kvirt_to_pa(pos);
if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
return -EAGAIN;
start += PAGE_SIZE;
pos += PAGE_SIZE;
if (size > PAGE_SIZE)
size -= PAGE_SIZE;
else
size = 0;
}
return 0;
}
/***************************************************************************/
/* USB functions */
/* This function gets called when a new device is plugged in or the usb core
44
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
45
* is loaded.
*/
static void *usb_pwc_probe(struct usb_device *udev, unsigned int ifnum, const struct
usb_device_id *id)
{
struct pwc_device *pdev = NULL;
int vendor_id, product_id, type_id;
int i, hint;
int features = 0;
int video_nr = -1; /* default: use next available device */
char serial_number[30], *name;
/* Check if we can handle this device */
Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n", udev>descriptor.idVendor, udev->descriptor.idProduct, ifnum);
/* the interfaces are probed one by one. We are only interested in the
video interface (0) now.
Interface 1 is the Audio Control, and interface 2 Audio itself.
*/
if (ifnum > 0)
return NULL;
vendor_id = udev->descriptor.idVendor;
product_id = udev->descriptor.idProduct;
if (vendor_id == 0x0471) {
switch (product_id) {
case 0x0302:
Info("Philips PCA645VC USB webcam detected.\n");
name = "Philips 645 webcam";
type_id = 645;
break;
case 0x0303:
Info("Philips PCA646VC USB webcam detected.\n");
name = "Philips 646 webcam";
type_id = 646;
break;
case 0x0304:
Info("Askey VC010 type 2 USB webcam detected.\n");
name = "Askey VC010 webcam";
type_id = 646;
break;
case 0x0307:
Info("Philips PCVC675K (Vesta) USB webcam detected.\n");
name = "Philips 675 webcam";
type_id = 675;
break;
case 0x0308:
Info("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
name = "Philips 680 webcam";
type_id = 680;
break;
case 0x030C:
Info("Philips PCVC690K (Vesta Pro Scan) USB webcam
detected.\n");
name = "Philips 690 webcam";
type_id = 690;
break;
case 0x0310:
Info("Philips PCVC730K (ToUCam Fun) USB webcam detected.\n");
name = "Philips 730 webcam";
type_id = 730;
break;
case 0x0311:
Info("Philips PCVC740K (ToUCam Pro) USB webcam detected.\n");
name = "Philips 740 webcam";
type_id = 740;
break;
case 0x0312:
45
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
46
Info("Philips PCVC750K (ToUCam Pro Scan) USB webcam
detected.\n");
name = "Philips 750 webcam";
type_id = 750;
break;
case 0x0313:
Info("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n");
name = "Philips 720 webcam";
type_id = 720;
break;
default:
return NULL;
break;
}
}
else if (vendor_id == 0x069A) {
switch(product_id) {
case 0x0001:
Info("Askey VC010 type 1 USB webcam detected.\n");
name = "Askey VC010 webcam";
type_id = 645;
break;
default:
return NULL;
break;
}
}
else if (vendor_id == 0x046d) {
switch(product_id) {
case 0x08b0:
Info("Logitech QuickCam Pro 3000 USB webcam detected.\n");
name = "Logitech QuickCam Pro 3000";
type_id = 740; /* CCD sensor */
break;
case 0x08b1:
Info("Logitech QuickCam for Notebook Pro USB webcam
detected.\n");
name = "Logitech QuickCam Notebook Pro";
type_id = 740; /* CCD sensor */
break;
case 0x08b2:
Info("Logitech QuickCam 4000 Pro USB webcam detected.\n");
name = "Logitech QuickCam Pro 4000";
type_id = 740; /* CCD sensor */
break;
case 0x08b3:
Info("Logitech QuickCam Zoom USB webcam detected.\n");
name = "Logitech QuickCam Zoom";
type_id = 740; /* CCD sensor */
break;
case 0x08B4:
Info("Logitech QuickCam Zoom (new model) USB webcam
detected.\n");
name = "Logitech QuickCam Zoom";
type_id = 740; /* CCD sensor */
break;
case 0x08b5:
Info("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
name = "Logitech QuickCam Orbit";
type_id = 740; /* CCD sensor */
features |= FEATURE_MOTOR_PANTILT;
break;
case 0x08b6:
case 0x08b7:
case 0x08b8:
Info("Logitech QuickCam detected (reserved ID).\n");
name = "Logitech QuickCam (res.)";
type_id = 730; /* Assuming CMOS */
break;
default:
return NULL;
46
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
47
break;
}
}
else if (vendor_id == 0x055d) {
/* I don't know the difference between the C10 and the C30;
I suppose the difference is the sensor, but both cameras
work equally well with a type_id of 675
*/
switch(product_id) {
case 0x9000:
Info("Samsung MPC-C10 USB webcam detected.\n");
name = "Samsung MPC-C10";
type_id = 730;
break;
case 0x9001:
Info("Samsung MPC-C30 USB webcam detected.\n");
name = "Samsung MPC-C30";
type_id = 740;
break;
default:
return NULL;
break;
}
}
else if (vendor_id == 0x041e) {
switch(product_id) {
case 0x400c:
Info("Creative Labs Webcam 5 detected.\n");
name = "Creative Labs Webcam 5";
type_id = 730;
break;
case 0x4011:
Info("Creative Labs Webcam Pro Ex detected.\n");
name = "Creative Labs Webcam Pro Ex";
type_id = 740;
break;
default:
return NULL;
break;
}
}
else if (vendor_id == 0x04cc) {
switch(product_id) {
case 0x8116:
Info("Sotec Afina Eye USB webcam detected.\n");
name = "Sotec Afina Eye";
type_id = 730;
break;
default:
return NULL;
break;
}
}
else if (vendor_id == 0x0d81) {
switch(product_id) {
case 0x1900:
Info("Visionite VCS-UC300 USB webcam detected.\n");
name = "Visionite VCS-UC300";
type_id = 740; /* CCD sensor */
break;
case 0x1910:
Info("Visionite VCS-UM100 USB webcam detected.\n");
name = "Visionite VCS-UM100";
type_id = 730; /* CMOS sensor */
break;
default:
return NULL;
break;
}
}
else
47
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
48
return NULL; /* Not any of the know types; but the list keeps growing.
*/
memset(serial_number, 0, 30);
usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
Trace(TRACE_PROBE, "Device serial number is %s\n", serial_number);
if (udev->descriptor.bNumConfigurations > 1)
Info("Warning: more than 1 configuration available.\n");
/* Allocate structure, initialize pointers, mutexes, etc. and link it to the
usb_device */
pdev = kmalloc(sizeof(struct pwc_device), GFP_KERNEL);
if (pdev == NULL) {
Err("Oops, could not allocate memory for pwc_device.\n");
return NULL;
}
memset(pdev, 0, sizeof(struct pwc_device));
pdev->type = type_id;
pdev->vsize = default_size;
pdev->vframes = default_fps;
pdev->features = features;
if (vendor_id == 0x046D && product_id == 0x08B5)
{
/* Logitech QuickCam Orbit
The ranges have been determined experimentally; they may differ
from cam to cam.
Also, the exact ranges left-right and up-down are different for my
cam
*/
pdev->angle_range.pan_min = -7000;
pdev->angle_range.pan_max = 7000;
pdev->angle_range.tilt_min = -3000;
pdev->angle_range.tilt_max = 2500;
pdev->angle_range.zoom_min = -1;
pdev->angle_range.zoom_max = -1;
}
init_MUTEX(&pdev->modlock);
pdev->ptrlock = SPIN_LOCK_UNLOCKED;
pdev->udev = udev;
init_waitqueue_head(&pdev->frameq);
pdev->vcompression = pwc_preferred_compression;
memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template));
strcpy(pdev->vdev.name, name);
SET_MODULE_OWNER(&pdev->vdev);
pdev->vdev.priv = pdev;
pdev->release = udev->descriptor.bcdDevice;
Trace(TRACE_PROBE, "Release: %04x\n", pdev->release);
/* Now search device_hint[] table for a match, so we can hint a node number.
*/
for (hint = 0; hint < MAX_DEV_HINTS; hint++) {
if (((device_hint[hint].type == -1) || (device_hint[hint].type == pdev>type)) &&
(device_hint[hint].pdev == NULL)) {
/* so far, so good... try serial number */
if ((device_hint[hint].serial_number[0] == '*') ||
!strcmp(device_hint[hint].serial_number, serial_number)) {
/* match! */
video_nr = device_hint[hint].device_node;
Trace(TRACE_PROBE, "Found hint, will try to register as
/dev/video%d\n", video_nr);
break;
}
}
}
48
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
49
i = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, video_nr);
if (i < 0) {
Err("Failed to register as video device (%d).\n", i);
kfree(pdev); /* Oops, no memory leaks please */
return NULL;
}
else {
Info("Registered as /dev/video%d.\n", pdev->vdev.minor & 0x3F);
}
/* occupy slot */
if (hint < MAX_DEV_HINTS)
device_hint[hint].pdev = pdev;
Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev);
return pdev;
}
/* The user janked out the cable... */
static void usb_pwc_disconnect(struct usb_device *udev, void *ptr)
{
struct pwc_device *pdev;
int hint;
lock_kernel();
pdev = (struct pwc_device *)ptr;
if (pdev == NULL) {
Err("pwc_disconnect() Called without private pointer.\n");
unlock_kernel();
return;
}
if (pdev->udev == NULL) {
Err("pwc_disconnect() already called for %p\n", pdev);
unlock_kernel();
return;
}
if (pdev->udev != udev) {
Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n");
unlock_kernel();
return;
}
#ifdef PWC_MAGIC
if (pdev->magic != PWC_MAGIC) {
Err("pwc_disconnect() Magic number failed. Consult your scrolls and try
again.\n");
unlock_kernel();
return;
}
#endif
/* We got unplugged; this is signalled by an EPIPE error code */
if (pdev->vopen) {
Info("Disconnected while webcam is in use!\n");
pdev->error_status = EPIPE;
}
/* Alert waiting processes */
wake_up_interruptible(&pdev->frameq);
/* Wait until device is closed */
while (pdev->vopen)
schedule();
/* Device is now closed, so we can safely unregister it */
Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n");
video_unregister_device(&pdev->vdev);
/* Free memory (don't set pdev to 0 just yet) */
kfree(pdev);
/* search device_hint[] table if we occupy a slot, by any chance */
for (hint = 0; hint < MAX_DEV_HINTS; hint++)
if (device_hint[hint].pdev == pdev)
device_hint[hint].pdev = NULL;
49
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
50
unlock_kernel();
}
/* *grunt* We have to do atoi ourselves :-( */
static int pwc_atoi(const char *s)
{
int k = 0;
k = 0;
while (*s != '\0' && *s >= '0' && *s <= '9') {
k = 10 * k + (*s - '0');
s++;
}
return k;
}
/*
* Initialization code & module stuff
*/
static
static
static
static
static
static
static
static
char *size = NULL;
int fps = 0;
int fbufs = 0;
int mbufs = 0;
int trace = -1;
int compression = -1;
int leds[2] = { -1, -1 };
char *dev_hint[MAX_DEV_HINTS] = { };
MODULE_PARM(size, "s");
MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga");
MODULE_PARM(fps, "i");
MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 530");
MODULE_PARM(fbufs, "i");
MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve");
MODULE_PARM(mbufs, "i");
MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers");
MODULE_PARM(trace, "i");
MODULE_PARM_DESC(trace, "For debugging purposes");
MODULE_PARM(power_save, "i");
MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off");
MODULE_PARM(compression, "i");
MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed)
to 3 (high compression)");
MODULE_PARM(leds, "2i");
MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
MODULE_PARM(dev_hint, "0-20s");
MODULE_PARM_DESC(dev_hint, "Device node hints");
MODULE_DESCRIPTION("Philips USB & OEM webcam driver");
MODULE_AUTHOR("Nemosoft Unv. <nemosoft@smcc.demon.nl>");
MODULE_LICENSE("GPL");
static int __init usb_pwc_init(void)
{
int i, sz;
char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" };
Info("Philips PCA645/646 + PCVC675/680/690 + PCVC730/740/750 webcam module
version " PWC_VERSION " loaded.\n");
Info("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPCC10 and MPC-C30,\n");
Info("the Creative WebCam 5, SOTEC Afina Eye and Visionite VCS-UC300 and VCSUM100.\n");
if (fps) {
if (fps < 4 || fps > 30) {
50
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
51
Err("Framerate out of bounds (4-30).\n");
return -EINVAL;
}
default_fps = fps;
Info("Default framerate set to %d.\n", default_fps);
}
if (size) {
/* string; try matching with array */
for (sz = 0; sz < PSZ_MAX; sz++) {
if (!strcmp(sizenames[sz], size)) { /* Found! */
default_size = sz;
break;
}
}
if (sz == PSZ_MAX) {
Err("Size not recognized; try size=[sqcif | qsif | qcif | sif |
cif | vga].\n");
return -EINVAL;
}
Info("Default image size set to %s [%dx%d].\n",
sizenames[default_size], pwc_image_sizes[default_size].x,
pwc_image_sizes[default_size].y);
}
if (mbufs) {
if (mbufs < 1 || mbufs > MAX_IMAGES) {
Err("Illegal number of mmap() buffers; use a number between 1
and %d.\n", MAX_IMAGES);
return -EINVAL;
}
default_mbufs = mbufs;
Info("Number of image buffers set to %d.\n", default_mbufs);
}
if (fbufs) {
if (fbufs < 2 || fbufs > MAX_FRAMES) {
Err("Illegal number of frame buffers; use a number between 2
and %d.\n", MAX_FRAMES);
return -EINVAL;
}
default_fbufs = fbufs;
Info("Number of frame buffers set to %d.\n", default_fbufs);
}
if (trace >= 0) {
Info("Trace options: 0x%04x\n", trace);
pwc_trace = trace;
}
if (compression >= 0) {
if (compression > 3) {
Err("Invalid compression setting; use a number between 0
(uncompressed) and 3 (high).\n");
return -EINVAL;
}
pwc_preferred_compression = compression;
Info("Preferred compression set to %d.\n", pwc_preferred_compression);
}
if (power_save)
Info("Enabling power save on open/close.\n");
if (leds[0] >= 0)
led_on = leds[0];
if (leds[1] >= 0)
led_off = leds[1];
/* Big device node whoopla. Basicly, it allows you to assign a
device node (/dev/videoX) to a camera, based on its type
& serial number. The format is [type[.serialnumber]:]node.
Any camera that isn't matched by these rules gets the next
available free device node.
*/
for (i = 0; i < MAX_DEV_HINTS; i++) {
char *s, *colon, *dot;
51
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
52
/* This loop also initializes the array */
device_hint[i].pdev = NULL;
s = dev_hint[i];
if (s != NULL && *s != '\0') {
device_hint[i].type = -1; /* wildcard */
strcpy(device_hint[i].serial_number, "*");
/* parse string: chop at ':' & '/' */
colon = dot = s;
while (*colon != '\0' && *colon != ':')
colon++;
while (*dot != '\0' && *dot != '.')
dot++;
/* Few sanity checks */
if (*dot != '\0' && dot > colon) {
Err("Malformed camera hint: the colon must be after the
dot.\n");
return -EINVAL;
}
if (*colon == '\0') {
/* No colon */
if (*dot != '\0') {
Err("Malformed camera hint: no colon + device
node given.\n");
return -EINVAL;
}
else {
/* No type or serial number specified, just a
number. */
device_hint[i].device_node = pwc_atoi(s);
}
}
else {
/* There's a colon, so we have at least a type and a
device node */
device_hint[i].type = pwc_atoi(s);
device_hint[i].device_node = pwc_atoi(colon + 1);
if (*dot != '\0') {
/* There's a serial number as well */
int k;
dot++;
k = 0;
while (*dot != ':' && k < 29) {
device_hint[i].serial_number[k++] = *dot;
dot++;
}
device_hint[i].serial_number[k] = '\0';
}
}
#if PWC_DEBUG
Debug("device_hint[%d]:\n", i);
Debug(" type
: %d\n", device_hint[i].type);
Debug(" serial# : %s\n", device_hint[i].serial_number);
Debug(" node
: %d\n", device_hint[i].device_node);
#endif
}
else
device_hint[i].type = 0; /* not filled */
} /* ..for MAX_DEV_HINTS */
Trace(TRACE_PROBE, "Registering driver at address 0x%p.\n", &pwc_driver);
return usb_register(&pwc_driver);
}
static void __exit usb_pwc_exit(void)
{
Trace(TRACE_MODULE, "Deregistering driver.\n");
usb_deregister(&pwc_driver);
52
USB 2.0
Mikael Backlund
Wearable Computer HT 2003
53
Info("Philips webcam module removed.\n");
}
module_init(usb_pwc_init);
module_exit(usb_pwc_exit);
53
Download