symbian-laiteajuriohjelmointi filesymbian-laiteajuriohjelmointi perustuu...
TRANSCRIPT
Toni Lahnalampi
SYMBIAN-LAITEAJURIOHJELMOINTI
Opinnäytetyö KESKI-POHJANMAAN AMMATTIKORKEAKOULU Tietotekniikan koulutusohjelma Syyskuu 2008
KESKI-POHJANMAAN TIIVISTELMÄ AMMATTIKORKEAKOULU Tietotekniikan koulutusohjelma Työn tekijä: Toni Lahnalampi Työn nimi: Symbian laiteajuriohjelmointi Päivämäärä: 8.9.2008 Sivumäärä 33 + 5 liitettä Työn ohjaaja: ins. (AMK) Jussi Nissilä Työn valvoja: DI Sakari Männistö Symbian OS on tämän hetken suosituin älypuhelinten käyttöjärjestelmä. Matkapuhelin koostuu useista erilaisista laitteista, joita ohjaamaan tarvitaan laiteajureita. Opinnäytetyö syntyi Sesca Mobile Software Oy:n tarpeesta perehtyä Symbian-laiteajuriohjelmointiin. Opinnäytetyön tarkoituksena oli laiteajuriohjelmointiin perehtyminen ja esimerkkilai-teajurin rakentaminen. Tavoitteena oli, että työn tuloksena syntyvää esimerkkiajuria ja opinnäytetyötä voidaan käyttää apuna yrityksen sisäisessä koulutuksessa. Symbian-laiteajuriohjelmointi perustuu laiteajuriohjelmistokehykseen, jonka ehdoilla laiteajurit rakennetaan. Laiteajuri toimii Symbian-puhelimen kernel-tasolla, johon nor-maalilla ohjelmalla ei ole suoritusoikeuksia. Ohjelmistokehyksen avulla luodaan kanava käyttäjätasolta kernel-tasolle, ja laiteajurin käyttäminen tavallisesta ohjelmasta tulee mah-dolliseksi. Laiteajuriohjelmistokehyksen tehtävänä on helpottaa laiteajuriohjelmointia ja parantaa laiteajurin tietoturvaa. Tutkiva osa keskittyi pääasiassa laiteajuriohjelmistokehyksen toimintaan. Aluksi pereh-dyttiin yleisesti Symbian OS:ään ja laiteajuriohjelmistokehykseen. Seuraavaksi siirryttiin laiteajuriohjelmoinnissa tarvittaviin luokkiin ja laiteajurin ohjelmointiin. Työssä selvite-tään, mitä kernel-tasolla tapahtuu sillä aikaa, kun laiteajuriohjelmaa ajetaan. Työ kohdistui esimerkkilaiteajurin rakentamiseen. Siinä selvitettiin yksityiskohtaisesti, miten laiteajurikoodin suoritus etenee käyttäjäsäikeeltä kernel-säikeelle. Lisäksi tarkastel-tiin, miten laiteajuri käsittelee synkronisia ja asynkronisia pyyntöjä ja miten kernel-säie ilmoittaa käyttäjäsäikeelle asynkronisen toimenpiteen valmistumisesta. Avainsanat: laiteajuri, Symbian OS, käyttöjärjestelmä, ohjelmointi
CENTRAL OSTROBOTHNIA ABSTRACT POLYTECHNIC Degree Programme in Information Technology Author: Toni Lahnalampi Name of thesis: Symbian Device Driver Programming Date: 8 September 2008-03-17 Pages: 33 + 5 Appendices Instructor: Jussi Nissilä Supervisor: Sakari Männistö Symbian OS is the most popular operating system in smart phones. A mobile phone consists of many kinds of devices which need device drivers. This thesis was done for Sesca Mobile Software OY which needed a reseach of the Symbian device driver model. The purpose of the thesis was to examine device driver programming and to build a model device driver. The model driver and the thesis could be used later in internal training. Symbian device driver programming is based on the device driver framework. The device driver is on the kernel-side, while the program using the device is on the user-side. The device driver framework creates a channel from the user-side to the kernel-side. The framework makes programming of the device driver easier and it also allows a safe access to the kernel-side. The research part of the thesis is about understanding the device driver framework. At first the device driver framework and the Symbian operating system were explained in general. The next phase was to introduce the device driver classes and how the device driver should be programmed. The thesis clarified the kernels´ way of processing the de-vice driver program in the device driver framework. Implementing of the model device driver focused in detail on how the execution of the device driver proceeded from the user-side thread to the kernel thread. It also introduced a couple of methods how the device driver handled synchronous and asynchronous requests, and how the kernel thread signalled the user thread, when the asynchronous re-quest was complete. Keywords: Symbian OS, device driver, operating system, programming
LYHENTEET
EKA epoc kernel architecture
DLL dynamic link library
LDD logical device driver
PDD physical device driver
DFC deferred function call
ISR interrupt service routine
IRQ Interrupt request
KÄSITTEET
Keskeytys: Suoritettavana oleva ohjelma keskeytetään ja ohjelman suoritus siirtyy kes-
keytyksen aiheuttaneelle laitteelle.
Pre-emptiivinen: Pre-emptiivisessä tosiaikakäyttöjärjestelmässä pienemmän prioriteetin
toiminto keskeytetään välittömästi, kun korkeamman prioriteetin toiminto on valmiina
ajettavaksi.
Mutex: (Mutual exclution primitive) Säikeiden synkronointimekanismi, jossa mutex-
muuttujalla lukitaan järjestelmä kriittisten toimenpiteiden suorituksen ajaksi. Niin kauan
kuin mutex-muuttujan arvo on 1, säie pysyy lukittuna.
Kernelin lukittuminen : keskeytykset on sallittu, mutta jos keskeytys kohdistuu säikee-
seen, joka suorittaa kriittistä koodia, keskeytystä ei palvella.
Järjestelmän lukittuminen: Asetetaan mutex, joka estää osoiteavaruuden muokkaami-
sen.
ESIPUHE
Opinnäytetyöni on tehty Sesca Mobile Software Oy:lle. Sesca Mobile Software OY on
osa Sesca Groupia, joka on vuonna 2005 perustettu konserniyhtiö. Sesca Group työllistää
noin 600 ihmistä automaatio-, telekommunikaatio- ja rakennusalalla, ja toimintaa on 12
toimipisteellä Euroopan alueella. Esitän kiitokset työtovereilleni, erityisesti työn ohjaajal-
le Ohjelmistosuunnittelija Jussi Nissilälle, joka tarjosi työn tekemiseen tarvittavaa kan-
nustusta, tukea ja motivaatiota. Lisäksi haluan kiittää koulun ohjelmistotekniikkapuolen
opettajia, erityisesti työn ohjaajaa ja tarkastajaa yliopettaja Sakari Männistöä.
1 JOHDANTO 1
2 EPOC-KERNELIN HISTORIA 3
3 SYMBIAN OS ARKKITEHTUURI 5
3.1 OMAP-alusta 5
3.2 Muistit 6
3.3 Ajastimet 7
3.4 DMA 8
3.5 Näyttö 8
3.6 Äänilaitteet 9
3.7 Virranhallinta 9
3.8 Keskeytykset 10
3.8.1 ISR 11
3.8.2 DFC 11
3.8.3 Viestijono 12
4 LAITEAJURIT 14
4.1 Yleistä 14
4.2 Symbian-laiteajuriohjelmistokehys 15
4.3 LDD 16
4.4 PDD 16
4.5 Symbian-laiteajuriluokat 16
4.5.1 RBusLogicalChannel 16
4.5.2 DLogicalDevice 17
4.5.3 DLogicalChannel 17
4.5.4 DPhysicalDevice 18
4.5.5 DBase 18
4.6 Laiteajurin ohjelmointi 18
4.6.1 Laiteajuri-DLL-tiedoston lataaminen 18 4.6.2 Laiteajurikanavan avaaminen 20
4.6.3 Laiteajurikanavan sulkeminen 21
4.6.6 Synkronisen pyynnön lähettäminen 22 4.6.7 Asynkronisen pyynnön lähettäminen 23 4.6.8 Asynkronisen pyynnön keskeyttäminen 24 4.6.9 Laiteajurin sulkeminen 25
5 ESIMERKKILAITEAJURI 27
5.1 PDD-factory 27
5.2 PDD 27
5.3 LDD-factory 28
5.4 LDD 28
5.4.1 DoCreate-metodi 28
5.4.2 HandleMsg-metodi 29
5.4.3 DoControl-metodi 29
5.4.4 Synkronisen viestin lähettäminen kernel-säikeelle 29 5.4.5 DoRequest-metodi 30
5.4.6 Asynkronisen viestin lähettäminen kernel-säikeelle 30 5.4.7 Asynkronisen viestin vastaanottaminen kernel-säikeeltä 31
5.5 Käyttäjätason ohjelma 31
5.5.1 Open-metodi 31
5.5.2 Synkroniset metodit 32
5.5.3 Asynkroniset metodit 32
5.5.4 Laiteajurin sulkeminen 32
6 JOHTOPÄÄTÖKSET 33
LIITTEET
1/1. PDD factory 1/2. PDD factory 1/3. PDD factory 1/4. PDD factory 2/1. PDD 2/2. PDD 2/3. PDD 2/4. PDD 2/5. PDD 3/1. LDD factory 3/2. LDD factory 3/3. LDD factory 3/4. LDD factory 4/1. LDD 4/2. LDD 4/3. LDD 4/4. LDD 4/5. LDD 4/6. LDD 4/7. LDD 4/8. LDD 4/9. LDD 4/10. LDD 4/11. LDD 5/1. Käyttäjätason ohjelma 5/2. Käyttäjätason ohjelma 5/3. Käyttäjätason ohjelma 5/4. Käyttäjätason ohjelma 5/5. Käyttäjätason ohjelma5/6. Käyttäjätason ohjelma5/7. Käyttäjätason ohjelma
1
1 JOHDANTO
Matkapuhelimet ovat 1990-luvulta saakka kehittyneet huimaa vauhtia. Kännykät alkavat
rakenteeltaan ja toiminnaltaan muistuttaa yhä enemmän tietokoneita, joissa puhelutoimin-
not ovat vain pienenä lisäominaisuutena kameran, videokameran, MP3-soittimen, Inter-
net-selaimen, pikaviestiohjelman, kalenterin ja 3D-videopelien ohella. Tämän päivän
matkapuhelimesta löytyy jo saman verran tehoa kuin 90-luvun puolenvälin pöytäkonees-
ta, joten kännykän tulevaisuus näyttää tuovan tullessaan runsaasti uusia mahdollisuuksia.
Opinnäytetyöni tarkoitus oli perehtyä Symbian-laiteajuriohjelmointiin. Työssä käsitellään
perusteellisesti Symbian-laiteajuriohjelmistokehys. Lisäksi tutkitaan matkapuhelimissa
yleisesti käytössä olevien I/O-väylien toimintaperiaate ja lopuksi selvitetään ohjelmisto-
kehyksen toimintaperiaate. Työn käytännön sovelluksena toteutettiin Symbian-
laiteajuriohjelmistokehykseen perustuva, yksinkertainen ja havainnollinen laiteajurin läh-
dekoodi.
Työ rajattiin niin, että siinä käsitellään kaikki laiteajuriohjelmistokehyksen ohjelmoinnin
kannalta oleelliset asiat. Tarkoituksena oli tehdä työ, jota lukemalla ymmärtäisi ohjelmis-
tokehyksen toiminnan periaatteet ja johon syventymällä olisi myös mahdollista oppia
tekemään laiteajurisovelluksia.
Opinnäytetyön suurimpana ongelmana oli se, että laiteajuriohjelmointi tapahtuu Symbia-
nin kenel-tasolla. Kernel-tason käyttäminen on tietoturvasyistä rajattu siten, että doku-
mentaatioon pääsee käsiksi ainoastaan Symbian platinum partner -lisenssillä, joka myön-
netään vain Symbiania kehittäville yrityksille.
Symbian-laiteajuriohjelmoinnista löytyi ainoastaan yksi julkinen lähdeteos, Jane Salesin
kirjoittama ja Wileyn kustantama Symbian OS Internals. Kirjan kirjoittaja on osallistunut
Symbian EKA2-kernel -kehitystyöhön, joten kirjassa on välillä hyvinkin yksityiskohtaista
tietoa Symbian OS:n toiminnasta.
Symbian OS:ssä on kaksi tasoa. käyttäjätaso, jossa ohjelmat ajetaan rajoitetuin oikeuksin
ja kernel-taso, jossa ohjelmilla on täydet oikeudet. Normaali Symbian-ohjelma toimii
2
käyttäjätasolla, eikä sillä ole pääsyä kernel-tasolle. Laiteajuri taas on aina kernel-tasolla.
Laiteajuriohjelmistokehys avaa turvallisesti kanavan käyttäjätason ohjelmalta laiteajurille.
Symbian OS -ohjelman suoritus on asynkronista. Normaalissa Symbian-ohjelmassa sitä ei
huomaa, mutta kernel-tasolla asynkronisyyden ymmärtäminen on välttämätöntä. Kernel-
tasolla ohjelma saa suoritusaikaa prosessorikeskeytysten avulla. Symbianissa keskeytys
saadaan aikaan tekemällä ISR (interrupt service request), josta ajastetaan DFC (deferred
function call). Ajastetut DFC:t suoritetaan DFC-jonosta DFC:n prioriteetin perusteella.
Ohjelmistokehyksessä kommunikointi tapahtuu käyttäjätason ohjelmasäikeen viestien
avulla. Jokaisella käyttäjäsäikeellä on oma viestiolio. Kaikkien käyttäjäsäikeiden viestit
ohjataan kernel-säikeessä toimivan loogisen kanavan toteuttamaan viestijonoon, joka puo-
lestaan on assosioitu kernelin DFC-jonon kanssa, eli viestijonon viestejä ajetaan kernelin
DFC-jonossa aina, kun kernelillä ei ole tärkeämpää tekemistä.
3
2 EPOC-KERNELIN HISTORIA
Epoc-kernelin kehitys alkoi vuonna 1984, kun Psion Ltd. julkaisi Organizer-nimisen
kämmentietokoneen. Laitteessa oli 8-bittinen prosessori, mutta siinä ei ollut käyttöjärjes-
telmää. Ohjelmina siinä oli kello ja laskukone. (Sales 2005, 1–3.)
1986 Psion julkaisi seuraavan sukupolven kämmentietokoneen, Organizer II, jossa oli 8-
bittinen prosessori, mutta käyttöjärjestelmää oli parannettu varsinkin muistinkäsittelyn
osalta. Laite tarjosi myös tuen OPL- (Open programming language) kielelle, jonka avulla
oli mahdollista tehdä yksinkertaisia kolmannen osapuolen ohjelmia. (Sales 2005, 1–3.)
1990-luvun alussa markkinoille tuli simpukan mallinen Organizer, josta oli myös yritys-
malli sekä kannettava tietokone, jotka kaikki toimivat 16-bittisen EPOC-
käyttöjärjestelmän ja Intelin 8086-arkkitehtuuriin perustuvissa laitteissa. OPL-kielen li-
säksi laitteeseen lisättiin rajapinta, jonka avulla siihen voitiin asentaa kolmannen osapuo-
len tekemiä C-kielisiä sovelluksia. Avoimuus asetti suuria haasteita laitteen kernelille.
Kernel piti dokumentoida hyvin ja piti varautua huonosti kirjoitettuihin ohjelmiin, ettei
virheitä sisältävä ohjelma kaataisi koko käyttöjärjestelmää. (Sales 2005, 1–3.)
16-bittinen EPOC-käyttöjärjestelmä asettuu tosiaikaisen sulautetun järjestelmän ja tieto-
koneen käyttöjärjestelmän väliin. Se oli kuin tosiaikajärjestelmä, koska sitä ajettiin ROM-
muistilta ja se oli kuin tietokoneen käyttöjärjestelmä, sillä siinä oli kattava määrä ohjelmia
ja tuki kolmannen osapuolen ohjelmille. Tämän lisäksi EPOC-kernel oli tehokas muisti-
ja virrankulutuksen suhteen. (Sales 2005, 1–3.)
EPOC32-käyttöjärjestelmä julkaistiin Psionin 5-sarjan kämmentietokoneissa vuonna
1997. Sen kernel, EKA1 (epoc kernel architecture 1), sisälsi 16-bittisen EPOC-kernelin
parhaat ominaisuudet, mutta siitä oli karsittu pois vanhanaikaiset 8086-arkkitehtuurista
peräisin olevat ominaisuudet. EKA1 suunniteltiin alusta asti tukemaan erilaisia prosesso-
riarkkitehtuureja, kun 16-bittinen oli tehty aina 8086-arkkitehtuurin ytimelle. EPOC32-
käyttöjärjestelmällä toimivat laitteet olivat vakaita, ja ne sopivat hyvin kämmentietoko-
neille. (Sales 2005, 1–3.)
4
1998 Psion yhdistyi suurten puhelinvalmistajien, Motorolan, Ericssonin ja Nokian kanssa,
ja EPOC-käyttöjärjestelmä nimettiin Symbian OS:ksi. Kun Symbian OS:ää alettiin kehit-
tää matkapuhelimille, EKA1 ei enää riittänytkään, sillä matkapuhelimen käyttöjärjestel-
mältä vaaditaan tosiaikaominaisuuksia, joita EKA1:ssä ei ollut. EKA1:n suunnittelussa
huomattiin myös puutteita, joiden vuoksi yksinkertainen ajuripäivitys saattoikin vaatia
koko kernelin uudelleenkääntämisen. (Sales 2005, 1–3.)
Nämä asiat johtivat siihen, että EPOC-kernelistä alettiin kehittää uutta versiota. EKA2
suunniteltiin 1998. Se on parannettu versio EKA1:stä tuoden mukanaan myös matkapuhe-
limen käyttöjärjestelmältä vaadittavat tosiaikaominaisuudet ja moniajon. Ensimmäiset
EKA2:lla varustetut Symbian-puhelimet tulivat markkinoille 2003 ja niiden kehitys jat-
kuu vielä tänäkin päivänä. (Sales 2005, 1–3.)
5
3 SYMBIAN OS ARKKITEHTUURI
3.1 OMAP-alusta
Tällä hetkellä yksi yleisimmistä ohjelmaprosessoripiireistä on Texas Instrumentsin ARM-
prosessoriarkkitehtuuriin perustuva OMAP-alusta. OMAP onkin lähes jokaisen suuren
matkapuhelinvalmistajan käytössä. Alusta koostuu parhaimmillaan neljästä, omaan käyt-
tötarkoitukseensa optimoidusta prosessorista, joista jokainen voi suorittaa omaa tehtä-
väänsä toisista riippumatta. (Texas Instruments Ltd.)
Järjestelmän pääprosessorina toimii vähävirtainen ARM-prosessori, joka on optimoitu
virrankulutuksen suhteen. Siinä on useita erilaisia virransäästötiloja, joita järjestelmä voi
käyttää. ARM-prosessorin tukena on digitaalisignaaliprosessori ja huippupuhelimissa
video- ja 3D-kiihdytin. (Texas Instruments Ltd.)
Prosessoripiiri sisältää erillisen RAM-muistin, jota käytetään apuna järjestelmän käynnis-
tämisessä sekä sisäisenä puskurina. Esimerkiksi LCD-kontrollerin pitää päivittää puheli-
men näyttöä 60 kertaa sekunnissa, mikä tarkoittaa 16-bittisellä QVGA-näytöllä 8,78 Mt
tiedonsiirtoa sekunnissa. Jos LCD-kontrolleri käyttää tiedonsiirtoon piirin sisäistä RAM-
muistia, säästetään huomattava määrä tehoa ja tavallisen RAM-muistin kaistaa. Sisäinen
RAM-muisti voidaan myös optimoida niin, että se vähentää LCD-kontrollerin virrankulu-
tusta. (Texas Instruments Ltd.)
6
KUVIO 1. OMAP2420 prosessori (Texas Instruments Ltd.)
3.2 Muistit
Symbian OS hallitsee muistinkäyttöä MMU:n (Memory Management Unit) avulla. MMU
toimii prosessorin ja fyysisen muistin välissä ja sen tehtävä on muuttaa virtuaalinen
muistiosoite fyysiseksi muistiosoitteeksi. Fyysiset muistiosoitteet ovat muistissa suurena
lohkona. MMU jaottelee fyysisen muistin virtuaalisiin lohkoihin, jolloin muistinkäsittely
helpottuu ja ohjelmointivirheiden määrä vähenee. (Sales 2005, 25–32.)
Välimuisti on hyvin nopeaa muistia, joka kykenee käsittelemään tietoa samalla kellotaa-
judella, millä prosessori sitä syöttää. Välimuistia käytetään puskurina hitaamman RAM-
muistin ja prosessorin välissä. Prosessori suorittaa käskyjä suurimmalla todennäköisyy-
dellä sellaisilla muistialueilla, joita on käytetty lähiaikoina. Tämä tieto pidetään välimuis-
tissa, josta se on nopeasti prosessorin saatavilla. Symbian OS:ssä voi olla useita välimuis-
teja, jotka on optimoitu erilaisiin tehtäviin. (Sales 2005, 25–32.)
7
Mobiililaitteissa RAM-muistina käytetään erityistä SDRAM-muistia, joka on optimoitu
virrankulutuksen suhteen. Tämän vuoksi se on neljä kertaa hitaampaa kuin normaalin
pöytäkoneen RAM-muisti. (Sales 2005, 25–32.)
Ohjelmat ja asetukset tallennetaan flash-muistille. Flash on häviämätöntä muistia, jota
voidaan kirjoittaa ja lukea. Se kestää rajua käsittelyä ja useita kirjoitus- ja lukukertoja.
Flash-muistia on kahta eri tyyppiä, NOR ja NAND. NOR-flashia käytetään prosessorilta
ajettavan XIP-koodin ajamiseen, ja käyttöjärjestelmän voi myös käynnistää suoraan
NOR-flashiltä. (Sales 2005, 25–32.)
NOR-flash eroaa NAND-flashista siten, että NOR-flashin muistialue on jaettu muis-
tiosoitteisiin, samalla tavalla kuin RAM-muistissa. NAND-muisti taas on jaettu sivuihin,
joiden koko on tyypillisesti 512 bittiä, ja sen lukeminen ja kirjoittaminen tehdään aina
sivu kerrallaan, joten tieto pitää puskuroida RAM-muistiin ennen luku- tai kirjoitusope-
raatiota. NAND-muistin etuna on NOR-muistia edullisempi hinta ja nopeampi, jopa 10-
kertainen kirjoitusnopeus NOR-muistiin verrattuna. Se ei tosin ole niin luotettavaa kuin
NOR. (Sales 2005, 25–32.)
3.3 Ajastimet
Symbianin minimivaatimuksena on vähintään 1 ms:n tarkkuudella toimiva ajastin, jonka
muistiin voi kirjoittaa ja josta voi lukea tietoa. Usein puhelimessa on kuitenkin useita eri
tarkoituksiin tarkoitettuja ajastimia. Ajastinta ohjataan tietyllä taajuudella toimivan kel-
losignaalin avulla. Kellosignaali etenee ajastinpiirille, joka tunnistaa signaalin pulssit ja
laskee niiden avulla sykäyksiä. Ajastinpiirejä voi olla monessa eri tasossa, jolloin alem-
man tason ajastimen pyörähtäessä ympäri lähetetään signaali alemman tason ajastimelle,
joka kasvattaa omaa arvoaan yhdellä. Puhelimen ajastin ei voi olla riippuvainen prosesso-
rin toiminnasta, sillä ajastinpiirin pitää toimia silloinkin, kun prosessori on lepotilassa.
(Sales 2005, 34–35.)
8
3.4 DMA
DMA (direct memory access) tarkoittaa sitä, että laitteet voivat DMA:n kautta kirjoittaa ja
lukea muistia niin, että tieto siirretään suoraan muistin ja DMA-laitteen välillä ilman pro-
sessorin apua. DMA:n käyttäminen vähentää prosessorin keskeytyskuormaa ja parantaa
näin järjestelmän vakautta. (Sales 2005, 36–37.)
DMA-kontrolleri on järjestelmäväylän isäntälaite, joka voidaan ohjelmoida siirtämään
suuria datamääriä muistin ja lisälaitteiden välillä. DMA:ssa voi olla useita kanavia, joita
pitkin tietoa siirretään samanaikaisesti useille laitteille. Symbianin DMA-kontrollerissa
kanavia pitäisi olla yhtä monta kuin DMA:n tarvitsemia laitteitakin on. (Sales 2005, 36–
37.)
3.5 Näyttö
Yksi Symbian-puhelimen tärkeimmistä komponenteista on näyttö. Nykyajan puhelimissa
käytetään nestekidenäyttöjä, jotka kykenevät toistamaan 16-bittisen väriavaruuden. Ylei-
siä Symbian-puhelimen resoluutioita ovat 176 x 208 ja 320 x 240. Kännyköiden näytöissä
on viime vuosina LCD-näyttöjen kehittymisen ohella ollut trendinä kasvattaa näytön reso-
luutiota ja kokoa. Näytönpäivitystä hoidetaan käyttäen apuna ohjelmaprosessorin sisäistä
puskuria, jossa pidetään tietoa näytöllä näkyvien pikseleiden tilasta. Jos käytetään 320 x
240 resoluutiota ja näytönpäivitys on 60 Hz, näyttö vaatii nopeudeltaan 8,78 Mt/s olevan
kaistan ja 150 kt muistia. (Sales 2005, 37–38.)
LCD-näyttöjä on kahta eri tyyppiä, tyhmiä ja älykkäitä. Tyhmä ei sisällä minkäänlaista
ohjauselektroniikkaa, vaan ohjaaminen hoidetaan erillisen LCD-kontrollerin avulla, mikä
tarkoittaa, että DMA:n täytyy päivittää näyttöä 60 Hz taajuudella silloin, kun näyttö on
päällä. Älykäs näyttö sisältää oman LCD-kontrollerin ja puskurimuistin näytön tilan yllä-
pitämiseen. Älykkään näytön ruudunpäivitysnopeutta voidaan muuttaa ja siinä voidaan
päivittää kokonaisen ruudun sijaan yksittäisiä ruudun osia. Nopeaa ruudunpäivitystä käy-
tetään, kun katsotaan videota tai pelataan pelejä. Suurimman osan ajasta puhelin toimii
kuitenkin virransäästön vuoksi matalalla päivitystaajuudella. (Sales 2005, 37–38.)
9
3.6 Äänilaitteet
Symbian-puhelimessa käytetään kahdentyyppistä äänenpakkausta. Puheluäänessä tavoit-
teena ovat pieni virrankulutus ja pieni viive, jolloin ääni on pakattava tehokkaasti. Mul-
timediaäänessä taas tavoitellaan hyvää äänenlaatua. Symbian OS:n äänet on pulssikoodi-
moduloitu ja laatu vaihtelee 8 kHz:n puheluäänen ja 48 kHz:n stereoäänen välillä. (Sales
2005, 39.)
Symbian OS äänilaitteen käyttö on yksinkertaista. Ensin asetetaan äänen taso ja käytettä-
vä output-linja, jonka jälkeen ääni syötetään laitteelle sopivalla taajuudella. Äänen siirros-
sa käytetään usein DMA-kontrolleria, jolloin säästetään huomattava määrä prosessorite-
hoa. (Sales 2005, 39.)
3.7 Virranhallinta
Kaikki puhelimet saavat virtansa akusta, joten toimivan puhelimen edellytys on riittävä
määrä tehoa ja pieni virrankulutus. Nämä vaatimukset huomioiden Symbian-puhelimeen
valitaan usein ARM:n valmistama prosessori, jossa on useita virrankulutusta vähentäviä
ominaisuuksia. (Sales 2005, 41–42.)
Symbian-järjestelmän tärkein virransäästöominaisuus on vapaatila, jossa ei tehdä mitään.
Symbian-puhelinten virransäästö perustuukin enimmäkseen siihen, että puhelin on mah-
dollisimman paljon vapaatilassa ja jos tehdään jotain, se tehdään mahdollisimman tehok-
kaasti. (Sales 2005, 41–42.)
Virrankulutuksen kannalta tehokkain ratkaisu olisi käyttää jokaisella puhelimen laitteella
erillistä laitteistokiihdytintä yleiskäyttöisen ohjelmaprosessorin sijaan. Laitetta ohjattaisiin
omalla tarkoitukseensa optimoidulla prosessorilla ja laiteajuriohjelma koodattaisiin lait-
teistokiihdyttimen piirilevylle. Yleiskäyttöisen prosessorin käyttäminen on kuitenkin pal-
jon halvempaa, kuin moniprosessorijärjestelmän, joten laitevalmistajan on löydettävä
kultainen keskitie hinnan ja laadun välillä. (Sales 2005, 41–42.)
3.8 Keskeytykset
Symbian-laiteajureiden tekeminen edellyttää, että
emptiivisen tiedonsiirron
keskeytyspyyntöjä. Prosessori
ritusta odottaville säikeille
järjestyksessä. Prosessori
nanokernelin ajastin
les 2005, 219.)
Kernel-ohjelmoinnissa
hetkellä hyvänsä. Ohjelman s
jälkeen tilanteesta, missä oltiin ennen keskeytyksen saapumista.
keytysten käsittelyyn ISR
Jokainen keskeytys laukaisee ISR:n
DFC:t DFC-jonossa, jo
Laiteajuriohjelmistoke
puskuroidaan viestijono
Jonoon asetettuja viestijonon pyyntöjä
pyynnöt on lähetetty jonoon
KUVIO 2. Laitekeskeytyksen suoritus laiteajurilla
aiteajureiden tekeminen edellyttää, että ymmärretään
tiedonsiirron periaatteet. Laitteet saavat ajoaikaa lähettämällä pr
Prosessori suorittaa samanaikaisesti useita eri
tusta odottaville säikeille aikaviipaleita, joita suoritetaan ajastusalgoritmin määräämässä
rosessori käsittelee tuhansia keskeytyksiä sekunnissa
lähettää prosessorille keskeytyksiä yhden milli
ohjelmoinnissa on varauduttava siihen, että ohjelman suoritus voi
Ohjelman suoritusta on kyettävä jatkamaan keskeytyksen palvelemisen
, missä oltiin ennen keskeytyksen saapumista. Symbian
keytysten käsittelyyn ISR-(interrupt service call) ja DFC-(Deferred function call)
laukaisee ISR:n, joka vuorostaan ajastaa DFC:n.
jonossa, jota suoritetaan erillisessä säikeessä. (Sales 2005, 527
ohjelmistokehys tarjoaa mallin, jossa pyynnöt käyttäjäohjelmasta laiteajurille
puskuroidaan viestijonoksi. Mallin toiminta on kuvattu sekvenssikaaviona kuviossa 2.
viestijonon pyyntöjä suoritetaan DFC:nä siinä järjestyksessä, missä
pyynnöt on lähetetty jonoon. (Sales 2005, 527.)
KUVIO 2. Laitekeskeytyksen suoritus laiteajurilla
10
ymmärretään asynkronisen ja pre-
lähettämällä prosessorille
eri säikeitä jakamalla suo-
usalgoritmin määräämässä
ekunnissa, sillä pelkästään
milli sekunnin välein. (Sa-
ohjelman suoritus voi keskeytyä millä
keskeytyksen palvelemisen
Symbian OS käyttää kes-
(Deferred function call)olioita.
vuorostaan ajastaa DFC:n. Kernel suorittaa
Sales 2005, 527.)
pyynnöt käyttäjäohjelmasta laiteajurille
Mallin toiminta on kuvattu sekvenssikaaviona kuviossa 2.
siinä järjestyksessä, missä
11
3.8.1 ISR
ISR (interrupt service routine) on olio, jonka Symbian OS suorittaa, kun laitteella tapah-
tuu keskeytys. ISR:n suorituksen aikana järjestelmä on lukittu. Tämä tarkoittaa, että ker-
nelin osoiteavaruuden muokkaaminen on estetty sillä aikaa, kun ISR on suorituksessa.
Tästä syystä ISR:n suorituksen pitää olla mahdollisimman nopea. ISR palvelee keskey-
tyksen yleensä ohjaamalla suorituksen DFC:lle ja vapauttaa järjestelmän lukituksen. (Sa-
les 2005, 223.)
3.8.2 DFC
DFC on luokka, joka määrittelee funktion ajettavaksi DFC-jonossa. DFC:llä on prioriteet-
ti, joka on välillä 0–7. Suoritus tapahtuu ensisijaisesti prioriteetin ja toissijaisesti saapu-
misjärjestyksen perusteella. (Sales 2005, 529–530.)
DFC:n toimintaperiaate näkyy kuviosta 2. Laite lähettää keskeytyksen, jolloin kernel suo-
rittaa ISR:n. ISR ajastaa DFC:n. DFC suoritetaan asynkronisesti DFC-jonossa. DFC-jono
toimii omassa säikeessä, ja sen tehtävä on suorittaa DFC, kun sellainen on tarjolla. Kun
tarjolla on useita DFC:itä, niitä suoritetaan yksi kerrallaan prioriteetin ja saapumisjärjes-
tyksen perusteella. DFC:n tultua valmiiksi kutsuvan säikeen TRequestStatus-olion tila
muutetaan, jolloin kutsun tehnyt säie tietää pyynnön valmistuneen. (Sales 2005, 529–
530.)
Laiteajuriohjelmistokehys käyttää DFC:tä viestijonon ohella käsittelemään käyttäjätasolta
laiteajurille tulevia pyyntöjä. DFC:t asetetaan normaalisti käyttämään Symbian-kernelissä
vakiona olevia DFC-jonoja. Kaikissa tapauksissa sekään ei riitä, vaan joudutaan käyttä-
mään erillistä DFC-jonoa. Erillisen jonon käyttäminen edellyttää jonon synkronoimista.
(Sales 2005, 529–530.)
Kernel tarjoaa kaksi DFC-jonoa, jotka ovat DfcThread0 ja DfcThread1. DfcThread0 on
matalan prioriteetin jono, jota käytetään sarjaliikenne-, ääni-, verkko- ja näppäimistö lai-
teajureissa. Jonoa DfcThread0 käytetään laitteilla, joilla ei ole tiukkoja tosiaikavaatimuk-
sia. Vaatimusten kasvaessa voidaan käyttää DfcThread1-jonoa, joka on korkeamman
12
prioriteetin jono. Sitä tulee kuitenkin käyttää varauksella, sillä väärin käytettynä se saattaa
aiheuttaa viivettä nanokernelin ajastimeen. (Sales 2005, 529-530.)
3.8.3 Viestijono
Viestijono on kaksinkertaisesti linkitetty lista, joka on assosioitu DFC:n kanssa. Jokaises-
ta viestijonon viestistä ajastetaan DFC, jonka DFC-jono käsittelee saapumisjärjestyksessä.
Viestijonon avulla käyttäjäsäikeestä voi lähettää viestin kernel-säikeelle. Käyttäjätasolta
lähetetyt viestit tallennetaan viestijonoon TThreadMessage-olioina. (Sales 2005, 147–
150, 531–534.)
Jokainen Symbian OS -säie omistaa instanssin TThreadMessage-oliosta. Kun laiteajurille
lähetetään pyyntö, se asetetaan säikeen TThreadMessage-olioon ja olio lähetetään viesti-
jonoon käyttäen viestijonon staattista SendReceive-metodia. Kuvio 3 kuvaa, kuinka käyt-
täjäpuolen ohjelma lähettää viestin laiteajurille. (Sales 2005, 147–150, 531–534.)
Kun järjestelmä käyttää useita ohjelmia, jotka käyttävät laiteajureita, järjestelmän synk-
ronointi monimutkaistuu jatkuvasti. Viestijonojen avulla pyynnöt saadaan putkitettua ja
järjestelmän hallinta helpottuu. (Sales 2005, 147–150, 531–534.)
KUVIO 3. Käyttäjäpuolelta tulevan
Käyttäjäpuolelta tulevan viestin lähettäminen laiteajurille
13
lähettäminen laiteajurille
4 LAITEAJURIT
4.1 Yleistä
Laiteajuriohjelmistoke
resursseihin paljastamatta S
avulla voidaan tehdä uusia laiteajureita ilman, että
muutoksia. Laitetta ohjaava koodi
maalilla Symbian-ohjelmalla ei ole käyttöoikeuksia. Laiteajuri
voidaan avata kanava
käyttää käyttäjäsäikeestä
tehtuuria.
KUVIO 4. Symbian-ytimen rakenne
ohjelmistokehyksen tehtävänä on antaa käyttäjätason ohjelmalle pääsy laitt
paljastamatta Symbian-kernelin toimintaa käyttäjäohjelmalle. Lisäksi sen
avulla voidaan tehdä uusia laiteajureita ilman, että käyttäjätason
oksia. Laitetta ohjaava koodi suoritetaan useimmiten kernel
ohjelmalla ei ole käyttöoikeuksia. Laiteajuriohjelmistoke
ta kanava käyttäjäsäikeen ja kernel-säikeen välille, jolloin laiteajuria voidaan
käyttää käyttäjäsäikeestä. Kuvio 4 kuvaa Symbianin laiteajuriohjelmistoke
ytimen rakenne, (Sales 2005, 4)
14
ohjelmalle pääsy laitteen
toimintaa käyttäjäohjelmalle. Lisäksi sen
käyttäjätason koodiin tarvitsee tehdä
useimmiten kernel-säikeessä, johon nor-
ohjelmistokehyksen avulla
jolloin laiteajuria voidaan
ohjelmistokehyksen arkki-
4.2 Symbian-laiteajuri
Symbian OS:n laiteajuri
luokista. Suuri osa ohjelmistokehyksen toiminn
taakse. Laiteajurin tekijän pitää vain ylimääritellä tarvittavat luokat
suunnitellulla tavalla. Kuvio
(Sales 2005, 501–502
KUVIO 5. Laiteajuriluokkien suhteet
Laiteajuriohjelmistoke
LDD (logical device driver) ja PDD (physical device driver). LDD ja
rajapinnat, joiden avulla käyttäjä voi turvallisesti käyttää järjestelmään kytketyn laitteen
resursseja. Laiteajuri voidaan toteuttaa kahdella tavalla. Jos laiteajurin looginen kanava
periytetään DLogicalChannelBase
keen sisällä. Jos laiteajuri periytetään DLogicalChannel
rille putkitetaan viestijonon avulla
aiteajuri ohjelmistokehys
n laiteajuriohjelmistokehys koostuu useista vuorovaikutuksessa toimivista
luokista. Suuri osa ohjelmistokehyksen toiminnasta on piilotettu
Laiteajurin tekijän pitää vain ylimääritellä tarvittavat luokat
suunnitellulla tavalla. Kuvio 5 esittelee laiteajuriohjelmistokehyksessä käytettävät
502.)
iluokkien suhteet
ohjelmistokehys käyttää kahdenlaisia DLL-tiedostoja, jotka ovat
LDD (logical device driver) ja PDD (physical device driver). LDD ja
joiden avulla käyttäjä voi turvallisesti käyttää järjestelmään kytketyn laitteen
Laiteajuri voidaan toteuttaa kahdella tavalla. Jos laiteajurin looginen kanava
periytetään DLogicalChannelBase-luokasta, ajuria ajetaan käyttäjätasolla
ä. Jos laiteajuri periytetään DLogicalChannel-luokasta,
viestijonon avulla kernel-tason säikeelle. (Sales 2005, 501
15
koostuu useista vuorovaikutuksessa toimivista
asta on piilotettu abstraktien rajapintojen
Laiteajurin tekijän pitää vain ylimääritellä tarvittavat luokat, jotta ajuri toimii
hyksessä käytettävät luokat.
tiedostoja, jotka ovat nimeltään
LDD (logical device driver) ja PDD (physical device driver). LDD ja PDD toteuttavat
joiden avulla käyttäjä voi turvallisesti käyttää järjestelmään kytketyn laitteen
Laiteajuri voidaan toteuttaa kahdella tavalla. Jos laiteajurin looginen kanava
käyttäjätasolla kutsuvan säi-
kaikki pyynnöt laiteaju-
2005, 501–502.)
16
4.3 LDD
LDD on dynaamisesti linkitettävä kirjasto ja se sisältää omat toteutukset loogisesta kana-
vasta (DLogicalChannel) ja LDD-factorystä (DLogicalDevice). LDD sisältää funktion
CreateLogicalDevice, jonka tehtävä on palauttaa osoitin DLogicalDevice-olioon. LDD
kommunikoi käyttäjätason kanssa laiteajuriohjelmistokehyksen läpi RBusLogicalChan-
nel-luokasta perityn rajapinnan kanssa. LDD:n tarkoitus on toteuttaa laiteajurin alusta-
riippumattomat osat ja laiteajuriohjelmistokehykseltä periytyvät puhtaat virtuaalimetodit.
Jos laiteajurin suunnittelussa päädytään käyttämään PDD:tä, LDD:ssä on toteutettava
rajapinta PDD:lle. (Sales 2005, 477–478.)
4.4 PDD
PDD on myös dynaamisesti linkitettävä kirjasto, ja se sisältää toteutuksen PDD-factorystä
(DPhysicalDevice) ja fyysisestä kanavasta (DBase) PDD sisältää funktion Crea-
tePhysicalDevice, jonka tehtävä on palauttaa osoitin DPhysicalDevice-olioon. PDD:n
käyttäminen on valinnaista, ja usein sen käyttäminen on katsottava tapauskohtaisesti.
PDD:n tulisi sisältää ainoastaan alustariippuvainen koodi. PDD:n tehtävänä onkin toisaal-
ta kommunikoida laitteen ohjelmarajapinnan kanssa, mutta myös toteuttaa rajapinta
LDD:lle. PDD ei koskaan kommunikoi suoraan käyttäjätason kanssa, vaan kaikki pyyn-
nöt kulkevat LDD:n läpi. LDD sisältää jäsenmuuttujan iPdd, johon PDD-olio tallenne-
taan. Käyttämällä PDD:tä helpotetaan laajojen laiteajurisovellusten sovittamista toiselle
alustalle. (Sales 2005, 477–478.)
4.5 Symbian-laiteajuriluokat
4.5.1 RBusLogicalChannel
Luokka toteuttaa käyttäjätason kahvan kernel-tason loogiseen kanavaan. Käyttäjätasolta
laiteajurille tulevat pyynnöt ohjataan ohjelmistokehyksen avulla kernel-tasolle tämän luo-
kan läpi. Luokan metodien avulla avataan kanava kernel-tasolle, lähetetään kaikki lai-
teajurin tarvitsemat asynkroniset ja synkroniset pyynnöt
kronisen pyynnön lähetys.
4.5.2 DLogicalDevice
DLogicalDevice on LDD
tellä DLogicalDevice
Install. Luokan tehtävä on
myös tiedot laiteajurin
olevien instanssien lukumäärä
4.5.3 DLogicalChannel
DLogicalChannel on
täjätasolta kernel-tasolle
rol-pyynnöt hoidetaan
mällä tavalla. (Sales, 509
KUVIO 6. Looginen kanava
teajurin tarvitsemat asynkroniset ja synkroniset pyynnöt ja tarvittaessa
kronisen pyynnön lähetys. (Sales 2005, 514.)
DLogicalDevice
LDD-factoryn abstrakti kantaluokka. LDD-factory:n pitää ylimäär
tellä DLogicalDevice-luokan puhtaat virtuaalimetodit, jotka ovat
tehtävä on suorittaa laiteajurin vaatimat alustukset
laiteajurin versiosta, oikeuksista, ominaisuuksista ja
instanssien lukumäärästä. (Sales, 510–513.)
DLogicalChannel
loogisen kanavan abstrakti kantaluokka, jolla luodaan kanava
tasolle. RBusLogicalChannel-luokalta tulevat DoRequest
hoidetaan DLogicalChannel-luokan HandleMsg-metodissa
, 509–513.)
. Looginen kanava
17
tarvittaessa keskeytetään asyn-
factory:n pitää ylimääri-
luokan puhtaat virtuaalimetodit, jotka ovat GetCaps-, Create-, ja
suorittaa laiteajurin vaatimat alustukset. Tämä luokka sisältää
ja laiteajurin käynnissä
abstrakti kantaluokka, jolla luodaan kanava käyt-
DoRequest- ja DoCont-
metodissa kuvion 6 esittä-
18
4.5.4 DPhysicalDevice
DPhysicalDevice on PDD-factoryn abstrakti kantaluokka, joka on hyvin samankaltainen,
kuin DLogicalDevice. Luokan Create-metodin avulla luodaan instanssi fyysisestä kana-
vasta ja Validate-metodin avulla verrataan, onko PDD yhteensopiva LDD:n kanssa. (Sa-
les, 513–515.)
4.5.5 DBase
DBase on kernel-tason kantaluokka, josta periytetään PDD:n fyysinen kanava. Luokka ei
sisällä puhtaita virtuaalifunktioita, vaan se suunnitellaan laitekohtaisesti. Fyysinen kanava
keskustelee loogisesn kanavan kanssa kernel-säikeen sisällä, joten niiden kommunikointi
hoituu normaalin olioiden välisen kommunikoinnin avulla. PDD:ssä toteutetaan rajapinta
fyysiselle laitteelle ja LDD:lle. (Sales 2005, 513–515.)
4.6 Laiteajurin ohjelmointi
4.6.1 Laiteajuri-DLL-tiedoston lataaminen
Laiteajuri ladataan dynaamisesti käyttöön käyttäjätasolta. Kun jokin ohjelma haluaa käyt-
tää laiteajuria, se avaa kanavan kernel-tasolle. Ennen kuin laiteajuria voi käyttää, ajuri
pitää ladata käyttöön kutsumalla kuviossa 8 olevaa LoadLogicalDevice-metodia. (Sales
2005, 504–505.)
KUVIO 7. LDD:n lataaminen (Sales 2005, 504–505.)
Metodi etsii järjestelmäpolusta aFileName-parametrin mukaista LDD tai PDD päätteistä
tiedostoa. Tiedostonimessä ei ole väliä, käyttääkö tiedostopäätteitä, sillä rajapinta lisää
TInt User::LoadLogicalDevice(const TDesC &aFileName );
19
puuttuvan tiedostopäätteen automaattisesti. Tiedoston löytyessä LoadLogicalDevice-
metodi tarkastetaan tiedoston UID:n. Jos UID täsmää, kernel kutsuu DLL-entry-point-
makroa, DECLARE_STANDARD_LDD. Makron tehtävänä on luoda LDD-factory-olio.
Makro toteutetaan LDD:hen kuvion 8 mukaisesti. (Sales 2005, 504–505.)
KUVIO 8. LDD-entry-point-makro (Sales 2005, 505, 515.)
Samalla, kun LDD-factory luodaan, kernel kutsuu LDD-factoryn toisen vaiheen konstruk-
toria Install, josta on esimerkki kuviossa 9. Siinä laiteajurioliolle pitää antaa vähintään
nimi, mutta siinä voidaan myös suorittaa muita laiteajurin vaatimia alustuksia. Jos install-
funktio menee läpi ilman virheilmoituksia, kernel lisää laiteajuriolion kernel-oliosäiliöön.
Kernelissä on laiteajureita varten kaksi oliosäiliötä, yksi PDD- ja yksi LDD-olioita varten.
Oliosäiliöiden avulla järjestelmä pitää kirjaa avoimista laiteajuriolioista, jolloin olioiden
varaama muisti voidaan vapauttaa, jos tapahtuu virhe, tai jos laiteajuri suljetaan. Laiteaju-
rin lataamista kuvaava sekvenssikaavio löytyy kuviosta 10. (Sales 2005, 505, 515.)
KUVIO 9. Install-metodi (Sales 2005, 505, 515.)
DECLARE_STANDARD_LDD { return new LddFactory; }
_LIT(KLddName "Example");
TInt DSimpleSerialLDD::Install();
{
return ( SetName( &KLddName ) );
KUVIO 10. Laiteajurin lataaminen
4.6.2 Laiteajurikanavan
Kanavan avaaminen
metodia kuvion 11 mukaisesti
tyyppinen, joten kutsu
mistokehyksen läpi
metodille, josta on esimerkki kuviossa
stanssin luomisesta. Kernel
sena. Jos kanavaa ei voi luoda, palautetaan virheilmoitus.
KUVIO 11. DoCreate
KUVIO 12. Create-metodi
DoCreate (aLddName, aV
DExampleLddFactory
{
aChannel = new SimpleSerialChannel;
return aChannel ?
}
Laiteajurin lataaminen
kanavan avaaminen
avaaminen alkaa kutsumalla käyttäjätason RBusLogicalChannel::DoCreate
mukaisesti. Tätä metodia ei voi kutsua suoraan, koska se on protected
kutsu tehdään wrapper-funktion avulla. Metodi ohja
hyksen läpi kernel-puolen LDD-factory-luokassa ylimääritellylle
esimerkki kuviossa 12. Tämä metodi vastaa LDD
Kernel palauttaa kanavan osoittimen ohjelmistoke
. Jos kanavaa ei voi luoda, palautetaan virheilmoitus. (Sales 2005, 508
. DoCreate-metodi (Sales 2005, 508–509.)
metodi (Sales 2005, 508–509.)
DoCreate (aLddName, aV ersion, aUnit, aPddName
DExampleLddFactory ::Create()
aChannel = new SimpleSerialChannel;
return aChannel ? KErrNone : KErrNoMemory;
20
gicalChannel::DoCreate-
. Tätä metodia ei voi kutsua suoraan, koska se on protected-
etodi ohjaa suorituksen ohjel-
luokassa ylimääritellylle Create-
Tämä metodi vastaa LDD:n kernel-tason in-
ohjelmistokehykselle viittauk-
(Sales 2005, 508–509.)
aPddName, aInfo);
KErrNone : KErrNoMemory;
Jos LDD:ssä on määritelty, että laiteajuri käyttää PDD:tä,
LDD:lle sopivan PDD:
hys alustaa DlogicalChannelBase
Base::DoCreate-metodia. Jos LDD:n alustus onnistuu,
Channel-oliosäiliöön ja luo kahvan, jo
uksessa kernel sulkee
moituksen. Kanavan avaamisprosessi näkyy
508–509.)
KUVIO 13. Loogisen kanavan avaaminen
4.6.3 Laiteajurikanavan sulkeminen
Laiteajuri suljetaan käyttämällä User::FreeLogicalDevice
metodeja, joiden käytöstä
säikeelle, jossa laiteajuri
dler::DeviceFree-metodille
Jos LDD:ssä on määritelty, että laiteajuri käyttää PDD:tä, kernel
LDD:lle sopivan PDD:n, joka tallennetaan LDD:n iPdd-jäsenmuuttujaan.
hys alustaa DlogicalChannelBase-luokasta perityn olion kutsumalla DLogicalChanne
metodia. Jos LDD:n alustus onnistuu, kernel lisää LDD
oliosäiliöön ja luo kahvan, joka lopulta palautetaan käyttäjätasolle.
sulkee LDD:n ja siihen mahdollisesti liittyvän PDD:n
Kanavan avaamisprosessi näkyy sekvenssikaaviona kuviossa 1
Loogisen kanavan avaaminen
kanavan sulkeminen
Laiteajuri suljetaan käyttämällä User::FreeLogicalDevice- tai User::FreePhysicalDevice
käytöstä on esimerkki kuviossa 14. Funktion suoritus ohjataan kernel
laiteajuriohjelmistokehys ohjaa suorituksen kernel
metodille, joka on kuvattu kuviossa 15. Jos kernel
21
kernel etsii tässä kohtaa
jäsenmuuttujaan. Ohjelmistoke-
luokasta perityn olion kutsumalla DLogicalChannel-
lisää LDD-olion ELogical-
käyttäjätasolle. Muussa tapa-
PDD:n ja palauttaa virheil-
sekvenssikaaviona kuviossa 13. (Sales 2005,
tai User::FreePhysicalDevice-
. Funktion suoritus ohjataan kernel-
ohjaa suorituksen kernel-säikeen ExecHan-
kernel löytää oliosäiliöstä
22
olion, jonka nimi vastaa annettua nimeä, olio poistetaan ja olion varaama muisti vapaute-
taan. (Sales 2005, 508.)
KUVIO 14. Loogisen kanavan sulkeminen (Sales 2005, 508.)
KUVIO 15. ExecHandler::DeviceFree-metodi (Sales 2005, 508.)
4.6.6 Synkronisen pyynnön lähettäminen
Synkronisia pyyntöjä käytetään yleensä laiteajurin tilakoneessa asettamaan tai kysymään
laiteajurin tilaa. Synkroninen pyyntö lukitsee kutsuvan säikeen kernel-säikeen suorituksen
ajaksi, joten synkronisten pyyntöjen pitää olla mahdollisimman lyhyitä. (Sales 2005, 502)
Kuvio 16 kuvaa sekvenssikaaviota synkronisesta pyynnöstä. Käyttäjätasolta kutsutaan
SetOn-metodia, joka ohjataan RBusLogicalChannel::DoControl-metodille. Ohjelmistoke-
hys kutsuu HandleMsg-metodia, joka saa ID-parametrinä positiivisen luvun tai nollan.
Tällöin kutsu ohjataan DoControl-metodille, joka puolestaan kutsuu PDD:n SetOn-
metodia. (Sales 2005, 502.)
TInt User::FreeLogicalDevice(const TDesC &aDeviceNa me)
TInt ExecHandler::DeviceFree(const TDesC8& aName,
TInt aDeviceType)
KUVIO 16. synkronisen
4.6.7 Asynkronisen pyynnön lähettäminen
Asynkronisia pyyntöjä käytetään
nön avulla voi esimerkiksi
hitaammalta laitteelta
rittamaan muita tehtäviä
että pyyntö on tallennettu
jatkuu samanaikaisesti.
käyttäjäsäikeelle TRequestStatus
Kuvio 17 kuvaa asynkronisen pyynnön käsittelyä.
SendHello-metodia. Suoritus ohjataan RBusLogicalChannel::DoRequest
kutsuu ohjelmistokehyksen kautta LDD:n HandleMsg
rinä negatiivisen ID:n, jolloin suoritus ohjataan
Send-metodia. PDD:llä käynnistyy ajastin, joka lauettuaan kutsuu
. synkronisen pyynnön lähetys (Sales 2005, 502.)
Asynkronisen pyynnön lähettäminen
yyntöjä käytetään pitkään kestävien toimenpiteiden suorittamiseen
avulla voi esimerkiksi jäädä odottamaan, kunnes tieto on vastaanotettu prosessoria
hitaammalta laitteelta. Kun asynkronista pyyntöä suoritetaan, järjestelmä on vapaana su
muita tehtäviä. Kernel pitää kutsuvaa säiettä lukittuna ainoastaan niin kauan,
tallennettu kernel-säikeelle. Sen jälkeen molempien säikeiden toiminta
jatkuu samanaikaisesti. Ilmoitus asynkronisen toimenpiteen valmistumisesta lähetetään
TRequestStatus-olion tilamuutoksella. (Sales 2005, 502
kuvaa asynkronisen pyynnön käsittelyä. Käyttäjätasolta kutsutaan asynkronista
metodia. Suoritus ohjataan RBusLogicalChannel::DoRequest
hyksen kautta LDD:n HandleMsg-metodia. HandleMsg saa parame
rinä negatiivisen ID:n, jolloin suoritus ohjataan DoRequest-metodille, joka kutsuu PDD:n
metodia. PDD:llä käynnistyy ajastin, joka lauettuaan kutsuu
23
den suorittamiseen. Pyyn-
tieto on vastaanotettu prosessoria
järjestelmä on vapaana suo-
lukittuna ainoastaan niin kauan,
Sen jälkeen molempien säikeiden toiminta
valmistumisesta lähetetään
(Sales 2005, 502.)
täjätasolta kutsutaan asynkronista
metodia. Suoritus ohjataan RBusLogicalChannel::DoRequest-metodille, joka
metodia. HandleMsg saa paramet-
metodille, joka kutsuu PDD:n
metodia. PDD:llä käynnistyy ajastin, joka lauettuaan kutsuu LDD:n SendHelloDfc-
metodia. Kun pyyntö on käsitelty, laukaistaan
pyynnön valmistumisesta
KUVIO 17. Asynkronisen pyynnön käsittely
4.6.8 Asynkronisen pyynnön keskeyttäminen
Jos käyttäjä keskeyttää
dleMsg-metodille ID:n, jonka arvo on
kuvion 18 mukaisesti
jonossa odottavat operaatiot
pyyntö on käsitelty, laukaistaan Dfc, joka lähettää
pyynnön valmistumisesta RequestComplete-metodin avulla. (Sales
synkronisen pyynnön käsittely (Sales 2005, 502.)
Asynkronisen pyynnön keskeyttäminen
keskeyttää odottavan asynkronisen pyynnön, ohjelmistoke
metodille ID:n, jonka arvo on KMaxTint. HandleMsg-funktio
mukaisesti. Metodi ohjaa pyynnön DoCancel-metodille, joka
odottavat operaatiot, valmistelee resurssit peruutettavaksi ja keskeyttää
24
, joka lähettää käyttäjätasolle tiedon
Sales 2005, 502.)
ohjelmistokehys lähettää Han-
funktio käsittelee viestin
metodille, joka peruuttaa viesti-
peruutettavaksi ja keskeyttää kaikki
25
odottavat DFC:t ja ajastimet. Esimerkki DoCancel-metodista löytyy kuviosta 19. (Sales
2005, 543.)
KUVIO 18. ECloseMsg:n toteuttaminen HandleMsg-metodissa (Sales 2005, 543.)
KUVIO 19. DoCancel-metodi (Sales 2005, 543.)
4.6.9 Laiteajurin sulkeminen
Kun käyttäjäohjelma on lähettänyt laiteajurille keskeytyspyynnön, ohjelmistokehys käsit-
telee viestin ja alkaa sulkea tarvittavia palveluita, jotta kanava voidaan sulkea lopullisesti.
Laiteajurin sulkemisen näkee kuviosta 20. Ohjelmistokehys suljetaan Stop-metodilla ja
avoinna olevat pyynnöt suoritetaan loppuun KErrAbort-parametrillä. (Sales 2005, 543.)
...
if (id==(TInt)ECloseMsg)
{
// Channel Close
DoCancel(RExample::EAllRequests);
m.Complete(KErrNone, EFalse);
return;
void DExampleLdd::DoCancel()
{
if(iReceiveHelloStatus)
{
iPdd->ReceiveCancel();
iReceiveHelloDfc.Cancel();
iReceiveHelloDescriptor = NULL;
26
KUVIO 20. Laiteajurin sulkeminen (Sales 2005, 543.)
DSimpleSerialPDD::Shutdown( )
{
if( iStatus == EActive )
{
Stop;
}
Complete(EAll, KErrAbort);
iDfc.Cancel();
iTimer.Cancel();
27
5 ESIMERKKILAITEAJURI
5.1 PDD-factory
Liitteessä 1 on Pdd-factoryn lähdekoodi. Pdd-factoryn tulee ylimääritellä DPhysicalDe-
vice-luokasta perityt puhtaat virtuaalifunktiot. Create-funktion avulla luodaan instanssi
fyysisestä kanavasta, Install-funktio toimii toisen vaiheen konstruktorina. PDD-factoryn
GetCaps-funktio palauttaa ajurin suoritusoikeudet ja Validate-funktio tarkastaa, sopiiko
PDD yhteen LDD:n kanssa.
5.2 PDD
Fyysisen kanavan lähdekoodi on liitteessä 2. Fyysisen kanavan tulee toteuttaa rajapinnat
fyysiselle laitteelle sekä loogiselle kanavalle. Käyttäjätasolta ei ole suoraan pääsyä fyysi-
seen kanavaan, vaan kommunikointi tapahtuu LDD:n rajapinnan läpi. Fyysisellä kanaval-
la ei ole erityistä kantaluokkaa, josta se tulisi periyttää, vaan se periytetään DBase-
luokasta.
Esimerkkilaiteajurissa ei käytetä fyysistä laitetta, vaan sen tarkoitus on havainnollistaa,
miten laiteajuriohjelmistokehystä käytetään LDD:n ja PDD:n väliseen kommunikointiin.
Koska sekä PDD että LDD toimivat samassa kernel-säikeessä, kommunikointi tapahtuu
suorien funktiokutsujen avulla.
Esimerkkilaiteajurissa on toteutettu synkroniset funktiot SetOn ja SetOff. Nämä funktiot
muuttavat fyysisen kanavan sisältämän TBool-muuttujan arvo ja muuttujan tila voidaan
tarkastaa GetState-funktion avulla.
Asynkronisesta funktiosta esimerkkinä ovat Send- ja Receive-funktiot. Esimerkkiajuri
mallintaa suuren datamäärän tallentamista NTimer-ajastimen avulla. NTimer alustetaan
PDD:n konstruktorissa, ja ajastin on käynnissä niin kauan, kun laiteajuri suljetaan. Kun
tietoa lähetetään tai vastaanotetaan, SendHello tai ReceiveHello käynnistää ajastimen,
joka tietyn ajan kuluttua laukaisee staattisen SendTimerCallback-funktion avulla ei staat-
tisen SendCallback-funktion, joka puolestaan kertoo LDD:lle toiminnon valmistumisesta.
28
5.3 LDD-factory
LDD-factoryn lähdekoodi on liitteessä 3. LDD-factoryluokan kantaluokka on DLogical-
Device, ja LDD-factory on hyvin samankaltainen kuin PDD-factory. LDD-factoryn avulla
loogisesta kanavasta saadaan kahva, joka palautetaan käyttäjätasolle. LDD-factory-
jäsenmuuttujien avulla määritellään esimerkkilaiteajurin ohjelmaversio. LDD-factoryn
tietoja käytetään RDevice-luokan avulla.
DLogicalDevice esittelee kolme puhdasta virtuaalifunktiota ja muutaman jäsenmuuttujan,
jotka pitää toteuttaa LDD-factoryluokassa. Create-funktio luo instanssin fyysisestä kana-
vasta ja Install-funktio toimii toisen vaiheen konstruktorina, ja siinä annetaan laiteaju-
rioliolle nimi, jonka avulla laiteajuriin myöhemmin viitataan.
5.4 LDD
Loogisen kanavan lähdekoodi on liitteessä 4. Kun laiteajuri on luotu LDD-factoryn avul-
la, laiteajuriohjelmistokehyksen suoritus jatkuu LDD:n konstruktorista. Konstruktori alus-
taa säikeen DFC-jonot ja tallentaa säikeen osoittimen myöhempää käyttöä varten. Kon-
struktorista ohjelman suoritus siirtyy DoCreate-metodiin.
5.4.1 DoCreate-metodi
DoCreate on loogisen kanavan toisen vaiheen konstruktori. DoCreate-metodin tehtävänä
on tarkastaa, että kutsuvalla ohjelmalla on tarvittavat oikeudet käyttää ajuria. Metodi tar-
kastaa myös, onko laiteajurin versio yhteensopiva käyttäjäohjelman pyytämän version
kanssa.
Laiteajuriohjelmistokehys vaatii, että DoCreate-metodissa asetetaan laiteajurin käyttämä
DFC-jono kutsumalla SetDfcQ-funktiota. SetDfcQ-funktio asettaa ohjelmistokehyksen
29
tarjoaman DFC:n osoittamaan haluttuun DFC-jonoon ja luo samalla yhteyden viestijonon
ja DFC-jonon välille. SetDfcQ-funktion jälkeen kutsutaan TMessageQue::Receive-
funktiota, jolloin viestijono alkaa vastaanottaa viestejä. Kun viesti tulee saataville, lai-
teajuriohjelmakehys asettaa sen DFC-jonoon ja viestin suoritus alkaa HandleMsg-
metodissa.
5.4.2 HandleMsg-metodi
Esimerkkiajurissa HandleMsg-metodi tutkii TThreadMessage-olion iValue-muuttujan
arvoa, joka voi olla tyyppiä ECloseMsg, KMaxTInt, negatiivinen tai edellisen negaatio eli
nolla tai positiivinen. ECloseMsg kertoo, että viestijonoa ollaan sulkemassa, ja KMaxTInt
kertoo, että odottava asynkroninen viesti halutaan peruuttaa. Jos ID ei ole kumpikaan
näistä, tiedetään, että käyttäjätaso on lähettämässä laitteelle viestiä. Nolla tai positiivinen
arvo tarkoittaa synkronista ja negatiivinen asynkronista viestiä. HandleMsg-funktio ohjaa
viestit ID:n perusteella DoControl-, DoCreate-, DoRequest- tai DoCancel-metodille. Me-
todit vastaavat nimeämiseltään käyttäjätason RBusLogicalChannel-luokan määrittelemiä
puhtaita virtuaalimetodeja.
5.4.3 DoControl-metodi
DoControl-metodissa käsitellään laiteajurille tulleet synkroniset pyynnöt. Synkroninen
metodi pystyy käsittelemään kolme parametria, aFunction, a1 ja a2. AFunction sisältää
RExample-luokassa luetteloidun ID-numeron, jonka perusteella päätetään, miten ohjel-
man suoritusta jatketaan. A1- ja a2-parametreissä lähetetään käyttäjätasolta tietoa kernel-
tasolle.
5.4.4 Synkronisen viestin lähettäminen kernel-säikeelle
Esimerkkilaiteajurin LDD määrittelee kolme metodia, joissa käsitellään synkronisia pyyn-
töjä. SetOn- ja SetOff-metodin suoritus siirretään PDD:lle, jossa ne muuttavat iState-
muuttujan arvoa. Laitteen tilaa voi kysyä GetState-funktion avulla. Tämä funktio kopioi
PDD:n iState-muuttujan arvon käyttäjäsäikeelle. Kun synkronista pyyntöä suoritetaan,
30
koko järjestelmä on lukittuna. Kun synkroninen pyyntö on käsitelty, järjestelmän lukitus
avataan ja käyttäjäohjelman suoritus jatkuu.
5.4.5 DoRequest-metodi
DoRequest-metodissa käsitellään laiteajurille tulevat asynkroniset pyynnöt. Parametreja
asynkronisessa pyynnössä on neljä kappaletta. Ensimmäinen aReqNo sisältää id-
numeron, jonka avulla pyyntö tunnistetaan, toinen sisältää viittauksen kutsuvan säikeen
TRequestStatus-olioon ja parametrien a1 ja a2 avulla siirretään tietoa käyttäjäsäikeestä
kernel-säikeelle. Viestin tunnistus tehdään id-numeron avulla. Esimerkiksi, jos halutaan
lähettää viesti, id on ESendHello ja suoritetaan SendHello-metodi.
5.4.6 Asynkronisen viestin lähettäminen kernel-säikeelle
SendHello-metodi tarkastaa ensin, onko kutsuva säie jo suorituksessa. Jos se on, palaute-
taan virheilmoitus KErrInUse. Muussa tapauksessa luetaan käyttäjäsäikeeltä tiedot kernel-
säikeeseen Kern::ThreadDesRead-metodin avulla. Ensimmäisenä parametrinä metodi
ottaa DThread-tyyppisen olion, jota säilötään iThread-jäsenmuuttujassa. DThread-luokka
välittää kutsuvan säikeen tiedot kernel-säikeelle. Toinen parametri on osoitin säikeen
TThreadMessage-olion a1-muuttujaan, joka tässä tapauksessa on aHello. Tämän deskrip-
torin sisältämä tieto tullaan kopioimaan kernel-säikeen sisältämään muuttujaan. A2-
muuttuja annetaan metodin kolmantena parametrinä ja viimeisenä kohta, mistä tiedon
lukeminen halutaan aloittaa.
LDD lähettää deskriptorin PDD:lle, joka tallentaa sen omaan iSendHelloBuffer-
jäsenmuuttujaansa. Lähetystä hidastetaan keinotekoisesti nanokernel-ajastimen avulla,
jotta asynkroninen toiminta tulee paremmin esille. Kun lähetys on valmis, PDD suorittaa
callback-funktion, joka kutsuu LDD:n SendHelloComplete-funktiota. SendHelloComple-
te tallentaa mahdollisen virheilmoituksen ja asettaa viestin DFC-jonoon. Kun DFC on
suoritettu, DFC-jono kutsuu SendHelloDfc-metodia, joka taas ohjaa pyynnön DoSend-
HelloComplete-metodille. Metodi kertoo käyttäjäsäikeelle pyynnön valmistumisesta
Kern::RequestComplete-metodilla, joka ottaa parametrikseen säikeen tunnisteen iThread,
pyynnön tilan iSendHelloStatus ja virheviestin result.
31
5.4.7 Asynkronisen viestin vastaanottaminen kernel-säikeeltä
Jos halutaan vastaanottaa viesti laiteajurilta, id on EReceiveHello, jolloin LDD:llä suori-
tetaan ReceiveHello-metodi, jolle annetaan parametriksi aStatus ja a1. Jotta viesti voidaan
vastaanottaa, on tarkastettava, että kutsuvalla säikeellä on oikeudet vastaanottaa viesti. Jos
oikeudet löytyvät, kutsutaan ReceiveHello-metodia ja annetaan sille parametrit aStatus ja
a1.
ReceiveHello-metodin suoritus alkaa tarkastamalla onko kyseinen toimenpide jo mahdol-
lisesti käynnissä. Toimenpiteen ollessa käynnissä palautetaan virheilmoitus. Muuten suo-
ritetaan PDD:llä Receive-metodi, joka suorittaa callback-funktion avulla LDD:n SendHel-
loComplete-metodin. Metodi tallentaa mahdollisen virheilmoituksen ja lisää viestin DFC-
jonoon. Kun jono on ajettu, kutsutaan DFC-funktiota ReceiveHelloDfc, joka kutsuu edel-
leen DoReceiveHelloComplete-Metodia. Tässä metodissa kirjoitetaan deskriptori kernel-
säikeeltä käyttäjäsäikeelle Kern::ThreadDesWrite-funktion avulla. Parametrinä annetaan
säikeen tunnus iThread, käyttäjäsäikeen deskriptorin osoitin iReceiveHelloDescriptor ja
kernel-säikeen iReceiveHelloBuffer, joka sisältää kopioitavat tiedot. Lopuksi kutsutaan
Kern::RequestComplete, joka ottaa parametrinä säikeen tunnuksen iThread, viestin sta-
tuksen iReceiveHelloStatus ja mahdollisen virheilmoituksen result. RequestComplete
kertoo käyttäjäsäikeelle, että pyydetty toiminto on suoritettu.
5.5 Käyttäjätason ohjelma
Käyttäjätason esimerkkiohjelma on liitteessä 5. Ohjelman suoritus alkaa lataamalla lai-
teajurit käyttöön. Jos laiteajuri ei käytä PDD:tä, sitä ei tarvitse ladata. Jos ohjelman suori-
tus päättyy virheeseen, lopetetaan ohjelman suoritus ja palautetaan virheviesti.
5.5.1 Open-metodi
Seuraavaksi avataan kahva käyttäjätasolta kernel-tason loogiseen kanavaan. Open-metodi
on wrapper-funktio, joka kutsuu RBusLogicalChannel-luokalta periytyvää DoCreate-
32
metodia, joka avaa kanavan kernel-säikeeseen. Vastaavasti muitakin käyttäjätason pyyn-
töjä varten on tehty omat wrapperit, jotka helpottavat laiteajurin käyttöä.
5.5.2 Synkroniset metodit
Synkronisia wrapper-funktioita ovat SetOn, SetOff ja GetState. Kaikki näiden funktioiden
pyynnöt kutsuvat RBusLogicalChannel-luokalta periytyvää DoControl-metodia. DoCont-
rol lähettää säikeen tiedot parametreissä. DoControl-metodi ei tarvitse id-numeron lisäksi
välttämättä muita parametrejä.
5.5.3 Asynkroniset metodit
Asynkronisia pyyntöjä kutsutaan metodien SendHello, SendHelloCancel, ReceiveHello ja
ReceiveHelloCancel avulla. Kaikki funktiot kutsuvat RBusLogicalChannel-luokalta pe-
riytyvää DoRequest-metodia. DoRequest lähettää viestit kernel-tasolle TThreadMessage-
olioina ja pakollisina parametreinä pitää antaa id- ja TRequestStatus-olio. Yleensä asyn-
kronisen pyynnön mukana lähetetään myös tietorakenne, johon metodi tallettaa palautetut
tiedot.
5.5.4 Laiteajurin sulkeminen
Lopussa laiteajuri suljetaan User::FreeLogicalDevice- ja User::FreePhysicalDevice-
metodilla. Tämän metodin kutsumisen jälkeen laiteajuriohjelmistokehys alkaa sulkea lai-
teajuria. Kun kaikki odottavat pyynnöt on suoritettu, laiteajuri voidaan sulkea.
33
6 JOHTOPÄÄTÖKSET
Opinnäytetyön tuloksena syntyi laiteajuriohjelmistokehyksen ehdoilla rakennettu esi-
merkkiohjelma. Opinnäytetyön toteutuksessa oli hankalinta lähteiden vähäisyys ja se, että
suureen osaan materiaalista pääsee käsiksi ainoastaan rekisteröityneenä Symbian-
kehittäjänä. Osassa lähteitä onkin jouduttu käyttämään vanhaa, avointa EKA1-
dokumentaatiota, jonka ohjelmistorajapinnat eroavat suuresti EKA2:sta, joten lähteiden
paikkansapitävyyteen oli kiinnitettävä erityistä huomiota. EKA2-kernel laiteajureista ei
löytynyt kuin yksi avoin lähdeteos, jota onkin käytetty opinnäytetyössä todella paljon.
Laiteajuriohjelmistokehys käyttää hyvin laajalla skaalalla erilaisia ohjelmointimenetelmiä
inline-funktioista puhtaisiin virtuaalifunktioihin. Lisäksi laiteajuriohjelmistokehys toimii
ohjelmoinnin kannalta katsoen hyvin matalalla tasolla, ja työn ymmärtäminen vaati mm.
Symbian OS:n keskeytysten ja säikeiden toiminnan ymmärtämisen. Vaikka koulu tarjo-
aakin perusteet ohjelmointiin, opinnäytetyön tekemisen taustalla oli valtava määrä asioita,
jotka piti opetella ja ymmärtää, ennen kuin laiteajuriohjelmistokehyksen toiminta alkoi
selvitä.
Esimerkkilaiteajurin toteutuksessa on pyritty mahdollisimman yksinkertaiseen toteutuk-
seen ja laiteajurin suunnittelussa on keskitytty käyttämään suositeltua Symbian-
ohjelmointitapaa. Opinnäytetyö oli todella haastava, ja se kehitti tekijänsä ohjelmointitai-
toja ja antoi näkemystä ohjelmistokehittäjän työnkuvasta. Opinnäytetyön tekeminen oli
mielenkiintoinen ja haastava projekti.
34
LÄHTEET Sales, Jane 2005, Symbian Symbian OS Internals: Real-time Kernel Programming: John Wiley & Sons Ltd. Symbian Ltd, Device driver quide, www-dokumentti. Saatavissa: http://www.symbian.com/Developer/techlib/v70docs/SDL_v7.0/doc_source/BasePorting/DeviceDrivers/. Luettu 23.6.2008 Symbian Ltd 2, Developer Library, www-dokumentti. Saatavissa: http://www.symbian.com/Developer/techlib/v70sdocs/_index/index.html. Luettu 25.6.2008 Texas Instruments Ltd, OMAP-alustan dokumentaatio, www-dokumentti. Saatavissa: http://www.omap.com. Luettu 20.6.2008
PDD-factory LIITE 1/1
/** * PDD factory */ #ifndef __EXAMPLE_PDD_FACTORY_H__ #define __EXAMPLE_PDD_FACTORY_H__ #include <kern_priv.h> _LIT(KExamplePddName,"EXAMPLE.template"); class DExamplePddFactory : public DPhysicalDevice { public: enum TMinimumLDDVersion { EMinimumLddMajorVersion=1, EMinimumLddMinorVersion=0, EMinimumLddBuild=0 //Not used }; public: DExamplePddFactory(); public: // from DPhysicalDevice virtual TInt Create(DBase*& aChannel, TInt aUni t, const TDesC8* aInfo, const TVersion& aVer); virtual TInt Install(); virtual void GetCaps(TDes8& aDes) const; virtual TInt Validate(TInt aUnit, const TDesC8* aInfo, const TVersion& aVer); }; #endif /*__EXAMPLE_PDD_FACTORY_H__ */ // End of file
PDD-factory LIITE 1/2
/* * PDD factory */ //INCLUDES #include "example_pdd_factory.h" #include "example_pdd.h" #include "example_device.h" //------------------------------------------------- ----------- // PDD Standard DLL export function Creates a DPh ysicalDe-vice derived object, DExamplePddFactory //------------------------------------------------- ----------- // DECLARE_STANDARD_PDD() { return new DExamplePddFactory; } //------------------------------------------------- ----------- // Constructor //------------------------------------------------- ----------- // DExamplePddFactory::DExamplePddFactory() { // Set version number for this device iVersion=RExample::VersionRequired(); } //------------------------------------------------- ----------- // TInt DExamplePddFactory::Install() // Second stage constructor Install() // Sets a name to the device driver. Other device driver initializations may allso be done here //------------------------------------------------- ----------- // TInt DExamplePddFactory::Install() { return SetName(&KExamplePddName); } //------------------------------------------------- ----------- // void DExamplePddFactory::GetCaps(TDes8& aDes) const
PDD-factory LIITE 1/3
// Returns device driver capabilities. //------------------------------------------------- ----------- // void DExamplePddFactory::GetCaps(TDes8& aDes) const { // Create a capabilities object DExamplePdd::TCaps caps; caps.iVersion = iVersion; // Zero the buffer TInt maxLen = aDes.MaxLength(); aDes.FillZ(maxLen); // Copy cpabilities TInt size=sizeof(caps); if(size>maxLen) size=maxLen; aDes.Copy((TUint8*)&caps,size); } //------------------------------------------------- ----------- // TInt DExamplePddFactory::Create(DBase*& aChann el, TInt aUnit, const TDesC8* aInfo, const TVersion& aVer) // Creates physical channel instance and returns i t to LDD // //------------------------------------------------- ----------- // TInt DExamplePddFactory::Create(DBase*& aChannel, T Int /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/) { //We don't need these parameters //(void)aUnit; //(void)aInfo; //(void)aVer; // Create a new physical channel DExamplePdd* channel=new DExamplePdd; aChannel=channel; if (!channel) return KErrNoMemory; return channel->DoCreate(); } //------------------------------------------------- ----------- // TInt DExamplePddFactory::Validate(TInt aUnit, const TDesC8* aInfo, const TVersion& aVer) // Checks if pdd is compatible with ldd
PDD-factory LIITE 1/4
//------------------------------------------------- ----------- // TInt DExamplePddFactory::Validate(TInt aUnit, const TDesC8* /*aInfo*/, const TVersion& aVer) { if ((!Kern::QueryVersionSupported(iVersion,aVer )) || (!Kern::QueryVersionSupported(aVer,TVersion(EMinimu mLddMajorVersion,EMinimumLddMinorVersion,EMinimumLddBuild))) ) return KErrNotSupported; // aInfo is not needed //(void)aInfo; return KErrNone; } //End of file
PDD LIITE 2/1
/** * Physical channel */ #ifndef __EXAMPLE_PDD_H__ #define __EXAMPLE_PDD_H__ #include <kern_priv.h> #include "example_pdd_factory.h" #include "example_ldd.h" class DExamplePdd : public DBase { public: class TCaps { public: TVersion iVersion; }; public: DExamplePdd(); ~DExamplePdd(); TInt DoCreate(); public: //Interface communicating with LDD virtual TInt SetOn() ; virtual TInt SetOff() ; virtual TInt GetState() const; virtual TInt Send(const TDes8& aHello); virtual void SendCancel(); virtual TInt Receive(TDes8& aHello); virtual void ReceiveCancel(); private: static void SendTimerCallback(TAny* aPtr); void SendCallback(); static void ReceiveTimerCallback(TAny* aPtr); void ReceiveCallback(); private: TInt iState; TInt iTimerDelay; NTimer iSendTimer; NTimer iReceiveTimer; TBuf8<256> iSendBuffer; TDes8* iReceiveBuffer; public: DExampleLdd* iLdd; }; #endif /*__EXAMPLE_PDD_H__ */
PDD LIITE 2/2
/* * Physical Channel */ //INCLUDES #include "example_pdd.h" #include "example_device.h" //------------------------------------------------- ----------- // Default constructor //------------------------------------------------- ----------- // DExamplePdd::DExamplePdd() : iState(RExample::EOff), iTimerDelay(100000), iSendTimer(SendTimerCallback, this), iReceiveTimer(ReceiveTimerCallback, this) { // Do Nothing } //------------------------------------------------- ----------- // Destructor //------------------------------------------------- ----------- // DExamplePdd::~DExamplePdd() { // Do Nothing } //------------------------------------------------- ----------- // Demanded by device driver interface // Must return KErrNone //------------------------------------------------- ----------- // TInt DExamplePdd::DoCreate() { return KErrNone; } //------------------------------------------------- ----------- // TInt DExamplePdd::SetOn() // This sets device on
PDD LIITE 2/3
//------------------------------------------------- ----------- // TInt DExamplePdd::SetOn() { iState = 1; return KErrNone; } //------------------------------------------------- ----------- // TInt DExamplePdd::SetOff() // This sets device Off //------------------------------------------------- -----------// TInt DExamplePdd::SetOff() { iState = 0; return KErrNone; } //------------------------------------------------- ----------- // TInt DExamplePdd::IsOn() // This returns value indicating if device is on o r of //------------------------------------------------- -----------// TInt DExamplePdd::GetState() const { return iState; } //------------------------------------------------- ----------- // TInt DExamplePdd::Send(const TDes8& aHello) // This sets iBuffer and starts iSendTimer. Timer will wait iTimerDelay amount of NTimer tics //------------------------------------------------- ----------- // TInt DExamplePdd::Send(const TDes8& aHello) { iSendBuffer = aHello.Right(iSendBuffer.MaxSize( )); iSendTimer.OneShot(iTimerDelay); return KErrNone; } //------------------------------------------------- ----------- // void DDriver1Device::SendCancel() // Cancel pending operation
PDD LIITE 2/4
//------------------------------------------------- ----------- // void DExamplePdd::SendCancel() { iSendTimer.Cancel(); } //------------------------------------------------- ----------- // TInt DExamplePdd::Receive(TDes8& aHello) // This sets iBuffer and starts iReceiveTimer. Tim er will wait iTimerDelay amount of NTimer tics //------------------------------------------------- ----------- // TInt DExamplePdd::Receive(TDes8& aHello) { //set PDD iReceiveBuffer reference to LDD iRece iveBuffer iReceiveBuffer = &aHello; iReceiveTimer.OneShot(iTimerDelay); return KErrNone; } //------------------------------------------------- ------------------------------------------------------------ - // void DDriver1Device::ReceiveCancel() // Cancel pending operaiton //------------------------------------------------- ----------- // void DExamplePdd::ReceiveCancel() { iReceiveTimer.Cancel(); } //------------------------------------------------- ----------- // void SendTimerCallback(TAny* aPtr) // This Calls non static function SendCallback //------------------------------------------------- -----------// void DExamplePdd::SendTimerCallback(TAny* aPtr) { ((DExamplePdd*)aPtr)->SendCallback(); } //------------------------------------------------- ----------- // void DExamplePdd::SendCallback() // This Calls LDD->SendCallback
PDD LIITE 2/5
//------------------------------------------------- ----------- // void DExamplePdd::SendCallback() { iLdd->SendHelloComplete(KErrNone); } //------------------------------------------------- ----------- // void DExamplePdd::ReceiveTimerCallback(TAny* aP tr) // This Calls Non-Static function ReceiveCallback //------------------------------------------------- ----------- // void DExamplePdd::ReceiveTimerCallback(TAny* aPtr) { ((DExamplePdd*)aPtr)->ReceiveCallback(); } //------------------------------------------------- ----------- // void DExamplePdd::ReceiveCallback() // This calls LDD->ReceiveComplete //------------------------------------------------- -----------// void DExamplePdd::ReceiveCallback() { iLdd->ReceiveHelloComplete(KErrNone); } //End of file
LDD-factory LIITE 3/1
/** * LDD factory */ #ifndef __EXAMPLE_LDD_FACTORY_H__ #define __EXAMPLE_LDD_FACTORY_H__ #include <kern_priv.h> // INCLUDES class DExampleLddFactory : public DLogicalDevice { public: DExampleLddFactory(); ~DExampleLddFactory(); public: //From DLogicalDevice virtual TInt Install(); virtual void GetCaps(TDes8& aDes) const; virtual TInt Create(DLogicalChannelBase*& aChan nel); }; #endif /*__EXAMPLE_LDD_FACTORY_H__ */ // End of file
LDD-factory LIITE 3/2
/* * The LDD factory is an instance of a class derived from DLogicalDevice. */ //The class provides a framework in the standard wa y, where some of the //functions are virtual and can be overridden, and others are pure //virtual and an implementation is obligatory. In a ddition, a derived //class can add data members and functions, includi ng C++ constructors //and a destructor. // // #include "example_ldd_factory.h" #include "example_ldd.h" #include "example_device.h" _LIT(KPanicCategory,"example"); // // DExampleLddFactory // //------------------------------------------------- ----------- // PDD Standard DLL export function Creates a DLo gicalDe-vice derived object, DExampleLddFactory //------------------------------------------------- ----------- // DECLARE_STANDARD_LDD() { return new DExampleLddFactory; } //------------------------------------------------- ----------- // Constructor // initialize iVersion and iParseMask here //------------------------------------------------- ----------- // DExampleLddFactory::DExampleLddFactory()
LDD-factory LIITE 3/3
{ // Set version number for this device iVersion=RExample::VersionRequired(); iParseMask=KDeviceAllowPhysicalDevice; } //------------------------------------------------- ----------- // Destructor //------------------------------------------------- ----------- // DExampleLddFactory::~DExampleLddFactory() { } //------------------------------------------------- ----------- // Second stage constructor // Sets name of logical channel instnce // // @return KErrNone if successful, otherwise one o f the other system wide error codes. //------------------------------------------------- ----------- // TInt DExampleLddFactory::Install() { return SetName(&RExample::Name()); } //------------------------------------------------- ----------- // Get drivers capabilities // // @param aDes Descriptor into which capabilities informa-tion is to be written. //------------------------------------------------- ----------- // void DExampleLddFactory::GetCaps(TDes8& aDes) const { // Create a capabilities object RExample::TCaps caps; caps.iVersion = iVersion; // Write it back to user memory Kern::InfoCopy(aDes,(TUint8*)&caps,sizeof(caps) ); }
LDD-factory LIITE 3/4
//------------------------------------------------- ----------- // Creates logical channel object. // // @param aChannel Set to point to the created Log ical Channel // // @return KErrNone if successful, otherwise one o f the other system wide error codes. //------------------------------------------------- -----------// TInt DExampleLddFactory::Create(DLogicalChannelBase *& aChan-nel) { aChannel=new DExampleLdd; if(!aChannel) return KErrNoMemory; return KErrNone; } //End of file
LDD LIITE 4/1
/** * Logical channel */ #ifndef __EXAMPLE_LDD_H__ #define __EXAMPLE_LDD_H__ #include <kern_priv.h> #include "example_ldd_factory.h" //Forward declaration class DExamplePdd; class DExampleLdd : public DLogicalChannel { public: DExampleLdd(); virtual ~DExampleLdd(); public: //From DObject virtual TInt RequestUserHandle(DThread* aThread , TOwner-Type aType); public: // From DLogicalChannelBase TInt DoCreate(TInt aUnit, const TDesC8* aInfo, const TVersion& aVersion); private: //panic reasons enum TPanic { ERequestAlreadyPending = 1 }; private: // PDD synchronous methods TInt GetState(TAny* aValue); TInt SetOn(); TInt SetOff(); private: //PDD asynchronous methods TInt SendHello(TRequestStatus* aStatus,const TD esC8* aHello); void SendHelloCancel(); void DoSendHelloComplete(); static void SendHelloDfc(TAny* aPtr); TInt ReceiveHello(TRequestStatus* aStatus,TDes8 * aBuf-fer); void ReceiveHelloCancel(); void DoReceiveHelloComplete(); static void ReceiveHelloDfc(TAny* aPtr); public: // PDD callback functions virtual void SendHelloComplete(TInt aResult); virtual void ReceiveHelloComplete(TInt aResult) ; private: //From DLogicalChannel void HandleMsg(TMessageBase* aMsg); TInt DoControl(TInt aFunction, TAny* a1, TAny* a2) ;
LDD LIITE 4/2
TInt DoRequest(TInt aReqNo, TRequestStatus* aSt atus, TA-ny* a1, TAny* a2); void DoCancel(TUint aMask); private: inline DExamplePdd* Pdd(); private: DThread* iThread; // Variables to handle SendHello TRequestStatus* iSendHelloStatus; TDfc iSendHelloDfc; TInt iSendHelloResult; TBuf8<256> iSendHelloBuffer; // Variables to handle ReceiveHello TDes8* iReceiveHelloDescriptor; TRequestStatus* iReceiveHelloStatus; TDfc iReceiveHelloDfc; TInt iReceiveHelloResult; TBuf8<256> iReceiveHelloBuffer; }; inline DExamplePdd* DExampleLdd::Pdd() { return (DExamplePdd*) iPdd; } #endif /*__EXAMPLE_LDD_H__ */ // End of File
LDD LIITE 4/3
/* * Logical Channel */ #include "example_device.h" #include "example_ldd.h" #include "example_pdd.h" _LIT(KPanicCategory,"ExampleLdd"); //------------------------------------------------- ----------- // Constructor //------------------------------------------------- ----------- DExampleLdd::DExampleLdd() : iSendHelloDfc(SendHell oDfc, this, 1), //initialize dfc queues iReceiveHelloDfc(ReceiveHelloDfc, this, 1) { iThread = &Kern::CurrentThread(); iThread->Open(); } //------------------------------------------------- ----------- // Destructor //------------------------------------------------- ----------- DExampleLdd::~DExampleLdd() { // Cancel all processing that we may be doing N OT INHE-RITED METHOD... DoCancel(RExample::EAllRequests); // Close our reference on the client thread Kern::SafeClose((DObject*&)iThread,NULL); } //------------------------------------------------- ----------- // TInt DExampleLdd::DoCreate(TInt /*aUnit*/, cons t TDesC8* /*aInfo*/, const TVersion& aVer) // LDD Second stage constructor // Validate capabilities, check version, initializ e any DFC queues // // aUnit and aInfo are not used here //------------------------------------------------- ----------- //
LDD LIITE 4/4
TInt DExampleLdd::DoCreate(TInt /*aUnit*/, const TD esC8* /*aInfo*/, const TVersion& aVer) { if(!Kern::CurrentThreadHasCapability(ECapability_No ne,__PLATSEC_DIAGNOSTIC_STRING("Checked by DRIVER1"))) return KErrPermissionDenied; if (!Kern::QueryVersionSupported(RExample::VersionRequ ired(),aVer)) return KErrNotSupported; SetDfcQ(Kern::DfcQue0()); // Sets DLogicalChann el::iDfcQ iMsgQ.Receive(); iSendHelloDfc.SetDfcQ(iDfcQ); iReceiveHelloDfc.SetDfcQ(iDfcQ); Pdd()->iLdd=this; return KErrNone; } //------------------------------------------------- ----------- // TInt DExampleLdd::RequestUserHandle(DThread* aT hread, TOwnerType aType) // Called when thread requires handle to this chan nel // Only our client can have a handle //------------------------------------------------- -----------// TInt DExampleLdd::RequestUserHandle(DThread* aThrea d, TOw-nerType aType) { if (aType!=EOwnerThread || aThread!=iThread) return KErrAccessDenied; return KErrNone; } //------------------------------------------------- ----------- // void DExampleLdd::HandleMsg(TMessageBase* aMsg) // Process a message // Compares the value of id to DLogicalChannel inh erited enums ECloseMsg and KMaxTInt and // if id is negative or zero/positive. Negative me ans that message is asynchronous DoRequest and
LDD LIITE 4/5
// positive that message is synchronous DoControl //------------------------------------------------- -----------// void DExampleLdd::HandleMsg(TMessageBase* aMsg) { TThreadMessage& m=*(TThreadMessage*)aMsg; TInt id=m.iValue; if (id==(TInt)ECloseMsg) { // Channel Close DoCancel(RExample::EAllRequests); m.Complete(KErrNone, EFalse); return; } if (id==KMaxTInt) { // DoCancel DoCancel(m.Int0()); m.Complete(KErrNone,ETrue); return; } if (id<0) { // DoRequest TRequestStatus* pS=(TRequestStatus*)m.Ptr0( ); TInt r=DoRequest(~id,pS,m.Ptr1(),m.Ptr2()); if (r!=KErrNone) Kern::RequestComplete(iThread,pS,r); m.Complete(KErrNone,ETrue); } else { // DoControl TInt err=DoControl(id,m.Ptr0(),m.Ptr1()); m.Complete(err,ETrue); } } //------------------------------------------------- ----------- // TInt DExampleLdd::DoControl(TInt aFunction, TAn y* a1, TAny* a2) // Process synchronous operation // Compares value of aFunction to synchronous enum opera-tions ESetOn, ESetOff and EIsOn //------------------------------------------------- -----------
LDD LIITE 4/6
// TInt DExampleLdd::DoControl(TInt aFunction, TAny* a 1, TAny* /*a2*/) { TInt r = KErrNone; switch (aFunction) { case RExample::ESetOn: r = Pdd()->SetOn(); break; case RExample::ESetOff: r = Pdd()->SetOff(); break; case RExample::EGetState: r = GetState(a1); break; default: r=KErrNotSupported; break; } return r; } //------------------------------------------------- ----------- // TInt DExampleLdd::DoRequest(TInt aReqNo, TReque stStatus* aStatus, TAny* a1, TAny* a2) // Process asynchronous operation // Function compares value of aReqNo to the enum E SendHello and EReceiveHello //------------------------------------------------- ----------- // TInt DExampleLdd::DoRequest(TInt aReqNo, TRequestSt atus* aS-tatus, TAny* a1, TAny* /*a2*/) { TInt r =KErrNone; switch(aReqNo) { case RExample::ESendHello: r=SendHello(aStatus,(const TDesC8*)a1); break;
LDD LIITE 4/7
case RExample::EReceiveHello: // Check for capability ECapability_Non e, that allways passes if(iThread->HasCapabili-ty(ECapability_None,__PLATSEC_DIAGNOSTIC_STRING("Ch ecked by EXAMPLE_LDD"))) r=ReceiveHello(aStatus,(TDes8*)a1); else r=KErrPermissionDenied; break; default: r=KErrNotSupported; break; } return r; } //------------------------------------------------- ----------- // Cancels asyncronous operation // Calls SendHelloCancel or ReceiveHelloCancel dep ending from pending operation //------------------------------------------------- -----------// void DExampleLdd::DoCancel(TUint aMask) { if( aMask&( 1 << RExample::ESendHello ) ) SendHelloCancel(); if( aMask&( 1 << RExample::EReceiveHello ) ) ReceiveHelloCancel(); } TInt DExampleLdd::SetOn() { Pdd()->SetOn(); return KErrNone; } TInt DExampleLdd::SetOff() { Pdd()->SetOff(); return KErrNone; }
LDD LIITE 4/8
//------------------------------------------------- ----------- // TInt DExampleLdd::IsOn() // Check if PDD iOn = 1 and write this value back to user thread // thread, dst, src, size //------------------------------------------------- ----------- // TInt DExampleLdd::GetState(TAny* aValue) { TInt state = Pdd()->GetState(); return Kern::ThreadRawWrite(iThread, aValue, (T A-ny*)state, sizeof(aValue)); } //------------------------------------------------- ----------- // TInt DExampleLdd::SendHello(TRequestStatus* aSt a-tus,const TDesC8* aHello) // Sends hello descriptor from cient thread to ker nel thread //------------------------------------------------- ----------- // TInt DExampleLdd::SendHello(TRequestStatus* aStatus ,const TDesC8* aHello) { // if Thread is currently in use if(iSendHelloStatus) { Kern::ThreadKill(iThread,EExitPanic,ERequestAlready Pending,KPanicCategory); return KErrInUse; } TInt r=Kern::ThreadDesRead(iThread,aHello,iSendHelloBuff er,0); if(r!=KErrNone) return r; Pdd()->Send(iSendHelloBuffer); if(r!=KErrNone) return r; iSendHelloStatus = aStatus; return KErrNone;
LDD LIITE 4/9
} //------------------------------------------------- ----------- // void DExampleLdd::SendHelloCancel() // Cancels pending asynchronous operation //------------------------------------------------- ----------- // void DExampleLdd::SendHelloCancel() { if(iSendHelloStatus) { // Tell PDD to stop processing the request Pdd()->SendCancel(); // Cancel DFC iSendHelloDfc.Cancel(); // Complete clients request Kern::RequestComplete(iThread,iSendHelloStatus,KErr Cancel); } } //------------------------------------------------- ----------- // void DExampleLdd::SendHelloComplete(TInt aResul t) // Triggered from PDD and it signals that sending hello is complete //------------------------------------------------- ----------- // void DExampleLdd::SendHelloComplete(TInt aResult) { // Save result code iSendHelloResult = aResult; // Queue DFC iSendHelloDfc.Add(); // Queues DFC from ISR } //------------------------------------------------- ----------- // void DExampleLdd::SendHelloDfc(TAny* aPtr) // Triggered by DFC. Calls DoSendHelloComplete //------------------------------------------------- ----------- // void DExampleLdd::SendHelloDfc(TAny* aPtr) { ((DExampleLdd*)aPtr)->DoSendHelloComplete(); }
LDD LIITE 4/10
//------------------------------------------------- ----------- // void DExampleLdd::DoSendHelloComplete() // Inform client thread that asynchronous operatio n has completed //------------------------------------------------- ----------- // void DExampleLdd::DoSendHelloComplete() { TInt result = iSendHelloResult; Kern::RequestComplete(iThread,iSendHelloStatus, result); } //------------------------------------------------- ----------- // TInt DExampleLdd::ReceiveHello(TRequestStatus* aSta-tus,TDes8* aPtr) // Receive hello descriptor from kernel thread to user thread //------------------------------------------------- ----------- // TInt DExampleLdd::ReceiveHello(TRequestStatus* aSta -tus,TDes8* aPtr) { // If receive hello is allready in process if(iReceiveHelloStatus) { Kern::ThreadKill(iThread,EExitPanic,ERequestAlready Pending,KPanicCategory); return KErrInUse; } TInt r= Pdd()->Receive(iReceiveHelloBuffer); if(r!=KErrNone) return r; iReceiveHelloStatus = aStatus; iReceiveHelloDescriptor = aPtr; return KErrNone; } //------------------------------------------------- ----------- // void DExampleLdd::ReceiveHelloCancel()
LDD LIITE 4/11
// Cancel pending asynchronous operation //------------------------------------------------- ----------- // void DExampleLdd::ReceiveHelloCancel() { if(iReceiveHelloStatus) { Pdd()->ReceiveCancel(); iReceiveHelloDfc.Cancel(); iReceiveHelloDescriptor = NULL; Kern::RequestComplete(iThread,iReceiveHelloStatus,K ErrCancel); } } //------------------------------------------------- ----------- // void DExampleLdd::ReceiveHelloComplete(TInt aRe sult) // Triggered by PDD from ISR //------------------------------------------------- -----------// void DExampleLdd::ReceiveHelloComplete(TInt aResult ) { iReceiveHelloResult = aResult; iReceiveHelloDfc.Add(); } //------------------------------------------------- ----------- // void DExampleLdd::ReceiveHelloDfc(TAny* aPtr) // Triggered by DFC queue. Calls DoReceiveHelloCom plete //------------------------------------------------- ----------- // void DExampleLdd::ReceiveHelloDfc(TAny* aPtr) { ((DExampleLdd*)aPtr)->DoReceiveHelloComplete(); } //------------------------------------------------- ----------- // void DExampleLdd::DoReceiveHelloComplete() // Copy descriptor from kernel thread to user thre ad // if PDD has failed, send error // Inform client thread that asynchronous operatio n has completed
LDD LIITE 4/12
//------------------------------------------------- ----------- // void DExampleLdd::DoReceiveHelloComplete() { TInt re-sult=Kern::ThreadDesWrite(iThread,iReceiveHelloDesc riptor,iReceiveHelloBuffer,0); iReceiveHelloDescriptor = NULL; if(iReceiveHelloResult!=KErrNone) result = iReceiveHelloResult; Kern::RequestComplete(iThread,iReceiveHelloStatus,r esult); } //End of file
Käyttäjätason ohjelma LIITE 5/1
/*
* User side API
*
*/
//INCLUDES
#ifndef __EXAMPLE_DEVICE_H__
#define __EXAMPLE_DEVICE_H__
#include <e32cmn.h>
#include <e32ver.h>
#ifndef __KERNEL_MODE__
#include <e32std.h>
#endif
class RExample : public RBusLogicalChannel
{
public: // Structure holding version information
class TCaps
{
public:
TVersion iVersion;
};
public: //
TInt Open();
TInt SetOn();
TInt SetOff();
TInt GetState(TInt aState);
void SendHello(TRequestStatus& aStatus,const TD esC8&
aData);
Käyttäjätason ohjelma LIITE 5/2
void SendHelloCancel();
void ReceiveHello(TRequestStatus& aStatus,TDes8 & aBuf-
fer);
void ReceiveHelloCancel();
public: //Inline functions
inline static const TDesC& Name();
inline static TVersion VersionRequired();
public: // enumeration for driver state information
enum TState
{
EOff,
EOn
};
private: //enumeration for sychronous Control messa ges
enum TControl
{
ESetOn,
ESetOff,
EGetState
};
private: //enumeration bitset for asychronous Requ est mes-
sages
enum TRequest
{
ESendHello = 0x0001,
EReceiveHello = 0x0010,
EAllRequests = 0x0011
};
// LDD is a friend
friend class DExampleLdd;
};
Käyttäjätason ohjelma LIITE 5/3
//------------------------------------------------- ---------
--------------------------------------------------- ---------
---------------------------------------------
// inline const TDesC& RExample::Name()
// Returns Device driver name
//------------------------------------------------- ---------
--------------------------------------------------- ---------
---------------------------------------------
//
inline const TDesC& RExample::Name()
{
_LIT(KExampleDriverName,"EXAMPLE");
return KExampleDriverName;
}
//------------------------------------------------- ---------
--------------------------------------------------- ---------
---------------------------------------------
// inline TVersion RExample::VersionRequired()
// returns version number that device driver is re quires
//------------------------------------------------- ---------
--------------------------------------------------- ---------
---------------------------------------------
//
inline TVersion RExample::VersionRequired()
{
const TInt KMajorVersionNumber=1;
const TInt KMinorVersionNumber=0;
const TInt KBuildVersionNumber=KE32BuildVersion Number;
return TVer-
sion(KMajorVersionNumber,KMinorVersionNumber,KBuild VersionNu
mber);
}
Käyttäjätason ohjelma LIITE 5/4
#endif /* __EXAMPLE_DEVICE_H__ */
Käyttäjätason ohjelma LIITE 5/5
/** * User side API */ #include <e32test.h> #include "example_device.h" LOCAL_D RTest test(_L("EXAMPLE_DRIVER_TEST")); _LIT(KExampleLddFileName,"EXAMPLE_LDD"); _LIT(KExamplePddFileName,"EXAMPLE_PDD"); _LIT8(KSendHello,"hello"); GLDEF_C TInt E32Main() { test.Title(); TInt r; test.Start(_L("Loading Physical Device")); r=User::LoadPhysicalDevice(KExamplePddFileName) ; test(r==KErrNone || r==KErrAlreadyExists); test.Next(_L("Loading Logical Device")); r=User::LoadLogicalDevice(KExampleLddFileName); test(r==KErrNone || r==KErrAlreadyExists); test.Next(_L("Open Device")); // Opens handle to user side factory object RDevice device; r=device.Open(RExample::Name()); test(r==KErrNone); //Get driver capabilities test.Next(_L("Get Device Capabilities")); RExample::TCaps caps; TPckg<RExample::TCaps> capsPckg(caps); capsPckg.FillZ(); // Zero 'caps' so we can tell if Get-Caps has really filled it device.GetCaps(capsPckg); TVersion expectedVer(RExample::VersionRequired( )); test(caps.iVersion.iMajor==expectedVer.iMajor); test(caps.iVersion.iMinor==expectedVer.iMinor); test(caps.iVersion.iBuild==expectedVer.iBuild); test.Next(_L("Close Device")); device.Close(); //close RDevice
Käyttäjätason ohjelma LIITE 5/6
//Open Ldd test.Next(_L("Open Logical Channel")); RExample ldd; r=ldd.Open(); test(r==KErrNone); test.Next(_L("Is driver on or off?")); TInt isOn = RExample::EOff; r=ldd.GetState(isOn); test(r==KErrNone); //Turn device on test.Next(_L("Turn device ON")); r=ldd.SetOn(); test(r==KErrNone); test.Next(_L("Did the state change?")); TInt state = RExample::EOff; r=ldd.GetState(state); test(r==KErrNone); //Check if other client can access the client test.Next(_L("Check access by wrong client")); RExample ldd2=ldd; r=ldd2.Duplicate(RThread(),EOwnerProcess); test(r==KErrAccessDenied); //Send hello to LDD test.Next(_L("SendHello")); TRequestStatus status; ldd.SendHello(status,KSendHello); //Cancel transfer test.Next(_L("SendHelloCancel")); ldd.SendHelloCancel(); User::WaitForRequest(status); r=status.Int(); test(r==KErrCancel); //send data again test.Next(_L("SendHello")); ldd.SendHello(status,KSendHello); User::WaitForRequest(status); r=status.Int(); test(r==KErrNone); //get same data back from device driver test.Next(_L("ReceiveHello")); TBuf8<256> buffer; ldd.ReceiveHello(status,buffer);
Käyttäjätason ohjelma LIITE 5/7
test.Next(_L("ReceiveHelloCancel")); ldd.ReceiveHelloCancel(); User::WaitForRequest(status); r=status.Int(); test(r==KErrCancel); test.Next(_L("ReceiveHello")); buffer.FillZ(buffer.MaxLength()); buffer.Zero(); ldd.ReceiveHello(status,buffer); User::WaitForRequest(status); r=status.Int(); test(r==KErrNone); /* TInt expectedSize = config.iPddBufferSize; if(expectedSize>(&KTestSendHello)->Size()) expectedSize = (&KTestSendHello)->Size(); test(buffer.Size()==expectedSize); test(buffer==(&KTestSendHello)->Right(expectedS ize));*/ test.Next(_L("Close Logical Channel")); ldd.Close(); __KHEAP_MARKEND; //Unloading operations test.Next(_L("Unload Logical Device")); r=User::FreeLogicalDevice(RExample::Name()); test(r==KErrNone); test.Next(_L("Unload Physical Device")); TName pddName(RExample::Name()); _LIT(KVariantExtension,".template"); pddName.Append(KVariantExtension); r=User::FreePhysicalDevice(pddName); test(r==KErrNone); test.End(); return(0); } #ifndef __KERNEL_MODE__ //------------------------------------------------- ----------- // TInt RExample::Open() //------------------------------------------------- -----------
Käyttäjätason ohjelma LIITE 5/8
// TInt RExample::Open() { return Do-Create(Name(),VersionRequired(),KNullUnit,NULL,NULL ,EOwnerThread); } //------------------------------------------------- ----------- // TInt RExample::SetOn() //------------------------------------------------- ----------- // TInt RExample::SetOn() { return DoControl(ESetOn); } //------------------------------------------------- ----------- // TInt RExample::SetOff() //------------------------------------------------- -----------// TInt RExample::SetOff() { return DoControl(ESetOff); } //------------------------------------------------- ----------- // TInt RExample::GetState(TInt aState) //------------------------------------------------- ----------- // TInt RExample::GetState(TInt aState) { return DoControl(EGetState,(TAny*)&aState); } //------------------------------------------------- ----------- // void RExample::SendHello(TRequestStatus& aStatu s,const TDesC8& aHello) //------------------------------------------------- ----------- // void RExample::SendHello(TRequestStatus& aStatus,co nst TDesC8& aHello)
Käyttäjätason ohjelma LIITE 5/9
{ DoRequest(ESendHello,aStatus,(TAny*)&aHello); } //------------------------------------------------- ----------- // void RExample::SendHelloCancel() //------------------------------------------------- ----------- // void RExample::SendHelloCancel() { DoCancel(ESendHello); } //------------------------------------------------- ----------- // void RExample::ReceiveHello(TRequestStatus& aSt a-tus,TDes8& aBuffer) //------------------------------------------------- ----------- // void RExample::ReceiveHello(TRequestStatus& aStatus ,TDes8& aBuffer) { DoRequest(EReceiveHello,aStatus,(TAny*)&aBuffer ); } //------------------------------------------------- ----------- // void RExample::ReceiveHelloCancel() //------------------------------------------------- ----------- // void RExample::ReceiveHelloCancel() { DoCancel(EReceiveHello); } #endif /* __KERNEL_MODE__ */ //End of file