ΤΕΧΝΟΛΟΓΙΚΟ ΕΚΠΑΙ∆ΕΥΤΙΚΟ Ι∆ΡΥΜΑ (ΤΕΙ) ∆ΥΤΙΚΗΣ ΜΑΚΕ∆ΟΝΙΑΣ ΠΑΡΑΡΤΗΜΑ ΚΑΣΤΟΡΙΑΣ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ & ΤΕΧΝΟΛΟΓΙΑΣ ΥΠΟΛΟΓΙΣΤΩΝ Υλοποίηση δροµολογητή (Router Implementation) ΠΤΥΧΙΑΚΗ ΕΡΓΑΣΙΑ του ∆ΗΜΗΤΡΙΟΥ Α. ΡΟΥΣΗ (ΑΕΜ: 355) Επιβλέπων : ∆ηµήτριος Φωτιάδης Καθηγητής Εφαρµογών Καστοριά Νοέµβριος 2012 Η παρούσα σελίδα σκοπίµως παραµένει άδεια 2 ΤΕΧΝΟΛΟΓΙΚΟ ΕΚΠΑΙ∆ΕΥΤΙΚΟ Ι∆ΡΥΜΑ (ΤΕΙ) ∆ΥΤΙΚΗΣ ΜΑΚΕ∆ΟΝΙΑΣ ΠΑΡΑΡΤΗΜΑ ΚΑΣΤΟΡΙΑΣ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ & ΤΕΧΝΟΛΟΓΙΑΣ ΥΠΟΛΟΓΙΣΤΩΝ Υλοποίηση δροµολογητή (Router Implementation) ΠΤΥΧΙΑΚΗ ΕΡΓΑΣΙΑ του ∆ΗΜΗΤΡΙΟΥ Α. ΡΟΥΣΗ (ΑΕΜ: 355) Επιβλέπων : ∆ηµήτριος Φωτιάδης Καθηγητής Εφαρµογών Εγκρίθηκε από την τριµελή εξεταστική επιτροπή την: ............................ Ον/µο Μέλος Ε.Π. Ιδιότητα Μέλους Ε.Π. / ............................ Ον/µο Μέλος Ε.Π. Ιδιότητα Μέλους Ε.Π. / ............................ Ον/µο Μέλος Ε.Π. Ιδιότητα Μέλους Ε.Π. Καστοριά Νοέµβριος 2012 3 ROUTER NGW100 Firmware version 1.0.0 MyThesis.org 4 Copyright © 2012 – ∆ηµήτριος Α. Ρούσης Απαγορεύεται η αντιγραφή, αποθήκευση και διανοµή της παρούσας εργασίας, εξ ολοκλήρου ή τµήµατος αυτής, για εµπορικό σκοπό. Επιτρέπεται η ανατύπωση, αποθήκευση και διανοµή για σκοπό µη κερδοσκοπικό, εκπαιδευτικής ή ερευνητικής φύσης, υπό την προϋπόθεση να αναφέρεται η πηγή προέλευσης και να διατηρείται το παρόν µήνυµα. Οι απόψεις και τα συµπεράσµατα που περιέχονται σε αυτό το έγγραφο εκφράζουν αποκλειστικά τον συγγραφέα και δεν αντιπροσωπεύουν τις επίσηµες θέσεις του ΤΕΙ ∆υτικής Μακεδονίας. 5 Σε όσους αγαπούν αυτό που κάνουν 6 Περιεχόµενα Περιεχόµενα ........................................................................................................................................ 7 Ευρετήριο εικόνων ............................................................................................................................ 11 Ευρετήριο πινάκων ........................................................................................................................... 14 Ευχαριστίες ....................................................................................................................................... 15 Περίληψη........................................................................................................................................... 16 Εισαγωγή .......................................................................................................................................... 17 Το προαπαιτούµενο θεωρητικό υπόβαθρο .................................................................................. 17 ∆οµή εργασίας .............................................................................................................................. 18 Θεωρητικό µέρος...................................................................................................................... 18 Πρακτικό µέρος ........................................................................................................................ 19 Παραρτήµατα............................................................................................................................ 20 1ο ΚΕΦΑΛΑΙΟ ...................................................................................................................................... 22 Ανάπτυξη Ενσωµατωµένων Συστηµάτων Linux............................................................................... 22 Εισαγωγή ...................................................................................................................................... 22 1.1 Τι είναι το Linux....................................................................................................................... 23 1.2 Το ενσωµατωµένο Linux......................................................................................................... 23 1.3 Οι τύποι των ενσωµατωµένων συστηµάτων Linux................................................................. 24 1.4 Γιατί να προτιµήσουµε το Linux .............................................................................................. 25 1.5 Έτοιµες διανοµές και εργαλεία ανάπτυξης ............................................................................. 27 1.6 Μεθοδολογία σχεδίασης και ανάπτυξης ................................................................................. 28 1.7 Εργαλεία ανάπτυξης και αποσφαλµάτωσης........................................................................... 30 1.8 Οι τύποι ανάπτυξης ................................................................................................................ 31 1.9 Οι τύποι αποσφαλµάτωσης .................................................................................................... 33 1.10 Η αρχιτεκτονική ενός ενσωµατωµένου συστήµατος Linux ................................................... 34 1.11 Εκκίνηση συστήµατος........................................................................................................... 36 1.11.1 Τύποι εκκίνησης ............................................................................................................ 37 Εκκίνηση από µνήµη στερεάς κατάστασης.......................................................................... 38 Εκκίνηση από σκληρό δίσκο ................................................................................................ 39 Εκκίνηση µέσω δικτύου........................................................................................................ 39 1.12 Μονάδα διαχείρισης µνήµης (MMU) ..................................................................................... 39 1.13 ∆ιαχείριση δευτερεύουσας µνήµης ....................................................................................... 41 2ο ΚΕΦΑΛΑΙΟ ...................................................................................................................................... 43 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux ............................................................. 43 Εισαγωγή ...................................................................................................................................... 43 2.1 Έτοιµες αλυσίδες εργαλείων ανάπτυξης ................................................................................ 43 2.2 Πακέτο υποστήριξης συστήµατος........................................................................................... 44 2.3 Αλυσίδα εργαλείων µεταγλώττισης......................................................................................... 44 2.4 ∆ηµιουργία της αλυσίδας µεταγλώττισης cross ...................................................................... 45 2.4.1 Binutils ............................................................................................................................. 45 2.4.2 Οι κεφαλίδες του Linux kernel ......................................................................................... 46 2.4.3 Η βιβλιοθήκη C ................................................................................................................ 46 2.4.4 Η βιβλιοθήκη νηµάτων .................................................................................................... 47 2.4.5 Εκδόσεις πακέτων λογισµικού και διαχείριση ................................................................. 48 2.4.6 Επισκόπηση δηµιουργίας της αλυσίδας cross ................................................................ 50 2.5 Αυτοµατοποιηµένα συστήµατα ............................................................................................... 50 2.5.1 Εναλλακτικές βιβλιοθήκες C ............................................................................................ 51 2.5.2 Το αυτοµατοποιηµένο σύστηµα Buildroot ....................................................................... 51 2.6 Παραµετροποίηση uClibc, kernel και BusyBox µέσω Buildroot ............................................. 60 2.7 Γραµµή εντολών ..................................................................................................................... 64 2.8 Βασικές εντολές φλοιού .......................................................................................................... 64 2.9 Προσοµοιωτές τερµατικών ..................................................................................................... 66 2.9.1 Ο προσοµοιωτής τερµατικού C-Kermit ........................................................................... 67 7 2.9.2 Ο προσοµοιωτής τερµατικού PuTTY .............................................................................. 69 2.10 IDEs για Linux....................................................................................................................... 71 2.10.1 Το IDE AVR32 Studio.................................................................................................... 71 3ο ΚΕΦΑΛΑΙΟ ...................................................................................................................................... 73 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux.......................................................................... 73 Εισαγωγή ...................................................................................................................................... 73 3.1 Αρχιτεκτονικές µικροελεγκτών ................................................................................................ 74 3.2 Οδηγοί συσκευών και Linux Kernel ........................................................................................ 76 3.2.1 Linux kernel ..................................................................................................................... 79 3.2.2 Modules ........................................................................................................................... 80 3.3 Παράδειγµα ανάπτυξης module ............................................................................................. 81 3.4 ∆ίαυλοι και διεπαφές............................................................................................................... 83 3.4.1 USB ................................................................................................................................. 83 3.4.2 I2C Linux Framework ...................................................................................................... 85 Ο ενσωµατωµένος Linux driver για το I2C........................................................................... 85 Παραµετροποίηση του Kernel.............................................................................................. 86 I2C µέσω GPIO .................................................................................................................... 87 Εγκατάσταση των I2C modules ........................................................................................... 89 Χρήση της διεπαφής I2C...................................................................................................... 90 Ανάπτυξη I2C Linux driver ................................................................................................... 93 3.4.3 SPI................................................................................................................................... 93 3.5 Είσοδοι και Έξοδοι (I/Os)........................................................................................................ 99 3.5.1 Το υποσύστηµα tty ........................................................................................................ 101 3.5.2 RS-232 .......................................................................................................................... 107 3.5.3 GPIO.............................................................................................................................. 107 Χρήση της διεπαφής /dev .................................................................................................. 109 Είσοδος και έξοδος σηµάτων GPIO................................................................................... 111 Έλεγχος GPIO µέσω module............................................................................................. 112 Linux LED Framework ....................................................................................................... 112 Χρήση του LED Framework στο user-space ..................................................................... 114 3.6 Modem .................................................................................................................................. 115 3.7 Επικοινωνία µε τη µνήµη ...................................................................................................... 115 3.7.1 ∆ηµιουργία του συστήµατος αρχείων root .................................................................... 116 Παράµετροι µνήµης flash ................................................................................................... 116 Το σύστηµα αρχείων JFFS2 .............................................................................................. 117 Το εργαλείο mkfs.jffs2 ........................................................................................................ 117 ∆ιαµόρφωση και εγκατάσταση........................................................................................... 117 SPI modules....................................................................................................................... 120 Μεταγλώττιση και πρόσβαση............................................................................................. 121 3.8 Υλικό δικτύωσης ................................................................................................................... 121 4ο ΚΕΦΑΛΑΙΟ .................................................................................................................................... 123 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux ............................................... 123 Εισαγωγή .................................................................................................................................... 123 4.1 Το TCP/IP στα ενσωµατωµένα συστήµατα Linux ................................................................ 123 4.1.1 Το TCP/IP και το µοντέλο αναφοράς OSI ..................................................................... 124 4.1.2 Οι Απαιτήσεις του TCP/IP από το ενσωµατωµένο Linux .............................................. 124 4.1.3 Η στοίβα TCP/IP στο Linux ........................................................................................... 126 Αρχικοποίηση της στοίβας TCP/IP στον Router NGW100 ................................................ 127 4.2 Linux Sockets ....................................................................................................................... 128 4.2.1 Τί είναι η διεπαφή socket............................................................................................... 128 4.2.2 Οι βασικότερες δοµές διαχείρισης sockets.................................................................... 129 4.2.3 Οικογένειες πρωτοκόλλων ............................................................................................ 129 4.2.4 Αρχικοποίηση επιπέδου socket .................................................................................... 130 4.2.5 Πρωτόκολλο IP και sockets........................................................................................... 131 4.2.6 Η διεπαφή Socket ......................................................................................................... 132 4.2.7 Socket buffers ............................................................................................................... 135 8 4.2.8 Τα είδη των sockets ...................................................................................................... 135 Packet Sockets .................................................................................................................. 136 Raw Sockets ...................................................................................................................... 136 Netlink Sockets και πρωτόκολλο Netlink ........................................................................... 137 Routing Sockets ................................................................................................................. 137 Rtnetlink Sockets ............................................................................................................... 138 4.2.9 Το πρόγραµµα netstat................................................................................................... 138 4.2.10 ∆ιεπαφή socket και IPv6 ............................................................................................. 138 4.3 ∆ροµολόγηση πακέτων ........................................................................................................ 138 4.3.1 Πίνακες δροµολόγησης ................................................................................................. 139 Route cache ....................................................................................................................... 139 RPDB ................................................................................................................................. 139 Η βάση FIB αναλυτικά........................................................................................................ 141 Οι προγραµµατιστικές διεπαφές της FIB ........................................................................... 142 4.3.2 ∆ροµολογητές και δροµολόγηση IP .............................................................................. 142 ∆ροµολόγηση εισερχόµενων πακέτων (LAN WAN)...................................................... 143 ∆ροµολόγηση εξερχόµενων πακέτων (LAN WAN) ....................................................... 143 Η κεφαλίδα IP..................................................................................................................... 144 Αποστολή και λήψη πακέτων µέσω του πρωτοκόλλου IP................................................. 145 4.4 Τα πρωτόκολλα ARP, ICMP και IGMP................................................................................. 146 4.4.1 ARP ............................................................................................................................... 146 4.4.2 ICMP.............................................................................................................................. 147 4.4.3 IGMP ............................................................................................................................. 147 4.5 Οδηγοί δικτύου ..................................................................................................................... 148 4.5.1 H διεπαφή δικτύου......................................................................................................... 148 4.5.2 Ανάπτυξη οδηγού δικτύου............................................................................................. 149 4.5.3 Ο οδηγός δικτύου MACB............................................................................................... 149 NAPI................................................................................................................................... 150 ethtool API.......................................................................................................................... 150 Στατιστικά ........................................................................................................................... 150 Promiscuous mode ............................................................................................................ 151 Έλεγχος σφαλµάτων.......................................................................................................... 151 4.6 Απόδοση δικτύωσης του µικροελεγκτή AP7000................................................................... 152 Εισαγωγή πρακτικού µέρους ...................................................................................................... 153 5ο ΚΕΦΑΛΑΙΟ .................................................................................................................................... 154 Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 .................................................. 154 Εισαγωγή .................................................................................................................................... 154 5.1 Περιγραφή και λογικό διάγραµµα του Router NGW100 ....................................................... 154 5.2 Ο µικροελεγκτής AP7000...................................................................................................... 157 5.3 ∆ιαθέσιµη µνήµη................................................................................................................... 159 5.3.1 Χάρτης φυσικών διευθύνσεων ...................................................................................... 164 5.4 Ethernet PHY και MAC ......................................................................................................... 165 5.5 Συνδεσιµότητα RS232 .......................................................................................................... 168 5.6 Ελεγκτής πλακέτας – Board Controller................................................................................. 169 5.7 ∆ιεπαφές προγραµµατισµού JTAG και NEXUS ................................................................... 171 5.8 Σύστηµα χρονισµού .............................................................................................................. 173 5.9 Κύκλωµα επαναφοράς (reset) .............................................................................................. 174 5.10 Σύστηµα τροφοδοσίας ........................................................................................................ 175 5.11 ∆ιαστάσεις πλακέτας .......................................................................................................... 178 5.12 Σχέδιο συναρµολόγησης .................................................................................................... 178 5.13 Σχηµατικά και PCB του Router NGW100 ........................................................................... 180 5.14 Επέκταση δυνατοτήτων υλικού........................................................................................... 180 6ο ΚΕΦΑΛΑΙΟ .................................................................................................................................... 182 Προγραµµατισµός και εκκίνηση του Router NGW100 .................................................................... 182 Εισαγωγή .................................................................................................................................... 182 6.1 Το σύστηµα ανάπτυξης host ................................................................................................ 182 9 6.2 Το πακέτο υποστήριξης συστήµατος Atmel Linux BSP 3.0 ................................................. 183 6.2.1 Τροποποίηση του πακέτου υποστήριξης Atmel Linux BSP 3.0.................................... 184 6.3 ∆ηµιουργία της εικονικής µηχανής Ubuntu 9.04................................................................... 185 6.3.1 Login µε δικαιώµατα root............................................................................................... 187 6.3.2 ∆ηµιουργία του κεντρικού κατάλογου εργασίας /rousis ................................................ 188 6.4 Το περιβάλλον ανάπτυξης AVR32 ....................................................................................... 188 6.4.1 Εγκατάσταση Buildroot και αλυσίδας GNU AVR32....................................................... 188 6.4.2 Το εξειδικευµένο λογισµικό του Router NGW100 ......................................................... 193 6.4.3 Εγκατάσταση του AVR32 Studio................................................................................... 193 6.5 Βοηθητικό λογισµικό ............................................................................................................. 196 6.6 ∆ιαµέριση µνήµης ................................................................................................................. 196 6.7 Η συσκευή JTAGICE mkII .................................................................................................... 197 6.8 Σύνδεση JTAGICE mkII µε το σύστηµα host........................................................................ 198 6.9 Εγγραφή U-Boot στην παράλληλη µνήµη ............................................................................ 202 6.10 Εγγραφή Linux Kernel στην παράλληλη µνήµη ................................................................. 206 6.11 Εγγραφή συστήµατος αρχείων /usr .................................................................................... 207 6.12 ∆ιαδικασία εκκίνησης .......................................................................................................... 209 7ο ΚΕΦΑΛΑΙΟ .................................................................................................................................... 217 Οι εφαρµογές του Router NGW100 ................................................................................................ 217 Εισαγωγή .................................................................................................................................... 217 7.1 Ανάπτυξη δοκιµαστικής εφαρµογής ..................................................................................... 217 7.1.2 Με χρήση κειµενογράφου και γραµµής εντολών........................................................... 218 7.1.3 Μέσω του AVR32 Studio............................................................................................... 221 7.2 Προσθήκη πακέτων λογισµικού στο Buildroot...................................................................... 226 7.2.1 Παράδειγµα προσθήκης εφαρµογής στο σύστηµα Buildroot ........................................ 227 7.3 Οι εφαρµογές του Router NGW100...................................................................................... 230 7.3.1 Πακέτο εφαρµογών BusyBox ........................................................................................ 231 7.3.2 Εφαρµογή inetd .......................................................................................................... 233 7.3.3 Εφαρµογή httpd .......................................................................................................... 233 7.3.4 Εφαρµογές για το Web interface................................................................................... 234 Εφαρµογή haserl ............................................................................................................ 234 Εφαρµογή awk ................................................................................................................... 234 Εφαρµογή webif............................................................................................................... 235 1.3.5 Eφαρµογές διαχείρισης των διεπαφών δικτύου ............................................................ 235 Εφαρµογή ifconfig ........................................................................................................ 235 Εφαρµογές ifup και ifdown............................................................................................ 236 7.3.6 Eφαρµογή route .......................................................................................................... 237 7.3.7 Εφαρµογή iptables ................................................................................................... 239 7.3.8 Εφαρµογές για DNS και DHCP server .......................................................................... 240 Εφαρµογή dnsmasq .......................................................................................................... 240 Εφαρµογή udhcpc ............................................................................................................ 241 7.3.9 Εφαρµογή telnetd...................................................................................................... 241 7.3.10 Εφαρµογή dropbear ................................................................................................. 242 7.3.11 Εφαρµογή ProFTPD ................................................................................................... 243 7.3.12 Εφαρµογή ethtool ................................................................................................... 244 7.3.13 Εφαρµογή portmap ................................................................................................... 245 7.3.14 Εφαρµογή bridge...................................................................................................... 245 7.3.15 Εφαρµογές ενηµέρωσης – εµφάνισης ώρας και ηµεροµηνίας.................................... 246 ΠΑΡΑΡΤΗΜΑΤΑ.................................................................................................................................. 248 ΒΙΒΛΙΟΓΡΑΦΙΑ ................................................................................................................................... 290 10 Ευρετήριο εικόνων ∆ιασυνδεδεµένος τύπος ανάπτυξης ενσωµατωµένων συστηµάτων Linux ........................................... 31 Ανάπτυξη ενσωµατωµένων συστηµάτων Linux µε αφαιρούµενο αποθηκευτικό µέσο ......................... 32 Αυτόνοµος τύπος ανάπτυξης ενσωµατωµένων συστηµάτων Linux ..................................................... 33 Η αρχιτεκτονική ενός ενσωµατωµένου συστήµατος Linux .................................................................... 34 ∆ιάταξη µνήµης στερεάς κατάστασης ................................................................................................... 38 Χάρτης µνήµης και µετάφραση εικονικών διευθύνσεων σε φυσικές ..................................................... 40 ∆ιάγραµµα µονάδας διαχείρισης µνήµης MMU..................................................................................... 41 Το περιβάλλον Ubuntu Software Center ............................................................................................... 53 Περιβάλλον παραµετροποίησης του αυτοµατοποιηµένου συστήµατος buildroot ................................. 53 Το υποµενού Toolchain του Buildroot ................................................................................................... 57 Τα περιεχόµενα του καταλόγου dl του συστήµατος Buildroot ............................................................... 59 Γραφικό περιβάλλον παραµετροποίησης της uClibc 0.9.30 ................................................................. 60 Γραφικό περιβάλλον παραµετροποίησης του Linux Kernel 2.6.27.6 ................................................... 62 Γραφικό περιβάλλον παραµετροποίησης του BusyBox 1.13.1............................................................. 63 Εκτέλεση της εντολής loadb σε περιβάλλον Uboot ............................................................................... 69 Παράθυρο διαλόγου παραµετροποίησης του τερµατικού Putty............................................................ 70 Ρυθµίσεις σειριακής επικοινωνίας του τερµατικού Putty ....................................................................... 70 Εκτέλεση της εντολής ls σε περιβάλλον γραµµής εντολών BusyBox ................................................... 71 Οι αρχιτεκτονικές που υποστηρίζονται από τον Linux Kernel............................................................... 74 Περιβάλλον παραµετροποίησης Linux Kernel – Υποµενού: Device Drivers ........................................ 78 Ανατοµία του Linux Kernel .................................................................................................................... 79 Ενεργοποίηση υποστήριξης modules από τον Linux Kernel ................................................................ 80 Παράδειγµα ανάπτυξης module ............................................................................................................ 82 Ενεργοποίηση υποστήριξης USB από τον Linux Kernel ...................................................................... 84 Γραφική αναπαράσταση της επικοινωνίας USB στον Linux Kernel ...................................................... 84 Γραφική αναπαράσταση των µερών του Linux driver I2C .................................................................... 86 Ενεργοποίηση υποστήριξης I2C από τον Linux Kernel ........................................................................ 87 Ενεργοποίηση υποστήριξης I2C µέσω GPIO από τον Linux Kernel .................................................... 87 Ενεργοποίηση υποστήριξης SPI από τον Linux Kernel ........................................................................ 94 Γραφική αναπαράσταση της λειτουργίας του υποσυστήµατος TTY ................................................... 107 Ενεργοποίηση υποστήριξης GPIO από τον Linux Kernel................................................................... 108 Ενεργοποίηση του Linux LED Framework .......................................................................................... 113 Σύνδεση DataFlash µε µικροελεγκτή µέσω SPI .................................................................................. 116 Ενεργοποίηση υποστήριξης συσκευών MTD από τον Linux Kernel................................................... 118 Ενεργοποίηση CFI driver για την ανάγνωση µνηµών Flash ............................................................... 119 Ενεργοποίηση υποστήριξης µνηµών AT45xxx της Atmel από τον Linux Kernel................................ 119 Ενεργοποίηση υποστήριξης συστήµατος αρχείων JFFS2 από τον Linux Kernel............................... 120 Η ενσωµατωµένη στοίβα TCP/IP του Linux Kernel............................................................................. 126 Το µοντέλο επικοινωνίας client – server ............................................................................................. 133 Γραφική αναπαράσταση της χρήσης Sockets από εφαρµογες Linux ................................................. 136 Η εντολή route στον Router NGW100................................................................................................. 140 Η εντολή ifconfig στον Router NGW100.............................................................................................. 140 Η εντολή ping στον Router NGW100 .................................................................................................. 144 Ανάλυση κεφαλίδας IP µέσω του αναλυτή πακέτων Wireshark.......................................................... 145 Μπλοκ διάγραµµα του υλικού του Router NGW100 ........................................................................... 156 Το το package του µικροελεγκτή AP7000 ........................................................................................... 158 Οπτική αναγνώριση των µνηµών του Router NGW100...................................................................... 159 Η παράλληλη µνήµη AT49BV642D-70TU........................................................................................... 160 Η σειριακή µνήµη AT45DB642D ......................................................................................................... 161 Η µνήµη SDRAM MT48LC16M16A2................................................................................................... 161 ∆ιασύνδεση µνήµης SDRAM µε µικροελεγκτή αρχιτεκτονικής AVR32 ............................................... 162 Η ιεραρχία µνήµης του Router NGW100 και τα σηµεία φόρτωσης του Linux..................................... 163 Το ολοκληρωµένο DP83848I (Ethernet PHY) ..................................................................................... 165 θύρες LAN και WAN, Ethernet controllers και ο µικροελεγκτής AP7000 ............................................ 166 Η θύρα Ethernet J3026G01DNL ......................................................................................................... 166 Σύνδεση του ολοκληρωµένου DP83848I (Ethernet PHY) µε τον µικροελεγκτή AP7000 .................... 167 11 Η διάταξη των ακροδεκτών (pinout) του ολοκληρωµένου DP83848I (Ethernet PHY) ........................ 168 Το ολοκληρωµένο MAX3232ECAE+................................................................................................... 169 Η διάταξη των ακροδεκτών του ολοκληρωµένου MAX3232ECAE+ ................................................... 169 Ο µικροελεγκτής ATtiny24-20SSU (board controller).......................................................................... 169 Η διάταξη των ακροδεκτών του µικροελεγκτή ATtiny24-20SSU ......................................................... 170 Η συνδεσµολογία του ATtiny24 µε τον µικροελεγκτή AP7000 ............................................................ 170 Η διάταξη των ακροδεκτών JTAG ....................................................................................................... 172 10 pin Header για σύνδεση JTAG ....................................................................................................... 172 Σχηµατικό σύνδεσης του 10 pin Header µε τον µικροελεγκτή AP7000.............................................. 172 Σχηµατικό σύνδεσης του εξωτερικού ρολογιού (external clock) µε τον µικροελεγκτή AP7000........... 173 Χαµηλοπερατό σύστηµα φιλτραρίσµατος συχνοτήτων (PLL low-pass filter) ...................................... 173 Το κύκλωµα επαναφοράς (reset) του Router NGW100 ...................................................................... 174 Ο διακόπτης reset SKHUAF010.......................................................................................................... 174 Το τροφοδοτικό του Router NGW100 ................................................................................................. 175 Η θύρα τροφοδοσίας RASM722P ....................................................................................................... 175 Το εσωτερικό κύκλωµα µετατροπής τάσης ......................................................................................... 176 Η γέφυρα ανόρθωσης τεσσάρων διόδων DF10S ............................................................................... 176 θεωρητική γραφική αναπαράσταση της λειτουργίας µιας γέφυρας ανόρθωσης................................. 176 Το ολοκληρωµένο LM2717 (DC/DC converter) .................................................................................. 177 Η διάταξη των ακροδεκτών του ολοκληρωµένου LM2717 .................................................................. 177 Το µηχανικό σχέδιο του PCB του Router NGW100 ............................................................................ 178 Το σχέδιο συναρµολόγησης του Router NGW100.............................................................................. 179 Επέκταση δυνατοτήτων υλικού του Router NGW100 ......................................................................... 181 Τα περιεχόµενα του πακέτου Atmel Linux BSP 3.0 ............................................................................ 183 Τα αρχεία εγκατάστασης του περιβάλλοντος ανάπτυξης.................................................................... 185 Τα περιεχόµενα του αρχείου rousis_toolchain.tar.gz .......................................................................... 185 Η εφαρµογή VMware µε εγκατεστηµένη την εικονική µηχανή Ubuntu 9.04 (jaunty) + AVR32 ........... 186 Τα αρχεία από τα οποία αποτελείται µια εικονική µηχανή στο VMware Player .................................. 186 Η επιφάνεια εργασίας του τελικού εικονικού συστήµατος ανάπτυξης host ......................................... 187 Τα αρχεία εγκατάστασης του περιβάλλοντος ανάπτυξης.................................................................... 188 Τα περιεχόµενα του καταλόγου binaries του συστήµατος Buidroot.................................................... 191 Τα περιεχόµενα του συµπιεσµένου αρχείου rootfs.avr32.tar.bz2 ....................................................... 192 Παράθυρο διαλόγου µετά την εγκατάσταση της µηχανής JAVA ......................................................... 194 Η αρχική οθόνη φόρτωσης του AVR32 Studio.................................................................................... 194 Παράθυρο διαλόγου για την επιλογή καταλόγου εργασίας του AVR32 Studio ................................... 195 Tο κεντρικό περιβάλλον εργασίας του AVR32 Studio......................................................................... 195 Η συσκευή programmer - emulator JTAGICE mkII της Atmel............................................................. 198 Η καλωδιοταινία σύνδεσης JTAG probe ............................................................................................. 199 Η διεπαφή υλικού JTAG του Router NGW100.................................................................................... 199 Σύνδεση της συσκευής JTAGICE mkII µε τον Router NGW100 ......................................................... 200 Θύρες, USB, RS232 και τροφοδοσίας στο πίσω µέρος της συσκευής JTAGICE mkII....................... 200 ∆ιάγραµµα σύνδεσης του συστήµατός host µε τον Router NGW100 ................................................. 200 Εκτέλεση της εντολής cpuinfo –F µέσω του προγράµµατος avr32program και του JTAGICE mkII ... 201 Εκτέλεση της εντολής readregs µέσω του προγράµµατος avr32program και του JTAGICE mkII...... 202 Eκκαθάριση της παράλληλης µνήµης Flash µέσω του JTAGICE mkII ............................................... 202 Εγγραφή του λογισµικού εκκίνησης στην παράλληλη µνήµη µέσω του JTAGICE mkII ..................... 202 Καλώδιο σειριακής επικοινωνίας RS232............................................................................................. 203 Παράµετροι σειριακής επικοινωνίας της εφαρµογής PuTTY............................................................... 203 Το command prompt του U-Boot......................................................................................................... 204 Εκτλελεση της εντολής help στο U-Boot ............................................................................................. 205 Εκτλελεση της εντολής flinfo στο U-Boot............................................................................................. 206 Εγγραφή Linux Kernel στην παράλληλη µνήµη µέσω του JTAGICE mkII.......................................... 206 SanDisk MMC/SD-card ....................................................................................................................... 207 Εγγραφή συστήµατος αρχείων /usr από την SD-card µέσω της εντολής dd ...................................... 208 ∆ιαδικασία εκκίνησης στάδιο 1 ............................................................................................................ 210 ∆ιαδικασία εκκίνησης στάδιο 2 ............................................................................................................ 211 ∆ιαδικασία εκκίνησης στάδιο 3 ............................................................................................................ 211 ∆ιαδικασία εκκίνησης στάδιο 4 ............................................................................................................ 212 ∆ιαδικασία εκκίνησης στάδιο 5 ............................................................................................................ 212 ∆ιαδικασία εκκίνησης στάδιο 6 ............................................................................................................ 212 12 ∆ιαδικασία εκκίνησης στάδιο 7 ............................................................................................................ 213 ∆ιαδικασία εκκίνησης στάδιο 8 ............................................................................................................ 214 ∆ιαδικασία εκκίνησης στάδιο 9 ............................................................................................................ 214 ∆ιαδικασία εκκίνησης στάδιο 10 .......................................................................................................... 215 ∆ιαδικασία εκκίνησης στάδιο 11 .......................................................................................................... 215 ∆ιαδικασία εκκίνησης στάδιο 12 .......................................................................................................... 216 Ολοκλήρωση διαδικασίας εκκίνησης – έναρξη γραµµής εντολών BusyBox ....................................... 216 Καλώδιο συνεστραµµένων ζευγών UTP cat5e ................................................................................... 218 ∆ηµιουργία άδειου αρχείου στην Ubuntu 9.04 .................................................................................... 219 Τα αρχεία του καταλόγου application µετά τη µεταγλώττιση του hello.c............................................. 219 Μεταφορά της εφαρµογής hello στον Router NGW100 µέσω εντολών FTP ..................................... 220 Εκτέλεση της δοκιµαστικής εφαρµογής hello µέσω Telnet ................................................................. 220 Παράθυρο διαλόγου µετά την εκτέλεση του αρχείου AVR32-Studio.sh µε διπλό κλικ........................ 221 Το κεντρικό περιβάλλον εργασίας του AVR32 Studio......................................................................... 222 AVR32 Studio - ∆ηµιουργία νέου C project – Βήµα 1 ......................................................................... 222 AVR32 Studio - ∆ηµιουργία νέου C project – Βήµα 2 ......................................................................... 223 AVR32 Studio - ∆ηµιουργία νέου αρχείου πηγαίου κώδικα C ............................................................ 224 AVR32 Studio - Περιεχόµενα καταλόγου Debug για το project "dokimastiki-efarmogi" ...................... 224 Μεταφορά αρχείου dokimastiki-efarmogi.elf στον Router NGW100 µέσω του FileZilla FTP client .... 225 Εκτέλεση της δοκιµαστικής εφαρµογής hello µέσω του τερµατικού PuTTY ....................................... 226 Buildroot – Πακέτο λογισµικού webif ................................................................................................... 228 Buildroot - Τα περιεχόµενα του καταλόγου package/webif ................................................................. 229 Εκτέλεση της εντολής busybox στον Router NGW100 ....................................................................... 232 Εκτέλεση της εντολής ethtool για τη διεπαφή eth1.............................................................................. 245 Βοήθεια για την εντολή brctl της εφαρµογής bridge ............................................................................ 245 13 Ευρετήριο πινάκων Τα προγράµµατα του πακέτου binutils .................................................................................................. 46 Εντολές Linux ........................................................................................................................................ 65 Flags και λειτουργία της συνάρτησης at32_select_gpio ....................................................................... 89 Παράµετροι λειτουργίας της µνήµης SDRAM...................................................................................... 162 Χάρτης φυσικών διευθύνσεων του AP7000 ....................................................................................... 164 Η διαθέσιµη µνήµη του Router NGW100 ............................................................................................ 164 Λεπτοµέρειες σύνδεσης των Ethernet PHYs ...................................................................................... 165 Οι MAC διευθύσεις του Router NGW100 ............................................................................................ 168 Μηνύµατα απόκρισης του PMbus ....................................................................................................... 171 Η διαµόρφωση των φίλτρων PLL ........................................................................................................ 174 14 Ευχαριστίες Θα ήθελα να ξεκινήσω ευχαριστώντας τους γονείς µου, Θανάση και Μαργαρίτα Ρούση, καθώς και τον αδερφό µου Άγγελο Ρούση, γιατί είναι πάντα εκεί για ‘µένα και µε υποστηρίζουν σε όλες µου τις προσπάθειές. Επιπλέον θα ήθελα να ευχαριστήσω την Ελένη Βασιλάκη, για την πολυδιάστατη και ουσιαστική στήριξη που µου πρόσφερε κατά τη διάρκεια της σχεδίασης, της ανάπτυξης και της συγγραφής της εργασίας αυτής. Ιδιαίτερα ευχαριστώ τον επιβλέποντα καθηγητή µου, κύριο ∆ηµήτρη Φωτιάδη, για την υποµονή, την πολύτιµη βοήθειά του και την κατανόησή του, όλους αυτούς τους µήνες. Αλλά και το ΑΤΕΙ ∆υτικής Μακεδονίας για ότι καλό µου προσέφερε κατά τη διάρκεια της φοίτησής µου. Ευχαριστώ τον Linus Torvalds, τον δηµιουργό του Linux Kernel και το ίδρυµα ελεύθερου λογισµικού FSF (Free Software Foundation) και τον ιδρυτή του Richard Stallman, για όλη τους τη συνεισφορά στην επιστήµη των υπολογιστών. Χωρίς αυτούς η παρούσα εργασία απλά δεν θα υπήρχε. Ευχαριστώ την εταιρεία Atmel για τα άρτια εργαλεία της, την τεκµηρίωσή της και το ευγενικό της helpdesk. Επίσης ευχαριστώ πολύ την κοινότητα και το forum των AVR Freaks (ιδιαίτερα τον χρήστη hce), όπου καµία απορία και κανένα πρόβληµα δεν µένουν αναπάντητα. Τέλος, ευχαριστώ την µηχανή αναζήτησης Google η οποία µου έλυσε τα χέρια, τόσο σε επίπεδο εύρεσης απαραίτητων πληροφοριών, όσο και για την άψογη υπηρεσία µετάφρασης που παρέχει. 15 Περίληψη Η εργασία αυτή αφορά την διαδικασία υλοποίησης του οικιακού δροµολογητή Router NGW100, ο οποίος είναι σε θέση να συνδέει τους υπολογιστές ενός τοπικού δικτύου µε το Internet. Η δροµολόγηση των πακέτων από και προς το τοπικό δίκτυο, γίνεται µε ασφάλεια και αξιοπιστία µέσω ειδικού λογισµικού. Η απόδοση του Router NGW100 είναι τέτοια, ώστε να µπορεί να υποστηρίζει έναν αξιοπρεπή ρυθµό µετάδοσης για κάθε συνδεδεµένο σε αυτόν, σύστηµα. Επίσης παρέχονται διεπαφές διαχείρισης και παραµετροποίησης του router, τόσο σε επίπεδο γραµµής εντολών, µέσω των πρωτοκόλλων Telnet και SSH, όσο και σε επίπεδο γραφικού περιβάλλοντος, µέσω του Web interface. Στην πρώτη περίπτωση απαιτούνται εξειδικευµένες γνώσεις από τον διαχειριστή, αλλά παρέχεται µεγαλύτερη ευελιξία και πιο προχωρηµένος έλεγχος. Στην δεύτερη περίπτωση, το περιβάλλον διαχείρισης είναι ιδιαιτέρα εύκολο και φιλικό προς το χρήστη, καθώς απαιτείται απλά ένας φυλλοµετρητής και το ποντίκι του υπολογιστή. 16 Εισαγωγή Ουσιαστικά, η παρούσα εργασία, αποτελεί µια προσπάθεια δηµιουργίας του συγγράµµατος το οποίο θα ήταν πολύ χρήσιµο και επιθυµητό να υπάρχει έτοιµο, από την πρώτη στιγµή. Η ελληνική βιβλιογραφία είναι πολύ περιορισµένη σε ότι αφορά την επιστήµη των υπολογιστών. Εποµένως χρειάστηκαν εκατοντάδες ώρες µελέτης αγγλόφωνων συγγραµµάτων αλλά και αναζήτησης στο ∆ιαδίκτυο. Πολλά κεφάλαια τα οποία γράφτηκαν µε πολύ κόπο, δεν υπάρχουν εδώ, αφού αφαιρέθηκαν λόγω περιεχοµένου που έβγαινε εκτός θέµατος. Επίσης πολλά κεφάλαια ξαναγράφτηκαν απ’ την αρχή. Αυτό συνέβη επειδή αρχικά δεν υπήρχε κάποιο προκαθορισµένο σχεδιάγραµµα. Ήταν αδύνατο να υπάρχει. Το αντικείµενο των ενσωµατωµένων συστηµάτων Linux ήταν πρωτόγνωρο και τα θέµατα που το αφορούσαν, άπειρα. Η αφαίρεση περιττών πληροφοριών απαιτεί καλή γνώση του αντικειµένου του οποίου αναλύεται κάθε φορά. Για να είναι κάτι τέτοιο δυνατόν, θα πρέπει να υπάρχει το απαραίτητο και προαπαιτούµενο θεωρητικό υπόβαθρο. Αυτό θα επιτρέψει την επικέντρωση της ανάλυσης στα σηµαντικότερα θέµατα και όχι σε δευτερεύοντα ζητήµατα. Το προαπαιτούµενο θεωρητικό υπόβαθρο Η συγγραφή µιας πτυχιακής εργασίας η οποία αφορά την υλοποίηση ενός δροµολογητή είναι ένα γιγάντιο εγχείρηµα για τις γνώσεις ενός προπτυχιακού φοιτητή. Το θεωρητικό υπόβαθρο το οποίο προαπαιτείται, είναι πολύ συνοπτικά το παρακάτω: Θεωρία λειτουργικών συστηµάτων Τοπικά δίκτυα και δίκτυα ευρείας περιοχής Πρωτόκολλα δροµολόγησης Το λειτουργικό σύστηµα Linux Linux Kernel 2.6 Λογισµικό εκκίνησης Το ενσωµατωµένο Linux Αυτοµατοποιηµένα συστήµατα ανάπτυξης (πχ: Buildroot) Το TCP/IP στον Linux kernel Οικιακοί δροµολογητές (τρόπος λειτουργίας και διαχείριση) Προγραµµατισµός στον φλοιό του Linux ∆ιερµηνευτικές γλώσσες (πχ: Perl και awk) Γλώσσες προγραµµατισµού Assembly και C Ανάπτυξη εφαρµογών Linux Ανάπτυξη διεπαφών χρήστη (πχ: Web Interface) Οδηγοί συσκευών Linux Πρωτόκολλα και διεπαφές επικοινωνίας υλικού (πχ: SPI, I2C κλπ) Ενσωµατωµένα συστήµατα – σχεδίαση µε µικροελεγκτή Θεωρία ηλεκτρονικής Ψηφιακή σχεδίαση Μετρήσεις Σχεδίαση Σχηµατικού – PCB, µε λογισµικό CAD Απαιτείται πολύ βαθειά αντίληψη της λειτουργίας του υλικού αλλά και του λογισµικού. Επίσης απαιτείται αντίληψη του πως αυτά τα δύο συνενώνονται ώστε να αποτελούν ένα σύστηµα που λειτουργεί και φέρει εις πέρας χρήσιµες λειτουργίες. 17 Η λίστα που παρατέθηκε πιο πάνω είναι όπως είπαµε, πολύ συνοπτική. Αυτό είναι εύκολο να το αντιληφθεί κανείς αν αναφερθεί για παράδειγµα, ότι για τη σχεδίαση του Router NGW100 χρησιµοποιήθηκαν ακόµα και γλώσσες Web, όπως HTML, JavaScript και CSS. Το βασικότερο τµήµα λογισµικού το οποίο απασχόλησε σε αρκετά µεγάλο µέρος της εργασίας, είναι το ενσωµατωµένο Linux. Το ενσωµατωµένο Linux και πιο συγκεκριµένα ο Linux Kernel, µπορεί να έχει γραφτεί σε γλώσσα C, όµως οι ειδικοί µηχανισµοί του αποτελούνται από χιλιάδες συναρτήσεις και περίπλοκες δοµές δεδοµένων, που το κάνουν να µοιάζει µε µια ξεχωριστή και ανεξάρτητη γλώσσα προγραµµατισµού. Κατά τη διάρκεια της υλοποίησης του δροµολογητή Router NGW100, χρησιµοποιήθηκαν και τα Windows αλλά και το Linux ως συστήµατα ανάπτυξης. Το ένα λειτουργικό σύστηµα, κάλυπτε τα κενά του άλλου. Ταυτόχρονα όµως, γινόταν και µια πιο σφαιρική προσέγγιση των διαθέσιµων επιλογών ανάπτυξης ενσωµατωµένων συστηµάτων Linux. Στο κείµενο της παρούσας εργασίας δεν αναφέρονται µονό τα εργαλεία που χρησιµοποιήθηκαν κατά την ανάπτυξη αλλά και κάποια επιπλέον, εξίσου δηµοφιλή και αποτελεσµατικά. Αυτό γίνεται για να δοθεί η ευκαιρία σε οποίον διαβάσει κάποια στιγµή την εργασία, να αποφασίσει για το ποιο εργαλείο του ταιριάζει περισσότερο ανάλογα µε τις ιδιαίτερες απαιτήσεις του συστήµατος που πρόκειται να υλοποιήσει. Για τον ίδιο σκοπό δηµιουργήθηκε και ειδική ιστοσελίδα για την οποία υπάρχουν περισσότερες λεπτοµέρειες σε αντίστοιχο παράρτηµα. ∆οµή εργασίας Η εργασία χωρίζεται σε τρία κύρια µέρη: Θεωρητικό µέρος Πρακτικό µέρος Παραρτήµατα Θεωρητικό µέρος Στο θεωρητικό µέρος παρατίθεται το βασικό υπόβαθρο που αφορά τα ενσωµατωµένα συστήµατα Linux και τις δυνατότητες δικτύωσής τους. Πιο συγκεκριµένα, το θεωρητικό µέρος αποτελείται από τα εξής κεφάλαια: 1ο Κεφάλαιο – Ανάπτυξη Ενσωµατωµένων Συστηµάτων Linux 2ο Κεφάλαιο – Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux 3ο Κεφάλαιο – Το υλικό που υποστηρίζει το ενσωµατωµένο Linux 4ο Κεφάλαιο – ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux Στο πρώτο κεφάλαιο γίνεται εισαγωγή στα ενσωµατωµένα συστήµατα Linux. Αναφέρονται οι έτοιµες διανοµές ενσωµατωµένου Linux, τα εργαλεία ανάπτυξης και οι σηµαντικότεροι προµηθευτές τους. Γίνεται µια γενική περιγραφή της µεθοδολογίας ανάπτυξης ενσωµατωµένων συστηµάτων Linux. Εξετάζονται οι τύποι ανάπτυξης, όπου αναφέρεται για πρώτη φορά το µοντέλο host – target. Αναλύονται επιφανειακά έννοιες που αφορούν την εκκίνηση, τη διαχείριση της µνήµης και την αρχιτεκτονική ενός ενσωµατωµένου συστήµατος Linux. Στο δεύτερο κεφάλαιο δίνεται έµφαση στην ανάλυση των εργαλείων ανάπτυξης ενσωµατωµένων συστηµάτων Linux. Εισάγεται η έννοια της διασταυρούµενης αλυσίδας µεταγλώττισης, και της τοπικής µεταγλώττισης. Περιγράφεται το αυτοµατοποιηµένο σύστηµα Buildroot και η παραµετροποίηση της βιβλιοθήκης uClibc, του Linux Kernel, του πολυεργαλείου BusyBox, καθώς και του ίδιου του Buildroot, προκειµένου να µπορεί να δηµιουργηθεί το λογισµικό που 18 θα εκτελείται στον Router NGW100. Επίσης, γίνεται αναφορά στη γραµµή εντολών, τις εντολές shell, τους προσοµοιωτές τερµατικού και τα γραφικά περιβάλλοντα ανάπτυξης IDEs. Το τρίτο κεφάλαιο αφορά το υλικό που υποστηρίζει το ενσωµατωµένο Linux. Αφού γίνει µια συνοπτική περιγραφή των διαφόρων αρχιτεκτονικών µικροελεγκτών που υποστηρίζει ο Linux Kernel, ακολουθεί µια αρκετά λεπτοµερής ανάλυση των οδηγών συσκευών. Επειδή ένας οδηγός συσκευής είναι δυνατόν να φορτώνεται και κατά τη διάρκεια εκτέλεσης του Kernel, ως module, παρουσιάζεται αντίστοιχο παράδειγµα. Αµέσως µετά αναλύονται οι σηµαντικότεροι δίαυλοι και οι σηµαντικότερες διεπαφές υλικού που εντοπίζονται συχνότερα στα ενσωµατωµένα συστήµατα Linux και ειδικότερα στον Router NGW100. Τέλος παρατίθενται ορισµένες πληροφορίες σχετικά µε την επικοινωνία µε την µνήµη και γίνεται µια εισαγωγή για το επόµενο κεφάλαιο το οποίο αφορά την δικτύωση ενσωµατωµένων συστηµάτων Linux. Στο τέταρτο κεφάλαιο, το οποίο είναι και το τελευταίο του θεωρητικού µέρους, παρουσιάζεται συνοπτικά η δικτύωση και η δροµολόγηση στα ενσωµατωµένα συστήµατα Linux. Σε αυτό το κεφάλαιο υπάρχουν θεµελιώδεις έννοιες της δικτύωσης. Μία από αυτές είναι και η στοίβα TCP/IP. Μία άλλη, είναι τα Linux sockets. Επίσης γίνεται αναφορά στα πρωτόκολλα ARP, ICMP και IGMP. Το κεφάλαιο ολοκληρώνεται µε την ανάλυση των οδηγών δικτύου και την απόδοση δικτύωσης του µικροελεγκτή AP7000. Στην παράγραφο που αφορά τους οδηγούς δικτύου παρουσιάζεται και ο οδηγός MACB που χρησιµοποιεί ο Router NGW100 για την επικοινωνία του µέσω Ethernet. Ο κώδικας του συγκεκριµένου οδηγού απασχολεί και ένα ολόκληρο παράρτηµα της εργασίας αυτής. Πρακτικό µέρος Στο πρακτικό µέρος αναλύεται εκτενώς το υλικό και το λογισµικό από το οποίο αποτελείται ο Router NGW100. Αποτελείται από τα εξής κεφάλαια: 5ο Κεφάλαιο – Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 6ο Κεφάλαιο – Προγραµµατισµός και εκκίνηση του Router NGW100 7ο Κεφάλαιο – Οι εφαρµογές του Router NGW100 Στο πέµπτο κεφάλαιο, τα ηλεκτρικά και τα ηλεκτρονικά χαρακτηριστικά του Router NGW100, παρουσιάζονται µέσω της περιγραφής των λεπτοµερειών των αντίστοιχων ηλεκτρικών και ηλεκτρονικών στοιχείων, που βρίσκονται συγκολληµένα στο PCB του. Τα σηµαντικότερα από αυτά είναι: ο µικροελεγκτής AP7000, η µνήµη, το Ethernet PHY, το ολοκληρωµένο για την επικοινωνία RS232 µε τον υπολογιστή, ο ελεγκτής πλακέτας, η διεπαφή προγραµµατισµού JTAG, τα συστήµατα χρονισµού και reset, και το σύστηµα τροφοδοσίας της πλακέτας. Κάποια λογικά διαγράµµατα που επίσης παρουσιάζονται, προσδίδουν µια επιπλέον κατανόηση για το υλικό του Router NGW100 αλλά και για τον τρόπο επικοινωνίας του υλικού αυτού µε τον Kernel και το υπόλοιπο λογισµικό. Επίσης παρουσιάζονται ορισµένα από τα κυριότερα µηχανικά χαρακτηριστικά, καθώς και το σχέδιο συναρµολόγησης. Το έκτο κεφάλαιο είναι πολύ σηµαντικό καθώς για πρώτη φορά το σύστηµά µας αποκτά κάποια επαφή µε το περιβάλλον. Σε αυτό πραγµατοποιούνται ο προγραµµατισµός και η εκκίνηση του Router NGW100. Βήµα προς βήµα, παρουσιάζονται όλες οι ενέργειες που έγιναν για τη δηµιουργία του περιβάλλοντος ανάπτυξης host AVR32. Για παράδειγµα, αναφέρεται το πώς χρησιµοποιήθηκε το πακέτο BSP της ATMEL προκειµένου να είναι διαθέσιµα εξειδικευµένα εργαλεία και εξειδικευµένος κώδικας που αφορά το υλικό. Επίσης βήµα προς βήµα, παρουσιάζεται και ό τρόπος µεταφοράς του λογισµικού εκκίνησης, του Linux Kernel, και του περιβάλλοντος – συστήµατος αρχείων, χρήστη. Σε αυτό το σηµείο ο Router NGW100 µπορεί πλέον να αλληλεπιδρά µε το σύστηµα ανάπτυξης host και µε το δίκτυο. 19 Στο έβδοµο κεφάλαιο βρισκόµαστε στο τελικό στάδιο της εργασίας. Είµαστε σε θέση πλέον να γράφουµε εφαρµογές που θα εκτελούνται “επάνω” από τον Linux Kernel του Router NGW100. Αρχικά παρουσιάζεται ένα παράδειγµα ανάπτυξης της στοιχειώδους εφαρµογής hello.c, η οποία παρά το ότι, το µόνο που κάνει, είναι να εµφανίζει το µήνυµα Hello World!, αποτελεί ταυτόχρονα απόδειξη της σωστής λειτουργίας της διασταυρούµενης αλυσίδας µεταγλώττισης. Αµέσως µετά, δίνεται άλλο ένα παράδειγµα το οποίο όµως αφορά την προσθήκη πακέτου λογισµικού στο αυτοµατοποιηµένο σύστηµα Buildroot. Αυτό λειτουργεί ως εισαγωγή και ως µέσο κατανόησης, για να παρατεθούν έπειτα όλες οι εγκατεστηµένες εφαρµογές του Router NGW100. Μία από αυτές είναι και η webif στην οποία βασίζεται η ανάπτυξη του Web interface. Παραρτήµατα Στα παραρτήµατα γίνεται µια προσπάθεια να δοθούν κάποιες επιπλέον λεπτοµέρειες σχετικά µε τον Router NGW100. Τα παραρτήµατα είναι συνολικά τρία: Παράρτηµα A – Το σχηµατικό του Router NGW100 Παράρτηµα B – Τεκµηρίωση ανάπτυξης του οδηγού δικτύου MACB Παράστηµα C – Το πακέτο RBSP και o ιστότοπος MyThesis.org 20 ΘΕΩΡΗΤΙΚΟ ΜΕΡΟΣ 21 ΚΕΦΑΛΑΙΟ 1 Ανάπτυξη Ενσωµατωµένων Συστηµάτων Linux Εισαγωγή Παρά την ευρεία χρήση του Linux σε κινητά, οχήµατα, αεροσκάφη, αµυντικά όπλα, διαστηµικές αποστολές κλπ, υπάρχει πολύ µικρή βάση τεκµηριωµένης γνώσης για τη δηµιουργία, την εγκατάσταση και τον έλεγχο του Linux Kernel και των εργαλείων που χρησιµοποιούνται κατά την ανάπτυξη ενός ενσωµατωµένου συστήµατος Linux (Embedded Linux System). Έτσι, πριν προχωρήσουµε στην ανάπτυξη ενός τέτοιου συστήµατος θα πρέπει πρώτα να έχουµε κατανοήσει µια γενικότερη εικόνα. Σε αυτό το κεφάλαιο παρατίθενται κάποιες απαραίτητες γενικές γνώσεις που καλύπτουν το βασικό θεωρητικό υπόβαθρο ανάπτυξης ενσωµατωµένων συστηµάτων Linux και οι οποίες βοηθούν στην κατανόηση των παρακάτω θεµάτων: Τι είναι το Linux Το ενσωµατωµένο Linux Οι τύποι των ενσωµατωµένων συστηµάτων Linux Γιατί να προτιµήσουµε το Linux Έτοιµες διανοµές και εργαλεία ανάπτυξης Μεθοδολογία σχεδίασης και ανάπτυξης Εργαλεία ανάπτυξης και αποσφαλµάτωσης Επίσης θα εξετάσουµε κάποιες βασικές έννοιες που αφορούν τους τρόπους ανάπτυξης ενσωµατωµένων συστηµάτων Linux και θα παραθέσουµε πληροφορίες που αφορούν πιο εσωτερικά στοιχεία τους: Οι τύποι ανάπτυξης Οι τύποι αποσφαλµάτωσης Η αρχιτεκτονική ενός ενσωµατωµένου συστήµατος Linux Εκκίνηση συστήµατος Μονάδα διαχείρισης µνήµης (MMU) ∆ιαχείριση δευτερεύουσας µνήµης Ανάπτυξη Ενσωµατωµένων Συστηµάτων Linux - Κεφάλαιο 1 1.1 Τι είναι το Linux Το Linux είναι ένα Λειτουργικό Σύστηµα το οποίο δηµιουργήθηκε αρχικά από τον Linus Torvalds και τον οργανισµό FSF (Free Software Foundation) υπό την άδεια GNU (General Public License). Ο πυρήνας του Linux παρέχει µια µεγάλη ποικιλία βασικών λειτουργιών οι οποίες απαιτούνται από κάθε σύστηµα για να λειτουργήσει σωστά. Ένα επίπεδο πιο «πάνω» από τον πυρήνα βρίσκεται το λογισµικό εφαρµογών το οποίο βασίζεται σε συγκεκριµένες λειτουργίες του πυρήνα. Οι λειτουργίες αυτές αφορούν τον χειρισµό των συσκευών και την παροχή µιας ποικιλίας επιπέδων αφαίρεσης (abstraction layers), όπως είναι η εικονική µνήµη (virtual memory), οι διεργασίες (tasks ή processes), τα sockets, τα συστήµατα αρχείων κλπ. Για την εκκίνηση του πυρήνα του Linux χρησιµοποιείται συνήθως κάποιο εξειδικευµένο λογισµικό εκκίνησης. Στις µέρες µας, ο όρος “Linux” είναι κάπως συγκεχυµένος λόγω της ολοένα και αυξανόµενης δηµοτικότητάς του καθώς και της καθηµερινής του χρήσης από ανθρώπους οι οποίοι δεν είναι ειδικοί. Συνήθως ο όρος Linux χρησιµοποιείται εναλλακτικά όταν κάποιος θέλει να αναφερθεί είτε στον πυρήνα του Linux, είτε σε ένα σύστηµα Linux, είτε σε µια εφαρµογή που έχει στηθεί επάνω στον πυρήνα του Linux. Στη εργασία αυτή όταν θα αναφέρουµε τον όρο «Linux» θα εννοούµε τον πυρήνα του και τις εφαρµογές µας, αν αναφερόµαστε στο σύστηµά µας που θα είναι ο Router, ενώ θα εννοούµε κάποια διανοµή µε γραφικό περιβάλλον (πχ: Ubuntu), αν αναφερόµαστε στο σύστηµα ανάπτυξης που θα είναι κάποιος προσωπικός υπολογιστής ο οποίος θα περιέχει τα απαραίτητα εργαλεία ανάπτυξης. Όλα αυτά θα γίνονται όλο και πιο κατανοητά στη συνέχεια. Οι διανοµές Linux διαφέρουν στο σκοπό, το κόστος και το µέγεθος αλλά έχουν τον ίδιο στόχο. Ο στόχος είναι να παρέχουν στον τελικό χρήστη ένα προ-συσκευασµένο και συµπυκνωµένο σετ αρχείων και έναν µηχανισµό εγκατάστασης έτσι ώστε να µπορεί να εγκατασταθεί ο πυρήνας και οι εφαρµογές, σε διάφορες αρχιτεκτονικές και για διάφορους σκοπούς. 1.2 Το ενσωµατωµένο Linux Το ενσωµατωµένο Linux τυπικά αναφέρεται σε ένα πλήρες σύστηµα ή σε µια διανοµή που είναι στοχευµένη για ενσωµατωµένα συστήµατα. Παρ’ όλο που ο όρος “ενσωµατωµένο” υποδηλώνει µια ειδική έκδοση Linux, δεν υπάρχει κάποιος ειδικός τύπος του Linux kernel για εφαρµογές σε ενσωµατωµένα συστήµατα. Ο ίδιος πηγαίος κώδικας του πυρήνα που χρησιµοποιείται σε PCs ή σε Servers, µεταγλωττίζεται και για όλα τα είδη των διάφορων συστηµάτων που τον χρησιµοποιούν. Υπάρχουν όµως κάποιες παράµετροι που µπορούν να τροποποιούνται κατά τη µεταγλώττιση ώστε να αφαιρούνται περιττά χαρακτηριστικά και να προσθέτονται άλλα που είναι χρήσιµα. Για παράδειγµα, η υποστήριξη για terabytes µνήµης σε ένα ενσωµατωµένο σύστηµα είναι εντελώς περιττή και µπορεί να αφαιρεθεί. Στα πλαίσια της ανάπτυξης ενσωµατωµένων συστηµάτων Linux, γίνεται χρήση µιας σειράς από λογισµικά. Εκτός από τις δωρεάν εκδοχές, υπάρχει και µία αρκετά µεγάλη ποικιλία εµπορικών διανοµών ενσωµατωµένου Linux που σχεδιάζονται ειδικά για ενσωµατωµένα συστήµατα. Οι διανοµές αυτές παράγονται από κάποιους εξειδικευµένους προµηθευτές. Οι πιο σηµαντικοί από αυτούς είναι οι: MontaVista, Wind River, Lynuxworks, Timesys και Denx. Τα εργαλεία που αναπτύσσονται από αυτές τις εταιρείες είναι: cross-compilers, debuggers, εφαρµογές διαχείρισης έργων (projects), boot image builders κλπ. Αυτό είναι ουσιαστικά και αυτό που πληρώνουµε σε αυτές τις εταιρείες όταν στρεφόµαστε σε κάποια έτοιµη λύση. Το αν θα χρησιµοποιήσουµε κάποια έτοιµη λύση βέβαια, εξαρτάται καθαρά από τις οικονοµικές µας δυνατότητες και τις ειδικές µας γνώσεις στο αντικείµενο. Στα πλαίσια αυτής της εργασίας προτιµήθηκαν οι δωρεάν λύσεις. Οπότε χρειάστηκε να χτίσουµε τα δικά µας εργαλεία Ιστότοπος εργασίας: MyThesis.org 23 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητή (Router Implementation) και την δική µας προσαρµοσµένη διανοµή Linux. Αυτό, αν και απαίτησε υπερβολικά πολύ χρόνο, τελικά προσέφερε καλύτερη κατανόηση και µεγαλύτερη ανεξαρτησία. Τα πλεονεκτήµατα όµως της επιλογής έτοιµων λύσεων θα είναι πάντα η τεράστια εξοικονόµηση χρόνου και η τεχνική υποστήριξη που παρέχεται. 1.3 Οι τύποι των ενσωµατωµένων συστηµάτων Linux Η κατηγοριοποίηση των ενσωµατωµένων συστηµάτων Linux δεν είναι εύκολο να γίνει µε βάση την εφαρµογή τους. Για να εντοπιστούν πραγµατικές διαφορές θα πρέπει να βρούµε κάποια κριτήρια που θα παρέχουν πληροφορίες σχετικά µε τη δοµή του κάθε συστήµατος. Τα κριτήρια αυτά είναι: Το µέγεθος Οι χρονικοί περιορισµοί Η δυνατότητα δικτύωσης Ο βαθµός αλληλεπίδρασης του χρήστη µε το σύστηµα. Το µέγεθος ενός ενσωµατωµένου συστήµατος προσδιορίζεται από έναν αριθµό διαφορετικών παραγόντων που αφορά κυρίως τις φυσικές δυνατότητες των ολοκληρωµένων που υπάρχουν σε αυτό. Οι κυριότεροι παράγοντες είναι η ταχύτητα του µικροελεγκτή, η χωρητικότητα της κύριας µνήµης RAM και η χωρητικότητα των µέσων µόνιµης αποθήκευσης. Έτσι ανάλογα µε το µέγεθός τους, τα ενσωµατωµένα συστήµατα χωρίζονται σε µικρά, µεσαία και µεγάλα. Τα µικρά συστήµατα χαρακτηρίζονται από έναν µικροελεγκτή των 32bit χαµηλής κατανάλωσης και µνήµη ROM των 4ΜB. Η µνήµη συνήθως δεν είναι πραγµατική ROM αλλά Flash και µπορεί να φτάσει µέχρι και τα 32MB. Στα µεσαία συστήµατα, τα χαρακτηριστικά που εντοπίζουµε είναι: µικροελεγκτής µεσαίας κατανάλωσης, µε 32MB ή και µεγαλύτερη ROM (σχεδόν πάντα NOR Flash, ή ακόµη και NAND όταν υπάρχει δυνατότητα εκτέλεσης κώδικα από block-addressable NAND FLASH µνήµες) και 64 – 128 MB RAM. Μεσαία συστήµατα θεωρούνται τα mp3 players, τα PDAs, οι συσκευές δικτύωσης όπως είναι τα routers κλπ. Πρέπει να πούµε ότι κάποια από τα παραπάνω συστήµατα µπορούν να υποστηρίξουν (προς το παρόν) µέχρι και 32GB NAND Flash βοηθητικής µνήµης σε δευτερεύοντες αποθηκευτικούς χώρους. Τα µεγάλα συστήµατα χαρακτηρίζονται από έναν δυνατό επεξεργαστή ή από µια οµάδα επεξεργαστών σε συνδυασµό µε µεγάλα µεγέθη µνήµης RAM και µόνιµο αποθηκευτικό χώρο. Τα συστήµατα αυτά χρησιµοποιούνται συνήθως σε περιβάλλοντα στα οποία εκτελούνται µεγάλοι αριθµοί απαιτητικών υπολογισµών ώστε να επιτευχθούν συγκεκριµένες διεργασίες. Οι µεγάλοι τηλεπικοινωνιακοί σταθµοί και οι προσοµοιωτές πτήσης αποτελούν παραδείγµατα τέτοιων µεγάλων συστηµάτων. Γι’ αυτά τα συστήµατα το κόστος και οι πόροι που απαιτούν είναι δευτερεύοντα ζητήµατα. Το ζητούµενο είναι η επίτευξη ενός στόχου µε κάθε θυσία. Ένα τέτοιο παράδειγµα είναι και το αµυντικό σύστηµα µιας χώρας. Το σύστηµά µας παρόλο που λειτουργεί ως Router, ανήκει στην πρώτη κατηγορία, δηλαδή στα µικρά συστήµατα. Αυτό θα γίνει περισσότερο κατανοητό στο πρακτικό µέρος της εργασίας που θα ασχοληθούµε αποκλειστικά µε το υλικό και το λογισµικό του. Ας δούµε τώρα πως διαχωρίζονται τα ενσωµατωµένα συστήµατα Linux ως προς τους χρονικούς περιορισµούς. Υπάρχουν δύο τύποι χρονικών περιορισµών. Οι αυστηροί και οι ήπιοι. Στους αυστηρούς περιορισµούς απαιτείται η ανάδραση του συστήµατος να γίνεται σε ένα προκαθορισµένο χρονικό πλαίσιο, αλλιώς κάτι πολύ ανεπιθύµητο µπορεί να συµβεί. Ας πάρουµε για παράδειγµα, ένα µηχάνηµα κοπής ξυλείας όπου το χέρι ενός εργάτη πλησιάζει επικίνδυνα στην κορδέλα κοπής. Αν ο αισθητήρας που έχει τοποθετηθεί για τέτοιες Επικοινωνία: [email protected] 24 Ανάπτυξη Ενσωµατωµένων Συστηµάτων Linux - Κεφάλαιο 1 περιπτώσεις, στείλει το µήνυµα για το συµβάν στο σύστηµα και εκείνο δεν προχωρήσει άµεσα στην ακινητοποίηση της κορδέλας, τότε σίγουρα κάποια στιγµή θα προκληθεί σοβαρό εργατικό ατύχηµα. Ένα τέτοιο σύστηµα λοιπόν πρέπει να δουλεύει αυστηρά και σε πραγµατικό χρόνο (real time). Τα συστήµατα ήπιων χρονικών περιορισµών τα οποία είναι και τα πιο συνηθισµένα, δεν χρειάζεται να λειτουργούν σε πραγµατικό χρόνο. Για παράδειγµα, ένα µηχάνηµα αυτόµατης ανάληψης χρηµάτων δεν θα θεωρηθεί αναξιόπιστο αν αργήσει λίγο παραπάνω για να ολοκληρώσει µια εντολή συναλλαγής που του αναθέτουµε. Φυσικά ακόµη και στα συστήµατα αυτά, τα χρονικά όρια πρέπει να κινούνται σε κάποια λογικά πλαίσια διαφορετικά δίνεται στον χρήστη η εντύπωση ότι δε λειτουργούν σωστά. Συνεχίζοντας την κατηγοριοποίηση των ενσωµατωµένων συστηµάτων Linux θα ασχοληθούµε µε το κριτήριο δυνατότητας δικτύωσης. Με τον όρο “δυνατότητα δικτύωσης” καθορίζεται αν ένα ενσωµατωµένο σύστηµα µπορεί να συνδεθεί σε κάποιο δίκτυο ή όχι. Στις µέρες µας, περιµένουµε σχεδόν από κάθε συσκευή που αγοράζουµε να µπορεί να είναι προσβάσιµη µέσω δικτύου. Αυτό ορισµένες φορές ισχύει ακόµα και για τις “λευκές” οικιακές συσκευές (ψυγεία, πλυντήρια, κουζίνες κλπ). Όλα αυτά προσδίδουν νέες απαιτήσεις σε κάθε σύστηµα που σχεδιάζεται. Εποµένως, ένας ακόµη παράγοντας για τον οποίο επιλέγεται το Linux είναι και οι δυνατότητες δικτύωσης που παρέχει ο πυρήνας του. Ολοκληρώνοντας την αναφορά µας στους τύπους των ενσωµατωµένων συστηµάτων Linux θα εξετάσουµε την κατηγοριοποίησή τους ως προς τον βαθµό αλληλεπίδρασής τους µε τον τελικό χρήστη. Ο βαθµός αυτός είναι διαφορετικός για κάθε σύστηµα. Κάποια συστήµατα όπως είναι τα tablet PCs και τα PDAs βασίζονται σχεδόν ολοκληρωτικά στην αλληλεπίδρασή τους µε τον χρήστη παρέχοντάς ένα πλούσιο User Interface µε οθόνες αφής, πλούσια µενού και ηχητικές εντολές, ενώ άλλα, όπως είναι για παράδειγµα τα βιοµηχανικά συστήµατα ελέγχου παραγωγής, παρέχουν µόνο κάποια LEDs ενδείξεων και κουµπιά. 1.4 Γιατί να προτιµήσουµε το Linux Υπάρχει µεγάλο φάσµα κινήτρων για την ενσωµάτωση του Linux σε ένα ενσωµατωµένο σύστηµα. Πολλά από αυτά τα κίνητρα είναι ίδια µε εκείνα που µας κάνουν να επιλέγουµε το Linux ως Λειτουργικό Σύστηµα στους προσωπικούς υπολογιστές, τους servers και στους χώρους των επιχειρήσεων, ενώ άλλα είναι πιο εξειδικευµένα και αφορούν αποκλειστικά την φύση των ενσωµατωµένων συστηµάτων. Γενικά οι σηµαντικότεροι λόγοι που µας κάνουν να θέλουµε να χρησιµοποιούµε το Linux είναι οι παρακάτω: Ποιότητα και αξιοπιστία του κώδικα ∆ιαθεσιµότητα του κώδικα Ευρεία υποστήριξη υλικού Standards για πρωτόκολλα επικοινωνίας και λογισµικό ∆ιαθέσιµα εργαλεία Υποστήριξη από την κοινότητα Άδειες χρήσης λογισµικού Ανεξαρτησία από τον προµηθευτή Κόστος Ποιοτικός κώδικας είναι ο κώδικάς που προσφέρει επεκτασιµότητα, έχει σωστή δοµή, είναι ευανάγνωστος και παραµετροποιείται εύκολα. Η επεκτασιµότητα έχει να κάνει κυρίως µε τη δυνατότητα εύκολης προσθήκης νέων λειτουργιών. Για να µπορεί όµως να είναι εύκολο κάτι τέτοιο θα πρέπει µέσα στον κώδικα κάθε ξεχωριστή λειτουργία να έχει τη δική της ξεχωριστή, Ιστότοπος εργασίας: MyThesis.org 25 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητή (Router Implementation) ευδιάκριτη και ευανάγνωστη ενότητα. Η εύκολη παραµετροποίηση προκύπτει από τη δυνατότητα που παρέχει ο κώδικάς µας για την επιλογή των χαρακτηριστικών τα οποία θα είναι ή όχι, διαθέσιµα στην τελική εφαρµογή. Από την άλλη, αξιόπιστος κώδικας είναι ο κώδικας που παρέχει προβλεπτικότητα, ανοχή σε σφάλµατα και βιωσιµότητα. Η προβλεπτικότητα αφορά τη συµπεριφορά της τελικής εφαρµογής η οποία θα πρέπει να βρίσκεται µέσα στα πλαίσια που είχαν οριστεί εξ’ αρχής από εµάς. Η ανοχή σε σφάλµατα αφορά την οµαλή ανάδραση σε προβληµατικές περιστάσεις όπου επιπλέον θα πρέπει µέσω κατάλληλων µηνυµάτων να ειδοποιείται ο προγραµµατιστής σχετικά µε τη θέση µέσα στον κώδικα αλλά και το λόγο που προέκυψε το κάθε ξεχωριστό σφάλµα. Τέλος, η βιωσιµότητα αφορά την αδιάκοπη και ακέραια λειτουργία της εφαρµογής χωρίς κάποια υποβοήθηση από τον χρήστη για µεγάλα χρονικά διαστήµατα. Οι περισσότεροι προγραµµατιστές – µηχανικοί που έχουν ασχοληθεί µε τον πυρήνα του Linux, πιστεύουν ότι ο κώδικάς του πληροί όλες τις παραπάνω προϋποθέσεις και µπορεί να χαρακτηριστεί ποιοτικός και αξιόπιστος. Ως προς τη διαθεσιµότητα του κώδικα, είναι φανερό ότι το Linux υπερέχει κατά πολύ. Τόσο ο πηγαίος κώδικας, όσο και τα εργαλεία για να τον µεταγλωττίσουµε, είναι διαθέσιµα χωρίς περιορισµούς στην πρόσβασή τους από εµάς. Τα πιο σηµαντικά στοιχεία του Linux συµπεριλαµβανοµένου του kernel, διανέµονται υπό την άδεια χρήσης λογισµικού, GNU GPL (General Public License). Όταν κατά καιρούς προκύπτουν προβλήµατα στην πρόσβαση του πηγαίου κώδικα, η κοινότητα προσπαθεί άµεσα να τον αντικαταστήσει µε κάποιον άλλο αντίστοιχων ιδιοτήτων. Επίσης, οι διορθώσεις για προβλήµατα ασφαλείας είναι άµεσα διαθέσιµες και µπορούµε να αναβαθµίζουµε εύκολα και γρήγορα µε αυτές το σύστηµά µας. Τα κυριότερα πλεονεκτήµατα που προκύπτουν από τη διαθεσιµότητα του πηγαίου κώδικα, είναι η δυνατότητα που µας παρέχεται να µπορούµε να τον διορθώνουµε, να τον τροποποιούµε και να ψάχνουµε βαθύτερα σε αυτόν έτσι ώστε να καταλαβαίνουµε ευκολότερα τις λειτουργίες του και τις ιδιαιτερότητές του. Ένας άλλος λόγος ο οποίος µας οδηγεί στην επιλογή του Linux, είναι η ευρεία υποστήριξη υλικού που παρέχει. Το Linux υποστηρίζει πολλούς διαφορετικούς τύπους πλατφορµών υλικού και συσκευών. Επειδή οι περισσότεροι drivers γράφονται από την κοινότητα, µπορούµε να τους χρησιµοποιήσουµε µε τη σιγουριά ότι δεν θα πάψουν να υποστηρίζονται στο µέλλον όπως πιθανόν θα συνέβαινε σε περίπτωση που είχαν δηµιουργηθεί από κάποια εταιρεία. Ευρεία υποστήριξη υλικού, σηµαίνει επίσης ότι το Linux τρέχει σε δεκάδες διαφορετικές αρχιτεκτονικές µικροελεγκτών. Έτσι, βλέποντας κάποιον µικροελεγκτή µπορούµε να σκεφτούµε ότι πιθανότατα κάποιος έχει ήδη µπει στη διαδικασία προσαρµογής και παραµετροποίησης του πυρήνα ώστε να τον υποστηρίζει. Μπορούµε επίσης να περιµένουµε ότι η εφαρµογή που γράφουµε σε κάποια πλατφόρµα θα µπορεί εύκολα να µεταφερθεί σε µια άλλη µε πολύ µικρές αλλαγές. Αυτό ισχύει και για τους drivers. Ως προς τα πρότυπα του λογισµικού και των πρωτοκόλλων επικοινωνίας, το Linux παρέχει ευρεία υποστήριξη. Κάτι τέτοιο καθιστά εύκολη την ενσωµάτωση του σε ήδη υπάρχοντα frameworks καθώς και την ενσωµάτωση παλιότερων εκδόσεων λογισµικού σε αυτό. Έτσι, για παράδειγµα µπορεί εύκολα κάποιος να συνδέσει κάποιο σύστηµα Linux σε ένα ήδη υπάρχον δίκτυο Windows και να περιµένει από αυτό να εξυπηρετεί αιτήµατα µέσω του πρωτοκόλλου SAMBA. Το Linux µοιάζει µε το Unix και έτσι µπορούµε να µεταφέρουµε παλιές εφαρµογές του δεύτερου σε αυτό. Στη πραγµατικότητα, πολλές εφαρµογές που υπάρχουν εγκατεστηµένες στις διάφορες διανοµές, έχουν αρχικά γραφτεί για εµπορικές εκδόσεις του Unix και αργότερα µεταφέρθηκαν (ported) σε συστήµατα Linux. Σήµερα αρκετός κώδικας γράφεται για Linux πά- Επικοινωνία: [email protected] 26 Ανάπτυξη Ενσωµατωµένων Συστηµάτων Linux - Κεφάλαιο 1 ντα µε το κριτήριο της µεταφερσιµότητας. Μεταφερσιµότητα ακόµα και για συστήµατα που δεν είναι Linux, αφού είναι δυνατόν να τρέχουµε εφαρµογές Linux και σε Windows µέσω κάποιων βιβλιοθηκών συµβατότητας όπως είναι για παράδειγµα το Cygwin. Από τα παραπάνω αντιλαµβανόµαστε ότι υπάρχουν πολλά διαθέσιµα εργαλεία που έχουν γραφτεί για Linux και το γεγονός αυτό το κάνει ένα πολύ ευέλικτο λειτουργικό σύστηµα. Αν σκεφτούµε κάποια εφαρµογή την οποία χρειαζόµαστε είναι σχεδόν σίγουρο ότι κάποιοι από την κοινότητα θα έχουν ήδη νιώσει την ανάγκη να τη δηµιουργήσουν και να τη διαθέσουν δωρεάν στο Internet. Για να το αντιληφθεί καλύτερα αυτό κάποιος, αρκεί να επισκεφτεί τις ιστοσελίδες freshmeat.net και sourcefourge.net . Η υποστήριξη του Linux από την κοινότητά του είναι ένα σηµαντικότατο πλεονέκτηµα που έχει σε σχέση µε άλλα Λειτουργικά Συστήµατα. Σε αυτή την κοινότητα µπορούµε να νιώσουµε απόλυτα το πνεύµα του δωρεάν και ελεύθερου λογισµικού. Επίσης µέσω των αδειών χρήσης λογισµικού, µπορούµε να κάνουµε πράγµατα που ούτε θα µπορούσαµε να τα φανταστούµε µε βάση του τι ισχύει στην υπόλοιπη αγορά. Στην ουσία, µπορούµε να χρησιµοποιήσουµε, να τροποποιήσουµε και να αναδιανείµουµε το λογισµικό µας µε µοναδικό περιορισµό την παροχή των ίδιων ακριβώς δικαιωµάτων και στους αποδέκτες του. Είδαµε έως τώρα αρκετά από τα κυριότερα πλεονεκτήµατα που µας δίνουν σηµαντικά κίνητρα και λόγους έτσι ώστε να θέλουµε να χρησιµοποιήσουµε το Linux. Στη συνέχεια θα αναφέρουµε δύο ακόµα. Το πλεονέκτηµα της ανεξαρτησίας σε σχέση µε τον προµηθευτή (vendor) και το πλεονέκτηµα του κόστους του Linux. Ανεξαρτησία από τον προµηθευτή σηµαίνει ότι δε χρειάζεται να βασιστούµε σε κάποιον για να προµηθευτούµε το Linux ή για να το χρησιµοποιήσουµε. Αν όµως έχουµε επιλέξει ήδη κάποια εµπορική διανοµή ενός προµηθευτή και είµαστε δυσαρεστηµένοι, µπορούµε να τον αλλάξουµε αφού στην ουσία έχουµε τα ίδια δικαιώµατα µε αυτόν. Ορισµένοι προµηθευτές παρέχουν επιπλέον λογισµικό στις διανοµές τους, που δεν είναι open source. Για το κοµµάτι αυτό θα πρέπει να βρεθεί µια δική µας λύση ή κάποιος άλλος προµηθευτής. Τέτοια ζητήµατα πρέπει να λαµβάνονται σοβαρά υπ’ όψιν όταν επιλέγουµε διανοµή για το ενσωµατωµένο µας σύστηµα. Αφήσαµε το κόστος για το τέλος µιας και δεν έχει να κάνει µε το τεχνικό κοµµάτι του Linux. Είναι όµως, ίσως το σηµαντικότερο πλεονέκτηµα του Linux, σε σχέση µε άλλες λύσεις που υπάρχουν στην αγορά λογισµικού. Ιδιαίτερα τώρα που αυτές οι γραµµές γράφονται σε περίοδο βαθειάς οικονοµικής κρίσης. Γενικά, υπάρχουν τρία τµήµατα λογισµικού που κοστίζουν κατά την ανάπτυξη ενός κλασσικού ενσωµατωµένου συστήµατος: το αρχικό περιβάλλον ανάπτυξης τα επιπρόσθετα εργαλεία τα δικαιώµατα χρήσης Το «µηδενικό» κόστος του Linux είναι αποτέλεσµα των αδειών χρήσης ανοικτού λογισµικού και διαφέρει από οποιοδήποτε άλλο ενσωµατωµένο λειτουργικό σύστηµα. Με τη χρήση του Linux tα περισσότερα εργαλεία ανάπτυξης και τα τµήµατα του λειτουργικού είναι δωρεάν και οι άδειες υπό τις οποίες προστατεύονται, προστατεύουν την οικονοµική εκµετάλλευσή τους. 1.5 Έτοιµες διανοµές και εργαλεία ανάπτυξης Αρχικά θα πρέπει να επαναλάβουµε ότι δεν είναι υποχρεωτικό να αγοράσουµε µια έτοιµη διανοµή και τα έτοιµα εργαλεία ανάπτυξης που τη συνοδεύουν για να δηµιουργήσουµε ένα Ιστότοπος εργασίας: MyThesis.org 27 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητή (Router Implementation) ενσωµατωµένο σύστηµα Linux. Όλα τα απαραίτητα πακέτα λογισµικού είναι ήδη διαθέσιµα για κατέβασµα από το Internet, και είναι τα ίδια πακέτα τα οποία κατεβάζουν και τυποποιούν οι προµηθευτές διανοµών. Επίσης, οι έτοιµες λύσεις ίσως να µην ταιριάζουν απόλυτα σε αυτό που επιθυµούµε εµείς να κάνουµε. Γι’ αυτό αν θέλουµε να έχουµε τον έλεγχο των περιεχοµένων του συστήµατός µας, πρέπει να τα δηµιουργήσουµε «µόνοι» µας. Κατά τη διάρκεια της εκπόνησης αυτής της εργασίας χρειάστηκαν ολόκληρες εβδοµάδες απλά και µόνο για τη δηµιουργία του περιβάλλοντος ανάπτυξης. Ένα από τα δυσκολότερα κοµµάτια αυτού του περιβάλλοντος ήταν η αλυσίδα µεταγλώττισης (toolchain – θα δούµε περισσότερα γι’ αυτή σε αντίστοιχη ενότητα). Κάποιες στιγµές λοιπόν, η επιλογή µια έτοιµης λύσης από κάποιο προµηθευτή ήταν πολύ δελεαστική και λογική υπόθεση. Για την επιλογή αυτή υπάρχουν ορισµένα χαρακτηριστικά στις διάφορες διανοµές που αν τα εξετάσουµε µπορούµε να οδηγηθούµε σε κάποια σωστή απόφαση. Η ευκολία για παράδειγµα, της εγκατάστασης του περιβάλλοντος ανάπτυξης καθώς και της µεταφοράς του αποτελέσµατος στο ενσωµατωµένο µας σύστηµα, θα πρέπει να είναι σηµαντικά κριτήρια για την επιλογή έτοιµης διανοµής. Ορισµένοι ακόµα παράγοντες που θα πρέπει να προσέξουµε είναι η διαθέσιµη τεκµηρίωση (εσωτερική ή/και εξωτερική) και ο βαθµός παραµετροποίησης που µας δίνεται κατά τη διαδικασία εγκατάστασης καθώς και η αυτοµατοποίηση των διαδικασιών. Κάποιες φορές επίσης, ίσως είναι προτιµότερο να κινηθούµε αντίθετα από τη φυσική διαδικασία ανάπτυξης. ∆ηλαδή, πρώτα να επιλέξουµε διανοµή και µετά µικροελεγκτή ή πλακέτα ανάπτυξης (development board). Αν δεν είµαστε υποχρεωµένοι να χρησιµοποιήσουµε έναν συγκεκριµένο µικροελεγκτή, ίσως θα ήταν καλύτερα να ερευνήσουµε πρώτα την υποστήριξη που παρέχουν οι γνωστοί προµηθευτές ενσωµατωµένων διανοµών Linux (πχ MontaVista) για γνωστούς µικροελεγκτές και έπειτα να λειτουργήσουµε ανάλογα. Κατά την αναζήτηση όλων αυτών των πληροφοριών συγκεντρώθηκε αρκετή γνώση ώστε τελικά να µην είναι απαραίτητη µια έτοιµη διανοµή αλλά να χρησιµοποιηθούν µόνο τα απαραίτητα και δωρεάν εργαλεία. Αυτό παρ’ όλες τις δυσκολίες που υπήρξαν έδωσε µια αίσθηση ανεξαρτησίας και ικανοποίησης. Εξάλλου αυτός είναι και ο λόγος που επιλέξαµε το Linux. Το επιλέξαµε γιατί το µέλλον του δεν καθορίζεται από κανέναν εµπορικό παράγοντα, και για την ανεξαρτησία που µας παρέχει. 1.6 Μεθοδολογία σχεδίασης και ανάπτυξης Ο σχεδιασµός και η υλοποίηση ενός ενσωµατωµένου συστήµατος Linux µπορούν να πραγµατοποιηθούν µε συγκεκριµένο τρόπο. Η διαδικασία περιλαµβάνει πολλά στάδια και κάποια από αυτά µπορούν να γίνουν παράλληλα, ενώ άλλα µπορούν ακόµα και να παραληφθούν για την εξοικονόµηση χρόνου. Γενικά τα κυριότερα στάδια ανάπτυξης ενός ενσωµατωµένου συστήµατος Linux είναι τα εξής: Καθορισµός των στοιχείων (components) υλικού του συστήµατος ∆ιαµόρφωση (configuration) και δηµιουργία (build) του kernel ∆ηµιουργία του root ριζικού συστήµατος αρχείων (root filesystem) Εγκατάσταση και παραµετροποίηση του λογισµικού εκκίνησης (bootloader) Το πρώτο στάδιο όπως εύκολα αντιλαµβανόµαστε, είναι η επιλογή του υλικού. ∆ηλαδή, των υποσυστηµάτων που θα απαρτίζουν το ενσωµατωµένο µας σύστηµα. Μικροελεγκτής, µνήµη, ολοκληρωµένα, θύρες επικοινωνίας, αντιστάσεις, πυκνωτές και πολλά άλλα, “κόβονται και ράβονται” επάνω σε µια πλακέτα τυπωµένου κυκλώµατος για να αποτελέσουν εν τέλει, ένα ολοκληρωµένο ενσωµατωµένο σύστηµα, που στην παρούσα εργασία θα είναι ένας router. Το λογισµικό, δηλαδή ο προγραµµατισµός και η ανάπτυξη, είναι ξεχωριστά θέµατα που πραγµατοποιούνται σε επόµενα στάδια. Αυτό το µοντέλο είναι πολύ λογικό όταν αναπτύσ- Επικοινωνία: [email protected] 28 Ανάπτυξη Ενσωµατωµένων Συστηµάτων Linux - Κεφάλαιο 1 σουµε λογισµικό σε επίπεδο hardware. Σε αρκετές περιπτώσεις το πρόγραµµα που γράφουµε τρέχει µόνο σε ένα συγκεκριµένο σύστηµα, οπότε πρέπει πρώτα να γνωρίζουµε την αρχιτεκτονική του και έπειτα να γράψουµε κώδικα που θα εκτελείται σε αυτό. Το πρώτο στάδιο ανάπτυξης των ενσωµατωµένων συστηµάτων Linux είναι και το µοναδικό ίσως, που δεν µπορεί να πραγµατοποιηθεί παράλληλα µε άλλα στάδια. Ο καθορισµός των απαιτήσεων καθώς και η συµµόρφωση του Linux σε αυτές τις απαιτήσεις (πχ το µέγεθος συστήµατος αρχείων root) πρέπει να ολοκληρωθεί πριν απ’ οτιδήποτε άλλο. Λόγω της ταχέως µεταβαλλόµενης φύσης του Linux είναι πολύ πιθανό κάποιος να θέλει τις τελευταίες και καλύτερες εκδόσεις λογισµικού για το σύστηµά του. Σε αυτή την εργασία κάτι τέτοιο αποφεύχθηκε έτσι ώστε να µην δηµιουργηθούν προβλήµατα που θα οφείλονταν στην ανωριµότητα αυτών των νέων εκδόσεων. Επίσης κάθε τµήµα λογισµικού είναι πιθανό να εξαρτάται και από κάποιο άλλο (dependency) το οποίο θα πρέπει επίσης να αναβαθµιστεί και έτσι είναι πολύ εύκολο να επιδοθούµε σε έναν ατελείωτο αγώνα αναβαθµίσεων που στο τέλος µπορεί να οδηγήσει και σε κάποιο αδιέξοδο ασυµβατότητας ή να καταστήσει το σύστηµά µας ανεπαρκές από άποψη υλικού. Για τους λόγους αυτούς χρησιµοποιήθηκε ώριµο και δοκιµασµένο λογισµικό το οποίο δεν παρουσίαζε σφάλµατα και είχε λογικές απαιτήσεις από το σύστηµά µας. Αφού ολοκληρωθεί το πρώτο στάδιο και έχουµε επιλέξει τα κατάλληλα χαρακτηριστικά για το σύστηµά µας, µπορούµε στη συνέχεια να επιλέξουµε ποια έκδοση του kernel θα χρησιµοποιήσουµε καθώς και τις σχετικές ρυθµίσεις που απαιτούνται. Ανεξάρτητα από το εάν αποφασίσουµε να ακολουθήσουµε τις ενηµερώσεις του kernel, θα πρέπει να κρατάµε σταθερή την παραµετροποίηση του κατά τη διάρκεια της ανάπτυξης του συστήµατος. Αυτό θα βοηθήσει στο να µην αποκόπτονται κάποια τµήµατα τα οποία έχουν ολοκληρωθεί. Για να επιτευχθεί κάτι τέτοιο θα πρέπει να έχουµε µελετήσει πολύ καλά τις επιλογές παραµετροποίησης του kernel και να τις έχουµε προσαρµόσει στις απατήσεις του συστήµατός µας εξ’ αρχής. Αφού έχει πια αποφασιστεί η παραµετροποίηση του kernel, µπορεί να ακολουθήσει η δηµιουργία του. Παράλληλα µε το χειρισµό των διαφόρων θεµάτων του πυρήνα, µπορούµε να προχωρήσουµε στην δηµιουργία του κεντρικού συστήµατος αρχείων (root filesystem). Το κεντρικό σύστηµα αρχείων ενός ενσωµατωµένου συστήµατος είναι παρόµοιο µε αυτό ενός κανονικού υπολογιστή που τρέχει Linux, µε τη διαφορά ότι περιλαµβάνει ένα πολύ µικρότερο σύνολο εφαρµογών, βιβλιοθηκών και αρχείων που απαιτούνται για τη λειτουργία του συστήµατος. Στη συνέχεια ακολουθεί η επιλογή του λογισµικού εκκίνησης (bootloader). Η επιλογή εξαρτάται άµεσα από την αρχιτεκτονική του συστήµατος. Υπάρχουν διάφοροι bootloaders που γράφονται ο καθένας τους ειδικά για κάποιο σύστηµα. Ακόµα και bootloaders οι οποίοι αναπτύσσονται για την ίδια αρχιτεκτονική µπορεί να διαφέρουν αρκετά µεταξύ τους. Η µεθοδολογία του πακεταρίσµατος και της εκκίνησης ενός συστήµατος µοιάζει αρκετά µεταξύ των διαφορετικών αρχιτεκτονικών αλλά µπορεί να διαφέρει αρκετά σε σχέση µε τη συσκευή µόνιµης αποθήκευσης από την οποία το σύστηµα εκκινεί. Η εγκατάσταση και παραµετροποίηση των συσκευών αποθήκευσης και του λογισµικού εκκίνησης (bootloader), είναι οι τελικές εργασίες που αποµένουν για τη ολοκλήρωση της δηµιουργίας του συστήµατός µας. Κατά τη διάρκεια αυτών των βηµάτων τα διαφορετικά στοιχεία (δηλαδή ο bootloader, το σύστηµα αρχείων root και ο kernel) συνενώνονται προκειµένου να αποτελέσουν το λογισµικό ενός πλήρους ενσωµατωµένου συστήµατος Linux. Ιστότοπος εργασίας: MyThesis.org 29 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητή (Router Implementation) 1.7 Εργαλεία ανάπτυξης και αποσφαλµάτωσης Σε προηγούµενο κεφάλαιο που ασχοληθήκαµε µε το firmware των ενσωµατωµένων συστηµάτων αναφέραµε το µοντέλο host – target. Υπάρχουν δύο πτυχές σε αυτό το µοντέλο, η ανάπτυξη και η αποσφαλµάτωση. Πριν δοκιµάσουµε να εκτελέσουµε λογισµικό στο σύστηµα target θα πρέπει να έχουµε δηµιουργήσει µια σύνδεση µεταξύ αυτού και του συστήµατος host. Η σύνδεση θα λειτουργεί σαν οµφάλιος λώρος µέσω του οποίου θα µπορούµε να αλληλεπιδρούµε µε το target σύστηµα και να εξακριβώνουµε εάν οι εφαρµογές που αναπτύσσει λειτουργούν όπως προβλέπεται (debugging). Η απόκτηση ενός τέτοιου περιβάλλοντος απαιτεί την µεταγλώττιση των εφαρµογών και των βιβλιοθηκών του συστήµατος target. Αυτό επιτυγχάνεται παραµετροποιώντας ή χτίζοντας τους διάφορους compilers και τα υπόλοιπα εργαλεία ανάπτυξης για διασταυρωµένη ανάπτυξη (cross development). Χρησιµοποιώντας αυτά τα βοηθητικά προγράµµατα, µπορούµε να δηµιουργήσουµε εφαρµογές για το target σύστηµά µας και ένα περιβάλλον για περαιτέρω ανάπτυξη. Στη συνέχεια µπορούµε να επιλέξουµε κάποιο IDE το οποίο θα µας βοηθήσει να αναπτύξουµε το project µας ευκολότερα. Επίσης θα µας δώσει τη δυνατότητα να χρησιµοποιήσουµε συστήµατα διαχείρισης εκδόσεων λογισµικού, όπως το CVS (Concurrent Versions System) , το Subversion και το GIT. Λόγω των υψηλών δυνατοτήτων που διαθέτουν ορισµένα ενσωµατωµένα συστήµατα, µερικοί προγραµµατιστές επιλέγουν να ολοκληρώσουν όλη την ανάπτυξη στο σύστηµα target. Σε αυτή τη διαµόρφωση όλα τα εργαλεία µεταγλώττισης και ανάπτυξης βρίσκονται στο σύστηµα αυτό. Έτσι το µοντέλο host – target συνδυάζεται σε ένα και µόνο σύστηµα, παρέχοντας το άµεσο πλεονέκτηµα της ταχύτερης ανάπτυξης αφού στην ουσία δεν χρειάζεται να στηθεί κάποιο host σύστηµα. Όποιο σύστηµα ανάπτυξης και αν επιλέξουµε, κάποια στιγµή θα χρειαστεί να κάνουµε αποσφαλµάτωση στο σύστηµά µας. Αυτό µπορεί να επιτευχθεί τόσο µε τη βιβλιοθήκη gdb που είναι η πιο κοινώς χρησιµοποιούµενη για αποσφαλµάτωση γενικού σκοπού (general purpose debugging), όσο και µε µια απλή συνάρτηση της βιβλιοθήκης της C όπως είναι για παράδειγµα, η εµφάνιση κάποιων τιµών µέσω της συνάρτησης printf(). Κάποια προβλήµατα όµως που απαιτούν περισσότερη εσωτερική ανάλυση των λειτουργιών που τρέχουν κατά το χρόνο εκτέλεσης, µπορούν να εντοπιστούν µόνο µέσω συµβολικής αποσφαλµάτωσης (symbolic debugging). Η συµβολική αποσφαλµάτωση µπορεί να είναι πιο λεπτοµερής. Μπορεί να υποστηρίζει αποµακρυσµένη αποσφαλµάτωση µέσω της σειριακής επικοινωνίας, αποσφαλµάτωση του kernel, και εργαλεία για hardware debugging όπως είναι το JTAG και το BDM. Παρ’ όλα αυτά, ακόµα και η συµβολική αποσφαλµάτωση µπορεί να είναι ανεπαρκής σε κάποιες περιπτώσεις. Όταν παρουσιάζονται προβληµατικές κλήσεις συστήµατος από κάποια εφαρµογή, ή όταν πρέπει να επιλυθούν προβλήµατα συγχρονισµού, είναι καλύτερα να χρησιµοποιούνται εργαλεία ανίχνευσης όπως τα εργαλεία strace και LTT (Linux Trace Toolkit). Για προβλήµατα απόδοσης, υπάρχουν εργαλεία που είναι πιο προσαρµοσµένα στην διεργασία, όπως τα gprof και gcov. Όταν όµως όλα αυτά που αναφέραµε αποτυγχάνουν, τότε το καλύτερο που έχουµε να κάνουµε είναι να µελετήσουµε καλύτερα τη λειτουργία του kernel και τους συνηθέστερους λόγους για τους οποίους κάποιες φορές καταρρέει. Επικοινωνία: [email protected] 30 Ανάπτυξη Ενσωµατωµένων Συστηµάτων Linux - Κεφάλαιο 1 1.8 Οι τύποι ανάπτυξης Υπάρχουν τρεις βασικοί τύποι host – target ανάπτυξης ενσωµατωµένων συστηµάτων Linux: ∆ιασυνδεµένος (Linked setup) Αφαιρούµενου αποθηκευτικού µέσου (Removable Storage setup) Αυτόνοµος (Standalone setup) Στον διασυνδεδεµένο τύπο, τα συστήµατα target και host είναι, όπως φαίνεται και στην επόµενη εικόνα, µόνιµα συνδεδεµένα µεταξύ τους µε κάποιο φυσικό µέσο Το µέσο αυτό είναι συνήθως κάποιο σειριακό καλώδιο ή κάποιο καλώδιο Ethernet. Εικόνα 1. ∆ιασυνδεδεµένος τύπος ανάπτυξης ενσωµατωµένων συστηµάτων Linux H κύρια ιδιότητα αυτού του τύπου είναι ότι δεν υπάρχει κάποιο µέσο αποθήκευσης που να παρεµβάλλεται µεταξύ του host και του target. Όλες οι µεταφορές γίνονται µέσω του καλωδίου που ενώνει τα δύο αυτά συστήµατα. Όπως βλέπουµε και στην εικόνα, το σύστηµα host περιλαµβάνει το cross-platform περιβάλλον ανάπτυξης, ενώ το target, έναν κατάλληλο bootloader, έναν λειτουργικό kernel και ένα κεντρικό σύστηµα αρχείων (root filesystem). Εναλλακτικά, µπορούµε να χρησιµοποιήσουµε αποµακρυσµένα στοιχεία για την ανάπτυξή του target. Ο πυρήνας θα µπορούσε για παράδειγµα να είναι διαθέσιµος µέσω του πρωτοκόλλου TFTP (Trivial File Transfer Protocol) από τον host. Το root σύστηµα αρχείων θα µπορούσε επίσης να φορτώνεται µέσω του πρωτοκόλλου NFS αντί να βρίσκεται αποθηκευµένο σε κάποια µνήµη του target. Η χρήση ενός root συστήµατος αρχείων που φορτώνεται µέσω του NFS είναι µια πολύ καλή τακτική κατά τη διάρκεια της ανάπτυξης, γιατί έτσι αποφεύγουµε την συνεχή µεταφορά και αντιγραφή του τροποποιηµένου κώδικα από το σύστηµα host στο target. Θα δούµε αργότερα µε περισσότερη λεπτοµέρεια όλους τους δυνατούς τύπους εκκίνησης (boot configurations). Ο διασυνδεδεµένος τύπος ανάπτυξης είναι ο πιο συνηθισµένος (και είναι κατά κύριο λόγο ο τύπος µε τον οποίο αναπτύχθηκε το λογισµικό του router σε αυτή την εργασία). Το καλώδιο που υπάρχει σε αυτό τον τύπο ανάπτυξης και συνδέει το target σύστηµα µε το host, µπορεί να χρησιµοποιηθεί και για debugging (αποσφαλµάτωση). Πολλά ενσωµατωµένα συστήµατα παρέχουν ταυτόχρονα δυνατότητες σύνδεσης Ethernet και RS232 (σειριακή). Συνήθως η πρώτη χρησιµοποιείται λόγω της µεγάλης ταχύτητας µεταγωγής της, για το κατέβασµα του εκτελέσιµου image, του kernel, του root συστήµατος αρχείων και άλλων µεγάλων σε όγκο αντικειµένων, ενώ η σειριακή επικοινωνία χρησιµοποιείται για debugging (πχ µέσω κονσόλας µε την εφαρµογή Putty ή µε το Hyper Terminal των Windows). Σε καινούριους υπολογιστές πολλές φορές παραλείπεται η ενσωµάτωση σειριακή θύρας. Αυτό λύνεται εύκολα µε κάποιον USB to Serial προσαρµογέα (adaptor). Τον προσαρµογέα µπορούµε είτε να τον αγοράσουµε, είτε να τον σχεδιάσουµε και να τον κατασκευάσουµε πολύ εύκολα οι ίδιοι. Ο προσαρµογέας αυτός, παρέχει µια εικονική σειριακή θύρα (Virtual COM Ιστότοπος εργασίας: MyThesis.org 31 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητή (Router Implementation) Port) µέσω της οποίας µπορεί τελικά να υπάρξει επικοινωνία. Βέβαια σε περιπτώσεις που θέλουµε να κάνουµε πραγµατικό σειριακό debugging, η σειριακή θύρα είναι απαραίτητη. Ας περάσουµε όµως στον επόµενο τύπο ανάπτυξης, τον τύπο αφαιρούµενου αποθηκευτικού µέσου (Removable Storage Setup). Σε αυτή την περίπτωση δεν υπάρχει κάποιο καλώδιο που να συνδέει το σύστηµα host µε το σύστηµα target. Αντί αυτού, υπάρχει κάποια συσκευή αποθήκευσης η οποία γράφεται από το host: Εικόνα 2. Ανάπτυξη ενσωµατωµένων συστηµάτων Linux µε αφαιρούµενο αποθηκευτικό µέσο Έπειτα µεταφέρεται στο target και χρησιµοποιείται για την εκκίνηση της συσκευής. Όπως και µε τον προηγούµενο τύπο ανάπτυξης (Linked Setup), έτσι κι εδώ, το σύστηµα host έχει εγκατεστηµένο το cross-platform περιβάλλον ανάπτυξης. Παρ’ όλα αυτά, το σύστηµα target περιλαµβάνει και έναν πολύ µικρό bootloader για να µπορεί να διαβάζει από την συσκευή αποθήκευσης. Το υπόλοιπο λογισµικό (που έχει δηµιουργηθεί από το σύστηµα host) βρίσκεται αποθηκευµένο σε κάποια αφαιρούµενη συσκευή (πχ CompactFlash, MMC Card, και πιο σπάνια σε CD, DVD κλπ). Στην πραγµατικότητα το σύστηµα target είναι δυνατόν να µην περιλαµβάνει καµία µόνιµη µνήµη (εκτός από µια ελάχιστη ενσωµατωµένη flash για τον bootloader). Μπορεί να υπάρχει κάποια θύρα (socket) όπου κάποια αποσπώµενη µνήµη flash θα µπορεί να τοποθετείται και να αφαιρείται. Η µνήµη αυτή θα προγραµµατίζεται από κάποιον flash programmer στην µεριά του host και έπειτα θα τοποθετείται στη θύρα του target για κανονική λειτουργία. Αυτός ο τύπος ανάπτυξης ενσωµατωµένων συστηµάτων Linux, είναι πολύ δηµοφιλής κατά τις αρχικές φάσεις ανάπτυξης ενσωµατωµένων συστηµάτων. Μετά από τα αρχικά στάδια της ανάπτυξης ίσως είναι πιο πρακτικό να χρησιµοποιούµε τον πρώτο τύπο (Linked Setup), έτσι ώστε να αποφύγουµε την µεταφορά του αποθηκευτικού µέσου µεταξύ του host και του target κάθε φορά που κάνουµε αλλαγές στον kernel ή στο σύστηµα αρχείων root. Ο τρίτος τύπος ανάπτυξης (Standalone setup) ξεφεύγει λιγάκι από τα ενσωµατωµένα συστήµατα και από τα πλαίσια της εργασίας. Γι’ αυτό απλά θα αναφέρουµε µόνο τα πολύ βασικά. Εδώ υπάρχει µόνο ένα σύστηµα, το σύστηµα target. Το σύστηµα αυτό λειτουργεί ως ένα αυτόνοµο σύστηµα ανάπτυξης. Περιλαµβάνει όλο το απαραίτητο software για την εκκίνηση (boot), την λειτουργία και τα επιπρόσθετα εργαλεία που είναι απαραίτητα για την περαιτέρω ανάπτυξή του. Στην ουσία, αυτός ο τύπος είναι παρόµοιος µε έναν κανονικό προσωπικό υπολογιστή, µε τη διαφορά ότι το υλικό του δεν είναι συµβατικό υλικό για PC: Επικοινωνία: [email protected] 32 Ανάπτυξη Ενσωµατωµένων Συστηµάτων Linux - Κεφάλαιο 1 Εικόνα 3. Αυτόνοµος τύπος ανάπτυξης ενσωµατωµένων συστηµάτων Linux Σε αντίθεση µε τους άλλους τύπους ανάπτυξης, αυτός ο τύπος δεν απαιτεί επικοινωνία µε κάποιο άλλο σύστηµα, ούτε κάποιο cross-platform περιβάλλον ανάπτυξης, αφού όλα τα εργαλεία που απαιτούνται υπάρχουν στο σύστηµα target. Kαι οι τρεις τύποι ανάπτυξης που αναφέραµε, µπορούν να χρησιµοποιηθούν συνδυαστικά ή και όλοι µαζί, κατά την ανάπτυξη ενός ενσωµατωµένου συστήµατος Linux. 1.9 Οι τύποι αποσφαλµάτωσης Υπάρχουν τρεις βασικοί τύποι διεπαφών τους οποίους χρησιµοποιούν οι προγραµµατιστές για να συνδέσουν το σύστηµα target µε το host για αποσφαλµάτωση: Σειριακά (serial link) ∆ικτυακά (network interface) Με ειδικό υλικό αποσφαλµάτωσης (hardware debugging) Κάθε τύπος έχει τα δικά του πλεονεκτήµατα και περιορισµούς. Επίσης κάθε τύπος είναι κατάλληλος για συγκεκριµένες περιπτώσεις. Ο πιο απλός από αυτούς είναι η σειριακή σύνδεση. Αυτό συµβαίνει επειδή, το υλικό και το λογισµικό γι’ αυτή τη µορφή επικοινωνίας, είναι πολύ απλό και βρίσκεται σχεδόν πάντα υλοποιηµένο στους µικροελεγκτές. Υπάρχουν όµως δύο βασικοί περιοριστικοί παράγοντες. Ο ένας είναι η χαµηλή ταχύτητα και ο άλλος, ότι αν υπάρχει µόνο µια σειριακή θύρα στο ενσωµατωµένο µας σύστηµα, και η θύρα αυτή είναι ο µοναδικός διαθέσιµος τρόπος επικοινωνίας µε τον έξω κόσµο, είναι αδύνατο να κάνουµε ταυτόχρονα και debugging, αλλά και να επιβλέπουµε το σύστηµά µας µέσω κάποιου προσοµοιωτή τερµατικού (terminal emulator). Παρ’ όλα αυτά η χρήση ενός terminal δεν είναι πάντοτε απαραίτητη. Για παράδειγµα όταν κάνουµε debugging κατά την εκκίνηση του kernel µε τη χρήση κάποιου remote kernel debugger, δεν χρειάζεται να χρησιµοποιήσουµε κάποιου είδους terminal αφού ακόµη δεν έχει φορτωθεί ο φλοιός (shell) του kernel. Ας περάσουµε όµως στον δεύτερο τύπο debugging. Η χρήση µιας διεπαφής δικτύου όπως είναι το Ethernet, παρέχει πολύ υψηλότερες ταχύτητες από την σειριακή σύνδεση. Επιπλέον, το σύστηµα host µπορεί να δηµιουργήσει περισσότερες από µια συνδέσεις µε το σύστηµα target, µέσω της ίδιας γραµµής επικοινωνίας. Έτσι µπορούµε να συνεχίσουµε να αλληλεπιδρούµε µαζί του, ενώ την ίδια στιγµή κάνουµε debugging σε αυτό. Μπορούµε επίσης να συνδεθούµε ταυτόχρονα και µε τη σειριακή επικοινωνία. Για την δικτυακή επικοινωνία όµως απαιτείται και µια στοίβα πρωτοκόλλων δικτύωσης (networking stack). Η στοίβα αυτή περιλαµβάνεται στον Linux kernel. Όµως αφού η στοίβα αυτή βρίσκεται µέσα στον ίδιο τον kernel, είναι δύσκολο να κάνουµε debugging στο ενσωµατωµένο µας σύστηµα χρησιµοποιώντας την. Είναι σαν να προσπαθούµε να µετακινήσουµε ένα αντικείµενο ενώ στεκόµαστε Ιστότοπος εργασίας: MyThesis.org 33 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητή (Router Implementation) επάνω του. Για το λόγο αυτό τις περισσότερες φορές το debugging πραγµατοποιείται µέσω της σειριακής σύνδεσης. Οι δύο τύποι debugging που αναφέραµε, απαιτούν την ύπαρξη έστω και ενός ελάχιστου λογισµικού που θα αναγνωρίζει τις δοµικές µονάδες εισόδου – εξόδου (primitive I/O hardware) του target συστήµατος , για να επιτευχθεί είτε σειριακή, είτε δικτυακή επικοινωνία µε το host. Σε κάποιες περιπτώσεις όµως, όπως για παράδειγµα κατά την ανάπτυξη ενός συστήµατος από το µηδέν ή κατά το debugging του ίδιου του kernel, το λογισµικό αυτό δεν είναι διαθέσιµο. Για το λόγο αυτό απαιτείται µια διεπαφή αποσφαλµάτωσης που να παρέχει άµεσο έλεγχο µέσω hardware. Οι περισσότεροι µηχανικοί προκειµένου να επιτύχουν απευθείας έλεγχο µέσω υλικού, χρησιµοποιούν JTAG debuggers. Κατά την ανάπτυξη του router αυτής της εργασίας χρησιµοποιήθηκε η συσκευή JTAGICE mkII (programmer – debugger – emulator) της εταιρείας Atmel. 1.10 Η αρχιτεκτονική ενός ενσωµατωµένου συστήµατος Linux Tα συστήµατα Linux απαρτίζονται από πολλά και διαφορετικά συστατικά. Ας ρίξουµε µια µατιά στη γενική αρχιτεκτονική (generic architecture) ενός τέτοιου συστήµατος. Αυτό θα µας επιτρέψει να οριοθετήσουµε το κάθε ένα από αυτά τα συστατικά σε ένα πλαίσιο, να κατανοήσουµε τον τρόπο αλληλεπίδρασης µεταξύ τους και να εκµεταλλευτούµε τη δοµή τους. Στην εικόνα βλέπουµε ένα γενικό σχεδιάγραµµα της αρχιτεκτονικής ενός ενσωµατωµένου συστήµατος Linux: Εικόνα 4. Η αρχιτεκτονική ενός ενσωµατωµένου συστήµατος Linux Παρά το γεγονός του ότι η εικόνα περιλαµβάνει µεγάλο ποσοστό αφαίρεσης, είναι αρκετή για να µπορέσουµε σε αυτό το αρχικό στάδιο να αναλύσουµε αρκετά πράγµατα. Μπορούµε για παράδειγµα να πούµε ότι δεν υπάρχει µεγάλη διαφορά ανάµεσα σε ένα ενσωµατωµένο σύστηµα Linux και σε ένα PC που τρέχει Linux. Tα χαρακτηριστικά του υλικού που πρέπει να ικανοποιούνται γενικά από ένα ενσωµατωµένο σύστηµα έτσι ώστε να µπορεί να εκτελείται µε επιτυχία σε αυτό ο πυρήνας του Linux είναι: Επικοινωνία: [email protected] 34 Ανάπτυξη Ενσωµατωµένων Συστηµάτων Linux - Κεφάλαιο 1 Μικροελεγκτής 32bit µε µονάδα διαχείρισης µνήµης (MMU – Memory Management Unit) Επαρκής RAM ∆υνατότητες επικοινωνίας µέσω µονάδων εισόδου – εξόδου, για αλληλεπίδραση µε το περιβάλλον και για να είναι δυνατή η πραγµατοποίηση debugging Ο πυρήνας πρέπει να µπορεί να φορτώσει το root σύστηµα αρχείων σε κάποιας µορφής µόνιµη µνήµη, ή να µπορεί να το προσπελάσει µέσω δικτύου. Όπως βλέπουµε και στο σχεδιάγραµµα πιο πάνω, ακριβώς επάνω από το hardware βρίσκεται ο kernel. Ο σκοπός του είναι να διαχειρίζεται το υλικό ενώ ταυτόχρονα παρέχει οικείο περιβάλλον λογισµικού χρήστη (όπως το POSIX API και τα υπόλοιπα standard Application Interfaces ή APIs, επάνω στα οποία γράφονται οι διάφορες εφαρµογές). Ο kernel οδηγεί τις συσκευές, διαχειρίζεται τις µονάδες εισόδου – εξόδου, διαχειρίζεται το χρονοπρογραµµατισµό των διαφόρων λειτουργιών, ελέγχει το διαµοιρασµό της µνήµης, χειρίζεται σήµατα, και γενικότερα αναλαµβάνει κάθε διαχειριστικό καθήκον των πόρων του συστήµατος. Στον kernel υπάρχουν δύο µεγάλες κατηγορίες διαστρωµατωµένων υπηρεσιών (layered services) οι οποίες παρέχουν τη λειτουργικότητα που απαιτείται από τις εφαρµογές. Οι χαµηλού επιπέδου διεπαφές (low – level interfaces) είναι υπεύθυνες για τη διαµόρφωση του υλικού στο οποίο εκτελείται ο kernel και παρέχουν άµεσο έλεγχο προς τα ανώτερα στρώµατα για τη διαχείριση των πόρων, χρησιµοποιώντας ένα API ανεξάρτητο από το υλικό. Με λίγα λόγια το low – level interface, χειρίζεται το υλικό του συστήµατος αλλά ταυτόχρονα παρέχει και στα ανώτερα στρώµατα µια διαφανή επικοινωνία µε αυτό. Αυτό είναι κάτι απολύτως απαραίτητο αν σκεφτούµε ότι αλλιώς χειρίζεται τους καταχωρητές ή τις σελίδες της µνήµης ένας µικροελεγκτής αρχιτεκτονικής ARM και αλλιώς ένας AVR32. Αν θέλουµε να µιλήσουµε ακόµα πιο τεχνικά, µπορούµε να πούµε ότι τα low – level Interfaces χειρίζονται εντολές µικροελεγκτή, ενέργειες στη µνήµη, σχεδόν ανεξάρτητα από την αρχιτεκτονική, και επίσης αποτελούν βασική διεπαφή για τις συσκευές. Όλα αυτά στη συνέχεια προωθούνται στα υψηλότερα επίπεδα µέσω κεφαλίδων (headers), µακροεντολών (macros) και συναρτήσεων (wrapper functions). Πάνω από τα χαµηλά στρώµατα του πυρήνα, άλλα, υψηλότερου επιπέδου στρώµατα παρέχουν τη διαφάνεια για λειτουργίες που είναι κοινές σε όλα τα Linux συστήµατα. Τέτοιες λειτουργίες αφορούν διεργασίες, αρχεία, sockets, και σήµατα. Εφόσον τα χαµηλού επιπέδου APIs που παρέχονται από τον kernel είναι κοινά µεταξύ των διαφορετικών αρχιτεκτονικών, ο κώδικας που ενσωµατώνουν τα υψηλότερου επιπέδου APIs είναι σχεδόν ίδιος σε κάθε µια από αυτές τις αρχιτεκτονικές. Φυσικά πάντα υπάρχουν κάποιες εξαιρέσεις. Μεταξύ αυτών των δύο επιπέδων (low και high) o kernel χρειάζεται κάποιες φορές, διερµηνευτές (interpreters) για την κατανόηση µηνυµάτων που µεταφέρονται από και προς τις συσκευές. Τα διάφορα συστήµατα αρχείων (πχ JFFS) και τα πρωτόκολλα δικτύωσης (πχ ΤCP/IP), είναι παραδείγµατα τέτοιων µηνυµάτων που ο kernel προσπαθεί να µεταφράσει έτσι ώστε να παρέχει πρόσβαση από και προς τα δεδοµένα τους. Οι σκληροί δίσκοι είναι οι κατεξοχήν συσκευές αποθήκευσης για τα δεδοµένα των προσωπικών υπολογιστών. Στα ενσωµατωµένα συστήµατα τον ρόλο αυτό έχουν οι µνήµες flash. ο χώρος τους και το περιεχόµενό τους είναι διευθυνσιοδοτούµενο, και κάθε διεύθυνση αντιστοιχεί σε κάποιο block. Για να µπορούν να χρησιµοποιούνται από διαφορετικές αρχιτεκτονικές συστηµάτων, έχουν την ελάχιστη δυνατή µορφοποίηση (format). Όµως αφού το επίπεδο της διαµόρφωσης είναι το ελάχιστο δυνατό, είναι ταυτόχρονα ασαφές και ανεπαρκές όταν θέλουµε να οµαδοποιήσουµε δεδοµένα σε αρχεία. Για να επιτύχουµε την πρόσβαση σε επίπεδο αρχείου λοιπόν, θα πρέπει να εισάγουµε περισσότερη λογική και οργάνωση στα δεδοµένα αυτά. Θα πρέπει τα αρχεία και οι κατάλογοι να είναι αποθηκευµένα µε τέτοιο τρόπο ώστε να µπορούµε να τα προσπελαύνουµε κάθε φορά µε τον ίδιο τρόπο και χωρίς να αλλοι- Ιστότοπος εργασίας: MyThesis.org 35 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητή (Router Implementation) ώνονται κατά την προσπέλαση αυτή. Αυτός ακριβώς είναι ο σκοπός ενός συστήµατος αρχείων (File System). Ο kernel προκειµένου να µπορεί να φιλοξενήσει τα διάφορα συστήµατα αρχείων, περιλαµβάνει έναν αριθµό µηχανών συστήµατος αρχείων (filesystem engines) οι οποίες µπορούν να αναγνωρίσουν την δοµή που έχει ένας δίσκος και να ανακτήσουν ή να προσθέσουν αρχεία και καταλόγους από και προς τη δοµή αυτή αντίστοιχα. Όλες αυτές οι µηχανές συστηµάτων αρχείων, παρέχουν το ίδιο API στα ανώτερα στρώµατα του πυρήνα διαµέσου του εικονικού συστήµατος αρχείων (VFS – Virtual File System), έτσι ώστε η πρόσβαση στα διαφορετικά συστήµατα αρχείων να είναι ευκολότερη. Κατά τη διάρκεια της κανονικής του λειτουργίας, ο kernel απαιτεί την ύπαρξη τουλάχιστον ενός συστήµατος αρχείων. Του κεντρικού συστήµατος αρχείων (root filesystem). Από αυτό φορτώνει και την πρώτη εφαρµογή που θα εκτελεστεί στο σύστηµα. Επίσης βασίζεται σε αυτό και για περεταίρω ενέργειες, όπως την φόρτωση διάφορων modules και την παροχή ενός καταλόγου εργασίας (working directory) σε κάθε διεργασία που εκτελείται. Το σύστηµα αρχείων root µπορεί είτε να είναι αποθηκευµένο και να εκτελείται από µια µνήµη µόνιµης αποθήκευσης είτε να φορτώνεται στη RAM κατά τη διάρκεια της εκκίνησης του συστήµατος και έπειτα να εκτελείται από εκεί (κάπως έτσι λειτουργούν και τα LiveCD στους υπολογιστές). Η πρώτη περίπτωση αποθήκευσης είναι και η πιο συχνή. θα ήταν ίσως αναµενόµενο, ότι ακριβώς επάνω από τον kernel θα βρίσκαµε κανονικές εφαρµογές και άλλα βοηθητικά προγράµµατα (utilities), όµως οι υπηρεσίες που παρέχονται από τον kernel δεν είναι ακόµη κατάλληλες για να χρησιµοποιηθούν από εφαρµογές. Για το λόγο αυτό, έχουν δηµιουργηθεί βιβλιοθήκες (libraries) και ειδικά προγράµµατα συστήµατος (daemons). Η κύρια βιβλιοθήκη που χρησιµοποιείται από τις περισσότερες εφαρµογές Linux είναι η βιβλιοθήκη GNU της γλώσσας C µε την ονοµασία glibc. Για τα ενσωµατωµένα συστήµατα Linux µπορούν να χρησιµοποιηθούν υποκατάστατα αυτής της βιβλιοθήκης έτσι ώστε να αντισταθµιστεί το βασικό της µειονέκτηµα που είναι το µέγεθός της. Εν τω µεταξύ, σηµαντικές διεργασίες του συστήµατος (daemons), παρέχουν υπηρεσίες οι οποίες αξιοποιούνται από τις διάφορες εφαρµογές. Για παράδειγµα, ο διαχειριστής συστήµατος αρχείων συσκευών (device filesystem manager) udev, διαχειρίζεται συσκευές του καταλόγου /dev (πχ όταν USB συσκευές αποθήκευσης, προσθέτονται ή αφαιρούνται από το σύστηµα). Οι βιβλιοθήκες συνήθως συνδέονται δυναµικά (dynamic linking) µε τις εφαρµογές. ∆ηλαδή δεν είναι µέρος του εκτελέσιµου της εφαρµογής αλλά φορτώνονται στην περιοχή µνήµης της εφαρµογής κατά την εκκίνησή της. Αυτό επιτρέπει σε πολλές εφαρµογές να χρησιµοποιούν την ίδια έκδοση µιας βιβλιοθήκης χωρίς η κάθε µία τους να χρειάζεται ένα δικό της αντίγραφο. Η βιβλιοθήκη της C για παράδειγµα η οποία βρίσκεται στο σύστηµα αρχείων ενός συστήµατος, φορτώνεται µόνο µία φορά στη RAM του συστήµατος αυτού και διαµοιράζεται σε όσες εφαρµογές την χρειαστούν. Παρ’ όλα αυτά, σε κάποιες περιπτώσεις στα ενσωµατωµένα συστήµατα όπου οι βιβλιοθήκες είναι µέρος του εκτελέσιµου της εφαρµογής, η στατική σύνδεση (static linking) προτιµάται. Αυτό συµβαίνει γιατί εάν η εφαρµογή µας χρειάζεται µόνο ένα τµήµα της βιβλιοθήκης της C είναι προτιµότερο να φορτωθεί µόνο το τµήµα αυτό ώστε να µην δεσµευτούν επιπλέον πόροι που όπως γνωρίζουµε είναι αρκετά περιορισµένοι στα ενσωµατωµένα συστήµατα. 1.11 Εκκίνηση συστήµατος Κατά την εκκίνηση ενός συστήµατος (startup ή boot) συµµετέχουν τρία βασικά στοιχεία: ο bootloader, ο kernel και η διεργασία init. Ο bootloader (εκκινητής) είναι το πρώτο τµήµα λογισµικού που εκτελείται και εξαρτάται σε µεγάλο βαθµό από την αρχιτεκτονική του µικροελεγκτή του target συστήµατος αλλά και από το υπόλοιπο hardware. Έχουν γραφτεί αρκετοί Επικοινωνία: [email protected] 36 Ανάπτυξη Ενσωµατωµένων Συστηµάτων Linux - Κεφάλαιο 1 bootloaders για Linux (Lilo, GRUB, loadlin, Coreboot, U-Boot, RedBoot κλπ) αλλά στην παρούσα εργασία θα χρησιµοποιήσουµε τον U-Boot. Ο bootloader εκτελεί την αρχικοποίηση του υλικού (low-level initialization) και µεταπηδά στον κώδικα εκκίνησης του kernel. Ο αρχικός κώδικας εκκίνησης του kernel διαφέρει πάρα πολύ µεταξύ διαφορετικών αρχιτεκτονικών και έτσι πραγµατοποιεί και αυτός µε τη σειρά του αρχικοποίηση υλικού πριν εγκαθιδρύσει το κατάλληλο περιβάλλον. Εφόσον γίνουν όλα αυτά, ο kernel περνά τον έλεγχο στην συνάρτηση start_kernel() η οποία είναι ανεξάρτητη αρχιτεκτονικής. Η συνάρτηση αυτή µε τη σειρά της αρχικοποιεί το υψηλό επίπεδο (high-level initialization) του kernel, φορτώνει το κεντρικό σύστηµα αρχείων (root filesystem) και εκκινεί την διεργασία init. Κατά την αρχικοποίηση του υψηλού επιπέδου του kernel γίνονται επίσης και κάποιες άλλες εργασίες όπως είναι για παράδειγµα η χαρτογράφηση µνήµης (memory mapping) και οι συναρτήσεις σειριακής επικοινωνίας για debugging. Το υπόλοιπο της εκκίνησης του συστήµατος διεξάγεται στο χώρο του χρήστη (user space) από την διεργασία init και τα προγράµµατα εκκίνησης (startup scripts). 1.11.1 Τύποι εκκίνησης Ο τύπος της εκκίνησης που θα επιλέξουµε για το target σύστηµά µας θα επηρεάσει την επιλογή του bootloader που θα χρησιµοποιήσουµε, την παραµετροποίησή του, καθώς και το υλικό και το λογισµικό του συστήµατος ανάπτυξης host. Για παράδειγµα, αν επιλέξουµε δικτυακή εκκίνηση του συστήµατός µας, το σύστηµα host θα πρέπει να εξασφαλίσουµε ότι θα παρέχει κάποιες δικτυακές υπηρεσίες προς το σύστηµα target. Έτσι θα πρέπει να εντοπίσουµε την απαιτούµενη διαµόρφωση των παραµέτρων της εκκίνησης που απαιτούνται και κατά τη διάρκεια της σχεδίασης αλλά και στο τελικό προϊόν που θα κατασκευάσουµε. Έπειτα πρέπει να επιλέξουµε τον bootloader ο οποίος θα φροντίζει για τους διαφορετικούς τύπους εκκίνησης που θα χρησιµοποιούµε. Πριν προχωρήσουµε στην ανάλυση των τύπων εκκίνησης ας δούµε κάποια βασικά που αφορούν την εκκίνηση. Όλοι οι επεξεργαστές βρίσκουν και εκτελούν την πρώτη - πρώτη τους εντολή από µια διεύθυνση µνήµης που συνήθως είναι προκαθορισµένη από τον κατασκευαστή τους. Κάθε σύστηµα το οποίο έχει αναπτυχθεί γύρω από µια CPU διαθέτει κάποια ενσωµατωµένη µνήµη στερεάς κατάστασης (solid state). Η µνήµη αυτή είναι υπεύθυνη για την εκκίνηση του συστήµατος. Το επίπεδο πολυπλοκότητας του λογισµικού εκκίνησης και ο βαθµός στον οποίο αυτό χρησιµοποιείται στη συνέχεια, εξαρτώνται από το είδος του συστήµατός µας. Στους περισσότερους υπολογιστές το λογισµικό εκκίνησης (BIOS) είναι υπεύθυνο µόνο για τη φόρτωση του λειτουργικού συστήµατος από το δίσκο και για την παροχή δυνατότητας παραµετροποίησης του υλικού από τον χρήστη. Αντίθετα, στα ενσωµατωµένα συστήµατα, το λογισµικό εκκίνησης µπορεί να χρησιµοποιηθεί για οποιοδήποτε σκοπό και κάποιες φορές µάλιστα είτε δεν υπάρχει, είτε είναι το µόνο υπαρκτό λογισµικό του συστήµατος. Έτσι σε κάποιες περιπτώσεις µπορεί να εκτελείται ακόµη και καθ’ όλη τη διάρκεια λειτουργίας του ενσωµατωµένου συστήµατος. Αυτό συµβαίνει επειδή ένα ενσωµατωµένο σύστηµα χρησιµοποιείται για πάρα πολλούς και διαφορετικούς σκοπούς. Ορισµένες άλλες λειτουργίες του λογισµικού εκκίνησης είναι το debugging, η φόρτωση άλλου λογισµικού εκκίνησης, η αναβάθµιση του λογισµικού που υπάρχει στο ενσωµατωµένο σύστηµα κ.α. Τα ενσωµατωµένα συστήµατα Linux χαρακτηρίζονται από την απαίτηση που έχουν στο να φορτώσουν τον kernel και το κεντρικό σύστηµα αρχείων. Όπως αναφέραµε ο τρόπος φόρτωσής τους και η λειτουργία τους εξαρτάται από τις απαιτήσεις του συστήµατος και τον τύπο ανάπτυξής του. Ιστότοπος εργασίας: MyThesis.org 37 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητή (Router Implementation) Υπάρχουν τρεις διαφορετικοί τρόποι για την εκκίνηση ενός ενσωµατωµένου συστήµατος Linux: η εκκίνηση από µνήµη στερεάς κατάστασης, η εκκίνηση από δίσκο και η εκκίνηση από το δίκτυο. Ο κάθε ένας από αυτούς τους τύπους εκκίνησης έχει τις δικές του ρυθµίσεις και χρησιµοποιείται για διαφορετικό σκοπό. Εκκίνηση από µνήµη στερεάς κατάστασης Όταν η εκκίνηση πραγµατοποιείται από κάποια µνήµη στερεάς κατάστασης, στη µνήµη αυτή βρίσκονται αποθηκευµένα, ο αρχικός bootloader, οι παράµετροι ρυθµίσεων (configuration parameters), ο kernel και το σύστηµα αρχείων root. Στην εικόνα βλέπουµε την πιο κοινή περίπτωση διάταξης µιας τέτοιας µνήµης: Εικόνα 5. ∆ιάταξη µνήµης στερεάς κατάστασης Σκόπιµα δεν παρουσιάζονται κάποιες διευθύνσεις µνήµης επειδή σε κάθε σύστηµα το εύρος το διευθύνσεων διαφέρει αρκετά από κάθε άλλο σύστηµα. Οι τιµές των διευθύνσεων είναι µικρότερες στα αριστερά και µεγαλώνουν καθώς κινούµαστε δεξιά. Σε κάποιες περιπτώσεις ισχύσει το αντίθετο και έτσι ο bootloader βρίσκεται στην κορυφή του εύρους διευθύνσεων της συσκευής αποθήκευσης. Για το λόγο αυτό πολλές µνήµες flash παρέχονται µε υποστήριξη και για τις δυο περιπτώσεις (top – boot και bottom – boot). Ανάλογα µε τη διαµόρφωση, η περιοχή της µνήµης flash όπου βρίσκεται ο bootloader συχνά προστατεύεται από ειδικούς µηχανισµούς προστασίας έτσι ώστε να µην καταστρέφεται όταν γίνει κάποια λάθος εγγραφή στη µνήµη. Μπορεί βέβαια να συναντήσουµε και διαφορετικές µορφοποιήσεις ως προς τον αριθµό των τµηµάτων της µνήµης. Ενώ για παράδειγµα στην προηγούµενη εικόνα παρουσιάζονται τέσσερα τµήµατα, υπάρχουν περιπτώσεις που οι παράµετροι εκκίνησης και ο bootloader βρίσκονται στο ίδιο διαµέρισµα (partition). Ο kernel µπορεί επίσης να βρίσκεται στο ίδιο διαµέρισµα µε το root σύστηµα αρχείων (αυτό προϋποθέτει την ικανότητα του bootloader να διαβάζει το κεντρικό το root σύστηµα αρχείων). Επίσης ο kernel και το root σύστηµα αρχείων θα µπορούσαν να βρίσκονται πακεταρισµένα σε ένα ενιαίο αρχείο (image) το οποίο θα αποσυµπιέζεται στη µνήµη RAM πριν χρησιµοποιηθεί. Ανάλογα µε τις δυνατότητες του bootloader που έχουµε στη διάθεσή µας, µπορούν να δηµιουργηθούν και άλλες διαµορφώσεις στη µνήµη, κάθε µία µε τα δικά της πλεονεκτήµατα και µειονεκτήµατα. Μια διαµόρφωση µπορεί να κατηγοριοποιηθεί λαµβάνοντας υπ’ όψιν τέσσερα κριτήρια: τη χρήση της µνήµης flash, τη χρήση της RAM, την ευκολία αναβάθµισης και το χρόνος εκκίνησης (bootup time). Τα µέσα αποθήκευσης του λογισµικού εκκίνησης αρχικά προγραµµατίζονται µε κάποιον programmer (JTAG ή BDM). Αφού ολοκληρωθεί ο αρχικός προγραµµατισµός, µπορεί να επιτευχθεί ο επαναπρογραµµατισµός µέσω του bootloader αυτή τη φορά (εάν φυσικά ενσωµατώνει αυτή τη δυνατότητα), ή χρησιµοποιώντας το MTD (Memory Technology Device) υποσύστηµα του Linux. Επικοινωνία: [email protected] 38 Ανάπτυξη Ενσωµατωµένων Συστηµάτων Linux - Κεφάλαιο 1 Εκκίνηση από σκληρό δίσκο Εδώ ο kernel και το σύστηµα αρχείων root, βρίσκονται σε ένα σκληρό δίσκο. Ο bootloader έχει συνήθως περιορισµό µεγέθους τα 512 bytes ώστε να χωρά στον τοµέα εκκίνησης (boot sector). Ο σκοπός του είναι, είτε να φορτώνει έναν µεγαλύτερο και πιο ισχυρό δευτερεύον (secondary) bootloader, είτε να εκτελεί τον ίδιο τον kernel, απευθείας από το δίσκο. Ένα από τα συστήµατα αρχείων στο δίσκο, χρησιµοποιείται έπειτα ως το root σύστηµα αρχείων. Αν και δε συµβαίνει συχνά, αυτός ο τύπος εκκίνησης είναι βολικός αν θέλουµε για κάποιο λόγο να έχουµε σκληρό δίσκο ή κάποια CompactFlash στο ενσωµατωµένο µας σύστηµα. Εκκίνηση µέσω δικτύου Ο τελευταίος τύπος εκκίνησης ολοκληρώνεται µέσω δικτύου. Η φόρτωση του συστήµατος αρχείων root ή/και του kernel, επιτυγχάνεται µέσω µιας γραµµής που διασυνδέει κάποιο server µε την πλακέτα µας. Εποµένως υπάρχουν τρεις περιπτώσεις φόρτωσης: Σύστηµα αρχείων root και kernel – Θα πρέπει να υπάρχει bootloader που να υποστηρίζει τα πρωτόκολλα TFTP (Trivial File Transfer Protocol) και NFS (Network File System). Η περίπτωση αυτή είναι σπάνια. Μόνο ο kernel – Απαίτηση µόνο του πρωτοκόλλου TFTP από τον bootloader. Μόνο το σύστηµα αρχείων root – Σε αυτή την περίπτωση στη µνήµη του target υπάρχει ο kernel και αφού εκκινηθεί από τον bootloader, φορτώνει µέσω του πρωτοκόλλου NFS το σύστηµα αρχείων root. Στις περιπτώσεις που αναφέραµε υποθέτουµε ότι το σύστηµα host έχει εγκατεστηµένο λογισµικό για NFS και TFTP server. Για να αυτοµατοποιήσει ο bootloader την εύρεση της τοποθεσίας του server µπορεί να κάνει χρήση των πρωτοκόλλων BOOTP/DHCP. Σε αυτή την περίπτωση το target σύστηµά δε χρειάζεται να έχει κάποια στατική IP έτσι ώστε να επικοινωνήσει µε τον TFTP και τον NFS server. Η εκκίνηση µέσω δικτύου είναι πολύ πρακτική στα αρχικά στάδια της ανάπτυξης ή κατά τη διάρκεια του debugging, επειδή µας επιτρέπει να διαµοιραζόµαστε δεδοµένα πολύ γρήγορα µεταξύ του συστήµατος host και του συστήµατος target. Το µειονέκτηµα είναι ότι απαιτείται λογισµικό server και σωστή ρύθµισή των παραµέτρων του. 1.12 Μονάδα διαχείρισης µνήµης (MMU) Για την καλύτερη χρήση των διαθέσιµων πόρων, είναι σηµαντικό να κατανοήσουµε την διάταξη της µνήµης του συστήµατος και τις διαφορές µεταξύ του φυσικού χώρου διευθύνσεων (physical address map) και του χώρου εικονικών διευθύνσεων (virtual address map) του kernel. Κάτι επίσης πολύ σηµαντικό είναι ότι πολλές περιφερειακές συσκευές είναι προσβάσιµες µέσω του φυσικού χώρου διευθύνσεων του συστήµατος αλλά έχουν περιορισµένη ή καθόλου πρόσβαση µέσω του χώρου εικονικών διευθύνσεων. Η γνώση της φυσικής χαρτογράφησης της µνήµης (memory mapping) του συστήµατός µας, είναι σηµαντική γιατί µας παρέχει πληροφορίες στο πώς να διαµορφώσουµε τον kernel. Μας βοηθά να αναπτύξουµε τους δικούς µας drivers όταν έχουµε να κάνουµε µε χαρτογραφηµένες συσκευές (memory – mapped devices) και να παρέχουµε πληροφορίες στον bootloader σχετικά µε τις συσκευές που πρέπει να φορτώσει κατά την εκκίνηση. Πριν όµως απ’ οτιδήποτε άλλο, ας εξετάσουµε τις έννοιες των φυσικών και εικονικών διευθύνσεων στα ενσωµατωµένα συστήµατα Linux. Μια φυσική διεύθυνση χρησιµοποιείται για να δώσει ένα όνοµα σε ένα συγκεκριµένο κελί (cell) της φυσικής µνήµης του συστήµατός µας. Ιστότοπος εργασίας: MyThesis.org 39 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητή (Router Implementation) Έτσι, είµαστε σε θέση να αλληλεπιδρούµε µε τη θέση αυτή γράφοντας, διαβάζοντας, εκτελώντας ή σβήνοντας τα περιεχόµενά της. Οι φυσικές διευθύνσεις αναπαρίστανται από έναν προσηµασµένο ή µη-προσηµασµένο ακέραιο των 32 bit (ή των 8 bit αν έχουµε κάποιον οκτάµπιτο µικροελεγκτή). Μια εικονική ή γραµµική (linear) διεύθυνση είναι ένας µη-προσηµασµένος (unsigned) ακέραιος των 32 bit ο οποίος µπορεί να χρησιµοποιηθεί για την αναπαράσταση χώρου µέχρι 4GB (4,294,967,296 cells). Οι εικονικές διευθύνσεις συνήθως αναπαρίστανται σε δεκαεξαδική (hexadecimal) µορφή. Οι τιµές αυτών των διευθύνσεων έχουν εύρος, από 0x00000000 µέχρι και 0xffffffff. Στην εικόνα βλέπουµε ένα παράδειγµα χάρτη µνήµης και τη µετάφραση των εικονικών του διευθύνσεων σε φυσικές: Εικόνα 6. Χάρτης µνήµης και µετάφραση εικονικών διευθύνσεων σε φυσικές Υπάρχουν πάρα πολλοί λόγοι για τους οποίους η χρήση εικονικών διευθύνσεων είναι χρήσιµη. Ο σηµαντικότερος προκύπτει από τον περιορισµένο διαθέσιµο χώρο της φυσικής µνήµης. Όπως ξέρουµε η φυσική µνήµη αποτελείται από διαδοχικές περιοχές των 32bit. Κάθε τέτοια περιοχή ονοµάζεται λέξη (word) και έχει τη δική της διεύθυνσή στο υλικό. Αν η µνήµη έχει µέγεθος Ν λέξεων, τότε πολύ γενικά µπορούµε να πούµε ότι η πρώτη λέξη έχει διεύθυνση 0, η δεύτερη διεύθυνση 1, η τρίτη 2 και η τελευταία Ν-1. ∆ηλαδή ο αριθµός αυτός είναι πεπερασµένος. Όταν όµως οι διεργασίες που θέλουµε να εκτελούνται στο σύστηµά µας ξεπερνούν σε απαιτήσεις αυτό τον αριθµό, τότε η λύση είναι οι εικονικές διευθύνσεις. Εποµένως ο χώρος των εικονικών διευθύνσεων, θεωρητικά είναι µεγαλύτερος σε µέγεθος από εκείνο των φυσικών, έτσι ώστε όλες οι διεργασίες να «θεωρούν» ότι έχουν ελεύθερο χώρο στη διάθεσή τους και να εκτελούνται αναπόσπαστα (Τον χειρισµό των εικονικών διευθύνσεων τον αναλαµβάνει το Λ.Σ.). Η µετατροπή µιας εικονικής διεύθυνσης σε φυσική γίνεται από τη µονάδα διαχείρισης µνήµης (MMU – Memory Management Unit) που διαθέτει ο µικροελεγκτής µας. Σε κάποιους παλαιότερους µικροελεγκτές, η µονάδα αυτή δεν υπήρχε και το ρόλο αυτό αναλάµβανε κάποια εξειδικευµένη βιβλιοθήκη λογισµικού. Επικοινωνία: [email protected] 40 Ανάπτυξη Ενσωµατωµένων Συστηµάτων Linux - Κεφάλαιο 1 Όταν πραγµατοποιείται µια πρόσβαση στη µνήµη (µετά από αίτηµα της CPU), η MMU µεταφράζει την εικονική διεύθυνση που ζητήθηκε σε µια φυσική διεύθυνση, ενώ πριν έχει ελέγξει τα δικαιώµατα πρόσβασης στη θέση αυτή. Αν συµβεί κάποιο σφάλµα ή το λειτουργικό σύστηµα διακόψει αυτή τη διαδικασία, προκαλείται µια εξαίρεση (exception), επιτρέποντας έτσι το πρόβληµα να λυθεί µέσω λογισµικού. Η αρχιτεκτονική MMU χρησιµοποιεί σελιδοποίηση για να χαρτογραφήσει µια εικονική διεύθυνση των 32bit σε µια φυσική. Τα µεγέθη των σελίδων είναι 1, 4, 64, KB και 1MB. Κάθε σελίδα έχει τα δικά της δικαιώµατα πρόσβασης. Οι πληροφορίες που χρειάζονται προκειµένου να εκτελέσουµε την χαρτογράφηση εικονικών διευθύνσεων σε φυσικές, βρίσκονται σε έναν εξωτερικό πίνακα σελίδων (page table). Αυτός ο πίνακας περιέχει επίσης πληροφορίες προστασίας και άλλα δεδοµένα που απαιτούνται κατά τη διαδικασία της µετάφρασης των διευθύνσεων. Ο πίνακας σελίδων χρησιµοποιείται για κάθε πρόσβαση στη µνήµη προκειµένου να διαβάζονται οι πληροφορίες χαρτογράφησης για κάθε σελίδα. Για να επιταχυνθεί η διαδικασία µετάφρασης χρησιµοποιείται µια κρυφή µνήµη που ονοµάζεται TLB (Translation Lookaside Buffer). Η µνήµη TLB περιέχει τις n πιο πρόσφατα χρησιµοποιούµενες εγγραφές του πίνακα σελίδων. Ο αριθµός n εξαρτάται από την υλοποίηση του συστήµατος κάθε φορά. Η αρχιτεκτονική AVR32 του µικροελεγκτή AP7000 για παράδειγµα, παρέχει 2 TLBs µε µέχρι 64 εγγραφές η καθεµία, Μπορούν να χρησιµοποιηθούν ταυτόχρονα, η µία για πρόσβαση στη µνήµη και η άλλη για εντολές. Στην εικόνα βλέπουµε ένα γενικό διάγραµµα των όσων έχουµε πει έως τώρα: Εικόνα 7. ∆ιάγραµµα µονάδας διαχείρισης µνήµης MMU 1.13 ∆ιαχείριση δευτερεύουσας µνήµης Οι εικονικές διευθύνσεις που δεν αντιστοιχίζονται σε φυσικές, συνήθως αντιστοιχίζονται σε κάποια δευτερεύουσα µνήµη. Οι βασικότεροι µέθοδοι αντιστοίχισης είναι δύο: η κατάτµηση και η σελιδοποίηση, ή ο συνδυασµός τους, η κατατµηµένη σελιδοποίηση. Κατά τη σελιδοποίηση, η εικονική µνήµη διαιρείται σε ίσα και συνεχόµενα µέρη που λέγονται σελίδες. Με τον ίδιο τρόπο η φυσική µνήµη χωρίζεται σε ενότητες. Το µέγεθος ενότητας και σελίδας είναι το ίδιο. Το Λ.Σ. διατηρεί πίνακα αντιστοίχισης σελίδων και ενοτήτων. Για κάθε σελίδα, φαίνεται στον πίνακα η ενότητα και οι διευθύνσεις της φυσικής µνήµης στην οποία Ιστότοπος εργασίας: MyThesis.org 41 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητή (Router Implementation) αντιστοιχεί. Αν δεν αντιστοιχεί σε ενότητα σηµαίνει ότι βρίσκεται στη δευτερεύουσα µνήµη. Το κυριότερο µειονέκτηµα είναι ότι κάθε διεργασία καταλαµβάνει περισσότερο χώρο από ότι χρειάζεται. Η τελευταία σελίδα κάθε διεργασίας έχει ένα τµήµα µικρότερο ή µεγαλύτερο που είναι άχρηστο. Αυτό ονοµάζεται εσωτερικός κατακερµατισµός Η µέθοδος της κατάτµησης προσπαθεί να αποφύγει τον εσωτερικό κατακερµατισµό δίνοντας σε κάθε διεργασία όση µνήµη χρειάζεται. Χωρίζει τη µνήµη σε τµήµατα διαφορετικών µεγεθών και για κάθε ένα κρατά την αρχική διεύθυνση και µέγεθος. Από την άλλη, Η µέθοδος της κατατµηµένης σελιδοποίησης, συνδυάζει την εργασία σε τµήµατα σταθερού µεγέθους. Κάθε σελίδα αποτελείται µόνο από µία λέξη. Αυτό που πρέπει να πούµε πριν το κλείσιµο αυτού του κεφαλαίου είναι ότι µετά τη φόρτωση του Linux Kernel από τον bootloader, όλα τα προγράµµατα τα οποία εκτελούνται, χρησιµοποιούν εικονικές διευθύνσεις µνήµης. Επικοινωνία: [email protected] 42 ΚΕΦΑΛΑΙΟ 2 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux Εισαγωγή Όπως είναι ήδη γνωστό, τα εργαλεία ανάπτυξης ενσωµατωµένων συστηµάτων διαφέρουν από εκείνα που χρησιµοποιούνται συνήθως για την ανάπτυξη εφαρµογών σε PCs. Η διαφορά τους είναι ότι ενώ εκτελούνται σε κάποια συγκεκριµένη πλατφόρµα (πχ x86), δηµιουργούν εφαρµογές για µια άλλη (πχ AVR32). Για το λόγο αυτό ονοµάζονται και εργαλεία ανάπτυξης και µεταγλώττισης cross – platform. Γενικά υπάρχουν δύο τρόποι για να αποκτήσουµε τα εργαλεία αυτά. Μπορούµε να κατεβάσουµε δωρεάν τον πηγαίο κώδικά τους από το internet και να τον µεταγλωττίσουµε από την αρχή (build from scratch), να τα προµηθευτούµε µέσω κάποιας εταιρείας επί πληρωµή, κάποιου αντιπροσώπου ή κάποιου online project στο Internet. Σε αυτό το κεφάλαιο θα αναλύσουµε τα πιο σηµαντικά εργαλεία ανάπτυξης ενσωµατωµένων συστηµάτων. Η ανάλυση θα λειτουργήσει και σαν µια εισαγωγή για τα αντίστοιχα εργαλεία που χρησιµοποιήθηκαν στο πρακτικό µέρος αυτής της εργασίας, για την ανάπτυξη του Router NGW100. Τα θέµατα µε τα οποία θα ασχοληθούµε είναι τα ακόλουθα: Αλυσίδα εργαλείων µεταγλώττισης Έτοιµες αλυσίδες εργαλείων µεταγλώττισης Πακέτο υποστήριξης συστήµατος ∆ηµιουργία της αλυσίδας µεταγλώττισης cross Αυτοµατοποιηµένα συστήµατα Παραµετροποίηση uClibc, kernel και BusyBox µέσω Buildroot Γραµµή εντολών Βασικές εντολές φλοιού Προσοµοιωτές τερµατικών IDEs για Linux 2.1 Έτοιµες αλυσίδες εργαλείων ανάπτυξης Οι περισσότεροι κατασκευαστές αναπτυξιακών πλακετών προσφέρουν δωρεάν κάποιες αλυσίδες εργαλείων ανάπτυξης, έτοιµες για χρήση, µαζί µε τα προϊόντα τους. Συνήθως σε ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) αυτή την περίπτωση, εκτός από την αλυσίδα εργαλείων µεταγλώττισης συµπεριλαµβάνεται και κάποιο λογισµικό IDE. Επίσης µπορεί να παρέχονται και πρόσθετα (plug-ins) που επεκτείνουν τις δυνατότητες αυτού του IDE. Η αξία µιας έτοιµης αλυσίδας εργαλείων η οποία έχει ήδη µεταγλωττιστεί, αποσφαλµατωθεί και δοκιµαστεί ειδικά για ένα συγκεκριµένο σύστηµα, είναι µεγάλη, και αυτό µπορεί να το καταλάβει κανείς µόνο στην πράξη. Παρ’ όλο που όλα τα εργαλεία ανάπτυξης τα οποία απαιτούνται για την υλοποίηση ενός ενσωµατωµένου συστήµατος Linux είναι ανοικτού κώδικα και µπορούµε να τα κατεβάσουµε δωρεάν από το Internet, η διαδικασία ενσωµάτωσης, µεταγλώττισης και δοκιµής είναι χρονοβόρα, απαιτεί γνώσεις, και κάποιες φορές δεν ολοκληρώνεται ποτέ λόγω σφαλµάτων και ασυµβατοτήτων. Έτσι µια έτοιµη αλυσίδα εργαλείων η οποία έχει δηµιουργηθεί από κάποιον άλλο για εµάς, µπορεί να µας γλιτώσει αρκετό χρόνο και χρήµα. Αυτό θα µας επιτρέψει αφενός να ολοκληρώσουµε µε επιτυχία την ανάπτυξη του συστήµατός µας και αφετέρου να ασχοληθούµε περισσότερο µε την εφαρµογή µας. 2.2 Πακέτο υποστήριξης συστήµατος Τα ολοκληρωµένα που βρίσκονται συγκολληµένα στο PCB ενός ενσωµατωµένου συστήµατος Linux, συµπεριλαµβανοµένου και του µικροελεγκτή, πριν χρησιµοποιηθούν από τις εφαρµογές µας, θα πρέπει πρώτα να έχουν αρχικοποιηθεί και καταχωρηθεί όπως απαιτείται. Αυτό συνήθως γίνεται αρχικά από το λογισµικό εκκίνησης και στη συνέχεια από το Linux. Η αρχικοποίηση των ηλεκτρονικών στοιχείων (πχ µνήµες, Ethernet chips κλπ) επιτυγχάνεται από εξειδικευµένο κώδικα ο οποίος αναπτύσσεται από την κατασκευάστρια εταιρεία και µοιράζεται συνήθως δωρεάν, µε τη µορφή ενός ολοκληρωµένου πακέτου. Το πακέτο αυτό είναι γνωστό και ως BSP (Board Support Package). Στα ενσωµατωµένα συστήµατα Linux, ένα πακέτο BSP (ή LSP – Linux Support Package), είναι λογισµικό που απευθύνεται σε κάποιο συγκεκριµένο υλικό και εκτελείται σε συγκεκριµένο λειτουργικό σύστηµα. Σε ορισµένες περιπτώσεις µπορεί να παρέχεται µε κάποια χρηµατική επιβάρυνση αλλά τις περισσότερες φορές είναι δωρεάν και περιλαµβάνεται ως παρελκόµενο (σε µορφή CD, DVD κλπ) από τον προµηθευτή. Επίσης µπορεί να διατίθεται και για κατέβασµα από το internet. Σε ένα πακέτο BSP υπάρχει ένα σύνολο καταλόγων µέσα στους οποίους βρίσκονται όλα τα εργαλεία που χρειαζόµαστε για το σύστηµα που αναπτύσσουµε. Ένα από αυτά είναι και η κατάλληλη αλυσίδα εργαλείων (GNU και cross). Εκτός των άλλων, περιλαµβάνονται τροποποιήσεις του kernel και του bootloader οι οποίες έχουν σκοπό να προσαρµόσουν τα δύο αυτά τµήµατα λογισµικού, στα ιδιαίτερα χαρακτηριστικά του υλικού του συστήµατός µας. Επίσης περιλαµβάνονται τροποποιήσεις για τη χαρτογράφηση της µνήµης, των διακοπών και κάποιοι βασικοί drivers όπως είναι αυτός της σειριακής επικοινωνίας, του δικτύου και της µνήµης flash. Σε ένα BSP περιλαµβάνεται επίσης και η απαραίτητη γραπτή τεκµηρίωση. 2.3 Αλυσίδα εργαλείων µεταγλώττισης Ουσιαστικά µια αλυσίδα εργαλείων µεταγλώττισης (toolchain), διεκπεραιώνει την διαδικασία µετατροπής πηγαίου κώδικα σε εκτελέσιµο. Απλά στην περίπτωσή των ενσωµατωµένων συστηµάτων Linux, το εκτελέσιµο αυτό θα πρέπει να δηµιουργηθεί µε βάση τις ιδιαιτερότητες του Linux kernel καθώς και της αρχιτεκτονικής του εκάστοτε µικροελεγκτή. Πρέπει επίσης να πούµε, ότι και ο ίδιος ο kernel είναι κώδικας που θα πρέπει να γίνει εκτελέσιµος µέσω της αλυσίδας. Επικοινωνία: [email protected] 44 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux – Κεφάλαιο 2 Η βιβλιοθήκη C είναι διαµοιραζόµενη (shared) και λειτουργεί σαν περιτύλιγµα (wrapper) γύρω από το περιβάλλον διεπαφής του πυρήνα (Linux kernel API). Χρησιµοποιείται σαν διαµεσολαβητής από κάθε εφαρµογή που εκτελείται σε ένα σύστηµα Linux. Σε κάποιες αλυσίδες εργαλείων υπάρχουν και επιπρόσθετες βιβλιοθήκες λογισµικού (όπως είναι η βιβλιοθήκη zlib η οποία παρέχει υπηρεσίες συµπίεσης) καθώς και πολλά άλλα συµπληρωµατικά εργαλεία όπως: Debuggers Profilers Memory checkers Αφού ορίσαµε γενικά την αλυσίδα θα εξετάσουµε τώρα την αλυσίδα GNU Cross – Platform, ή εν συντοµία, την αλυσίδα cross. Μια τέτοια αλυσίδα δηµιουργείται έτσι ώστε να εκτελείται σε κάποια συγκεκριµένη πλατφόρµα (πχ x86) αλλά να παράγει εκτελέσιµο κώδικα για κάποια άλλη πλατφόρµα (πχ AVR32). Η αλυσίδα cross που θα δούµε στη συνέχεια περιλαµβάνει εκτελέσιµες εφαρµογές όπως είναι ο ld linker, o gas assembler ο gcc compiler, ο ar archiver καθώς και η βιβλιοθήκη glibc ή κάποια εναλλακτική της. Τα περισσότερα στοιχεία της αλυσίδας αποτελούν µέρος του GNU project και µπορούµε να τα κατεβάσουµε από τη διεύθυνση ftp.gnu.org/gnu. 2.4 ∆ηµιουργία της αλυσίδας µεταγλώττισης cross Η παραµετροποίηση και δηµιουργία της κατάλληλης αλυσίδας cross είναι µια απαιτητική διαδικασία η οποία προαπαιτεί την κατανόησή µας σε ότι αφορά τις εξαρτήσεις (dependencies) που υπάρχουν µεταξύ των διαφόρων πακέτων λογισµικού, τον ρόλο αυτών των πακέτων, τις ασυµβατότητες που προκύπτουν από τις διάφορες εκδόσεις τους αλλά και τον συνολικό χρόνο που απαιτείται. Επίσης απαιτούνται γνώσεις σχετικά µε: Τα διάφορα patches Την ειδική διαµόρφωση των εργαλείων λογισµικού για την πλατφόρµα του συστήµατος target Αν κάποιος επιθυµεί να δηµιουργήσει ο ίδιος την cross αλυσίδα, υπάρχει ένα ειδικό project που ονοµάζεται: “Cross Linux From Scratch ” και µπορούµε να το επισκεφτούµε εδώ: trac.cross-lfs.org. 2.4.1 Binutils Ένα σηµαντικό τµήµα της αλυσίδας είναι και το πακέτο binutils (binary utilities). Το πακέτο αυτό περιλαµβάνει βοηθητικά προγράµµατα τα οποία χρησιµοποιούνται συχνά για την διαχείριση των δυαδικών object αρχείων. Τα δύο κυριότερα προγράµµατα του πακέτου binutils είναι ο GNU assembler (as) και ο linker (ld). Στον πίνακα που ακολουθεί περιλαµβάνονται όλα τα βοηθητικά προγράµµατα του πακέτου binutils: Πρόγραµµα Λειτουργία as GNU assembler ld GNU linker gasp GNU assembler pre-processor Ιστότοπος εργασίας: MyThesis.org 45 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) ar ∆ηµιουργεί και διαχειρίζεται αρχειοθετηµένο περιεχόµενο (archive) nmu Παραθέτει σε λίστα τα περιεχόµενα ενός object αρχείου objcopy Αντιγράφει και µεταφράζει object αρχεία objdump Εµφανίζει πληροφορίες σχετικά µε το περιεχόµενο object αρχείων ranlib ∆ηµιουργεί ένα ευρετήριο µε τα περιεχόµενα ενός αρχείου αρχειοθέτησης readelf Εµφανίζει πληροφορίες για ένα object αρχείο µορφής ELF size Παραθέτει τα µεγέθη των τµηµάτων µέσα σε ένα object αρχείο strings Εκτυπώνει συµβολοσειρές εκτυπώσιµων χαρακτήρων σε αρχεία object strip Αφαιρεί σύµβολα από object αρχεία Μετατρέπει ετικέτες assembly που προκύπτουν από υπερφόρτωση συναρτήσεων C++ σε αντίστοιχά ονόµατα επιπέδου χρήστη (user-level) Μετατρέπει διευθύνσεις µνήµης σε αριθµούς γραµµής πηγαίου κώδικα c++filt Addr2line Πίνακας 1. Τα προγράµµατα του πακέτου binutils 2.4.2 Οι κεφαλίδες του Linux kernel Ένα ακόµη από τα στοιχεία που απαιτούνται για την δηµιουργία µιας αλυσίδας είναι το σετ κεφαλίδων του kernel (Linux Kernel Headers). Λόγω του ότι η βιβλιοθήκη C (η οποία είναι µέρος της αλυσίδας) λειτουργεί σαν περιτύλιγµα που παρέχει µια πιο εύκολη προγραµµατιστική διεπαφή για τις άµεσες κλήσεις που γίνονται προς τον kernel από τις εφαρµογές µας, κατά τη µεταγλώττισή της απαιτείται ένα υποσύνολο κεφαλίδων το οποίο περιγράφει το API του kernel. Από τον κατάλογο του πηγαίου κώδικα του kernel µπορούµε να εκτελέσουµε τις ακόλουθες εντολές προκειµένου να εγκατασταθούν οι κεφαλίδες µας: $ make ARCH=avr32 headers_check $ make ARCH=avr32 INSTALL_HDR_PATH=headers/ headers_install; 2.4.3 Η βιβλιοθήκη C Μία από τις σηµαντικότερες αποφάσεις που πρέπει να παρθούν όταν θέλουµε να δηµιουργήσουµε µια αλυσίδα είναι το ποια βιβλιοθήκη C θα χρησιµοποιήσουµε. Η standard GNU βιβλιοθήκη της C είναι αυτή που χρησιµοποιείται πιο συχνά σε συστήµατα Linux. Η ονοµασία της είναι glibc και είναι ακρωνύµιο των: GNU Library C. Τα πλεονεκτήµατα της είναι η εύκολη µεταφερσιµότητά (portability) της, η υψηλή της απόδοση και η συµβατότητά της µε τα περισσότερα standards (ISO C 99, POSIX, Unix98 κλπ). Μπορούµε να κατεβάσουµε τον πηγαίο κώδικα από τον ιστότοπο gnu.org/software/libc. Οι πλατφόρµες που υποστηρίζει η standard GNU βιβλιοθήκη της C, τη στιγµή που γράφεται αυτή η εργασία, είναι οι παρακάτω: Intel ix86 Motorola 680x0 DEC Alpha PowerPC (32 και 64 bit) Sparc Επικοινωνία: [email protected] 46 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux – Κεφάλαιο 2 Sparc64 ARM MIPS (kernel > 2.2.15) Intel IA64 (kernel > 2.4.0) IBM S/390 (32 και 64 bit) SH CRIS AMD x86-64 Εύκολα µπορεί να παρατηρήσει κάποιος ότι από την παραπάνω λίστα λείπει η πλατφόρµα AVR32. Αυτό δεν αποτελεί πρόβληµα καθώς υπάρχουν όπως θα δούµε στη συνέχεια, αρκετές ποιοτικές εναλλακτικές βιβλιοθήκες C, που έχουν γραφτεί για ενσωµατωµένα συστήµατα Linux και υποστηρίζουν την πλατφόρµα AVR32. Η βιβλιοθήκη glibc είναι πολύ πλούσια σε χαρακτηριστικά και όπως αναφέραµε έχει πολύ καλές επιδόσεις. Αρχικά όµως δε σχεδιάστηκε για ενσωµατωµένα συστήµατα µε περιορισµένους πόρους και µικρές χωρητικότητες µνήµης. Η ελάχιστη χωρητικότητα που απαιτείται για την βιβλιοθήκη αυτή από ένα σύστηµα, είναι 2MB. Αυτό όµως είναι µόνο ένα µέρος του προβλήµατος. Αν σκεφτούµε ότι κάθε πρόγραµµα που παράγεται από αυτή κατά τη µεταγλώττιση, ενσωµατώνει και κάποια αρχεία κεφαλής της, αυτόµατα επηρεάζεται και το µέγεθος αυτών των προγραµµάτων. Έτσι έχουν δηµιουργηθεί εναλλακτικές βιβλιοθήκες της glibc οι οποίες παράγουν εκτελέσιµα που απαιτούν έως και τη µισή χωρητικότητα και χρησιµοποιούν λιγότερη RAM. Οι πιο κοινώς χρησιµοποιούµενες είναι οι παρακάτω: Bionic – χρησιµοποιείται στο Android Dietlibc – χρησιµοποιείται γενικά σε ενσωµατωµένα συστήµατα uClibc – χρησιµοποιήθηκε στην παρούσα εργασία 2.4.4 Η βιβλιοθήκη νηµάτων Τα νήµατα (threads) είναι µια τεχνική προγραµµατισµού κατά την οποία πολλές ασύγχρονες εργασίες (tasks) υπάρχουν στον ίδιο χώρο µνήµης κάποιας διεργασίας. Για την υποστήριξη νηµάτων από τον Linux kernel έχουν δηµιουργηθεί κάποιες βιβλιοθήκες νηµάτων (threading libraries). Η πιο γνωστή από αυτές είναι η βιβλιοθήκη LinuxThreads και αρχικά περιλαµβανόταν σαν πρόσθετο στην βιβλιοθήκη glibc. Λόγω κάποιων προβληµάτων όµως που αντιµετώπιζε δηµιουργήθηκε µια νέα βιβλιοθήκη νηµάτων µε το όνοµα NPTL (New POSIX Threading Library) η οποία είναι πια µέρος της glibc. Επειδή όµως, η βιβλιοθήκη NPTL χρησιµοποιείται µε kernels έκδοσης 2.6 και πάνω, είναι αρκετά συνηθισµένο σε κάποια ενσωµατωµένα συστήµατα Linux που χρειάζεται να βασίζονται σε παλαιότερες εκδόσεις kernel και παρουσιάζουν περιορισµένη χρήση νηµάτων, να χρησιµοποιείται ακόµη η βιβλιοθήκη LinuxThreads. Για να ελέγξουµε ποια έκδοση βιβλιοθήκης νηµάτων χρησιµοποιεί το σύστηµά µας κατά τον χρόνο εκτέλεσής του, µπορούµε να χρησιµοποιήσουµε σε µια µικρή εφαρµογή την συνάρτηση confstr(): #define _XOPEN_SOURCE #include <unistd.h> #include <stdio.h> int main(void) Ιστότοπος εργασίας: MyThesis.org 47 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) { char name[128]; confstr(_CS_GNU_LIBPTHREAD_VERSION, name, sizeof(name)); printf("Threads library is: %s\n", name); return 0; } 2.4.5 Εκδόσεις πακέτων λογισµικού και διαχείριση Για την δηµιουργία µιας αλυσίδας η επιλογή του κάθε πακέτου λογισµικού που θα χρησιµοποιηθεί (GCC, glibc και binutils) είναι µία ακόµα απόφαση που θα πρέπει να παρθεί. Λόγω του ότι αυτά τα πακέτα έχουν αναπτυχθεί ξεχωριστά, δεν είναι πάντα συµβατά µεταξύ τους. Ακόµα και αν χρησιµοποιήσουµε την τελευταία έκδοση του καθενός τους, δεν είναι σίγουρο ότι δεν θα συναντήσουµε προβλήµατα. Θα πρέπει να δοκιµάσουµε ένα συνδυασµό προσαρµοσµένο τόσο στο host, όσο και στο target σύστηµά µας. Μπορούµε να ξεκινήσουµε από την τελευταία έκδοση του κάθε πακέτου και µέχρι να πετύχουµε το επιθυµητό αποτέλεσµα, να την αντικαθιστούµε µε κάποια παλαιότερη. Πρέπει παρ’ όλα αυτά να προσέξουµε όσο το δυνατόν περισσότερο, να µην χρησιµοποιήσουµε πολύ παλιές εκδόσεις επειδή από ένα σηµείο και µετά θα υπάρχει ο κίνδυνος να µην είναι συµβατές µε τις νεότερες εκδόσεις των υπόλοιπων πακέτων που µεταγλωττίζονται επιτυχώς. Με λίγα λόγια πρέπει να επιτύχουµε τη «χρυσή τοµή». Αν παρ’ όλα αυτά και πάλι δεν καταφέρνουµε να δηµιουργήσουµε επιτυχώς µια αλυσίδα, µπορούµε να εφαρµόσουµε κάποιο patch (µπάλωµα) στα πακέτα που δεν µεταγλωττίζονται. Τα patches αυτά µπορούµε να τα βρούµε στις ιστοσελίδες των κατασκευαστών που µας προµηθεύουν το αναπτυξιακό µας σύστηµα ή τον µικροελεγκτή για τον οποίο θέλουµε να δηµιουργήσουµε την αλυσίδα. Σε πολλές περιπτώσεις εκεί θα βρούµε και το σωστό συνδυασµό των εκδόσεων των πακέτων λογισµικού ή ακόµη και κάποια ήδη µεταγλωττισµένη αλυσίδα. Πρέπει να πούµε ότι η µεταγλώττιση µιας αλυσίδας, η οποία θα µας δίνει τη δυνατότητα να µεταγλωττίζουµε εφαρµογές για το target σύστηµά µας, ακόµη και αν είναι επιτυχής, σε κάποιες περιπτώσεις µπορεί να παράγει εσφαλµένα αποτελέσµατα. Αυτό µας δείχνει ότι πρόκειται για µια αρκετά περίπλοκη διαδικασία και µας βάζει συχνά στον πειρασµό να επιλέξουµε κάποια έτοιµη λύση. Κλείνοντας, και αφού αναφέραµε τον όρο patch, θα δώσουµε έναν ορισµό κι ένα παράδειγµα έτσι ώστε να γίνει κατανοητός. Ο λόγος που δηµιουργήθηκε αυτός ο µηχανισµός, είναι η εκ των υστέρων τροποποίηση ενός ήδη υπαρκτού τµήµατος λογισµικού. Κάτι τέτοιο είναι άκρως αναγκαίο από τη στιγµή που το λογισµικό αυτό το µοιράζονται πολλοί χρήστες και θέλουµε όλοι να έχουν την δυνατότητα να ενηµερώσουν οι ίδιοι το λογισµικό τους. Αρχικά πρέπει να πούµε ότι υπάρχουν δύο είδη patch, τα οποία είναι τα εξής: ∆υαδικού αρχείου (source code patch) Πηγαίου κώδικα (binary patch) Ένα binary patch είναι ένα εκτελέσιµο πρόγραµµα το οποίο έχει σχεδιαστεί για να τροποποιεί κάποιο άλλο πρόγραµµα. Οι τροποποιήσεις που γίνονται, συνήθως αφορούν αδυναµίες που έχουν διαπιστωθεί κατά τη χρήση του προγράµµατος που πρέπει να τροποποιηθεί. Οι αδυναµίες αυτές µπορεί να αφορούν την ασφάλεια, την απόδοση και πολλές φορές ακόµη και την επιτυχή εκτέλεσή του. Με λίγα λόγια ένα patch χρησιµοποιείται σαν ένα είδος ενηµέ- Επικοινωνία: [email protected] 48 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux – Κεφάλαιο 2 ρωσης (update). Ένα κακοσχεδιασµένο patch από την άλλη, µπορεί να δηµιουργήσει νέα προβλήµατα ή να επιδεινώσει αυτά που ήδη υπάρχουν. Ένα source code patch στο Linux, είναι ένα πρόγραµµα που ενηµερώνει αρχεία κειµένου σύµφωνα µε οδηγίες που δίνονται σε ένα άλλο αρχείο (patch file ή patch). Το αρχείο patch είναι επίσης αρχείο κειµένου που περιέχει µια λίστα διαφορών και προκύπτει από την εκτέλεση ενός ειδικού προγράµµατος µε την ονοµασία diff και µε παραµέτρους το αρχικό και το ενηµερωµένο αρχείο: $ diff -u palio_filename neo_filename > diafores.diff Το diff είναι ένα βοηθητικό πρόγραµµα σύγκρισης αρχείων που δίνει σαν έξοδο τις διαφορές µεταξύ δυο αρχείων. Το πρόγραµµα δείχνει τις αλλαγές που έγιναν ανά γραµµή (για τα αρχεία κειµένου). Σύγχρονες υλοποιήσεις υποστηρίζουν και δυαδικά αρχεία. Η έξοδος του diff είναι ένα patch. Για την εφαρµογή ενός patch µπορεί να χρησιµοποιηθεί η εξής εντολή: $ patch < diafores.diff Αυτή η εντολή λέει στο εργαλείο patch να εφαρµόσει τις διαφορές που περιγράφονται στο αρχείο diafores.diff. Αν πρόκειται να τροποποιηθούν και αρχεία σε υποφακέλους, τότε χρειάζεται η παράµετρος -pArithmos, όπου o Arithmos ισούται µε 1 αν ο φάκελος βάσης του δέντρου του πηγαίου κώδικα περιλαµβάνεται στις διαφορές, ή 0 αν δεν περιλαµβάνεται. Τα patches µπορούν να αναιρεθούν µε την παράµετρο '-R': $ patch -R < diafores.diff Συχνά θεωρείται σαν εργαλείο πηγαίου κώδικα, ενώ στην πραγµατικότητα µπορεί να χρησιµοποιηθεί σε οποιοδήποτε κείµενο. Όταν αναφερόµαστε σε αυτή την εργασία σε κάποιο patch θα εννοούµε πάντα την πρώτη περίπτωση. Ακολουθεί παράδειγµα patch (ngw100-change-spi-clock-on-dataflash.patch) µε το οποίο µπορούµε να τροποποιήσουµε την ταχύτητα του ρολογιού SPI για την ανάγνωση της dataflash µνήµης της πλακέτας µας: Index: linux-2.6.18/arch/avr32/boards/atngw/setup.c =================================================================== --- linux-2.6.18.orig/arch/avr32/boards/atngw/setup.c 2011-12-19 14:15:02.000000000 +0200 +++ linux-2.6.18/arch/avr32/boards/atngw/setup.c 2011-12-19 14:15:11.000000000 +0200 @@ -29,7 +29,7 @@ { .modalias = "mtd_dataflash", .controller_data = (void *)GPIO_PIN_PA(3), .max_speed_hz = 66000000, + .max_speed_hz = 10000000, .bus_num = 0, .chip_select = 0, }, Ουσιαστικά τροποποιείται η τιµή του µέλους .max_speed_hz της αντίστοιχης struct που βρίσκεται στο αρχείο setup.c (που υπάρχει για το setup της πλακέτας του Router NGW100 στον κατάλογο arch\avr32\boards\atngw100 του kernel), από 66MHz σε 10MHz. Η ταχύτητα αυτή, είναι κατάλληλη για την επικοινωνία του µικροελεγκτή µας µε την σειριακή µνήµη δεδοµένων flash, µέσω του πρωτοκόλλου SPI. Ιστότοπος εργασίας: MyThesis.org 49 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Κάτι που επίσης επιτυγχάνεται µε αυτό το patch, είναι να µπορεί ακόµα και κάποιος που δεν καταλαβαίνει αρκετά την λειτουργία του κώδικα, να επιτύχει την τροποποίηση της ταχύτητας του SPI ρολογιού. Τα diff files (αρχεία διαφορών) που αποτελούν την είσοδο του εργαλείου patch, είναι απλά αρχεία κειµένου. Εκτός από το πρόγραµµα diff, αρχεία διαφορών µπορούν να παραχθούν και από αρκετά άλλα προγράµµατα που ανήκουν στην κατηγορία των συστηµάτων διαχείρισης εκδόσεων (versioning systems), όπως το Subversion, το CVS, το RCS, το Mercurial και το Git. Το τελευταίο µάλιστα είναι και το σύστηµα το οποίο προτείνει και ταυτόχρονα χρησιµοποιεί για την διαχείριση των εκδόσεων του Linux kernel, ο Linus Torvalds. 2.4.6 Επισκόπηση δηµιουργίας της αλυσίδας cross Στο σύστηµα host στο οποίο θέλουµε να εγκαταστήσουµε την cross αλυσίδα ανάπτυξης, θα πρέπει να υπάρχει επίσης εγκατεστηµένη µια «ντόπια» (native) αλυσίδα έτσι ώστε να µεταγλωττίσουµε τον πηγαίο κώδικα της πρώτης. Αν δεν υπάρχει, θα πρέπει να εγκατασταθεί. Αυτό είναι πολύ λογικό αν σκεφτούµε ότι η cross-platform αλυσίδα είναι και αυτή ένα σύνολο προγραµµάτων που εκτελούνται στον υπολογιστή host και εποµένως ο πηγαίος κώδικάς τους θα πρέπει να µετατραπεί σε εκτελέσιµο τον οποία θα αντιλαµβάνεται το συγκεκριµένο σύστηµα, που όπως είπαµε θα είναι ένας προσωπικός υπολογιστής µε κάποια διανοµή Linux. Στο σηµείο αυτό ας κάνουµε µια επισκόπηση των βηµάτων µεταγλώττισης που απαιτούνται για την δηµιουργία της cross αλυσίδας: 1. 2. 3. 4. 5. Binary utilities περιλαµβάνουν τον assembler και τον linker Bootstrap compiler ή cross-compiler υποστήριξη µόνο της γλώσσας C Linux headers απαιτούνται για τη µεταγλώττιση της C library C library η glibc απαιτείται για τη λειτουργία του Full Compiler Full cross-compiler υποστηρίζει όλες τις γλώσσες της GCC Παρατηρούµε ότι ο cross-compiler µεταγλωττίζεται δύο φορές. Μία στο δεύτερο βήµα και µία στο τελευταίο. Αυτό είναι φυσιολογικό γιατί κάποιες γλώσσες οι οποίες υποστηρίζονται από τoν GCC, όπως είναι για παράδειγµα η C++, απαιτούν την υποστήριξή τους από τη glibc. Έτσι ο bootstrap compiler δηµιουργείται αρχικά µε υποστήριξη µόνο για κώδικα C. Στη συνέχεια και αφού µεταγλωττιστεί η C library (glibc), προκύπτει ο τελικός, ολοκληρωµένος compiler (GCC). Κάθε ένα από τα παραπάνω βήµατα απαιτεί επίσης κάποια στάδια για να ολοκληρωθεί και αυτά είναι συνήθως τα εξής: 1. 2. 3. 4. Εξαγωγή του πακέτου λογισµικού (extract) Παραµετροποίηση του πακέτου για cross-platform development (configure) ∆ηµιουργία του πακέτου (build) Εγκατάσταση του πακέτου (install) 2.5 Αυτοµατοποιηµένα συστήµατα Παρά την εκπαιδευτική αξία που παρουσιάζει η δηµιουργία µιας αλυσίδας εργαλείων από το µηδέν, τις περισσότερες φορές είναι προτιµότερο να χρησιµοποιούµε για την ανάπτυξη του συστήµατός µας κάποιο αυτοµατοποιηµένο σύστηµα cross αλυσίδας. Οι λόγοι για τους οποίους κάτι τέτοιο είναι προτιµότερο είναι οι ακόλουθοι: Επικοινωνία: [email protected] 50 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux – Κεφάλαιο 2 Λόγω του ότι η ανάπτυξη θα γίνεται σε αυτοµατοποιηµένο περιβάλλον, θα µπορεί να επαναλαµβάνεται µε πανοµοιότυπο τρόπο κι έτσι θα µας δίνεται η δυνατότητα να διορθώνουµε λάθη του κώδικά µας απλά εκτελώντας από την αρχή την αλυσίδα και χωρίς να υπάρχει κίνδυνος να παραλείψουµε κάποιο σηµαντικό βήµα. Στα περισσότερα αυτοµατοποιηµένα συστήµατα γίνεται χρήση αρχείων παραµετροποίησης έτσι ώστε να διαµορφώνονται τα χαρακτηριστικά της δηµιουργίας της cross αλυσίδας, ως προς τις εκδόσεις του λογισµικού, των στοιχείων που την απαρτίζουν καθώς και άλλων χαρακτηριστικών που σχετίζονται µε την δηµιουργία της. Αυτό παρέχει την λεγόµενη, εκτελέσιµη τεκµηρίωση (executable documentation), η οποία προσωπικά µου φάνηκε εξαιρετικά χρήσιµη γιατί εκτός της ευελιξίας που παρέχει, βοηθά τον προγραµµατιστή να κατανοεί περισσότερο τον κώδικά του και να τον χειρίζεται ευκολότερα. Με βάση τα δύο αυτά πλεονεκτήµατα µπορούµε εύκολα να συµπεράνουµε και ένα τρίτο. Εφόσον η διαδικασία δηµιουργίας της αλυσίδας µπορεί να πραγµατοποιηθεί µέσω ενός αρχείου παραµετροποίησης, αρκεί να µοιραστούµε µόνο το αρχείο αυτό µε άλλους προγραµµατιστές µε τους οποίους συνεργαζόµαστε έτσι ώστε να δουλεύουµε επάνω στο ίδιο αντικείµενο. Υπάρχουν αρκετά έτοιµα αυτοµατοποιηµένα συστήµατα δηµιουργίας cross αλυσίδων. Τα κυριότερα από αυτά είναι τα ακόλουθα: Buildroot Ptxdist Crosstool Τα δύο πρώτα συστήµατα ενσωµατώνουν την βιβλιοθήκη glibc ενώ το τρίτο χρησιµοποιεί την εναλλακτική βιβλιοθήκη uClibc. Στα πλαίσια αυτής της εργασίας χρησιµοποιήθηκε το σύστηµα Buildroot. 2.5.1 Εναλλακτικές βιβλιοθήκες C Όπως αναφέρθηκε ήδη, η βιβλιοθήκη glibc (GNU C library) δε σχεδιάστηκε για ενσωµατωµένα συστήµατα µε περιορισµένους πόρους και µικρές χωρητικότητες µνήµης. Για το λόγο αυτό αναφέραµε εναλλακτικές βιβλιοθήκες οι οποίες έχουν επαρκή λειτουργικότητα και το µέγεθός τους είναι µικρότερο. Μία από αυτές είναι και η uClibc. Η βιβλιοθήκη uClibc προέρχεται από το uClinux project το οποίο παρείχε τη δυνατότητα εκτέλεσης του Linux σε µικροελεγκτές που δεν διέθεταν µονάδα MMU (Memory Management Unit). Στη συνέχεια έγινε από µόνη της ανεξάρτητο project και αργότερα ενσωµατώθηκε στον kernel. Άρχισε επίσης να υποστηρίζει και µικροελεγκτές που διαθέτουν µονάδα MMU. Αν και δε βασίζεται στη βιβλιοθήκη glibc, η uClibc παρέχει παρόµοια λειτουργικότητα. Οι λειτουργίες που υπάρχουν και χρησιµοποιούνται ελάχιστα στη βιβλιοθήκη glibc έχουν αφαιρεθεί από την uClibc. Παρ’ όλα αυτά οι περισσότερες εφαρµογές οι οποίες µεταγλωττίζονται µε την glibc, µεταγλωττίζονται και µε την uClibc. Μπορούµε να κατεβάσουµε τη βιβλιοθήκη uClibc µε µια επίσκεψη στο uclibc.org. 2.5.2 Το αυτοµατοποιηµένο σύστηµα Buildroot Εφόσον η βιβλιοθήκη uClibc είναι µια εναλλακτική βιβλιοθήκη C που έχει δηµιουργηθεί για ενσωµατωµένα συστήµατα, η χρήση της προϋποθέτει την δηµιουργία και την ύπαρξη, µιας Ιστότοπος εργασίας: MyThesis.org 51 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) ειδικά προσαρµοσµένης αλυσίδας. Όπως και µε τη βιβλιοθήκη glibc, ο καλύτερος τρόπος για να δηµιουργήσουµε µια cross αλυσίδα είναι να χρησιµοποιήσουµε κάποιο αυτοµατοποιηµένο σύστηµα το οποίο θα αναλάβει την επίπονη αυτή διαδικασία. Για την βιβλιοθήκη uClibc έχει αναπτυχθεί ειδικά ένα τέτοιο σύστηµα µε την ονοµασία Buildroot. Ουσιαστικά το Buildroot είναι ένα σύνολο από Makefiles και patches τα οποία είναι υπεύθυνα για το κατέβασµα, την παραµετροποίηση και την µεταγλώττιση λογισµικού µε τις σωστές ρυθµίσεις. Τα makefiles, όπως και τα patches έχουν τη δική τους γλώσσα προγραµµατισµού. Σε αντίθεση όµως µε αυτά, τα makefiles χρησιµοποιούνται για να παρέχουν πληροφορίες στον compiler που αφορούν τον τρόπο µε τον οποίο θα µεταγλωττιστεί ένα στοιχείο λογισµικού. Ένα makefile µπορεί να δηµιουργείται αυτόµατα από κάποιο βοηθητικό πρόγραµµα ή να γράφεται εξ’ ολοκλήρου από εµάς. Σε περίπτωση που αναπτύσσουµε ένα module µε το IDE AVR32 Studio για παράδειγµα, το απαιτούµενο makefile µπορεί να δηµιουργηθεί αυτόµατα αν το επιθυµούµε. Το σύστηµα Buildroot µας δίνει τη δυνατότητα να δηµιουργήσουµε εύκολα µια cross αλυσίδα, ένα root σύστηµα αρχείων και ένα εκτελέσιµο image του kernel για το target σύστηµά µας. Το σύστηµα Buildroot µπορεί να χρησιµοποιηθεί ανεξάρτητα για κάθε µία από αυτές τις εργασίες ή ταυτόχρονα για όλες µαζί. Υποστηρίζει µικροελεγκτές µε ή και χωρίς µονάδα MMU, και επιπλέον, µε κάποιες τροποποιήσεις µπορεί να συνεργαστεί και µε τη βιβλιοθήκη glibc. Για να µελετήσουµε και να προµηθευτούµε το αυτοµατοποιηµένο σύστηµα Buildroot, µπορούµε να επισκεφτούµε τον ιστότοπο buildroot.uclibc.org. Στην παρούσα εργασία χρησιµοποιήθηκε (µετά από αρκετές διορθώσεις) το ειδικά διαµορφωµένο σύστηµα Buildroot που παρέχει η εταιρεία ATMEL µέσω του ιστότοπού της. Στο πακέτο αυτό είχαν ενσωµατωθεί οι κατάλληλοι drivers και υπήρχαν έτοιµες κάποιες πολύ βασικές ρυθµίσεις. Αυτός είναι και ο πιο ενδεδειγµένος τρόπος όταν αναπτύσσουµε ένα σύστηµα. Πρέπει να χρησιµοποιούµε σε καίρια σηµεία τα εργαλεία που προτείνει ο κατασκευαστής γιατί έτσι θα είµαστε βέβαιοι όταν προκύπτει κάποιο πρόβληµα, ότι ευθύνεται η δική µας δουλειά και όχι κάποια άλλη αόριστη παράµετρος που θα είναι δύσκολο να εντοπίσουµε. Μπορούµε επίσης να κατεβάσουµε το Buildroot µέσα από κάποιο terminal του συστήµατος host στο οποίο εκτελείται η διανοµή Linux που έχουµε επιλέξει (πχ Ubuntu), εκτελώντας την παρακάτω εντολή: $ wget http://buildroot.uclibc.org/downloads/snapshots/buildroot-snapshot.tar.bz2 Όταν ολοκληρωθεί η λήψη αποσυµπιέζουµε το αρχείο που λήφθηκε και πιθανότατα βρίσκεται στον κατάλογο home, και εισερχόµαστε σε αυτόν µε την εντολή cd. Οι αντίστοιχες εντολές αποσυµπίεσης και αλλαγής τρέχοντος καταλόγου εργασίας είναι οι παρακάτω: $ tar jxvf buildroot-snapshot.tar.bz2 $ cd buildroot Καθ’ όλη την διάρκεια της εργασίας µας µε το σύστηµα Buildroot θα πρέπει να υπάρχει µια ενεργή σύνδεση Internet έτσι ώστε να υπάρχει η δυνατότητα κατεβάσµατος των απαραίτητων πακέτων λογισµικού. Αν για κάποιο λόγο θέλουµε να κάνουµε offline build, δηλαδή να κατεβάσουµε ότι απαιτείται στον υπολογιστή µας και στη συνέχεια να αποσυνδεθούµε, µπορούµε να χρησιµοποιήσουµε την ακόλουθη εντολή: $ make source Στη συνέχεια µέσω της εντολής make και της παραµέτρου menuconfig εισερχόµαστε στο περιβάλλον παραµετροποίησης του αυτοµατοποιηµένου συστήµατος buildroot: Επικοινωνία: [email protected] 52 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux – Κεφάλαιο 2 $ make menuconfig Για να εκτελεστεί επιτυχώς αυτή η εντολή θα πρέπει προηγουµένως να έχουν εγκατασταθεί όλα τα πακέτα λογισµικού τα οποία προαπαιτούνται. Συνήθως ένα από αυτά τα πακέτα είναι το libncurses-dev. Αυτό γίνεται και µέσα από το γραφικό περιβάλλον εγκατάστασης Ubuntu Software Center: Εικόνα 8. Το περιβάλλον Ubuntu Software Center Όταν τελικά είναι όλα στη θέση τους, το παράθυρο διαλόγου που θα εµφανιστεί µετά την εντολή make menuconfig θα µοιάζει µε το ακόλουθο: Εικόνα 9. Περιβάλλον παραµετροποίησης του αυτοµατοποιηµένου συστήµατος buildroot Ιστότοπος εργασίας: MyThesis.org 53 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Γενικά η παραµετροποίηση του Buildroot αφορά τρεις κύριες κατηγορίες. Το υλικό, τη διαδικασία µεταγλώττισης και το λογισµικό. Όπου το λογισµικό µπορεί να χωριστεί σε τρεις υποκατηγορίες. Τον kernel, την αλυσίδα και τα πακέτα. Αν δεν θέλουµε να εµβαθύνουµε πολύ, µπορούµε να χρησιµοποιήσουµε κάποιο έτοιµο αρχείο παραµετροποίησης που πιθανόν παρέχεται από τον προµηθευτή του συστήµατός µας, και περιλαµβάνει τις βασικές, αρχικές ρυθµίσεις το οποίο µπορούµε να το βρούµε συνήθως σε κάποιο κατάλογο του buildroot (πχ στον κατάλογο configs) ή στο BSP που παρέχει ο προµηθευτής. Για να υποδείξουµε στο Buildroot που θα βρει αυτό το αρχείο αρκεί να µεταβούµε στην επιλογή “Load an Alternate Configuration File” και να πληκτρολογήσουµε εκεί τη διαδροµή του αρχείου παραµετροποίησης που θέλουµε. Αυτό µας δείχνει στην πράξη το πλεονέκτηµα της εκτελέσιµης τεκµηρίωσης, των αυτοµατοποιηµένων συστηµάτων δηµιουργίας cross αλυσίδας, που αναφέραµε και νωρίτερα. Το ίδιο ακριβώς αποτέλεσµα µπορούµε να επιτύχουµε και χωρίς να εισέλθουµε στο menu παραµετροποίησης εκτελώντας την παρακάτω εντολή: $ make onoma-target-systimatos_defconfig Αν όµως θέλουµε να εµβαθύνουµε περισσότερο και να προσθέσουµε ή να αφαιρέσουµε κάποιες λειτουργίες, θα πρέπει να κατανοήσουµε και να µάθουµε όλο το περιβάλλον του Buildroot. Μπορούµε να λαµβάνουµε πληροφορίες για την κάθε επιλογή του µενού επιλέγοντάς το και πατώντας το πλήκτρο ?. Οι πληροφορίες πλοήγησης στο menu είναι απλές και όπως είδαµε και στην εικόνα, υπάρχουν γραµµένες στο παράθυρο διαλόγου του περιβάλλοντος παραµετροποίησης. Το κυρίως µενού του Buildroot περιλαµβάνει συνήθως τις ακόλουθες επιλογές: Target Architecture – µας επιτρέπει να ορίσουµε την αρχιτεκτονική του target συστήµατός (πχ AVR32) µας για το οποίο η cross αλυσίδα θα παράγει εκτελέσιµα αρχεία. Target Architecture Variant (optional) – εδώ καθορίζεται η οικογένεια του µικροελεγκτή του συστήµατός µας (πχ AP7000). Target ABI – αυτό το υποµενού υποστηρίζεται µόνο από την αρχιτεκτονική MIPS και αν έχουµε επιλέξει κάποια άλλη αρχιτεκτονική, παύει να εµφανίζεται και στο menu. Target options – σε αυτό το υποµενού υπάρχουν ρυθµίσεις που αφορούν µόνο το περιβάλλον Buildroot και δεν επηρεάζουν καθόλου τη µεταγλώττιση της cross αλυσίδας. Build options – εδώ µπορούµε να ρυθµίσουµε διάφορες επιλογές σχετικά µε την διαδικασία δηµιουργίας της cross αλυσίδας. Οι ρυθµίσεις αυτές αφορούν τα εξής: ⇒ Τις εντολές που χρησιµοποιούνται για να πραγµατοποιήσουν εργασίες όπως είναι για παράδειγµα το κατέβασµα κάποιου αρχείου µέσω ftp ή µέσω κάποιου ιστότοπου, την αναζήτηση πηγαίου κώδικα µέσω των συστηµάτων Subversion και Git, την αποσυµπίεση αρχείων Gzip, Bzip2 και tar µέσω γραµµής εντολών. ⇒ Servers και τοποθεσίες λήψης χρήσιµων στοιχείων όπως είναι ο kernel, το GNU λογισµικό (GCC, binutils, πακέτα GDB) καθώς επίσης και τα διάφορα επιπλέον πακέτα που απαιτούνται. Επικοινωνία: [email protected] 54 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux – Κεφάλαιο 2 ⇒ Τον κατάλογο στον οποίο θα εγκατασταθούν η cross αλυσίδα και οι κεφαλίδες. Μπορούµε να τροποποιήσουµε αυτή την επιλογή εισάγοντας τη διαδροµή που επιθυµούµε. ⇒ Η κατάληξη _nofpu για τα ονόµατα των εκτελέσιµων που είναι συµβατά µε υλικό το οποίο δεν υποστηρίζει µονάδα πράξεων κινητής υποδιαστολής. ⇒ Το πρόθεµα (prefix) και το επίθεµα (suffix) που θέλουµε να έχουν οι κατάλογοι για την δηµιουργία αλυσίδων που υποστηρίζουν πολλαπλές αρχιτεκτονικές. Αυτή η λειτουργία δε συνίσταται να χρησιµοποιηθεί ⇒ Ένα προαιρετικό προσωποποιηµένο επίθεµα για να δώσουµε κάποια ονοµασία στην cross αλυσίδα που θα δηµιουργηθεί. Μπορούµε να βάλουµε την επωνυµία µας, το όνοµα της επιχείρησής µας ή ότι άλλο επιθυµούµε. ⇒ Μια έκδοση της µικροεφαρµογής strip και την νέα της έκδοση µε όνοµα sstrip. Μπορούµε να επιλέξουµε την νεότερη εκτός και αν παρουσιαστούν θέµατα ασυµβατότητας. Επίσης µπορούµε να επιλέξουµε να µην χρησιµοποιηθεί καµία (none). ⇒ Επιλογή για ρύθµιση της cross αλυσίδας έτσι ώστε να κάνει χρήση στατικών βιβλιοθηκών αντί για δυναµικές. Σε κάθε νέα (ή παλιότερη) έκδοση του Buildroot οι παραπάνω επιλογές αλλά και όσες θα αναλύσουµε στη συνέχεια, µπορεί να διαφέρουν (οι νέες λειτουργίες συνήθως συµβολίζονται µε την ένδειξη NEW). Αυτό όµως δεν είναι εµπόδιο για εµάς, αφού µετά από εξοικείωση µε την χρήση µιας έκδοσης µπορούµε εύκολα να προσαρµοζόµαστε στις ιδιαιτερότητες των υπολοίπων. Toolchain – αυτό το υποµενού διαχειρίζεται κάποιες βασικές ρυθµίσεις παραµετροποίησης για αλυσίδες που υποστηρίζονται από το Buildroot. Οι ρυθµίσεις αυτές είναι: ⇒ Buildroot Toolchain – Επιλογή για το αν θέλουµε να χρησιµοποιήσουµε µια εξωτερική αλυσίδα ή να δηµιουργήσουµε µια νέα. Είναι προτιµότερο να επιλέξουµε “Buildroot Toolchain”. ⇒ Kernel Headers – Επιλογή έκδοσης κεφαλίδων (πχ: 3.1.x). Η έκδοση που θα επιλέξουµε εξαρτάται από την έκδοση του kernel που θέλουµε να τρέχει στο target σύστηµά µας. Θα πρέπει να είναι ίδιες µεταξύ τους. ⇒ uClibc Options – Επιλογή έκδοσης της βιβλιοθήκης uClibc. Η καλύτερη επιλογή είναι η πιο πρόσφατη έκδοση (και όχι η daily snapshot). Επίσης µπορούµε να ορίσουµε το αρχείο παραµετροποίησης (πχ: uClibc0.9.32.config) της uClibc που επιθυµούµε και αν θα υπάρχουν κάποια έτοιµα δοκιµαστικά προγράµµατα για να επιβεβαιώσουµε την ορθή λειτουργία της βιβλιοθήκης uClibc. ⇒ Binutils Options – Επιλογή έκδοσης του πακέτου Binutils. Η πιο πρόσφατη έκδοσή είναι και η προτιµότερη (πχ: 2.18-avr32-1.0.1). ⇒ GCC Options – Επιλογή έκδοσης του GCC compiler. Και πάλι η πιο πρόσφατη έκδοση είναι η καλύτερη (πχ: 4.2.2-avr32-2.1.5), εκτός και αν θέ- Ιστότοπος εργασίας: MyThesis.org 55 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) λουµε να εξοικονοµήσουµε χώρο στη µνήµη του target συστήµατός µας, όποτε σε αυτή την περίπτωση επιλέγουµε κάποια παλαιότερη. Ένα άλλο σηµαντικό χαρακτηριστικό που θα µπορούµε να ενεργοποιήσουµε είναι η χρήση µιας µια διαµοιραζόµενης βιβλιοθήκης (libgcc). ⇒ GDB Options – Επιλογή δηµιουργίας και εγκατάστασης των λειτουργιών αποσφαλµάτωσης (GNU GDB debugger). Εδώ µπορούµε να αποφασίσουµε για το αν θα υπάρχει κάποιος gdb server στο target σύστηµά µας αλλά και το αν θα υπάρχει gdb server/client στο host σύστηµά µας. ⇒ Common Toolchain Options – Εδώ µπορούµε να επιλέξουµε αν θα παρέχεται υποστήριξη για αρχεία µεγαλύτερα από 2GB, αν θα υπάρχει υποστήριξη από την βιβλιοθήκη uClibc για τα πρωτόκολλα IPv6 και RPC, αν θα υποστηρίζονται µεγάλου εύρους χαρακτήρες (WCHAR – wide characters) οι οποίοι απαιτούνται από κάποια πακέτα λογισµικού καθώς και άλλες λεπτοµέρειες που αφορούν την υποστήριξη C++ και την βιβλιοθήκη νηµάτων που θα χρησιµοποιηθεί (πχ LinuxThreads ή NPTL). Στην επόµενη εικόνα βλέπουµε το υποµενού Toolchain καθώς και κάποιες από τις ρυθµίσεις και τα χαρακτηριστικά του συστήµατός µας: Επικοινωνία: [email protected] 56 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux – Κεφάλαιο 2 Εικόνα 10. Το υποµενού Toolchain του Buildroot System Configuration – Σε αυτό το υποµενού µπορούµε να ρυθµίσουµε τη διαχείριση των συσκευών (/dev management) για το αν θα καθορίζεται από κάποιο στατικό πίνακα ή δυναµικά µέσων των devtmpfs, udev και mdev. Μπορούµε να καθορίσουµε κάποιες διαδροµές αρχείων πινάκων που περιέχουν πληροφορίες δικαιω- Ιστότοπος εργασίας: MyThesis.org 57 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) µάτων πρόσβασης αλλά και διαδροµές που αφορούν πίνακες διαχείρισης συσκευών. Επίσης µπορούµε να επιλέξουµε την ταχύτητα (baudrate) της σειριακής επικοινωνίας καθώς και κάποιες άλλες λεπτοµέρειες που αφορούν το σύστηµα αρχείων και κάποια προσωπικά scripts που θα θέλαµε να εκτελούνται. Package Selection for the target – Αυτό το υποµενού παρέχει πάρα πολλές επιλογές που αφορούν πακέτα λογισµικού εφαρµογών που θα είναι διαθέσιµα στο target σύστηµά µας. Για παράδειγµα µπορούµε να επιλέξουµε αν θα υποστηρίζεται και σε ποια έκδοσή του, το πακέτο µικροεφαρµογών BusyBox. Επίσης υπάρχει δυνατότητα ρύθµισης µιας σειράς άλλων παραµέτρων που αφορούν τη συµπίεση αρχείων, την υποστήριξη ήχου και γραφικών µέσω δηµοφιλών βιβλιοθηκών, την υποστήριξη scripting προγραµµατισµού (PHP, Lua, microperl, tcl, haserl κλπ), βιβλιοθήκες συµπίεσης και κρυπτογράφησης, εφαρµογές δικτύου, διαχειριστές πακέτων, real-time δυνατότητες (µέσω xenomai), κειµενογράφοι και άλλα. Filesystem images – Εδώ επιλέγουµε τον τύπο του root συστήµατος αρχείων που θέλουµε να υποστηρίζει το target σύστηµά µας (πχ JFFS) και το αν αυτό θα είναι πακεταρισµένο (πχ: tar). Bootloaders – Σε αυτό το υποµενού καλούµαστε να επιλέξουµε το λογισµικό εκκίνησης που θα εκκινεί το target σύστηµά µας. Kernel – Τέλος, στο υποµενού αυτό µας δίνεται η δυνατότητα να επιλέξουµε την έκδοση του kernel, τον τύπο της δυαδικής του µορφής (πχ uImage), τις real time δυνατότητες, αλλά και κάποιες άλλες λεπτοµέρειες που αφορούν πιο εξεζητηµένα χαρακτηριστικά. Αφού έχουµε ολοκληρώσει τις ρυθµίσεις του Buildroot, τις αποθηκεύουµε και βγαίνουµε από το περιβάλλον παραµετροποίησης. Έπειτα εκτελούµε την εντολή make ακολουθουµένη από το όνοµα του αρχείου παραµετροποίησης του target συστήµατός µας και στη συνέχεια εκτελούµε ξανά την εντολή make χωρίς καµία παράµετρο αυτή τη φορά. Το Buildroot θα κατεβάσει, θα παραµετροποιήσει και θα εγκαταστήσει την cross αλυσίδα στον κατάλογο που του έχουµε ορίσει. Οι εντολές make που χρησιµοποιήθηκαν για το σύστηµά µας είναι οι ακόλουθες: $ make atngw100_defconfig $ make Αν µετά την εκτέλεση της τελευταίας make εµφανιστούν σφάλµατα, τότε πιθανότατα υπάρχει πρόβληµα µε κάποια dependencies και ανάλογα µε την υπόδειξη που παίρνουµε ως feedback από το terminal θα πρέπει να προβούµε στην εγκατάσταση αυτών των πακέτων. Αν όλα πάνε καλά τότε στον κατάλογο dl του συστήµατος Buildroot θα υπάρχουν τα αρχεία που φαίνονται στην επόµενη εικόνα (οι εκδόσεις τους µπορεί να διαφέρουν): Επικοινωνία: [email protected] 58 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux – Κεφάλαιο 2 Εικόνα 11. Τα περιεχόµενα του καταλόγου dl του συστήµατος Buildroot Πρέπει επίσης να υπενθυµίσουµε ότι αν και αρχικά υποθέσαµε πως η native αλυσίδα του συστήµατος host είναι ήδη εγκατεστηµένη, στην πράξη σχεδόν πάντοτε κάτι λείπει, και αυτό το κάτι θα πρέπει να το εγκαταστήσουµε χειροκίνητα. Ένας τρόπος για να το επιτύχουµε είναι να έχουµε βρει την λίστα των εφαρµογών που απατούνται. Ένας άλλος τρόπος είναι να εκτελούµε την εντολή make dependencies και κάθε φορά να εγκαθιστούµε τα πακέτα που µας υποδεικνύονται. Η διαδικασία αυτή πρέπει να επαναληφθεί µέχρι το αποτέλεσµα της εκτέλεσης της εντολής αυτής να είναι το κενό. ∆ηλαδή µέχρι να µην υπάρχει κάποιο άλλο dependency. $ make dependencies Μια ενδεικτική λίστα των dependencies (δηλαδή των εξαρτήσεων λογισµικού) που µπορεί να απαιτηθούν για την λειτουργία του συστήµατος Buildroot και την µεταγλώττιση της cross αλυσίδας είναι η και η ακόλουθη: Bison – Parser συµβατός µε τον συντακτικό αναλυτή YACC G++ – GNU C++ compiler Flex – Λεξικός αναλυτής Gettext – Εφαρµογές για παροχή πακέτων ξένων γλωσσών Ιστότοπος εργασίας: MyThesis.org 59 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Texinfo – Σύστηµα online τεκµηρίωσης για προβολή πληροφοριών βοήθειας Patch – Χειρίζεται αρχεία diff για να εφαρµόσει τα απαιτούµενα patches Εφόσον τελικά είναι όλα στη θέση τους και αφού έχει εκτελεστεί και η τελευταία make, θα αρχίσει η αυτόµατη διαδικασία δηµιουργίας της αλυσίδας αλλά και κάποιων άλλων βασικών εργαλείων. Η διαδικασία µπορεί να διαρκέσει από µερικά λεπτά έως και πάνω από µία ώρα, ανάλογα και µε τις δυνατότητες του host συστήµατός µας. 2.6 Παραµετροποίηση uClibc, kernel και BusyBox µέσω Buildroot Στην προηγούµενη παράγραφο χρησιµοποιήσαµε το αυτοµατοποιηµένο σύστηµα Buildroot για να δηµιουργήσουµε µια cross αλυσίδα βασισµένη στη C βιβλιοθήκη, uClibc, χρησιµοποιώντας τις default ρυθµίσεις από το αρχείο παραµετροποίησης που υπάρχει στον κατάλογο configs (atngw100_defconfig). Συνήθως η παραµετροποίηση αυτή είναι αρκετή για τη δηµιουργία ενός τυπικού ενσωµατωµένου συστήµατος Linux, όµως υπάρχουν περιπτώσεις στις οποίες θα θέλαµε να κάνουµε τροποποιήσεις. Οι τροποποιήσεις αυτές τις περισσότερες φορές αφορούν την προσθήκη κάποιων επιπλέων λειτουργιών ή την αφαίρεση κάποιων ήδη υπαρχόντων, για εξοικονόµηση πόρων. Επειδή αυτό δε συµβαίνει συχνά θα αναφέρουµε ονοµαστικά µόνο, τις επιλογές παραµετροποίησης. Η παραµετροποίηση της uClibc θα γίνει και πάλι µέσω του Buildroot. Για να εισέλθουµε στο γραφικό περιβάλλον αρκεί να πληκτρολογήσουµε στο terminal του συστήµατος host, την παρακάτω εντολή: $ make uclibc-menuconfig To παράθυρο διαλόγου που θα εµφανιστεί αν όλα έχουν πάει καλά φαίνεται στην εικόνα: Εικόνα 12. Γραφικό περιβάλλον παραµετροποίησης της uClibc 0.9.30 Επικοινωνία: [email protected] 60 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux – Κεφάλαιο 2 Αν δεν εµφανιστεί και λάβουµε κάποιο µήνυµα σφάλµατος (πχ: No rule to make target …), τότε ή έχουµε παραλείψει κάποιο βήµα ή έχουµε πληκτρολογήσει λάθος την εντολή. Μπορούµε να χρησιµοποιήσουµε κι εδώ κάποιο έτοιµο αρχείο παραµετροποίησης, αλλά θα πρέπει κάποιος να µας το παρέχει διαφορετικά θα πρέπει να κάνουµε τις ρυθµίσεις µόνοι µας. Ονοµαστικά λοιπόν, οι ρυθµίσεις που αφορούν τη βιβλιοθήκη uClibc είναι οι εξής: Target Architecture Target Architecture Features and Options General Library Settings Advanced Library Settings Networking Support String and Stdio Support Big and Tall Library Installation Options Security Options uClibc development/debugging options Αφού ολοκληρωθεί η παραµετροποίηση της βιβλιοθήκης uClibc σύµφωνα µε τις ανάγκες µας, θα πρέπει να αντιγράψουµε το αρχείο .config το οποίο περιλαµβάνει τις ρυθµίσεις µας, στον κατάλογο του Buildroot toolchain/uclibc. Στη συνέχεια εκτελούµε εκ νέου τις παρακάτω εντολές: $ make clean $ make Αυτό θα καταστήσει µόνιµες τις νέες ρυθµίσεις και θα ισχύουν κάθε φορά που θα εργαζόµαστε στο Buildroot, µέχρι να θελήσουµε να τις αλλάξουµε και πάλι. Τα ίδια ισχύουν και για τις ρυθµίσεις του kernel αλλά και για τις ρυθµίσεις του BusyBox. Στη συνέχεια θα αναφερθούµε επίσης επιγραµµατικά, και σε ότι αφορά την παραµετροποίηση του kernel. Για να εισέλθουµε στο menu επιλογών του kernel εκτελούµε σε γραµµή εντολών την παρακάτω εντολή: $ make linux26-menuconfig To παράθυρο διαλόγου που θα εµφανιστεί, φαίνεται στην εικόνα: Ιστότοπος εργασίας: MyThesis.org 61 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 13. Γραφικό περιβάλλον παραµετροποίησης του Linux Kernel 2.6.27.6 Ονοµαστικά οι ρυθµίσεις που αφορούν τον kernel είναι οι εξής: General setup Enable loadable module support Enable the block layer System type and features Power management options Bus options Executable file formats Networking support Device Drivers File systems Kernel hacking Security options Cryptographic API Library routines Αφού ολοκληρωθεί και η παραµετροποίηση του kernel συνεχίζουµε µε αυτή του BusyBox. Όπως και στις προηγούµενες δύο περιπτώσεις η εντολή που θα εκτελέσουµε είναι παρόµοια: $ make busybox-menuconfig Στην εικόνα βλέπουµε το µενού επιλογών του BusyBox: Επικοινωνία: [email protected] 62 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux – Κεφάλαιο 2 Εικόνα 14. Γραφικό περιβάλλον παραµετροποίησης του BusyBox 1.13.1 Ονοµαστικά οι ρυθµίσεις που αφορούν το BusyBox είναι οι εξής: Busybox Settings ---Applets--Archival Utilities Coreutils Console Utilities Debian Utilities Editors Finding Utilities Init Utilities Login/Password Management Utilities Linux Ext2 FS Progs Linux Module Utilities Linux System Utilities Miscellaneous Utilities Networking Utilities Ιστότοπος εργασίας: MyThesis.org 63 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Print Utilities Mail Utilities Process Utilities Runit Utilities Shells System Logging Utilities Απ’ ότι παρατηρούµε το menu του BusyBox χωρίζεται ουσιαστικά σε δύο κατηγορίες. Στις ρυθµίσεις (settings) και στις εφαρµογές (applets). Τα settings του BusyBox που µπορούµε να τροποποιήσουµε είναι τα εξής: General Configuration Build Options Debugging Options Installation Options (“make install” behavior) Busybox Library Training 2.7 Γραµµή εντολών Η διεπαφή γραµµής εντολών (CLI – Command Line Interface) όπως αναφέραµε είναι ένας µηχανισµός που µας επιτρέπει να αλληλεπιδρούµε µε το λειτουργικό σύστηµα. Μπορούµε επίσης να καλούµε άλλες εφαρµογές του ίδιου υπολογιστή ή να συνδεόµαστε στην γραµµή εντολών ενός αποµακρυσµένου ενσωµατωµένου συστήµατος που υποστηρίζει τη διεπαφή αυτή. Η διαδικασία είναι γενικά πολύ απλή. Υπάρχει κάποιο σύνολο διαθέσιµων εντολών από το οποίο επιλέγουµε κάθε φορά εκείνη την οποία είναι κατάλληλη για να ολοκληρωθεί η διεργασία που επιθυµούµε. Οι εντολές είναι και αυτές προγράµµατα τα οποία υπάρχουν συνήθως µέσα στον κατάλογο της εγκατάστασης bin ή σε άλλους καταλόγους του συστήµατος αρχείων. Όταν εµφανίζεται το command prompt (παρότρυνση πληκτρολόγησης εντολής ή δροµέας) αµέσως µετά το σύµβολο $, πληκτρολογούµε την εντολή µας και στη συνέχεια πατάµε το πλήκτρο Enter. Αυτό έχει ως άµεσο αποτέλεσµα το κέλυφος να αναζητήσει στο διαθέσιµο σύστηµα αρχείων το πρόγραµµα το οποίο θα έχει το ίδιο όνοµα µε την εντολή µας, προκειµένου να την εκτελέσει. Αφού ολοκληρωθεί η εκτέλεση µιας εντολής, το σύστηµα επιστρέφει σε µορφή γραµµών κειµένου, το αποτέλεσµα της εκτέλεσης. Το αποτέλεσµα συνήθως είναι κάποια απάντηση στην ερώτηση που κάναµε µέσω της εντολής µας, µια νέα διαθέσιµη γραµµή εντολών αν η εντολή µας δεν απαιτούσε να εµφανιστεί κάτι, ή κάποιο µήνυµα σφάλµατος που µας ενηµερώνει ότι πληκτρολογήσαµε εντολή που δεν υποστηρίζεται ή ότι απαιτείται κάποια περεταίρω ενέργεια. Τις εντολές µπορούµε να τις πληκτρολογούµε και να τις εκτελούµε µία – µία πατώντας το πλήκτρο Enter (carriage return), ή µπορούµε να τις οµαδοποιούµε σε ένα αρχείο έτσι ώστε να λειτουργούν σαν ένα ενιαίο πρόγραµµα (script ή shell script). 2.8 Βασικές εντολές φλοιού Πριν αναλύσουµε τους προσοµοιωτές τερµατικού θα αφιερώσουµε µια σύντοµη παράγραφο προκειµένου να παραθέσουµε κάποιες βασικές εντολές οι οποίες χρησιµοποιούνται συχνότερα στον φλοιό (shell) του Linux. Όλες οι εντολές που παρουσιάζονται αφορούν την διανοµή Ubuntu: Επικοινωνία: [email protected] 64 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux – Κεφάλαιο 2 Εντολές Linux Εντολή Περιγραφή Χρήσιµες παράµετροι ls <κατάλογος> εµφανίζει τα περιεχόµενα του τρέχοντος καταλόγου -l (εµφάνιση περιεχοµένων σε µορφή λίστας) -a (εµφάνιση όλων των αρχείων) cd <κατάλογος> αλλαγή καταλόγου <χωρίς παράµετρο> (επιστροφή στον κατάλογο home) .. (ένα επίπεδο επάνω) - (επιστροφή στον τελευταίο κατάλογο) mv <από> <σε> µετακίνηση αρχείου cp <από> <σε> αντιγραφή αρχείου -a (διατήρηση των χαρακτηριστικών του αρχείου) -r (αντιγραφή καταλόγου µε περιεχόµενα) rm <αρχείο> διαγραφή αρχείου ή άδειου καταλόγου -r (διαγραφή καταλόγου µε περιεχόµενα) cat <αρχείο> grep <κείµενο> <αρχείο> συνένωση αρχείων και εµφάνιση του αποτελέσµατος στην βασική µονάδα εξόδου εµφάνιση γραµµών κειµένου που ικανοποιούν κάποιο κριτήριο αναζήτησης find αναζήτηση αρχείων du εµφάνιση κατάστασης χρήσης της µονάδας αποθήκευσης pwd εµφάνιση του τρέχοντος καταλόγου εργασίας chmod xxx <αρχείο> αλλαγή δικαιωµάτων πρόσβασης ενός αρχείου chown user:group <αρχείο> αλλαγή ιδιοκτήτη αρχείου make εκτέλεση του makefile που υπάρχει στον τρέχον κατάλογο less <αρχείο> ανάγνωση αρχείου echo <κείµενο> εµφάνιση µιας γραµµής κειµένου man <εντολή> εγχειρίδιο εντολής wc καταµέτρηση λέξεων, γραµµών, χαρακτήρων και bytes sudo εκτέλεση αρχείου ως διαχειριστής (super user ή root) -n (εµφάνιση αριθµού γραµµής που βρέθηκε το κείµενο) -e ‘<κείµενο>’ (εµφάνιση αποτελεσµάτων που περιέχουν το κείµενο κλειδί ακριβώς όπως το ζητάµε) --max-depth X (εµφάνιση υποκαταλόγων σε βάθος που ορίζει το X) - x (καταµέτρηση µόνο των πραγµατικών αρχείων) - h (εµφάνιση αποτελεσµάτων σε αναγνώσιµη µορφή) - ne “\x00\x00\x00\x00” (δεκαεξαδική µορφή) -l (καταµέτρηση γραµµών) -w (καταµέτρηση λέξεων) -c (καταµέτρηση bytes) -m (καταµέτρηση χαρακτήρων) Πίνακας 2. Εντολές Linux Ιστότοπος εργασίας: MyThesis.org 65 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Ορισµένες από τις παραπάνω εντολές θα εµφανίσουν το αποτέλεσµά στη βασική µονάδα εξόδου (standard output) του συστήµατός µας. Μπορούµε αντί γι’ αυτό να αποθηκεύσουµε αυτά τα αποτελέσµατα σε ένα αρχείο προσθέτοντας το σύµβολο > µετά την εντολή, καθώς και το όνοµα του αρχείου στο οποίο θέλουµε να γίνει η αποθήκευση: cat > test.txt Μετά την εκτέλεση της παραπάνω εντολής θα είµαστε σε θέση να εισάγουµε το κείµενό µας. Όταν ολοκληρώσουµε την εισαγωγή µπορούµε µε τον συνδυασµό πλήκτρων Ctrl+D να βγούµε από τον κειµενογράφο αποθηκεύοντας ταυτόχρονα τα όσα πληκτρολογήθηκαν, στο αρχείο test.txt. Κάποιες φορές όµως είναι χρήσιµο να στείλουµε το αποτέλεσµα µιας εντολής σαν είσοδο σε µια άλλη (pipelining). Αυτό επιτυγχάνεται µε τον χαρακτήρα διασωλήνωσης | (pipe): εντολή1 | εντολή2 | εντολή3 |... Θα µπορούσαµε να πάρουµε ως παράδειγµα τις εντολές grep και wc. Αν υποθέσουµε ότι θέλουµε να καταµετρήσουµε τις γραµµές ενός αρχείου οπού περιέχεται µια συγκεκριµένη λέξη, η αντίστοιχη εντολή θα έµοιαζε µε την παρακάτω: grep <λέξη> <όνοµα αρχείου> | wc <παράµετροι> Φυσικά δεν είναι δυνατό να θυµόµαστε συνέχεια όλες τις εντολές του Linux και τις παραµέτρους τους µε την κάθε λεπτοµέρειά τους. Για το λόγο αυτό, σε κάθε εντολή έχει ενσωµατωθεί ένα µικρό κείµενο βοήθειας (manual) το οποίο εµφανίζεται µε την εντολή man. Για παράδειγµα, αν θέλουµε να µάθουµε τη λειτουργία της εντολής tail αρκεί να εκτελέσουµε στο terminal την παρακάτω εντολή: man tail Αφού εισέλθουµε στο εγχειρίδιο της εντολής και διαβάσουµε ότι µας ενδιαφέρει, µπορούµε στη συνέχεια να εξέλθουµε πιέζοντας το πλήκτρο q. 2.9 Προσοµοιωτές τερµατικών Ο πιο απλός και κοινός τρόπος για να επικοινωνήσουµε µε ένα ενσωµατωµένο σύστηµα είναι να χρησιµοποιήσουµε έναν προσοµοιωτή τερµατικού (terminal emulator) στο σύστηµα host και να συνδεθούµε µέσω κάποιας RS232 σειριακής θύρας. Για να καταλάβουµε τι είναι ένας προσοµοιωτής τερµατικού θα πρέπει να θυµηθούµε τα παλιά τερµατικά (dummy ή video terminals ή thin clients) ή κονσόλες (consoles), που χρησιµοποιούνταν για την πρόσβαση σε κάποιο ακριβό για την εποχή, κεντρικό υπολογιστή. Τα τερµατικά αυτά αποτελούνταν µόνο από οθόνη και πληκτρολόγιο και δεν διέθεταν σκληρό δίσκο και επεξεργαστή. Χρησιµοποιούσαν εξ’ ολοκλήρου τους πόρους του κεντρικού υπολογιστή στον οποίο συνήθως υπήρχε εγκατεστηµένο το λειτουργικό σύστηµα UNIX. Αυτό γινόταν κυρίως για λόγους οικονοµίας και αρχικά είχε εφαρµοστεί µόνο σε πανεπιστήµια, τράπεζες και κρατικούς φορείς. Αργότερα αναπτύχθηκαν και τερµατικά που διέθεταν κάποια επεξεργαστική ισχύ (smart terminals ή fat clients). Ο προσοµοιωτής τερµατικού είναι ένα πρόγραµµα που εξοµοιώνει τη λειτουργία της κονσόλας. Η βασική του λειτουργία είναι να επιτρέπει την πρόσβαση των χρηστών στο κέλυφος ή αλλιώς, στην γραµµή εντολών (command line) του λειτουργικού συστήµατος στο οποίο βρί- Επικοινωνία: [email protected] 66 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux – Κεφάλαιο 2 σκεται εγκατεστηµένος. Ο standard προσοµοιωτής τερµατικού στο GNU/Linux είναι ο xterm που εγκαθίσταται συνήθως µαζί µε τον Xserver. Το κέλυφος (shell) είναι ένα πρόγραµµα το οποίο παίρνει τις εντολές που πληκτρολογούµε στη γραµµή εντολών και τις δίνει στο λειτουργικό σύστηµα για να εκτελεστούν. Παλαιότερα αυτός ήταν και ο µόνος τρόπος επικοινωνίας µε έναν υπολογιστή που έτρεχε Linux ή Unix. Στα περισσότερα συστήµατα Linux το shell που χρησιµοποιείται είναι bash το οποίο αποτελεί µετεξέλιξη του sh. Παρ’ όλο που υπάρχουν αρκετοί προσοµοιωτές τερµατικού για Linux, δεν είναι όλοι αποτελεσµατικοί. Για παράδειγµα κάποιοι έχουν πρόβληµα επικοινωνίας µε τον bootloader κατά τη διάρκεια µεταφοράς αρχείων, κάποιοι άλλοι µε την κωδικοποίηση των χαρακτήρων που πληκτρολογούνται ή/και εµφανίζονται, και κάποιοι µε τους USB to Serial µετατροπείς που χρησιµοποιούνται όταν το σύστηµα host δε διαθέτει κάποια σειριακή θύρα. Σε αυτή την περίπτωση αρκετές φορές ευθύνονται και οι drivers που υπάρχουν διαθέσιµοι στο σύστηµα host για τον µετατροπέα. Οι πιο κοινώς χρησιµοποιούµενοι προσοµοιωτές τερµατικού για Linux είναι οι παρακάτω: Minicom C-Kermit PuTTY HyperTerminal Στα Windows τα πράγµατα είναι πιο εύκολα αφού οι drivers που υπάρχουν για τους µετατροπείς είναι περισσότερο δοκιµασµένοι και οι εφαρµογές είναι επίσης πιο σταθερές απ’ ότι στο Linux. Για παράδειγµα η εφαρµογή Putty σε συνδυασµό µε κάποιον USB σε Serial µετατροπέα της Prolific είναι ένας αξιόπιστος τρόπος επικοινωνίας µε σταθερή λειτουργία. 2.9.1 Ο προσοµοιωτής τερµατικού C-Kermit Η εφαρµογή C-Kermit έχει αναπτυχθεί από το πανεπιστήµιο Columbia και είναι µέρος του έργου µε την ονοµασία Kermit. Παρέχει έναν ενοποιηµένο περιβάλλον για δικτυακές λειτουργίες και υποστηρίζει µεγάλο αριθµό από διαφορετικές πλατφόρµες. Μπορούµε να την εγκαταστήσουµε στο σύστηµά µας µε τη βοήθεια της εντολής: $ sudo apt-get install ckermit Με την εντολή kermit µπορούµε να αρχίσουµε να χρησιµοποιούµε την εφαρµογή. Σε αντίθεση µε τον προσοµοιωτή Minicom, ο Kermit δεν παρέχει κάποιο οπτικοποιηµένο menu εντολών αλλά µπορούµε να µάθουµε τις εντολές του διαβάζοντας το εγχειρίδιο χρήσης του, είτε κατεβάζοντάς το από τη διεύθυνση columbia.edu/kermit/ckututor.pdf, είτε χρησιµοποιώντας την εντολή: $ man kermit Ο C-kermit παρέχει κάποια εργαλεία και κάποιους τρόπους επικοινωνίας µε το target σύστηµα στο οποίο θέλουµε να συνδεθούµε. Τα βασικότερα στοιχεία που υποστηρίζει είναι τα εξής: Προγραµµατισµό µέσω scripts Σειριακή επικοινωνία (άµεση ή µέσω κλήσης) Αυτόµατη κλήση modem Συνδέσεις TCP/IP Ιστότοπος εργασίας: MyThesis.org 67 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ Telnet SSH Rlogin FTP HTTP 1.0 Internet Kermit Service Για να χρησιµοποιήσουµε τον C-Kermit στο σύστηµα host θα πρέπει πρώτα να έχουµε καθορίσει τις παραµέτρους σύνδεσης µε το σύστηµα target. Για να το επιτύχουµε αυτό θα πρέπει να δηµιουργήσουµε ένα αρχείο παραµετροποίησης µε την επέκταση .kermrc το οποίο θα αντιγράψουµε στον κατάλογο που προτείνει η τεκµηρίωση του C-Kermit. Ένα παράδειγµα του κώδικα που θα περιλαµβάνεται στο αρχείο αυτό είναι και το ακόλουθο: ; Ιδιότητες γραµµής set modem type none set line /dev/ttyS0 set speed 115200 set carrier-watch off set handshake none set flow-control none ; ; ; ; ; ; Direct connection Device file Line speed No carrier expected No handshaking No flow control ; Ιδιότητες επικοινωνίας robust set receive packet-length 1000 set send packet-length 1000 set window 10 ; ; ; ; Most robust transfer settings macro Max pack len remote system should use Max pack len local system should use Nbr of packets to send until ack ; Ιδιότητες µεταφοράς αρχείων set file type binary set file names literal ; All files transferred are binary ; Don't modify filenames during xfers Αφού έχουµε δηµιουργήσει το αρχείο ρυθµίσεων µπορούµε να χρησιµοποιήσουµε τον CKermit εκτελώντας την παρακάτω εντολή στο terminal του host συστήµατός µας: $ Kermit -c Αν όλα έχουν πάει καλά, θα δούµε το εξής µήνυµα: Connecting to /dev/ttyS0, speed 115200 Escape character: Ctrl-\ (ASCII 28, FS): enabled Type the escape character followed by C to get back, or followed by ? to see other options. Παρά το ότι δε διατίθεται γραφικό περιβάλλον, µε τον C-Kermit µπορούµε να επικοινωνήσουµε µε το ενσωµατωµένο µας σύστηµα µέσω γραµµής εντολών και να κάνουµε ότι και µε τους υπόλοιπους προσοµοιωτές τερµατικού, αλλά ταυτόχρονα περιλαµβάνονται και κάποια µοναδικά χαρακτηριστικά. Για παράδειγµα, όταν εκκινούµε κάποια µεταφορά αρχείου µε τη βοήθεια του bootloader του target συστήµατός µας, ο bootloader περιµένει µέχρι να λάβει το αρχείο που θα του σταλεί από το σύστηµα host µέσω της κατάλληλης εντολής αποστολής αρχείου που διαθέτει ο C-Kermit. Στο target σύστηµα της εργασίας όπως έχουµε αναφέρει και όπως θα δούµε και αργότερα, ο bootloader που χρησιµοποιήθηκε είναι ο U-Boot. Για να λάβουµε κάποιο αρχείο µέσω του C-Kermit αρκεί να εισέλθουµε στο περιβάλλον του bootloader και να εκτελέσουµε την εντολή loadb. Το αποτέλεσµα θα είναι το target σύστηµά µας και πιο συγκεκριµένα ο bootloader να τεθεί σε αναµονή λήψης αρχείου όπως φαίνεται και στην παρακάτω εικόνα: Επικοινωνία: [email protected] 68 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux – Κεφάλαιο 2 Εικόνα 15. Εκτέλεση της εντολής loadb σε περιβάλλον Uboot Για να σταλεί το αρχείο σειριακά η µε οποιοδήποτε άλλο τρόπο, θα πρέπει στο σύστηµα host να υπάρχει εγκατεστηµένος ο προσοµοιωτής C-Kermit. Στη συνέχεια αφού έχουµε αποφασίσει για το αρχείο που θέλουµε να στείλουµε στο target σύστηµά µας, µέσω της γραµµής εντολών του C-Kermit εκτελούµε την κατάλληλη εντολή µεταφοράς αρχείων που είναι η send. 2.9.2 Ο προσοµοιωτής τερµατικού PuTTY Η εφαρµογή PuTTY είναι ένας δωρεάν και ανοικτού κώδικα προσοµοιωτής τερµατικού, ο οποίος παρέχει υποστήριξη, και µπορεί να λειτουργεί ως client, για τα πρωτόκολλα SSH, Telnet, Rlogin, raw TCP καθώς επίσης υποστηρίζει και συνδέσεις µέσω σειριακής επικοινωνίας, λειτουργώντας σαν σειριακή κονσόλα (serial console). ∆ηµιουργήθηκε από το πανεπιστήµιο MIT σε γλώσσα C και αρχικά είχε γραφτεί για να εκτελείται µόνο σε υπολογιστές που είχαν σαν λειτουργικό τους σύστηµα τα Windows. Σήµερα υπάρχουν εκδόσεις για Linux, Unix, Mac, αλλά και για λειτουργικά κινητών όπως Symbian, Android και Windows Mobile. Κάποιες από τις σηµαντικότερες λειτουργίες που περιλαµβάνει η εφαρµογή PuTTY είναι οι ακόλουθες: Αποθήκευση των sessions και των ρυθµίσεων για µελλοντική χρήση Υποστήριξη για συνδέσεις µέσω των τοπικών σειριακών θυρών Υποστήριξη Telnet Κρυπτογραφηµένη επικοινωνία µέσω SSH Γραµµή εντολών για SCP (secure copy) και SFTP (SSH FTP) clients που υποστηρίζεται από τα εκτελέσιµα αρχεία pscp.exe και psftp.exe αντίστοιχα. Έλεγχος µέσω port forwarding (τοπικά, αποµακρυσµένα ή δυναµικά) µε το πρωτόκολλο SSH Υποστήριξη IPv6 Υποστήριξη αλγόριθµων κρυπτογράφησης AES, DES κ.α. Πιστοποίηση δηµόσιου κλειδιού Στα Windows η εφαρµογή PuTTY είναι αυτόνοµη (standalone), δηλαδή δεν χρειάζεται εγκατάσταση. Υπάρχει ένα εκτελέσιµο αρχείο (putty.exe) το οποίο εκτελούµε µε διπλό κλικ του ποντικιού. Το παράθυρο διαλόγου που εµφανίζεται είναι αυτό που φαίνεται στην επόµενη εικόνα: Ιστότοπος εργασίας: MyThesis.org 69 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 16. Παράθυρο διαλόγου παραµετροποίησης του τερµατικού Putty Αν θέλουµε να συνδεθούµε σειριακά στο σύστηµα target τότε θα πρέπει να ρυθµίσουµε κάποιες παραµέτρους οι οποίες απαιτούνται. Για να το κάνουµε αυτό, στο αριστερό τµήµα του παραθύρου (Category), στην κατηγορία Connection, επιλέγουµε Serial. Θα εµφανιστεί το παράθυρο ρυθµίσεων των τοπικών σειριακών θυρών του συστήµατος host: Εικόνα 17. Ρυθµίσεις σειριακής επικοινωνίας του τερµατικού Putty Επικοινωνία: [email protected] 70 Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux – Κεφάλαιο 2 Η λειτουργία της εφαρµογής στο Linux είναι σχεδόν πανοµοιότυπη. Για να την κατεβάσουµε, να την εγκαταστήσουµε και να την εκκινήσουµε, εκτελούµε σε κάποιο terminal τις παρακάτω εντολές: $ sudo apt-get install putty $ putty Στη συνέχεια, µε τη βοήθεια ενός σειριακού καλωδίου συνδέουµε τη σειριακή θύρα του target συστήµατος µε εκείνη του host και κάνουµε κλικ στο κουµπί Open για να συνδεθούµε. Στην εικόνα βλέπουµε το περιβάλλον γραµµής εντολών του BusyBox και επίσης βλέπουµε να έχει εκτελεστεί η εντολή εµφάνισης περιεχοµένων ls. Εικόνα 18. Εκτέλεση της εντολής ls σε περιβάλλον γραµµής εντολών BusyBox 2.10 IDEs για Linux Όπως γνωρίζουµε, τα εργαλεία ανάπτυξης για τα ενσωµατωµένα συστήµατα Linux είναι ουσιαστικά ένα σύνολο εφαρµογών οι οποίες παρέχουν ως διεπαφή τους µια γραµµή εντολών. Για καιρό η ανάπτυξη γινόταν µε έναν απλό κειµενογράφο και τον φλοιό (shell) του λειτουργικού συστήµατος. Παρ’ όλα αυτά, όλο και περισσότερο η µέθοδος αυτή τείνει να καταργηθεί, καθώς τα συστήµατα IDE εξελίσσονται και γίνονται πιο πρακτικά και πιο δηµοφιλή για τους προγραµµατιστές. Τα συστήµατα IDE, ουσιαστικά προσφέρουν ένα γραφικό περιβάλλον διεπαφής για όλα τα εργαλεία (ή τις εφαρµογές) ανάπτυξης. Έτσι αντί να γράφουµε την εφαρµογή µας σε κάποιο κειµενογράφο και έπειτα να την εκτελούµε µέσω γραµµής εντολών, χρησιµοποιούµε ένα παραθυρικό περιβάλλον το οποίο είναι πιο φιλικό και πιο γρήγορο. Στην εργασία αυτή χρησιµοποιήθηκε το AVR32 Studio. 2.10.1 Το IDE AVR32 Studio Το AVR32 Studio είναι ένα ολοκληρωµένο γραφικό περιβάλλον ανάπτυξης που παρέχεται δωρεάν από την εταιρεία ATMEL και είναι βασισµένο στο ανοικτού λογισµικού IDE µε την ονοµασία Eclipse. Το Eclipse παρέχει µια ανοικτή πλατφόρµα ανάπτυξης η οποία αποτελείται από επεκτάσιµα πλαίσια εργασίας (frameworks) και εργαλεία, για τη δηµιουργία, την ανάπτυξη και τη διαχείριση λογισµικού. Επειδή είναι γραµµένο σε Java µπορεί να εκτελείται σε οποιοδήποτε σύστηµα host (Windows, Linux, Mac). Τα IDE που είναι βασισµένα σε Eclipse µπορεί να µας επιτρέπουν ακόµα και την παραµετροποίηση του συστήµατος αρχείων root καθώς και του ίδιου του kernel, µέσω ενός γραφικού περιβάλλοντος το οποίο είναι βασισµένο σε JAVA. Ιστότοπος εργασίας: MyThesis.org 71 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Το AVR32 Studio λοιπόν, είναι ένα IDE για τη δηµιουργία AVR32 εφαρµογών. Παρέχει ένα σύνολο εργαλείων όπως διαχείριση έργων (project management), διαχείριση εκδόσεων λογισµικού (CVS – Code Versioning System), επεξεργαστή κειµένου για κώδικα C/C++ µε συντακτικό µαρκάρισµα κώδικα (code highlighting) και συµπλήρωση κώδικα (code completion). Επίσης παρέχεται ενσωµατωµένος debugger µε δυνατότητες για stepping και breakpoints σε επίπεδο κώδικα (source level) ή σε επίπεδο εντολών µηχανής (instruction level). Μέσω του AVR32 Studio µπορούµε να έχουµε πλήρη εποπτεία του υλικού του µικροελεγκτή. Για παράδειγµα µπορούµε να γνωρίζουµε ανά πάσα στιγµή το περιεχόµενο των καταχωρητών, την κατάσταση της µνήµης και να έχουµε µια εικόνα για τις εισόδους και εξόδους του µικροελεγκτή µας. Το AVR32 Studio υποστηρίζει όλους τους 32bit µικροελεγκτές της ATMEL. ∆ίνει τη δυνατότητα για ανάπτυξη και αποσφαλµάτωση, για αυτόνοµες (δηλαδή χωρίς λειτουργικό σύστηµα) εφαρµογές άλλα και για εφαρµογές Linux (για την οικογένεια µικροελεγκτών AP7000). Για να επιτευχθεί η επικοινωνία του µε το σύστηµα target, θα πρέπει να µεσολαβεί ανάµεσα στο target και το host σύστηµα. κάποιος emulator ή/και κάποιος programmer (πχ: JTAGICE mkII, AVR ONE!, AVR dragon). Μέσω των βοηθητικών εφαρµογών avr32program και avr32gdbproxy το AVR32 Studio, µπορεί να επιτύχει τον προγραµµατισµό και την αποσφαλµάτωση εφαρµογών. Οι βοηθητικές εφαρµογές που περιλαµβάνονται, µας βοηθούν επίσης να τροποποιήσουµε κάποιες σηµαντικές παραµέτρους του συστήµατός µας όπως η τάση λειτουργίας του και ο χρονισµός του. Επικοινωνία: [email protected] 72 ΚΕΦΑΛΑΙΟ 3 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux Εισαγωγή Στο προηγούµενο κεφάλαιο καλύψαµε oορισµένα βασικά θέµατα που αφορούν τα εργαλεία ανάπτυξης λογισµικού για ενσωµατωµένα συστήµατα Linux. Σε αυτό το κεφάλαιο θα αναλύσουµε το υλικό το οποίο υποστηρίζεται από το Linux. Πιο συγκεκριµένα θα ασχοληθούµε µε τα εξής: Αρχιτεκτονικές µικροελεγκτών Οδηγοί συσκευών και Linux Kernel Παράδειγµα ανάπτυξης module ∆ίαυλοι και διεπαφές Είσοδοι και Έξοδοι (I/Os) Modem Επικοινωνία µε τη µνήµη Υλικό δικτύωσης Παρά το ότι θα αναλύσουµε αρκετά στοιχεία του υλικού που υποστηρίζει το Linux, θα παραλείψουµε κάποια άλλα τα οποία δεν κρίνονται αναγκαία για την υλοποίηση του συστήµατός µας καθώς και όλα αυτά που δεν έχουν ευρεία εφαρµογή (τουλάχιστον προς το παρόν) στα ενσωµατωµένα συστήµατα. Επίσης όταν κρίνεται χρήσιµο για την καλύτερη κατανόηση ενός όρου του κειµένου, θα παρατίθεται και το αντίστοιχο θεωρητικό υπόβαθρο. Φυσικά, θα ήταν αδύνατο στα πλαίσια µιας εργασίας όλα τα παραπάνω να αναλυθούν σε τέτοιο επίπεδο ώστε να µπορεί κάποιος µετά από µια απλή ανάγνωση να προχωρήσει και στην υλοποίησή τους. Αποτελούν όµως, µέρος µιας εικόνας που όσο πιο ολοκληρωµένη είναι τόσο πιο εύκολα µπορούµε να καταλάβουµε µε τι ασχολούµαστε και τι θέλουµε να κάνουµε. Σε αυτό το κεφάλαιο υπάρχουν ορισµένα παραδείγµατα ανάπτυξης και µεταγλώττισης κώδικα τα οποία πραγµατοποιούνται µέσω του συστήµατος Buildroot. Για να µπορούν να γίνουν κατανοητά θα πρέπει να συνδυαστούν µε το πρακτικό µέρος της εργασίας αλλά και µε το κεφάλαιο “Εργαλεία Ανάπτυξης Ενσωµατωµένων Συστηµάτων Linux” του θεωρητικού µέρους. ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) 3.1 Αρχιτεκτονικές µικροελεγκτών Το Linux εκτελείται σε ένα µεγάλο και συνεχώς αυξανόµενο αριθµό διαφορετικών αρχιτεκτονικών, αλλά δε χρησιµοποιούνται όλες αυτές οι αρχιτεκτονικές στα ενσωµατωµένα συστήµατα. Μια γρήγορη µατιά στον υποκατάλογο arch του Linux kernel, µας δείχνει τουλάχιστον 24 αρχιτεκτονικές οι οποίες υποστηρίζονται από τον επίσηµο πυρήνα, ενώ παράλληλα ετοιµάζονται και άλλες οι οποίες θα ενσωµατώνονται σε µελλοντικές εκδόσεις. Εικόνα 19. Οι αρχιτεκτονικές που υποστηρίζονται από τον Linux Kernel Στη συνέχεια παραθέτουµε µια λίστα των οκτώ πιο γνωστών αρχιτεκτονικών που υποστηρίζονται από το Linux. Από αυτές θα αναφέρουµε σύντοµα µόνο τις πρώτες τέσσερις, οι οποίες χρησιµοποιούνται περισσότερο στα ενσωµατωµένα συστήµατα: AVR32 (Atmel) ARM (ARM Holdings) PowerPC (Apple, IBM, Motorola) MIPS (MIPS Technologies) Intel x86 (Intel) M32R (RENESAS) Motorola 68000 (Motorola) SuperH (Hitachi) Η αρχιτεκτονική AVR32 της εταιρείας ATMEL, η οποία κατασκευάζει και τους µικροελεγκτές 8bit AVR για ενσωµατωµένα συστήµατα χαµηλών απαιτήσεων, είναι η πιο νέα στην αγορά των µικροελεγκτών 32bit. Η αρχιτεκτονική AVR32 συνδυάζει πολλές άλλες υπό-αρχιτεκτονικές και µπορεί να υποστηρίξει εντολές DSP καθώς και κύκλωµα βελτιστοποίησης Java εντολών (Java Hardware Acceleration). Η αρχιτεκτονική AVR32 παρέχει αρκετούς τύπους λειτουργίας µικροελεγκτών, µε υποστήριξη για 16bit εντολές σταθερού µήκους και 32bit εντολές εκτεταµένου µήκους. Κατά κάποιο τρόπο, η µορφή εντολών 16bit είναι παρόµοια στο σκοπό µε το σετ εντολών της αρχιτεκτονικής ARM Thumb που θα δούµε και αµέσως µετά. Το αρχικό port (µεταφορά) του Linux kernel για AVR32 ανακοινώθηκε το 2006. Τη στιγµή αυτή υποστηρίζεται µόνο η οικογένεια µικροελεγκτών AT32AP (AP7000, AP7001, AP7002) Επικοινωνία: [email protected] 74 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 καθώς και αρκετές αναπτυξιακές πλακέτες όπως η ATNGW100 που χρησιµοποιήθηκε στα πλαίσια αυτής της εργασίας. Η οικογένεια µικροελεγκτών ARM (Advanced RISC Machine), αναπτύσσεται από την εταιρεία ARM Holdings Ltd. Σε αντίθεση µε άλλους κατασκευαστές ολοκληρωµένων, όπως είναι η Intel, η Freescale και η Atmel, η εταιρεία ARM Holdings δεν κατασκευάζει τους δικούς της µικροελεγκτές. Αντί αυτού, σχεδιάζει για τους πελάτες της πλήρεις πυρήνες επεξεργαστών οι οποίοι είναι βασισµένοι στην αρχιτεκτονική ARM και τους χρεώνει µόνο για τον σχεδιασµό και την άδεια να ενσωµατώνουν τον πυρήνα αυτόν, επιτρέποντάς τους έτσι να δηµιουργήσουν τους δικούς τους ολοκληρωµένους µικροελεγκτές. Ένα τέτοιο παράδειγµα είναι και οι νέοι µικροελεγκτές της Atmel: SAM 3, 4, 7 και 9. Όλοι οι µικροελεγκτές που βασίζονται στον πυρήνα ARM υποστηρίζουν το ίδιο σετ εντολών (instruction set). Αυτό είναι ένα τεράστιο πλεονέκτηµα αφού ο κώδικας που έχει γραφτεί για έναν µικροελεγκτή µε πυρήνα ARM κάποιας εταιρείας, είναι απόλυτα συµβατός µε έναν µικροελεγκτή ίδιου πυρήνα, που έχει κατασκευαστεί από µια άλλη εταιρεία. Αυτό φυσικά ισχύει κυρίως για πηγαίο κώδικα που είναι γραµµένος σε assembly. Προ στιγµής υποστηρίζονται από τον Linux kernel τουλάχιστον 40 διαφορετικοί µικροελεγκτές µε πυρήνα ARM. Η αρχιτεκτονική PowerPC (PPC) είναι αποτέλεσµα συνεργασίας των εταιρειών Apple, IBM και Motorola. Συµπεριλαµβάνει ιδέες από την εργασία των τριών αυτών εταιρειών και ιδιαίτερα την βελτιστοποίηση απόδοσης µε την ενισχυµένη RISC αρχιτεκτονική της εταιρείας IBM που υπάρχει ακόµα και χρησιµοποιείται πολύ στους 64bit servers της. Η αρχιτεκτονική PPC αρχικά ήταν περισσότερο γνωστή για τη χρήση της στους υπολογιστές MAC της Apple αλλά τώρα χρησιµοποιείται και από άλλους προµηθευτές υπολογιστών, καθώς επίσης και στα ενσωµατωµένα συστήµατα. Σε σχέση µε τις υπόλοιπες αρχιτεκτονικές (i386, ARM, AVR32), η αρχιτεκτονική PPC έχει την καλύτερη υποστήριξη από τον πυρήνα του Linux. Αυτό οφείλεται κυρίως στο πλήθος των συσκευών Linux που βασίζονται σε αυτή. Ένας άλλος παράγοντας είναι και η ενσωµάτωσή της από µεγάλες εταιρείες του ενσωµατωµένου Linux, όπως είναι για παράδειγµα η εταιρεία MontaVista. Η αρχιτεκτονική MIPS έχει ενσωµατωθεί σε πολλές παιχνιδοµηχανές (Nintendo 64bit, Playstation κ.α.) και σε αρκετές άλλες καταναλωτικές ηλεκτρονικές συσκευές. Η εταιρεία έχει παρόµοια φιλοσοφία µε την ARM. ∆ηλαδή σχεδιάζει τον πυρήνα και στη συνέχεια των πουλά σε κατασκευαστές µικροελεγκτών. Η κύρια διαφορά τους είναι ότι η αρχιτεκτονική MIPS δεν υποστηρίζει το ίδιο σετ εντολών σε όλες τις υλοποιήσεις της. Οι πυρήνες MIPS έχουν ενσωµατωθεί από πολλούς µεγάλους κατασκευαστές µικροελεγκτών 32bit (πχ NXP από την Philips) και 64bit (πχ Broadcom, Toshiba κλπ). Επίσης η αρχιτεκτονική MIPS έχει ενσωµατωθεί και σε FPGA (Field Programmable Gate Area) συσκευές. Η υποστήριξη της αρχιτεκτονικής από το Linux είναι πιο περιορισµένη από τις υπόλοιπες που αναφέραµε. Η επιλογή της αρχιτεκτονικής AVR32 της εταιρείας ATMEL στην παρούσα εργασία, έγινε λόγω της πολύ βολικής αναπτυξιακής πλακέτας ATNGW100 και λόγω της ήδη υπάρχουσας καλής εµπειρίας µε την εν λόγω εταιρεία και τους 8bit µικροελεγκτές της. Αυτό µπορεί να µοιάζει µε υπεραπλούστευση της έρευνας για την επιλογή αναπτυξιακής πλακέτας όµως χρειάστηκαν τουλάχιστον δύο µήνες µελέτης για να παρθεί αυτή η απόφαση. Μπορεί θεωρητικά η σχεδίαση ενός ενσωµατωµένου συστήµατος Linux να έχει κάποια στάδια ώστε να καταλήξουµε στο τελικό σύστηµα, όµως στην πράξη επιλέγεται συνήθως κάποια λύση (αναπτυξιακή πλακέτα) στην οποία υπάρχει ήδη ένα έτοιµο κοµµάτι δουλειάς (κώδικας και PCB) που είναι ανοικτό και τεκµηριωµένο από τον προµηθευτή της. Αυτό διευκολύνει πολύ, ιδιαίτερα σε περιπτώσεις που προκύπτουν σφάλµατα. Σε αυτές τις περιπτώσεις µπορούµε να είµαστε σίγουροι ότι δεν ευθύνεται κάποιος παράγοντας που προκύπτει από Ιστότοπος εργασίας: MyThesis.org 75 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) λανθασµένη υλοποίηση του υλικού, και έτσι µπορούµε να γλιτώσουµε χρόνο αναζητώντας το σφάλµα µόνο σε ότι προσθέσαµε εµείς. Όταν πια έχουµε προτυποποιήσει το σύστηµά µας και η εφαρµογή µας λειτουργεί σωστά, µπορούµε να το αντιγράψουµε εφαρµόζοντας τις απαραίτητες τροποποιήσεις στο υλικό ή/και στο λογισµικό και να το αναπαράγουµε ως ένα νέο ανεξάρτητο προϊόν. 3.2 Οδηγοί συσκευών και Linux Kernel Σε ένα σύστηµα Linux µπορούµε να αλληλεπιδράσουµε µε το υλικό µέσα από το περιβάλλον του χρήστη (user space) και µέσα από το περιβάλλον του πυρήνα (Kernel space). Για να επιτύχουµε κάτι τέτοιο, στην πρώτη περίπτωση γράφουµε µια εφαρµογή, ενώ στη δεύτερη γράφουµε έναν driver. Ο χειρισµός του υλικού µέσω του Kernel space είναι γενικά ασφαλέστερος, ταχύτερος και πιο αποτελεσµατικός. Η επιλογή εξαρτάται κάθε φορά από την περίπτωση που καλούµαστε να αντιµετωπίσουµε. Παρ’ όλα αυτά ορισµένες φορές η δεύτερη περίπτωση είναι και η µοναδική επιλογή (το αντίθετο δεν ισχύει). Πριν ξεκινήσουµε την ανάλυση των οδηγών συσκευών του Linux πρέπει πρώτα να αναφέρουµε για ποιες συσκευές γράφονται αυτοί οι οδηγοί. Οι συσκευές για τις οποίες γράφονται οι οδηγοί στο Linux χωρίζονται σε τρεις κλάσεις: Συσκευές χαρακτήρων (char devices) Συσκευές τµηµάτων (block devices) ∆ιεπαφές δικτύωσης (network interfaces) Στο Linux όλες οι συσκευές ενώνονται µε κάποιο δίαυλο (bus). Ο δίαυλος αυτός µπορεί να είναι φυσικός (υλικό) ή ιδεατός (λογισµικό). Ο πρωτεύον σκοπός της ύπαρξης των διαύλων είναι η συνένωση παρόµοιων συσκευών, ο συντονισµός της αρχικοποίησης τους (initialization), ο τερµατισµός της λειτουργίας τους και η διαχείριση της τροφοδοσίας τους. Όταν µια συσκευή βρεθεί να ταιριάζει µε κάποιο driver, συνενώνονται µεταξύ τους. Ο τρόπος που θα πραγµατοποιηθεί αυτή η συνένωση εξαρτάται από τον δίαυλο που χρησιµοποιεί η συσκευή. Τις περισσότερες φορές το λογισµικό που είναι υπεύθυνο για τη διαχείριση του διαύλου διατηρεί και κάποιο πίνακα µε τις ονοµασίες όλων των συνδεδεµένων σε αυτόν συσκευών καθώς και των διαθέσιµων drivers. Συγκρίνοντας τις ονοµασίες των συσκευών µε εκείνες των drivers συµπεραίνονται και οι συνενώσεις που µπορούν να γίνουν. Για να ολοκληρωθεί η συνένωση µιας συσκευής µε κάποιον driver, καλείται η συνάρτηση probe() του driver η οποία µε τη σειρά της περνά ως όρισµα ένα δείκτη προς τη συσκευή αυτή. Από το σηµείο αυτό και µετά, είναι ευθύνη του driver να αρχικοποιήσει και να καταχωρήσει τη συσκευή στα κατάλληλα υποσυστήµατα του Linux Kernel. Αν πρόκειται για συσκευή που µπορεί να συνδέεται και να αποσυνδέεται κατά τη λειτουργία του συστήµατος (hotplug) ή αν πρόκειται για κάποιο module θα πρέπει πρώτα να καλείται η συνάρτηση remove() προκειµένου να γίνεται ασφαλής κατάργηση. Οι οδηγοί συσκευών ή drivers, είναι γενικά το σηµείο στο οποίο ο Linux kernel συναντά το υλικό και επικοινωνεί µε αυτό. Ο ρόλος τους είναι να κρύβουν τις λεπτοµέρειες λειτουργίας της συσκευής για την οποία έχουν γραφτεί και ταυτόχρονα να οδηγούν τις κλήσεις των εφαρµογών µας σε συγκεκριµένες λειτουργίες της συσκευής αυτής. Σε περίπτωση που δεν γράφουµε απλά µια εφαρµογή αλλά αναπτύσσουµε εµείς οι ίδιοι ένα ενσωµατωµένο σύστηµα Linux, οι drivers πρέπει να γραφτούν στο µεγαλύτερο µέρος τους από εµάς ή να τους προµηθευτούµε από κάποια εταιρεία ή οργανισµό. Τα είδη των drivers χωρίζονται ανάλογα µε τον σκοπό και τη λειτουργία τους, σε τρεις κύριες κατηγορίες: Επικοινωνία: [email protected] 76 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 Οδηγοί χαρακτήρων (char drivers) – Γράφονται για συσκευές χαρακτήρων (char devices). Οι συσκευές αυτές είναι προσβάσιµες µέσω ακολουθιών χαρακτήρων (bytes). Παραδείγµατα τέτοιων συσκευών είναι τα αρχεία και οι κονσόλες. Οδηγοί τµηµάτων (block drivers) – Γράφονται για συσκευές µόνιµης αποθήκευσης όπως είναι οι µνήµες Flash και οι οπτικοί δίσκοι. Οι συσκευές αυτές είναι προσβάσιµες µέσω του καταλόγου /dev του κεντρικού συστήµατος αρχείων (root filesystem). Οδηγοί δικτύου (network drivers) – Οι οδηγοί δικτύου αφού δηλώσουν (register) τον εαυτό τους στον kernel µέσω κατάλληλων δοµών δεδοµένων (structs) µπορούν στη συνέχεια να εµπλακούν στη λήψη και την αποστολή πακέτων, από και προς το δίκτυο αντίστοιχα. Οι drivers είναι επίσης και ένα καλό σηµείο εκκίνησης για την κατανόηση του kernel. Αν κάποιος είναι σε θέση να δηµιουργήσει έναν driver ή αν αντιλαµβάνεται την λειτουργία του, τότε θα είναι σε θέση να κατανοήσει σε µεγάλο βαθµό και τη λειτουργία του kernel. Ένα άλλο σηµείο εκκίνησης είναι η ανάπτυξη εφαρµογών µε την χρήση του API που παρέχει ο Kernel. Μέσω της εντολής make linux26-menuconfig του γραφικού περιβάλλοντος παραµετροποίησής του Kernel, στην ενότητα Device Drivers, µπορούµε να επιλέξουµε ποιοι drivers (και πως) θα υποστηρίζονται : Ιστότοπος εργασίας: MyThesis.org 77 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 20. Περιβάλλον παραµετροποίησης Linux Kernel – Υποµενού: Device Drivers Φυσικά για να εµφανίζεται στο παραπάνω menu ένας driver θα πρέπει είτε να υπάρχει ήδη υλοποιηµένος στον Kernel, είτε να τον έχουµε γράψει εµείς και να τον έχουµε εντάξει επιτυχώς στο περιβάλλον παραµετροποίησης. Οι οδηγοί που γράφουµε εµείς ανήκουν στον εξειδικευµένο κώδικα (board – specific code) του ενσωµατωµένου συστήµατος Linux που αναπτύσσουµε. Επικοινωνία: [email protected] 78 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 3.2.1 Linux kernel Ο πυρήνας του Linux επωµίζεται το έργο της διαχείρισης των διαθέσιµων πόρων µε τον αποτελεσµατικότερο δυνατό τρόπο. Ο Linux kernel αποτελείται από εκατοντάδες χιλιάδες γραµµές κώδικα και δεν είναι δυνατόν να αναλυθεί στα πλαίσια αυτής της εργασίας. Εξάλλου για το σκοπό αυτό υπάρχει πλούσια και αξιόλογη βιβλιογραφία (στα αγγλικά). Υπάρχει όµως η δυνατότητα να επισηµάνουµε το πώς µπορεί να κατηγοριοποιηθεί ανάλογα µε την λειτουργικότητά του. Μπορούµε λοιπόν να πούµε ότι ολόκληρος ο κώδικας του kernel αποτελείται από τα εξής τµήµατα: ∆ιαχείριση επεξεργασίας (Process management) ∆ιαχείριση µνήµης (Memory management) Συστήµατα αρχείων (Filesystems) Έλεγχος συσκευών (Device control) ∆ικτύωση (Networking) Ουσιαστικά αυτή η κατηγοριοποίηση είναι πολύ λογική αν σκεφτούµε ότι σε ένα σύστηµα συνήθως θα θέλουµε να επεξεργαστούµε δεδοµένα (επεξεργασία), να αποθηκεύσουµε κάπου (µνήµη) τα αποτελέσµατα σε αυστηρά ορισµένη µορφή (σύστηµα αρχείων), να τα παρουσιάσουµε σε κάποια οθόνη ή κάποια άλλη περιφερειακή συσκευή εξόδου (µέσω char ή block driver) και κάποια στιγµή να τα αποστείλουµε και σε άλλα συστήµατα του δικτύου (δικτύωση). Τα τµήµατα αυτά µπορούµε να τα δούµε σχηµατικά πως λειτουργούν µέσα στον Kernel, στην παρακάτω εικόνα: Εικόνα 21. Ανατοµία του Linux Kernel Ιστότοπος εργασίας: MyThesis.org 79 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Στην εικόνα µπορούµε επίσης να δούµε και κάτι άλλο πολύ σηµαντικό. Για τη διαχείριση της CPU και της κύριας µνήµης (πχ RAM), απαιτείται εξειδικευµένο λογισµικό (board specific code) και όχι κάποιος char ή block driver. Το λογισµικό αυτό συνήθως παρέχεται από τον προµηθευτή της µνήµης και της CPU στο πακέτο BSP. Το τµήµα του κώδικα που ασχολείται µε τον έλεγχο των συσκευών είναι αυτό που µας ενδιαφέρει περισσότερο όταν θέλουµε να αναπτύξουµε drivers για τις συσκευές µας. Επίσης το τµήµα της δικτύωσης είναι αυτό που ενσωµατώνει την στοίβα TCP/IP καθώς και τα πρωτόκολλα δροµολόγησης. Ουσιαστικά αυτά είναι και τα σηµαντικότερα τµήµατα για την ανάπτυξη ενός Router όπως είναι ο NGW100. 3.2.2 Modules Ένα από τα ισχυρότερα χαρακτηριστικά του Linux είναι η δυνατότητα που παρέχει για επέκταση ή και µείωση των λειτουργιών και των δυνατοτήτων του Kernel κατά το χρόνο εκτέλεσης. Έτσι η κάθε νέα λειτουργία που θέλουµε να προσθέσουµε δεν χρειάζεται να µεταγλωττιστεί στατικά µέσα στον kernel. Κάθε κοµµάτι κώδικα που µπορεί να προστεθεί στον Kernel κατά τον χρόνο εκτέλεσης ονοµάζεται module. Οι τύποι των modules που υποστηρίζονται είναι αρκετοί και ένας από αυτούς είναι και οι drivers των συσκευών. Κάθε module είναι ένα εκτελέσιµο αρχείο το οποίο δεν µπορεί να εκτελείται αυτόνοµα αλλά φορτώνεται δυναµικά στον kernel µέσω του προγράµµατος insmod (install module) και αφαιρείται δυναµικά µέσω του προγράµµατος rmmod (remove module). Η φόρτωση ή κατάργηση ενός module είναι εφικτή µόνο από τον root (administrator) του συστήµατος. Για να δούµε ποια modules υπάρχουν φορτωµένα στο σύστηµά µας χρησιµοποιούµε την εντολή lsmod (list modules). Για να υποστηρίζεται ο µηχανισµός των modules από τον Kernel θα πρέπει πριν τη µεταγλώττισή του να έχει ενεργοποιηθεί (µέσω της εντολής make linux26-menuconfig) η αντίστοιχη επιλογή (Enable Loadable Module Support) από το γραφικό περιβάλλον παραµετροποίησής του: Εικόνα 22. Ενεργοποίηση υποστήριξης modules από τον Linux Kernel Επικοινωνία: [email protected] 80 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 Εφόσον ένα module µπορεί να είναι τύπου driver module µπορεί να ανήκει και αυτό σε τρεις κλάσεις. Είναι οι εξής: char module (για τη φόρτωση char driver) block module (για τη φόρτωση block driver) network module (για τη φόρτωση network driver) Παρ’ όλα αυτά ο kernel µας δίνει τη δυνατότητα να δηµιουργούµε modules µε συνδυασµούς κλάσεων. 3.3 Παράδειγµα ανάπτυξης module Επειδή στο σύστηµά µας γίνεται χρήση modules για την φόρτωση ορισµένων drivers θα παραθέσουµε εδώ συνοπτικά τα στάδια ανάπτυξης ενός module. Θα δείξουµε επίσης πως φορτώνεται κατά την λειτουργία του Kernel του συστήµατός µας και πως καταργείται. Αρχικά πρέπει να δηµιουργήσουµε ένα αρχείο C. Ας υποθέσουµε ότι το όνοµα του αρχείου αυτού είναι rousis_module.c. Και ο κώδικάς του, είναι για το παράδειγµά µας ο παρακάτω: #include <linux/module.h> #include <linux/kernel.h> MODULE_DESCRIPTION("Test module"); MODULE_AUTHOR("Rousis Dimitrios – [email protected]"); MODULE_ALIAS("test"); int rousis_init_module(void) { printk(KERN_INFO "Hello world! \n"); return 0; } module_init(rousis_init_module); void rousis_cleanup_module(void) { printk(KERN_INFO "Goodbye world! \n"); } module_exit(rousis_cleanup_module); Η return 0; θα επιστρέψει µηδέν εάν εκτελεστεί επιτυχώς, διαφορετικά θα επιστραφεί κάποια αρνητική τιµή που όπως και σε άλλες περιπτώσεις στο Linux, έχει γίνει σύµβαση να θεωρείται σφάλµα. Τους κώδικες σφαλµάτων µπορούµε να τους εντοπίσουµε στα αρχεία κεφαλίδων errno-base.h και errno.h που βρίσκονται στον κατάλογο include/asmgeneric του πηγαίου κώδικα του Linux. Αφού ολοκληρώσαµε τον κώδικα του module, στη συνέχεια θα πρέπει να δώσουµε οδηγίες στον cross compiler για το πώς να µεταγλωττίσει τον κώδικα. Αυτό επιτυγχάνεται µε τη δηµιουργία ενός αρχείου µε την ονοµασία Makefile: obj-m := rousis_module.o KERNELDIR := /rousis/buildroot/project_build_avr32/atngw100/linux-2.6.27.6/ all: Ιστότοπος εργασίας: MyThesis.org 81 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) make ARCH=avr32 CROSS_COMPILE=avr32-linux- -C $(KERNELDIR) M=$(shell pwd) modules clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Οι γραµµές µετά τις ετικέτες all και clean, θα πρέπει να περιέχουν ένα tab, διαφορετικά κατά τη µεταγλώττιση θα προκύπτει σφάλµα (missing separator). Στη συνέχεια ενηµερώνουµε τις µεταβλητές περιβάλλοντος (environment variables) για το που µέσα στο σύστηµα buildroot βρίσκεται ο GCC compiler: export PATH=$PATH:/rousis/buildroot/build_avr32/staging_dir/bin Και τα δύο αρχεία που δηµιουργήσαµε είναι καλό να βρίσκονται µέσα στον ίδιο κατάλογο. Αφού είναι έτοιµα µε την εντολή cd εισερχόµαστε σε αυτόν και εκτελούµε την εντολή: make Αν ολοκληρωθεί επιτυχώς η µεταγλώττισή θα προκύψουν αρκετά νέα αρχεία. Ένα από αυτά θα είναι και το αρχείο rousis_module.ko το οποίο θα φορτώσουµε στη συνέχεια στο target σύστηµά µας µέσω της εντολής insmod. Στην επόµενη εικόνα βλέπουµε και τα υπόλοιπα αρχεία που προέκυψαν µετά την εκτέλεση της εντολής make: Εικόνα 23. Παράδειγµα ανάπτυξης module Αφού µεταφέρουµε το αρχείο στον Router NGW100 µε τον ftp client Filezilla, και ενώ βρίσκεται σε λειτουργία, εκτελούµε την παρακάτω εντολή έτσι ώστε ο kernel να φορτώσει και στη συνέχεια να εκτελέσει το module που δηµιουργήσαµε: insmod rousis_module.ko Κατά την δοκιµή ενός module καλό είναι να θέτουµε το επίπεδο καταγραφής της κονσόλας (console log level) του συστήµατός µας host σε 8 έτσι ώστε να εµφανίζονται µηνύµατα για debugging. Αυτό µπορεί να γίνει µε την εντολή: echo 8 4 1 7 > /proc/sys/kernel/printk Ως εναλλακτική λύση υπάρχει και η εντολή dmesg η οποία εµφανίζει τα µηνύµατα κατάστασης που υπάρχουν αποθηκευµένα σε µια ειδική µνήµη (message buffer) του kernel. Κλείνοντας πρέπει να πούµε ότι σε περίπτωση που το αρχείο rousis_module.c έπαιζε το ρόλο ενός driver ο οποίος ενσωµατώνεται στον Kernel και δεν φορτώνεται µε την µορφή module, δε θα χρειαζόταν καµία τροποποίηση. Η µακροεντολή module_init() το µόνο που θα είχε να κάνει, θα ήταν να ελέγξει εάν κατά την εκκίνηση του λειτουργικού συστήµατος εκτελέστηκε η συνάρτηση rousis_init_module(), ενώ η µακροεντολή module_exit() δε θα εκτελούνταν καθόλου. Επικοινωνία: [email protected] 82 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 3.4 ∆ίαυλοι και διεπαφές Οι δίαυλοι (Buses) και οι διεπαφές (Interfaces) είναι οι συνδετικοί κρίκοι µεταξύ του µικροελεγκτή και των περιφερειακών του συστήµατός µας. Κάθε δίαυλος και κάθε διεπαφή έχει κάποιες ιδιοµορφίες, και το επίπεδο υποστήριξης που παρέχει ο Linux kernel ποικίλει ανάλογα µε αυτές. Όταν σχεδιάζουµε ένα νέο ενσωµατωµένο σύστηµα Linux από την αρχή, µπορεί για ορισµένες συσκευές να µην έχουµε καµία αριθµήσιµη διάρθρωση διαύλων ανώτερου επιπέδου. Οι συσκευές αυτές µπορεί να βρίσκονται εντός της χαρτογραφηµένης µνήµης της CPU (memory – mapped devices). Το Linux παρέχει υποστήριξη ειδικά γι’ αυτού του είδους τις συσκευές, µέσω της χρήσης των συσκευών πλατφόρµας (platform devices). Αυτό επιτρέπει στον kernel να µπορεί να υποστηρίξει συσκευές που δεν µπορεί να απαριθµήσει (enumerate) όταν αναζητά την τοπολογία διαύλων κατά την εκκίνησή του. Αυτές οι συσκευές εντοπίζονται είτε µε τη χρήση εξειδικευµένου κώδικα που γράφεται ειδικά για µια συγκεκριµένη πλατφόρµα, είτε αναφέρονται στην διεπαφή µεταξύ του kernel και του bootloader. Για περισσότερη κατανόηση επί του θέµατος, απαιτείται η µελέτη σχετικά µε τη συγγραφή προγραµµάτων οδήγησης συσκευών (drivers) για το Linux. Ας ρίξουµε όµως µια γρήγορη µατιά σε κάποιες από τις διεπαφές και τους δίαυλους που θα εξετάσουµε στη συνέχεια αυτής της παραγράφου: USB (Universal Serial Bus) I2C (Inter – Integrated Circuit) η TWI (Two Wire Interface) SPI (Serial Protocol Interface) 3.4.1 USB Η υποστήριξη που παρέχει ο Linux kernel για συνδεσιµότητα USB είναι πάρα πολύ καλή και όλο και περισσότερα IDs κατασκευαστών προστίθενται συνεχώς (µια λίστα υπάρχει κι εδώ: linux-usb.org). Το κύριο συστατικό που παρέχει ο kernel για υποστήριξη USB είναι η στοίβα USB. Επίσης υποστηρίζει δύο είδη drivers: USB Host drivers – Εγκαθίστανται σε υπολογιστές για την υποστήριξη και τον έλεγχο συσκευών που συνδέονται σε αυτούς. Τέτοια παραδείγµατα είναι οι Web κάµερες, το ποντίκι, το πληκτρολόγιο κλπ. USB Device (ή gadget) drivers – Σε αυτή την περίπτωση ο driver βρίσκεται φορτωµένος στη µνήµη µιας συσκευής και φροντίζει µόνο για την επικοινωνία αυτής της συσκευής µε συστήµατα που διαθέτουν host drivers. Για παράδειγµα ένα router που διαθέτει USB χρειάζεται drivers µόνο για να επικοινωνεί µε κάποιον υπολογιστή και όχι για να συνδέονται άλλες συσκευές επάνω του. Η ενεργοποίηση για υποστήριξη USB από τον Kernel µπορεί να ενεργοποιηθεί µέσω του γραφικού περιβάλλοντός του επιλέγοντας: Device Drivers USB Support. Από το παράθυρο διαλόγου που εµφανίζεται επιλέγουµε USB Gadget Support αν θέλουµε το σύστηµά µας να λειτουργεί σαν συσκευή USB: Ιστότοπος εργασίας: MyThesis.org 83 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 24. Ενεργοποίηση υποστήριξης USB από τον Linux Kernel Οι USB drivers υπάρχουν ανάµεσα σε ορισµένα υποσυστήµατα του Kernel (πχ: block, net, char κλπ) και στους ελεγκτές του υλικού USB (Host Controllers). Το βασικό (core) λογισµικό USB που υπάρχει ενσωµατωµένο, παρέχει µια προγραµµατιστική διεπαφή για τους USB drivers ή οποία είναι ανεξάρτητη υλικού. Στην εικόνα µπορούµε να δούµε µια γραφική αναπαράσταση των όσων είπαµε: Εικόνα 25. Γραφική αναπαράσταση της επικοινωνίας USB στον Linux Kernel Επικοινωνία: [email protected] 84 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 Αν θέλουµε να γράψουµε κάποιους drivers για το ενσωµατωµένο µας σύστηµα θα πρέπει να έχουµε µελετήσει τον τρόπο ανάπτυξής τους για το Linux. Μια καλή αρχή είναι να επισκεφτούµε το επίσηµο site της οµάδας USB-IF που είναι το www.usb.org. Σε ένα σύστηµα Linux µπορούν να είναι συνδεδεµένες ταυτόχρονα µέχρι και 127 συσκευές. Ο ριζικός διανοµέας είναι υπεύθυνος για όλες τις συσκευές που συνδέονται σε αυτόν, είτε άµεσα, είτε µέσω κάποιου δευτερεύοντος USB hub. 3.4.2 I2C Linux Framework Σε αυτή την παράγραφο θα εξετάσουµε την υποστήριξη που παρέχει το Linux για το δίαυλο I2C (nxp.com). Επίσης θα δούµε πως οι εντολές του διαύλου SMBus (System Management Bus) µπορούν να δροµολογηθούν µέσω του Linux API σε συσκευές συµβατές µε το I2C. Στον µικροελεγκτή AP7000 ο δίαυλος I2C (ή TWI) µπορεί να ελεγχθεί και µέσω των πολύ ευέλικτων σηµάτων GPIO. Εποµένως θα εξετάσουµε και αυτή την περίπτωση. Ο ενσωµατωµένος Linux driver για το I2C Ο driver που παρέχει το Linux για το I2C αποτελείται από αρκετά µέρη. Ας δούµε κάποιες ορολογίες που χρησιµοποιούνται: ∆ίαυλος υλικού (Hardware bus): Πρόκειται για υλικό το οποίο έχει συγκεκριµένη διεπαφή χειρισµού µεταδόσεων. Στην αρχιτεκτονική AVR32 για παράδειγµα, το υλικό αυτό είναι το TWI ή ένα σύνολο σηµάτων GPIO. Προσαρµογέας (Adapter): Ο προσαρµογέας εξυπηρετεί την επικοινωνία µεταξύ του I2C και του TWI. Αλγόριθµος (Algorithm driver): Συνεργάζεται µε τον προσαρµογέα για την επίτευξη της επικοινωνίας. Η ύπαρξή του είναι απαραίτητη µόνο όταν το πρωτόκολλο επικοινωνίας I2C δεν είναι ενσωµατωµένο στο υλικό. Οδηγός (I2C driver): Πρόκειται για driver που υπάρχει υλοποιηµένος στον Kernel. Σε περίπτωση που η συσκευή ελέγχεται µέσω ειδικής διεπαφής (I2C Device Interface) που υπάρχει για τις εφαρµογές οι οποίες εκτελούνται στο user space, ο οδηγός αυτός δεν είναι απαραίτητος. ∆ιεπαφή συσκευής (I2C Device Interface): Πρόκειται για τη διεπαφή I2C η οποία υπάρχει για την εξυπηρέτηση εφαρµογών στο user space οι οποίες θέλουν να επικοινωνήσουν µε συσκευές I2C. Τα µέρη που αναφέραµε µπορούµε να τα δούµε και σχηµατικά στο πιο κάτω διάγραµµα: Ιστότοπος εργασίας: MyThesis.org 85 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 26. Γραφική αναπαράσταση των µερών του Linux driver I2C Μπορούµε να δούµε περισσότερες πληροφορίες και στην τεκµηρίωση του Kernel που υπάρχει για το I2C στον κατάλογο documentation/i2c. Παραµετροποίηση του Kernel Για να µπορούµε να επικοινωνήσουµε µέσω TWI και I2C στο Linux θα πρέπει πρώτα να έχουµε ενεργοποιήσει ορισµένα τµήµατα του Kernel. Αυτό όπως ήδη γνωρίζουµε γίνεται µέσω του εξειδικευµένου γραφικού περιβάλλοντος παραµετροποίησης του Kernel. Οι ρυθµίσεις που µας αφορούν βρίσκονται στο µενού Device Drivers I2C support: Επικοινωνία: [email protected] 86 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 Εικόνα 27. Ενεργοποίηση υποστήριξης I2C από τον Linux Kernel I2C µέσω GPIO Προκειµένου να δηµιουργήσουµε την µονάδα προσαρµογέα (adapter module) και την µονάδα αλγορίθµου (algorithm module) οι οποίες απαιτούνται για την παραµετροποίηση GPIO, πρέπει να επιλέξουµε Device Drivers -> I2C support -> I2C Hardware bus support Το module µε την ονοµασία GPIO-based bitbanging I2C, είναι αυτό που µας ενδιαφέρει και το ενεργοποιούµε (<Μ>) µε το space του πληκτρολογίου: Εικόνα 28. Ενεργοποίηση υποστήριξης I2C µέσω GPIO από τον Linux Kernel Ιστότοπος εργασίας: MyThesis.org 87 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Στη συνέχεια θα πρέπει να προσθέσουµε τη συσκευή µε την οποία θέλουµε να επικοινωνήσουµε. Για να γίνει αυτό θα πρέπει να αντιστοιχίσουµε κάποιους ακροδέκτες GPIO του µικροελεγκτή µας µε τη διεπαφή TWI την οποία αυτός διαθέτει. Στον Router NGW100, οι ίδιοι ακροδέκτες του µικροελεγκτή που χρησιµοποιούνται για την µονάδα TWI µπορούν να ρυθµιστούν µέσω του Kernel ώστε να λειτουργούν σαν δίαυλος I2C – GPIO. Οι ρυθµίσεις για τον δίαυλο I2C – GPIO περνιούνται στον προσαρµογέα από τα δεδοµένα πλατφόρµας (platform-data). Τα δεδοµένα αυτά καθορίζονται από µια δοµή δεδοµένων η οποία βρίσκεται στο αρχείο κεφαλής include/linux/i2c-gpio.h και περιλαµβάνει τον παρακάτω κώδικα: struct i2c_gpio_platform_data { unsigned int sda_pin; unsigned int scl_pin; int udelay; int timeout; unsigned int sda_is_open_drain:1; unsigned int scl_is_open_drain:1; unsigned int scl_is_output_only:1; }; Στον Router NGW100 η αρχικοποίηση της δοµής που είδαµε γίνεται στο αρχείο setup.c που βρίσκεται στον κατάλογο arch/avr32/boards/atngw100 του πηγαίου κώδικα του Kernel. Στο ίδιο αρχείο βρίσκεται και ένα στιγµιότυπο της δοµής platform_device το οποίο ουσιαστικά αντιπροσωπεύει τη πραγµατική συσκευή µε την οποία θέλουµε να επικοινωνήσουµε µέσω του διαύλου I2C – GPIO: static struct i2c_gpio_platform_data i2c_gpio_data = { .sda_pin = GPIO_PIN_PA(6), .scl_pin = GPIO_PIN_PA(7), .sda_is_open_drain = 1, .scl_is_open_drain = 1, .udelay = 2, /* ~100 kHz */ }; static struct platform_device i2c_gpio_device = { .name = "i2c-gpio", .id = 0, .dev = { .platform_data = &i2c_gpio_data, }, }; Το επόµενο βήµα που θα πρέπει να γίνει είναι να αρχικοποιήσουµε τους απαραίτητους ακροδέκτες του µικροελεγκτή για το I2C. Όλες οι απαραίτητες συναρτήσεις για να γίνει κάτι τέτοιο βρίσκονται στο αρχείο κεφαλής portmux.h το οποίο βρίσκεται µε τη σειρά του στον κατάλογο του πηγαίου κώδικα του Linux Kernel include/asm/arch. Μία από αυτές στην περίπτωση του Router NGW100 είναι η ακόλουθη: at32_select_gpio(unsigned int pin, unsigned long flags) Και είναι η µόνη που χρειαζόµαστε για να ρυθµίσουµε ένα σήµα GPIO. Ο ακέραιος αριθµός pin µπορεί να βρεθεί χρησιµοποιώντας ειδικές µακροεντολές που υπάρχουν στο αρχείο at32ap7000.h. Για παράδειγµα, προκειµένου να επιλέξουµε την γραµµή εισόδου – εξόδου 0, του ελεγκτή (PIO controller) Β, χρησιµοποιούµε την µακροεντολή GPIO_PIN_PB(0). Επικοινωνία: [email protected] 88 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 Τα διαθέσιµα flags για τη συνάρτηση at32_select_gpio, καθώς και η λειτουργία τους, παρατίθενται στον πίνακα που ακολουθεί: Λειτουργία Flag AT32_GPIOF_PULLUP Ενεργοποίηση pull-up αν το σήµα GPIO λειτουργεί σαν είσοδος AT32_GPIOF_OUTPUT Ρύθµιση ενός σήµατος GPIO να λειτουργεί σαν έξοδος AT32_GPIOF_HIGH Θέτει το pin εξόδου σε λογικό HIGH AT32_GPIOF_DEGLITCH Ενεργοποιεί το glitch-filter όταν το GPIO λειτουργεί σαν είσοδος AT32_GPIOF_MULTIDRV ∆ίνει τη δυνατότητα των drivers εξόδου να µπορούν να λειτουργούν σαν open drain (ανοικτός αγωγός) ούτως ώστε να υποστηρίζει πολλαπλούς εξωτερικούς drivers στο ίδιο pin. Πίνακας 3. Flags και λειτουργία της συνάρτησης at32_select_gpio Το παράδειγµα που ακολουθεί ρυθµίζει κατάλληλα τους GPIO ακροδέκτες του µικροελεγκτή AP7000 και καταχωρεί µια συσκευή I2C-GPIO. Έχει χρησιµοποιηθεί στον εξειδικευµένο κώδικα που γράφτηκε για τον Router NGW100 έτσι ώστε να υποστηρίζεται από τον Kernel. at32_select_gpio(i2c_gpio_data.sda_pin, AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH); at32_select_gpio(i2c_gpio_data.scl_pin, AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH); platform_device_register(&i2c_gpio_device); Στη συνέχεια πρέπει να προβούµε σε δύο ενέργειες. Την εγκατάσταση του driver για το TWI και την ενεργοποίηση του περιβάλλοντος διεπαφής συσκευής I2C για την υποστήριξη εφαρµογών στο user space. Μετά την επιλογή του driver προσθέτουµε την παρακάτω γραµµή στο στο αρχείο setup.c: at32_add_device_twi(unsigned int id) Για να εγκαταστήσουµε τον driver για το TWI, από το γραφικό περιβάλλον του Kernel επιλέγουµε: Device Drivers -> I2C support -> I2C Hardware bus support -> Atmel Two-Wire Interface (TWI). Προκειµένου να ενεργοποιήσουµε το I2C device interface, επιλέγουµε: Device Drivers -> I2C support -> I2C device interface. Εγκατάσταση των I2C modules Μετά την εκκίνηση του Router NGW100 θα πρέπει να φορτώνονται κάθε φορά, και όσα modules είναι απαραίτητα. Για την φόρτωση του TWI module θα πρέπει να εκτελεστεί η παρακάτω εντολή, είτε χειροκίνητα από εµάς, είτε από κάποιο εξειδικευµένο script που θα έχουµε επιλέξει να εκτελείται κατά την εκκίνηση: modprobe i2c-atmeltwi Ιστότοπος εργασίας: MyThesis.org 89 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Κάποια άλλα modules τα οποία είναι απαραίτητα, είναι τα i2c-gpio και i2c-algo-bit, τα οποία µπορούν επίσης να φορτωθούν µε την πιο πάνω εντολή. Το µόνο που µένει είναι να ρυθµίσουµε το I2C device interface έτσι ώστε να µπορούµε στη συνέχεια να το χρησιµοποιήσουµε µέσω των εφαρµογών µας για την επικοινωνία µε τις συσκευές I2C. Όπως ήδη γνωρίζουµε ό Kernel παρέχει ένα περιβάλλον διεπαφής γι’ αυτές. Ουσιαστικά πρόκειται για ένα driver όπου οι εφαρµογές (clients) αντιστοιχούνται µε αριθµηµένες συσκευές οι οποίες βρίσκονται επάνω σε ένα δίαυλο (bus). Η διεπαφή επικοινωνίας µε τον δίαυλο αυτό είναι µια συσκευή χαρακτήρων (character device) η οποία επιτρέπει σε µια user space εφαρµογή να επιλέξει κάποια συγκεκριµένη συσκευή του διαύλου, να ορίσει τις ρυθµίσεις της αλλά και να γράψει σε αυτή και να διαβάσει. Για να φορτωθεί η συσκευή θα πρέπει να εκτελεστεί η εντολή: modprobe i2c-dev Σε κάθε δίαυλο I2C αντιστοιχείται ένας κόµβος /dev/i2c-n όπου n είναι ο αριθµός του διαύλου αυτού. Αυτό στον Router NGW100 γίνεται αυτόµατα µέσω της εφαρµογής mdev η οποία είναι εγκατεστηµένη στο σύστηµα. Αν αυτό δεν ίσχυε ο κόµβος θα έπρεπε να δηµιουργηθεί χειροκίνητα µέσω της εντολής mknod. Οι κόµβοι συσκευών (device nodes) είναι συσκευές χαρακτήρων. Έχουν όνοµα, αριθµό (major - minor) και µπορούµε να τους αντιστοιχούµε δικαιώµατα όπως και σε ένα απλό αρχείο. Με την εντολή που ακολουθεί µπορούµε να δούµε σε µορφή λίστας τις συσκευές που υπάρχουν στο σύστηµά µας: ls -l /dev/i2c-0 Περισσότερες πληροφορίες υπάρχουν και στην τεκµηρίωση του Linux Kernel στον κατάλογο Documentation/i2c, στο αρχείο dev-interface. Στον ίδιο κατάλογο υπάρχει και λεπτοµερής ανάλυση της χρήσης της διεπαφής I2C που θα εξετάσουµε και στη συνέχεια. Χρήση της διεπαφής I2C Υπάρχουν δύο τρόποι για να επιτευχθεί επικοινωνία. Η διεπαφή εγγραφής / ανάγνωσης και η διεπαφή ioctl. Ανεξάρτητα όµως από το ποια θα επιλέξουµε θα πρέπει πρώτα να ανοίξουµε τη συσκευή ως εξής: int device_file; char filename[10] = "/dev/i2c-0"; if ((device_file = open(filename,O_RDWR)) < 0) { /* Error */ return -1; } Αυτό θα µας επιτρέψει να δούµε αν όλα πάνε καλά και αν η συσκευή µας έχει φορτωθεί και καταχωρηθεί σωστά. Αν επιλέξουµε την επικοινωνία µέσω της διεπαφής εγγραφής / ανάγνωσης, πρέπει πιο πριν να δώσουµε µια διεύθυνση στην συσκευή (slave) την οποία θέλουµε να χρησιµοποιήσουµε. Αυτό µπορεί να γίνει ως εξής: int slave_address = 0x12; /* I2C address */ if (ioctl(device_file,I2C_SLAVE,slave_address) < 0) { /* Error */ return -1; Επικοινωνία: [email protected] 90 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 } Στη συνέχεια δηµιουργούµε ένα buffer στο οποίο θα αποθηκεύονται δεδοµένα: buffer[0] = i2c_slave_register; buffer[1] = 0x11; buffer[2] = 0x48; Σε αυτό το σηµείο η συσκευή µας έχει µια διεύθυνση και υπάρχει δεσµευµένη µια περιοχή στη µνήµη η οποία αντιπροσωπεύει τη µνήµη αυτής της συσκευής. Εποµένως µπορούµε να κάνουµε µια εγγραφή σε αυτή. ∆ηλαδή να της στείλουµε δεδοµένα µέσω εφαρµογής που εκτελείται στο user space: if ( write(device_file,buffer,3) != 3) { /* Error */ return -1; } Για να κάνουµε ανάγνωση, δηλαδή να λάβουµε δεδοµένα από την συσκευή µας, µπορεί να χρησιµοποιηθεί ο ακόλουθος κώδικας: if (read(device_file,buffer,1) != 1) { /* Error */ } else { /* buffer[0] */ } Η θέση 0 του πίνακα buffer περιέχει το byte που διαβάστηκε. Αν επιλέξουµε να επικοινωνήσουµε µε την I2C slave συσκευή µας, µέσω της διεπαφής ioctl θα πρέπει επίσης να επιλέξουµε αν θα χρησιµοποιήσουµε το πρωτόκολλο I2C ή το πρωτόκολλο SMBus, µιας και τα υποστηρίζει και τα δύο. Και στις δύο περιπτώσεις ο προσαρµογέας (adapter) που είναι όπως είπαµε, ένα από τα τµήµατα του I2C driver που παρέχει το Linux, θα πρέπει να ελεγχτεί για να διαπιστωθεί εάν είναι πλήρως λειτουργικός. Αν επιλέξουµε το πρωτόκολλο I2C θα πρέπει να καταλάβουµε πρώτα τα δύο είδη µηνυµάτων που θα χρειαστούµε. Το πρώτο µήνυµα είναι το βασικό µήνυµα (standard message) και περιγράφεται στο αρχείο κεφαλής i2c-dev.h που υπάρχει στον κατάλογο linux. Ουσιαστικά πρόκειται για την παρακάτω δοµή: struct i2c_rdwr_ioctl_data { struct i2c_msg *msgs; /* pointers to i2c_msgs */ __u32 nmsgs; /* number of i2c_msgs */ }; Το δεύτερο µήνυµα είναι ένα µήνυµα I2C και η δοµή του περιγράφεται στο αρχείο κεφαλής i2c.h που επίσης υπάρχει στον κατάλογο linux : struct i2c_msg { __u16 addr; __u16 flags; __u16 len; __u8 *buf; }; /* slave address*/ /* msg length */ /* pointer to msg data */ Τα πεδία της δοµής και οι σηµασία τους παρατίθενται παρακάτω: Ιστότοπος εργασίας: MyThesis.org 91 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) addr – η διεύθυνση της συσκευής I2C (slave) που ελέγχουµε. flags – παίρνουν τιµές οι οποίες ενηµερώνουν αν πρόκειται για λειτουργία ανάγνωσης ή εγγραφής. Άλλες τιµές που µπορούν να πάρουν είναι αν πρόκειται για 10-bit addressing ή για 7-bit. Όλες αυτές οι σηµαίες αλλά και κάποιες επιπλέον, περιγράφονται και στο αρχείο κεφαλής i2c.h. len – ο αριθµός των bytes που θα διαβαστούν οι θα γραφτούν. buf – δείκτης που δείχνει στο buffer που είδαµε νωρίτερα. Η αρχικοποιηµένη δοµή της δοµής i2c_msg περνά τελικά ως όρισµα για µια κλήση ioctl. Με τον όρο “αρχικοποιηµένη” εννοούµε το στιγµιότυπο της δοµής i2c-msg που θα δηµιουργήσουµε στον κώδικα της εφαρµογής µας. Σε περίπτωση που θέλουµε να στείλουµε ή να λάβουµε περισσότερα του ενός bytes ταυτόχρονα, µπορούµε να χρησιµοποιήσουµε την µακροεντολή I2C_RDWR :Το ακόλουθο παράδειγµα µεταφέρει 3 bytes στη συσκευή slave. Τα 3 bytes µεταφέρουν µια διεύθυνση καταχωρητή και δύο bytes δεδοµένων: struct i2c_rdwr_ioctl_data work_queue; uint8_t msg_data[2] = {0,0}; work_queue.nmsgs = 1; work_queue.msgs = (struct i2c_msg*) malloc(work_queue.nmsgs * sizeof(struct i2c_msg)); work_queue.msgs[0].len = 2; work_queue.msgs[0].flags = 0; work_queue.msgs[0].addr = 0x1; /* slave address */ msg_data[0] = 0x1; /* register address*/ msg_data[1] = 0xA; /* data */ msg_data[2] = 0xB; /* data */ work_queue.msgs[0].buf = msg_data; if( ioctl(message.device_handle, I2C_RDWR, (unsigned long) &work_queue) < 0) { /* Error*/ } Αν αντί για το I2C, επιλέξουµε το πρωτόκολλο SMBus για την επικοινωνία µας µέσω της διεπαφής ioctl, µε την slave συσκευή, χρειαζόµαστε την εξής δοµή: struct i2c_smbus_ioctl_data { __u8 read_write; __u8 command; __u32 size; union i2c_smbus_data *data; }; Η σηµασία των µελών της δοµής i2c_smbus_ioctl_data είναι η εξής: read_write – καθορίζει το αν η πρόσβαση στη συσκευή θα αφορά εγγραφή ή ανάγνωση. Υπάρχουν επίσης και κάποιες µακροεντολές στο αντίστοιχο αρχείο κεφαλής. command – είναι το πρώτο byte που µεταφέρεται και συνήθως περιέχει τη διεύθυνση του καταχωρητή. size – καθορίζει το µήκος µεταφοράς δεδοµένων σε bytes. data – είναι δείκτης που δείχνει στην περιοχή που αποθηκεύονται τα δεδοµένα. Για τη διευκόλυνση πρόσβασης µέσω byte ή word χρησιµοποιείται η συνένωση (union) i2c_smbus_data: Επικοινωνία: [email protected] 92 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 union i2c_smbus_data { __u8 byte; __u16 word; __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length and one more for PEC */ }; Μία µεταφορά του ενός byte θα µπορούσε να είναι και η ακόλουθη: struct i2c_smbus_ioctl_data args; args.read_write = I2C_SMBUS_WRITE; args.command = 0x01; args.size = I2C_SMBUS_BYTE; args.data = NULL; ioctl(device_file,I2C_SMBUS,&args); Ανάπτυξη I2C Linux driver Ο χειρισµός των συσκευών I2C µπορεί όπως γνωρίζουµε να γίνει και από την περιοχή του Kernel µέσω ενός driver. Πριν ξεκινήσουµε να γράφουµε τον δικό µας driver µπορούµε να ψάξουµε στο Internet για να δούµε αν κάποιος άλλος το έχει κάνει ήδη. Μια καλή πηγή την οποία µπορούµε να εκµεταλλευτούµε είναι και ο ιστότοπος lm-sensors.org. Αν τελικά δεν βρεθεί κάποιος έτοιµος driver για τη συσκευή µας, τότε είµαστε αναγκασµένοι να τον δηµιουργήσουµε εµείς οι ίδιοι. Τα συγγράµµατα που υπάρχουν διαθέσιµα στο εµπόριο, η τεκµηρίωση που υπάρχει στον Linux Kernel (Documenttion/i2c), το datasheet του µικροελεγκτή µας αλλά και η τεκµηρίωση (πχ: application notes) που παρέχει ο προµηθευτής του, είναι υπεραρκετά για την επιτυχή έκβαση του εγχειρήµατος αυτού. 3.4.3 SPI Οι περισσότεροι µικροελεγκτές που χρησιµοποιούνται στα ενσωµατωµένα συστήµατα Linux έχουν ενσωµατωµένα κάποια ΙΟ interfaces τύπου SPI. Έτσι µπορούν να επικοινωνούν µε µνήµες SD ή MMC χωρίς να είναι απαραίτητη η διαµεσολάβηση κάποιου εξειδικευµένου controller. Αν και το βασικό (core) υποσύστηµα SPI που υπάρχει ενσωµατωµένο στον Kernel παρέχει κάποιες συναρτήσεις για την προσθήκη συσκευών SPI κατά το χρόνο εκτέλεσης του λειτουργικού, είναι καλύτερο να µην τις χρησιµοποιούµε. Ο λόγος είναι πως οι συναρτήσεις αυτές θεωρούν ότι έχουν ήδη διευθετηθεί όλες οι εξειδικευµένες παραµετροποιήσεις της πλατφόρµας που χρησιµοποιούµε (platform – specific setup) όπως είναι για παράδειγµα η πολυπλεξία των pins του µικροελεγκτή που οµαδοποιούνται σε θύρες (port multiplexing). Εποµένως είναι καλύτερα να αποφεύγουµε την χρήση τους. Έτσι ο απαιτούµενος κώδικας θα πρέπει να βρίσκεται µέσα στον εξειδικευµένο κώδικα που αφορά την πλακέτα µας (setup.c). Εκεί θα πρέπει να περιγράφονται όλες οι συσκευές SPI που υπάρχουν καθώς και ο τρόπος µε τον οποίο αυτές συνδέονται. Όπως θα δούµε αναλυτικότερα και στη συνέχεια, για κάθε συσκευή (master) στην οποία συνδέονται άλλες συσκευές, θα πρέπει να υπάρχει ένας πίνακας ο οποίος θα περιλαµβάνει πληροφορίες για ορισµένα χαρακτηριστικά τους. Το αρχείο κεφαλής <linux/spi/spi.h> είναι µια καλή αρχή ώστε να ξεκινήσουµε την µελέτη µας για το SPI καθώς και για τις πληροφορίες αυτές. Στο γραφικό περιβάλλον παραµετροποίησης του Kernel µπορούµε να επιλέξουµε πριν την SPI Support): µεταγλώττιση ποια υποστήριξη SPI θα υπάρχει (Device Drivers Ιστότοπος εργασίας: MyThesis.org 93 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 29. Ενεργοποίηση υποστήριξης SPI από τον Linux Kernel Τα αιτήµατα SPI πάντοτε τοποθετούνται αρχικά σε ουρές εισόδων / εξόδων (I/O queues) και εκτελούνται µε κάποιο αλγόριθµο FIFO (First In First Out). ∆ηλαδή εκτελείται πάντα το αίτηµα που φτάνει πρώτο (δοµή ουράς). Αυτό γίνεται ασύγχρονα (δηλαδή χωρίς ρολόι) µε τη χρήση αιτηµάτων ολοκλήρωσης (completion callbacks). Για απλές µεταφορές δεδοµένων υπάρχει τρόπος τα αιτήµατα αυτά να συγχρονίζονται. Γενικά τα είδη των drivers για SPI είναι δύο: Controller drivers (οδηγοί ελεγκτών) οι οποίοι µπορούν να ενσωµατώνονται σε µικροελεγκτές SoC και συνήθως υποστηρίζουν και τους δύο τύπους επικοινωνίας (master και slave). Οι drivers αυτοί χρησιµοποιούν τους καταχωρητές του hardware και µπορούν να χρησιµοποιούν κανάλια DMA. Protocol drivers (οδηγοί πρωτοκόλλου). Οι drivers αυτοί περνούν µηνύµατα µέσω κάποιου controller driver έτσι ώστε να επικοινωνήσουν µε κάποια master ή slave συσκευή στο άλλο άκρο της επικοινωνίας SPI. Μια δοµή struct spi_device ενθυλακώνει την διεπαφή του master µεταξύ των δύο αυτών τύπων drivers. Σε αυτή την περίπτωση δεν υπάρχει προγραµµατιστικό περιβάλλον για την πλευρά του slave. Υπάρχει µια ελάχιστη διεπαφή προγραµµατισµού SPI που βασίζεται περισσότερο σε drivers κάνοντας χρήση εξειδικευµένου κώδικα που παρέχεται από τον κατασκευαστή κάθε συγκεκριµένου συστήµατος. Το SPI εµφανίζεται στο sysfs σε διάφορα σηµεία. Κάποια από αυτά είναι και τα ακόλουθα: /sys/devices/.../CTLR Επικοινωνία: [email protected] φυσικό επίπεδο ενός συγκεκριµένου ελεγκτή SPI. 94 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 /sys/devices/.../CTLR/spiB.C spi_device στον δίαυλο B, Chip select C, addressing µέσω CTLR. /sys/bus/spi/devices/spiB.C symlink (symbolic link) προς την φυσική συσκευή. /sys/devices/.../CTLR/spiB.C/modalias αναγνωρίζει τον driver που θα πρέπει να χρησιµοποιηθεί µε την συγκεκριµένη συσκευή (για hotplug / coldplug). /sys/bus/spi/drivers/D driver για µία ή περισσότερες συσκευές SPI. /sys/class/spi_master/spiB symlink ή πραγµατική συσκευή που µπορεί να περιέχει πληροφορίες κατάστασης σχετικά ελεγκτή που χειρίζεται τον δίαυλο B. Όλες οι spiB.* συσκευές µοιράζονται έναν κοινό τοµέα διαύλου SPI µε σήµατα SCLK, MOSI, και MISO. Ο Linux kernel χρειάζεται αρκετές πληροφορίες έτσι ώστε να ρυθµίσει σωστά τις συσκευές SPI. Οι πληροφορίες αυτές του παρέχονται από εξειδικευµένο κώδικα που παρέχονται στο BSP. Το πρώτο είδος πληροφορίας που χρειάζεται, είναι µια λίστα µε τους SPI controllers που υποστηρίζονται από το σύστηµα που αναπτύσσεται. Για πλακέτες που βασίζονται σε SoC (System on Chip) θα υπάρχουν συνήθως συσκευές που υποστηρίζονται από την τρέχουσα πλατφόρµα (platform devices) και θα απαιτούνται συνήθως κάποια δεδοµένα της πλατφόρµας αυτής (platform_data) προκειµένου όλα να λειτουργήσουν σωστά. Η struct platform_device θα περιέχει πληροφορίες όπως η φυσική διεύθυνση του πρώτου καταχωρητή του SPI controller καθώς και η αντίστοιχη IRQ (Interrupt Request). Οι διάφορες πλατφόρµες που υπάρχουν συχνά τοποθετούν τη διαδικασία δήλωσης (register) SPI controller µαζί µε εκείνη της διαµόρφωσης των ακροδεκτών (pin configuration), έτσι ώστε τα αρχεία τύπου arch/.../mach-*/board-*.c των διάφορων πλατφορµών να µπορούν να µοιράζονται τον ίδιο κώδικα εγκατάστασης. Αυτό συµβαίνει επειδή τα συστήµατα SoC διαθέτουν πολλούς SPI controllers και µόνο εκείνοι που είναι πραγµατικά χρήσιµοι για το σύστηµα πρέπει να εγκαθίστανται και να δηλώνονται. Έτσι για παράδειγµα στα αρχεία arch/.../mach-*/board-*.c µπορεί να περιέχεται κώδικας όπως ο παρακάτω: #include <mach/spi.h> // mysoc_spi_data /* αν η υποδοµή mach-* δεν υποστηρίζει kernels που µπορούν να εκτελεστούν σε πολλαπλές πλακέτες, η pdata δεν θα µπορούσε να επωφεληθεί από την __init. */ static struct mysoc_spi_data __initdata pdata = { ... }; static __init board_init(void) { ... // αυτή η πλακέτα χρησιµοποιεί τον SPI controller #2 mysoc_register_spi(2, &pdata); ... } Επίσης ο κώδικας για κάποιο συγκεκριµένο SoC µπορεί να µοιάζει µε τον ακόλουθο: #include <mach/spi.h> static struct platform_device spi2 = { ... }; Ιστότοπος εργασίας: MyThesis.org 95 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) void mysoc_register_spi(unsigned n, struct mysoc_spi_data *pdata) { struct mysoc_spi_data *pdata2; pdata2 = kmalloc(sizeof *pdata2, GFP_KERNEL); *pdata2 = pdata; ... if (n == 2) { spi2->dev.platform_data = pdata2; register_platform_device(&spi2); } ... } Ακόµα και αν χρησιµοποιείται το ίδιο SoC σε δύο διαφορετικές πλακέτες ο κώδικας (platform_data) µπορεί να διαφέρει µεταξύ τους. Αυτό µπορεί να γίνει εύκολα κατανοητό αν σκεφτούµε ότι στο ένα σύστηµα µπορεί το SPI να κάνει χρήση ενός εξωτερικού ρολογιού, ενώ στο άλλο να χρησιµοποιείται ένα κεντρικό ρολόι (master clock). Το δεύτερο είδος πληροφορίας που χρειάζεται ο kernel ώστε να ρυθµίσει σωστά τις συσκευές SPI ενός ενσωµατωµένου συστήµατος Linux είναι µια λίστα µε όλες τις slave συσκευές που υπάρχουν στο target σύστηµα και κάποιες φορές κάποια επιπλέον δεδοµένα του συστήµατος αυτού έτσι ώστε ο SPI driver να λειτουργήσει σωστά. Συνήθως τα αρχεία arch/.../mach-*/board-*.c παρέχουν µια λίστα σχετικά µε τις SPI συσκευές που υπάρχουν σε κάθε πλακέτα. Ο κώδικας τους µπορεί να είναι παρόµοιος µε τον παρακάτω: static struct ads7846_platform_data ads_info = { .vref_delay_usecs = 100, .x_plate_ohms = 580, .y_plate_ohms = 410, }; static struct spi_board_info spi_board_info[] __initdata = { { .modalias = "ads7846", .platform_data = &ads_info, .mode = SPI_MODE_0, .irq = GPIO_IRQ(31), .max_speed_hz = 120000 .bus_num = 1, .chip_select = 0, }, }; Συγκεκριµένα στην περίπτωση του Router NGW100 της παρούσας εργασίας, ο κώδικας αυτός βρίσκεται στο αρχείο arch\avr32\boards\atngw100\setup.c και η δεύτερη struct είναι γραµµένη ως εξής: static struct spi_board_info spi0_board_info[] __initdata = { { .modalias = "mtd_dataflash", .max_speed_hz = 8000000, Επικοινωνία: [email protected] 96 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 .chip_select = 0, }, }; Στον κώδικα είδαµε πως παρέχονται εξειδικευµένες πληροφορίες της πλακέτας. Οι πληροφορίες αυτές αφορούν την ταχύτητα του ρολογιού (.max_speed_hz), ή τον τρόπο που ένας ακροδέκτης .irq συνδέεται. Επίσης αφορούν περιορισµούς των SPI συσκευών (chip – specific constraints) όπως είναι για παράδειγµα µια σηµαντική καθυστέρηση (delay) που αλλάζει µέσω της χωρητικότητας (capacitance) ενός ακροδέκτη. Οι πληροφορίες πλακέτας (board_info) θα πρέπει να παρέχουν αρκετές πληροφορίες έτσι ώστε το σύστηµα να δουλεύει χωρίς να χρειάζεται να φορτωθεί κάποιος οδηγός. Στη συνέχεια ο κώδικας αρχικοποίησης της πλακέτας καταχωρεί τον πίνακα που προκύπτει απ’ όλα τα παραπάνω στοιχεία µαζί µε την υποδοµή SPI έτσι ώστε να χρησιµοποιηθούν αργότερα κατά την καταχώρηση του SPI master controller: spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); Η αντίστοιχη συνάρτηση η οποία βρίσκεται στο αρχείο setup.c στον Router NGW100, είναι η παρακάτω: at32_add_device_spi(0, spi0_board_info, ARRAY_SIZE(spi0_board_info)); Όπως ίσως είναι ήδη κατανοητό, το SPI παρ’ όλο που απλοποιηµένα αναφέρεται ως διεπαφή, για την υλοποίησή του χρειάζεται να ασχοληθούµε µε αρκετές λεπτοµέρειες. Οι κυριότερες από αυτές αφορούν τα ηλεκτρικά χαρακτηριστικά, τη φυσική συνδεσµολογία των ολοκληρωµένων, το πρωτόκολλο επικοινωνίας , αλλά και τον κώδικα του master controller. Σε ένα ενσωµατωµένο σύστηµα Linux όλα αυτά υλοποιούνται επάνω από (user-space), ή µέσα, στον πυρήνα του Linux (kernel-space). Ας δούµε όµως πως µπορούµε να γράψουµε έναν οδηγό πρωτοκόλλου (protocol driver) για SPI. Αν και υπάρχουν drivers που είναι γραµµένοι για το user – space, οι περισσότεροι γράφονται για την περιοχή του kernel. Ένα τέτοιο παράδειγµα θα δούµε κι εδώ: static struct spi_driver CHIP_driver = { .driver = { .name = "CHIP", .owner = THIS_MODULE, }, .probe = CHIP_probe, .remove = __devexit_p(CHIP_remove), .suspend = CHIP_suspend, .resume = CHIP_resume, }; Ο πυρήνας (core) του SPI driver (που υπάρχει ήδη στον kernel) θα προσπαθήσει να δεσµεύσει τον παραπάνω driver που γράψαµε εµείς µε οποιοδήποτε ολοκληρωµένο του οποίου ο κώδικας board_info παρέχει ένα modalias για το CHIP. Επίσης ο κώδικας probe() θα µοιάζει µε τον ακόλουθο: static int __devinit CHIP_probe(struct spi_device *spi) { struct CHIP *chip; struct CHIP_platform_data *pdata; Ιστότοπος εργασίας: MyThesis.org 97 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) // υποθέτωντας ότι ο οδηγός απαιτεί board-specific δεδοµένα pdata = &spi->dev.platform_data; if (!pdata) return -ENODEV; // εκχώρηση µνήµης για το ολοκληρωµένο µέσω του driver chip = kzalloc(sizeof *chip, GFP_KERNEL); if (!chip) return -ENOMEM; spi_set_drvdata(spi, chip); ... etc return 0; } Από τη στιγµή που εκτελείται ο κώδικας probe() από τον driver, ο δεύτερος µπορεί να αποστείλει αιτήσεις I/O στην SPI συσκευή µέσω της struct spi_message. Γενικά ένα µήνυµα SPI (spi_message) είναι µια ακολουθία ενεργειών του πρωτοκόλλου που εκτελείται σαν µια ενιαία ακολουθία. Οι έλεγχοι που γίνονται από τον SPI driver αφορούν την κατεύθυνση ροής των δεδοµένων, τους I/O buffers που χρησιµοποιούνται, τα διαστήµατα των παύσεων µεταξύ των µεταδόσεων, την ενεργοποίηση / απενεργοποίηση του chipselect κ.α.. Τώρα ας δούµε και ένα παράδειγµα κώδικα για master controller driver. Ο κύριος σκοπός αυτού του driver είναι να παρέχει έναν spi_master. Για να προσδιορίσουµε τον master µπορούµε να χρησιµοποιήσουµε την συνάρτηση spi_alloc_master() και για να διαβάσουµε τα δεδοµένα που τον αφορούν (private data) την spi_master_get_devdata(): struct spi_master *master; struct CONTROLLER *c; master = spi_alloc_master(dev, sizeof *c); if (!master) return -ENODEV; c = spi_master_get_devdata(master); Αφού αρχικοποιηθεί ο spi_master θα πρέπει να καταχωρηθεί µέσω της συνάρτησης spi_register_master() ώστε να γίνει γνωστός και στο υπόλοιπο σύστηµα. Από ‘κει και µετά οι SPI συσκευές µπορούν να χρησιµοποιηθούν κανονικά. Σε περίπτωση που θέλουµε να αφαιρέσουµε τον SPI controller driver µπορούµε να το κάνουµε µέσω της παρακάτω συνάρτησης: spi_unregister_master() // αντίστροφη της spi_register_master() Καλό είναι επίσης να πούµε δύο λόγια και για την αριθµοδότηση διαύλου (bus numbering), καθώς και για τις µεθόδους (methods) SPI. Η αριθµοδότηση διαύλου είναι σηµαντική αφού έτσι αναγνωρίζει ο Linux kernel ένα δίαυλο SPI. Οι έγκυροι αριθµοί διαύλου ξεκινούν µε µηδέν. Στα SoC συστήµατα οι αριθµοί διαύλου Επικοινωνία: [email protected] 98 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 θα πρέπει να ταιριάζουν µε αυτούς που καθορίζονται από τον κατασκευαστή τους. Για παράδειγµα ο controller SPI2 θα πρέπει να έχει αριθµό διαύλου 2 και η spi_board_info θα πρέπει να χρησιµοποιεί αυτό τον αριθµό για συσκευές που συνδέονται σε αυτόν (τον SPI2). Αν δεν υπάρχει κάποιος τέτοιος αντιστοιχισµένος αριθµός µπορούµε να χρησιµοποιούµε κάποια αρνητική τιµή διαύλου. Αυτός ο αρνητικός αριθµός θα αντικατασταθεί δυναµικά στη συνέχεια από έναν άλλο αριθµό που θα επιλεγεί από το σύστηµα. Οι µέθοδοι SPI είναι οι παρακάτω: master->setup(struct spi_device *spi) – καθορίζει τον ρυθµό του ρολογιού (clock rate) της συσκευής, τον τύπο (mode) SPI και το µέγεθος των λέξεων (word sizes). master->transfer(struct spi_device *spi, struct spi_message *message) – είναι υπεύθυνη για την έναρξη και την ολοκλήρωση της µεταφοράς master->cleanup(struct spi_device *spi) – απελευθερώνει την κατάσταση που προκύπτει από την κλήση spi_device.controller_state. 3.5 Είσοδοι και Έξοδοι (I/Os) Όπως και µε τα άλλα λειτουργικά συστήµατα, ο Linux kernel υποστηρίζει ένα µεγάλο εύρος συσκευών εισόδου – εξόδου. ∆εν πρόκειται να αναλύσουµε όλες αυτές τις συσκευές, αλλά µόνο όσες κρίνεται χρήσιµο για την τεκµηρίωση της εργασίας αυτής. Για µια εις βάθος ανάλυση υπάρχει το Linux Documentation Project ή LDP (tldp.org). Το απαραίτητο θεωρητικό υπόβαθρο που θα αναλυθεί πριν την ανάλυση των I/Os είναι: Η διεπαφή tty Ας δούµε τώρα πως υποστηρίζεται η σειριακή επικοινωνία από τον Linux kernel. Για να το επιτύχουµε αυτό θα πρέπει να ασχοληθούµε µε τη σειριακή διεπαφή χαµηλού επιπέδου (Low Level Serial API) και µε τον driver του σειριακού hardware. Ο σειριακός driver χαµηλού επιπέδου (Low Level Serial Hardware Driver) είναι υπεύθυνος στο να παρέχει πληροφορίες σχετικά µε τη θύρα UART (uart_port) καθώς και ένα σύνολο µεθόδων ελέγχου (uart_ops) προς τον βασικό σειριακό οδηγό (core serial driver). Ο driver χαµηλού επιπέδου είναι υπεύθυνος επίσης και για τον χειρισµό διακοπών της θύρας UART καθώς και για την υποστήριξη εντολών κονσόλας (console). Ως προς την υποστήριξη κονσόλας, o βασικός σειριακός driver παρέχει έναν αριθµό χρήσιµων συναρτήσεων που αφορούν την αναγνώριση της σωστής δοµής της θύρας (uart_get_console), και την αποκωδικοποίηση τον ορισµάτων (παραµέτρων) της γραµµής εντολών (uart_parse_options). Υπάρχει επίσης µια πολύ χρήσιµη συνάρτηση (uart_write_console) που εκτελεί εγγραφή χαρακτήρα προς χαρακτήρα µεταφράζοντας τις νέες γραµµές (newlines) σε ακολουθίες CRLF (Carriage Return, Line Feed – χρήσιµο για συστήµατα που βασίζονται σε ASCII κωδικοποίηση). Ο σειριακός driver χαµηλού επιπέδου είναι επίσης υπεύθυνος για το κλείδωµα της σειριακής θύρας. Υπάρχουν τρεις τύποι κλειδώµατος (port->lock) οι οποίοι είναι οι εξής: spinlock Ιστότοπος εργασίας: MyThesis.org 99 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) tmpbuf semaphore overall semaphore Από την µεριά του core driver επιτυγχάνεται το κλείδωµα των ακόλουθων δεδοµένων (µπορεί να χρησιµοποιηθεί και από τον οδηγό χαµηλού επιπέδου): port->mctrl port->icount info->xmit.head (circ->head) info->xmit.tail (circ->tail) Ο σειριακός driver χαµηλού επιπέδου συνδέεται µε τον σειριακό core driver µέσω της διεπαφής uart_ops η οποία περιλαµβάνει όλες τις µεθόδους µέσω των οποίων µπορούµε να χειριστούµε το hardware. Οι κυριότερες µέθοδοι είναι οι παρακάτω: tx_empty(port) set_mctrl(port, mctrl) get_mctrl(port) stop_tx(port) start_tx(port) stop_rx(port) enable_ms(port) break_ctl(port,ctl) startup(port) shutdown(port) flush_buffer(port) set_termios(port,termios,oldtermios) pm(port,state,oldstate) type(port) release_port(port) request_port(port) config_port(port,type) verify_port(port,serinfo) ioctl(port,cmd,arg) Επίσης υπάρχουν και οι συναρτήσεις: uart_update_timeout(port,cflag,baud) uart_get_baud_rate(port,termios,old,min,max) uart_get_divisor(port,baud) uart_match_port(port1,port2) uart_write_wakeup(port) uart_register_driver(drv) uart_unregister_driver() uart_suspend_port() uart_resume_port() uart_add_one_port() uart_remove_one_port() Μπορούµε εύκολα να αντιληφθούµε την λειτουργικότητα των µεθόδων και των συναρτήσεων που παρατέθηκαν από την ονοµατολογία τους, από τον κώδικα που περιλαµβάνεται στους uart drivers του kernel καθώς και από το documentation που περιλαµβάνεται σε αυτόν. Θα ήταν άσκοπο να τις εξηγήσουµε µία προς µία, αφού αυτό που µας ενδιαφέρει περισσότερο είναι µια γενική µατιά στο uart hardware που υποστηρίζει ο kernel. Επικοινωνία: [email protected] 100 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 3.5.1 Το υποσύστηµα tty Το υποσύστηµα tty χρησιµοποιείται από το Linux για την υποστήριξη πληκτρολόγησης στην γραµµή εντολών κάποιου τερµατικού ή για τη χρήση κάποιας σειριακής σύνδεσης. Οι συσκευές tty µπορεί να είναι σειριακές θύρες, USB σε serial µετατροπείς καθώς και κάποια modem που απαιτούν ειδική επεξεργασία για να λειτουργήσουν κανονικά (όπως είναι για παράδειγµα οι συσκευές Winmodem). Κάθε tty driver πρέπει να δηµιουργεί µια struct tty_driver η οποία περιγράφει τον εαυτό του και καταχωρείται στο tty layer. Η δοµή tty_driver καθορίζεται στο αρχείο include/linux/tty_driver.h του πηγαίου κώδικα του kernel και µοιάζει µε την ακόλουθη: struct tty_driver { int magic; /* magic number for this structure */ const char *driver_name; const char *name; int name_base; /* offset of printed name */ short major; /* major device number */ short minor_start; /* start of minor device number*/ short num; /* number of devices */ short type; /* type of tty driver */ short subtype; /* subtype of tty driver */ struct termios init_termios; /* Initial termios */ int flags; /* tty driver flags */ int *refcount; /* for loadable tty drivers */ struct proc_dir_entry *proc_entry; /* /proc fs entry */ struct tty_driver *other; /* only used for the PTY driver */ /* * Pointer to the tty data structures */ struct tty_struct **table; struct termios **termios; struct termios **termios_locked; void *driver_state; /* only used for the PTY driver */ /* * Interface routines from the upper tty layer to the tty * driver. */ int (*open)(struct tty_struct *tty, struct file *filp); void (*close)(struct tty_struct *tty, struct file *filp); int (*write)(struct tty_struct *tty, int from_user, const unsigned char *buf, int count); void (*put_char)(struct tty_struct *tty, unsigned char ch); void (*flush_chars)(struct tty_struct *tty); int (*write_room)(struct tty_struct *tty); int (*chars_in_buffer)(struct tty_struct *tty); int (*ioctl)(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); void (*set_termios)(struct tty_struct *tty, struct termios *old); void (*throttle)(struct tty_struct *tty); void (*unthrottle)(struct tty_struct *tty); void (*stop)(struct tty_struct *tty); void (*start)(struct tty_struct *tty); void (*hangup)(struct tty_struct *tty); void (*break_ctl)(struct tty_struct *tty, int state); void (*flush_buffer)(struct tty_struct *tty); Ιστότοπος εργασίας: MyThesis.org 101 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) void void void int int (*set_ldisc)(struct tty_struct *tty); (*wait_until_sent)(struct tty_struct *tty, int timeout); (*send_xchar)(struct tty_struct *tty, char ch); (*read_proc)(char *page, char **start, off_t off, int count, int *eof, void *data); (*write_proc)(struct file *file, const char *buffer, unsigned long count, void *data); /* * linked list pointers */ struct tty_driver *next; struct tty_driver *prev; }; Το πρώτο µέλος λοιπόν της δοµής tty_driver είναι το µέλος magic πρέπει πάντα να αρχικοποιείται µε τιµή TTY_DRIVER_MAGIC έτσι ώστε το επίπεδο tty να γνωρίζει ότι πρόκειται για έναν πραγµατικό tty driver. Τα µέλη driver_name και name περιγράφουν τον driver και καλό είναι το πρώτο εξ’ αυτών να αρχικοποιείται σε κάτι περιγραφικό µιας και θα εµφανίζεται στον κατάλογο /proc/tty/drivers. Το µέλος name χρησιµοποιείται για να καθορίσει πιο θα είναι το βασικό όνοµα του driver (στο /dev ή στο udev). Ως ένα παράδειγµα µπορούµε να φέρουµε και τον serial driver που υπάρχει στον kernel όπου το µέλος driver_name αρχικοποιείται σε serial. Το µέλος name_base είναι απαραίτητο µόνο όταν η συσκευή µας δεν ξεκινά µε ελάχιστο αριθµό (minor number) το µηδέν. Σχεδόν για όλους τους drivers που γράφονται, η τιµή αυτού του µέλους πρέπει να αρχικοποιείται µε µηδέν. Τα µέλη major, minor_start και num περιγράφουν τον µέγιστο και τον ελάχιστο αριθµό απ’ όπου τελειώνει και ξεκινά η αρίθµηση των drivers στο επίπεδο tty. Το µέλος major θα πρέπει να παίρνει την τιµή που έχει αντιστοιχηθεί στον µέγιστο αριθµό του driver που έχουµε γράψει. Αν βρισκόµαστε στη διαδικασία συγγραφής ενός νέου driver µπορούµε να διαβάσουµε το αρχείο devices.txt που βρίσκεται στον κατάλογο documentation του πηγαίου κώδικα του kernel ώστε να βρούµε τον major (ή τον minor) αριθµό για τον driver αυτό. Το αρχείο αυτό είναι επίσης χρήσιµο και για όσους θέλουν να γνωρίζουν ποια ζεύγη major / minor αριθµών χρησιµοποιούνται και από ποιες συσκευές. Το µέλος minor_start ορίζεται για να προσδιορίζει που βρίσκεται ο πρώτος minor αριθµός για την συσκευή µας. Αν έχουµε αντιστοιχίσει εξ’ ολοκλήρου έναν major αριθµό στον driver που δηµιουργήσαµε, τότε ο minor_start πρέπει να τίθεται µηδέν. Τέλος το µέλος num περιγράφει πόσοι διαφορετικοί minor αριθµοί έχουν αντιστοιχηθεί από εµάς στον συγκεκριµένο driver. Έτσι, αν για παράδειγµα έχουµε αντιστοιχίσει σε κάποιον driver όλους τους major αριθµούς στο 188, τότε ο driver αυτός θα πρέπει να αρχικοποιεί τα µέλη που αναφέραµε πριν, ως εξής: major minor_start num 188 0 255 Τα µέλη type και subtype περιγράφουν τι είδους tty driver είναι ο driver που γράψαµε στο επίπεδο tty. Το µέλος type µπορεί να παίρνει τις παρακάτω τιµές: TTY_DRIVER_TYPE_SYSTEM: χρησιµοποιείται εσωτερικά από το υποσύστηµα tty έτσι ώστε να γίνεται αντιληπτό πως πρόκειται για έναν εσωτερικό tty driver. Αν χρησι- Επικοινωνία: [email protected] 102 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 µοποιηθεί η τιµή αυτή τότε η τιµή του µέλους subtype πρέπει να τεθεί σε SYSTEM_TYPE_TTY, SYSTEM_TYPE_CONSOLE, SYSTEM_TYPE_SYSCONS ή σε SYSTEM_TYPE_SYSPTMX. Αυτός ο τύπος δε θα πρέπει να χρησιµοποιείται από έναν απλό tty driver. TTY_DRIVER_TYPE_CONSOLE: Ο τύπος αυτός θα πρέπει να χρησιµοποιείται µόνο από τον console driver. TTY_DRIVER_TYPE_SERIAL: Η τιµή αυτή είναι η συνηθέστερη που παίρνει το µέλος type. Αν χρησιµοποιείται αυτή η τιµή, τότε το µέλος subtype θα πρέπει να τεθεί σε SERIAL_TYPE_NORMAL ή σε SERIAL_TYPE_CALLOUT, αναλόγως µε τον τύπο του driver. Μπορεί να χρησιµοποιηθεί από οποιονδήποτε serial driver. TTY_DRIVER_TYPE_PTY: Αν το µέλος type παίρνει αυτή την τιµή, τότε το µέλος subtype µπορεί να τίθεται είτε σε PTY_TYPE_MASTER,είτε σε PTY_TYPE_SLAVE. Χρησιµοποιείται από την διεπαφή ψευδό-τερµατικού (PTY – Pseudo Terminal Interface). Το µέλος init_termios χρησιµοποιείται για να αρχικοποιήσει τις ρυθµίσεις της σειριακής γραµµής και τις ταχύτητες επικοινωνίας της συσκευής όταν αυτή δηµιουργείται για πρώτη φορά. Το µέλος flags τίθεται σε µια ποικιλία τιµών bit ανάλογα µε τις ανάγκες του driver: TTY_DRIVER_INSTALLED: ο driver δεν µπορεί να καταχωρήσει τον εαυτό του στο επίπεδο tty, έτσι καλό είναι να µην χρησιµοποιούµε αυτή την τιµή. TTY_DRIVER_RESET_TERMIOS: όταν η τιµή αυτού του bit ενεργοποιείται (δηλαδή γίνεται 1) προκαλεί τον επαναπροσδιορισµό των ρυθµίσεων termios µετά την εκτέλεση και της τελευταίας διεργασίας της συσκευής. Αυτό είναι χρήσιµο σε περιπτώσεις drivers κονσόλας και pty. TTY_DRIVER_REAL_RAW: αν αυτό το bit είναι ενεργοποιηµένο σηµατοδοτεί ότι ο driver υποστηρίζει ανίχνευση σφαλµάτων και άλλες βελτιστοποιήσεις επικοινωνίας. TTY_DRIVER_NO_DEVFS: η ενεργοποίηση αυτού του bit αποτρέπει την δηµιουργία καταχώρησης στο devfs µετά την κλήση της tty_register_function. Αυτό είναι χρήσιµο για drivers που δηµιουργούν και καταργούν δυναµικά συσκευές ανάλογα µε το πότε αυτές υπάρχουν φυσικά ή όχι. Παραδείγµατα drivers που ενεργοποιούν το συγκεκριµένο bit είναι οι µετατροπείς USB σε serial, οι USB modem drivers και οι drivers USB Bluetooth tty. Συνεχίζοντας την ανάλυση της δοµής tty_driver εντοπίζουµε το µέλος refcount. Χρησιµοποιείται από το επίπεδο tty για τον χειρισµό της σωστής καταµέτρησης και αναφοράς του driver. Το µέλος proc_entry δεν θα πρέπει να τροποποιείται από τον tty driver. Αν ο tty driver ενσωµατώνει τις συναρτήσεις write_proc ή read_proc, τότε το µέλος proc_entry θα περιέχει την καταχώρηση του οδηγού που δηµιουργήθηκε από εµάς. Το µέλος other χρησιµοποιείται µόνο από τον pty driver και δε θα πρέπει να χρησιµοποιείται από κάποιον άλλο tty driver. Στη συνέχεια υπάρχουν κάποιοι δείκτες (pointers) που δείχνουν σε άλλες δοµές tty. Το µέλος table είναι ένας δείκτης πίνακα που περιέχει δείκτες της tty_struct. Τα δύο µέλη termios και termios_locked, είναι δείκτες σε έναν πίνακα που περιέχει δείκτες της δοµής termios. Όλοι αυτοί οι πίνακες θα πρέπει να έχουν τον ίδιο αριθµό πεδίων µε τον αριθµό του µέλους minor που είδαµε πιο πριν. Χρησιµοποιούνται από το επίπεδο tty για τον χειρισµό των minor συσκευών και δε θα πρέπει να τροποποιούνται από τον tty driver που γράφουµε. Ιστότοπος εργασίας: MyThesis.org 103 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Το µέλος driver_state χρησιµοποιείται µόνο από τον pty driver και δε θα πρέπει να χρησιµοποιείται από κανένα άλλο tty driver. Υπάρχει µια αρκετά µεγάλη λίστα µε διάφορους δείκτες συναρτήσεων στην δοµή tty_driver. Αυτοί οι δείκτες χρησιµοποιούνται από το επίπεδο tty για την επικοινωνία µε τον tty driver όταν αυτή είναι απαιτείται για την πραγµατοποίηση κάποιας ενέργειας. Η συνάρτηση open καλείται από το επίπεδο tty όταν η open (2) καλείται από τη συσκευή στην οποία έχει αντιστοιχηθεί ο tty driver που έχουµε γράψει. Το επίπεδο tty επιτυγχάνει αυτή την κλήση µέσω ενός δείκτη ο οποίος δείχνει στην δοµή tty_struct καθώς και µέσω ενός δείκτη αρχείου που έχει αντιστοιχηθεί στη συσκευή. Ίσως όλα τα παραπάνω, αλλά και αυτά που θα ακολουθήσουν, να είναι αρκετά πολύπλοκα και να µπερδεύουν ως ένα βαθµό. Όµως όταν κάποιος προσπαθήσει να τα υλοποιήσει στην πράξη, τα πράγµατα αρχίζουν σταδιακά να ξεκαθαρίζουν και όλα αποκτούν µεγαλύτερο νόηµα. Απαιτείται υποµονή, επιµονή και εφαρµογή. Η συνάρτηση close καλείται από το επίπεδο tty όταν η release (2) καλείται από τον δείκτη που δείχνει στο αρχείο ο οποίος δηµιουργήθηκε προηγουµένως µέσω της κλήσης της open (2). Αυτό σηµαίνει ότι η συσκευή θα πρέπει να κλείσει. Η συνάρτηση write καλείται από το επίπεδο tty όταν κάποια τα δεδοµένα πρόκειται να αποσταλούν στην tty συσκευή µας. Τα δεδοµένα αυτά µπορεί να προέρχονται είτε από την περιοχή χρήστη (user space), είτε από την περιοχή του πυρήνα (kernel space). Το µέλος from_user θα ενεργοποιηθεί αν τα δεδοµένα έρχονται από την περιοχή χρήστη. Η συνάρτηση write θα πρέπει να επιστρέψει τον αριθµό των bytes που γράφτηκαν τελικά στη συσκευή. Η συνάρτηση αυτή πρέπει να καθορίζεται για έναν tty driver. Η συνάρτηση put_char καλείται από το επίπεδο tty όταν ένας και µόνο χαρακτήρας πρέπει να γραφεί στη συσκευή. Αν δεν υπάρχει αρκετός χώρος γι’ αυτή την εγγραφή, ο χαρακτήρας που εστάλη µπορεί να αγνοηθεί. Αν ένας tty driver δεν καθορίζει αυτή τη συνάρτηση τότε για την εγγραφή ενός απλού χαρακτήρα θα καλείται η συνάρτηση write. Η συνάρτηση flush_chars καλείται όταν το επίπεδο tty έχει στείλει ένα αριθµό χαρακτήρων στον tty driver καλώντας την συνάρτηση put_char. Ο tty driver θα πρέπει να ενηµερώσει τη συσκευή ότι πρέπει να αποστείλει όλα τα δεδοµένα που αποµένουν σε αυτή, µέσω της σειριακής γραµµής (serial line). Η συνάρτηση write_room καλείται από το επίπεδο tty όταν αυτό θέλει να γνωρίζει πόσο ελεύθερο χώρο έχει ο tty driver στον καταχωρητή εγγραφής (write buffer) του. Αυτός ο αριθµός µεταβάλλεται µε το χρόνο καθώς οι καταχωρητές εγγραφής θα αδειάζουν. Η συνάρτηση chars_in_buffer καλείται όταν το επίπεδο tty θέλει να γνωρίζει πόσοι χαρακτήρες αποµένουν για αποστολή από τον καταχωρητή εγγραφής του tty driver. Η συνάρτηση ioctl καλείται από το επίπεδο tty όταν ή ioctl (2) καλείται από τη συσκευή. Η συνάρτηση αυτή επιτρέπει στον tty driver να ενσωµατώσει συναρτήσεις ioctl εξειδικευµένες προς τη συσκευή. Αν η συνάρτηση ioctl που ζητείται δεν υποστηρίζεται από τον driver επιστρέφεται η τιµή –ENOIOCTLCMD. Η συνάρτηση set_termios καλείται από το επίπεδο tty όταν οι ρυθµίσεις termios της συσκευής έχουν αλλάξει. Σε τέτοια περίπτωση ο tty driver θα πρέπει να αλλάξει τις ρυθµίσεις Επικοινωνία: [email protected] 104 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 των φυσικών χαρακτηριστικών της συσκευής ανάλογα µε τα διάφορα µέλη που υπάρχουν στην δοµή termios. Οι συναρτήσεις throttle και unthrottle χρησιµοποιούνται για τον έλεγχο της υπερχείλισης των καταχωρητών εισόδου του tty επιπέδου. Η συνάρτηση throttle καλείται όταν οι καταχωρητές εισόδου του επιπέδου tty είναι έτοιµοι να γεµίσουν. Ο tty driver θα πρέπει να ειδοποιήσει τη συσκευή ότι δεν µπορούν να σταλθούν άλλοι χαρακτήρες. Όταν οι καταχωρητές εισόδου αδειάσουν κάποια στιγµή και µπορούν να ληφθούν νέα δεδοµένα, τότε καλείται η συνάρτηση unthrottle. Έτσι ο tty driver ενηµερώνει τη συσκευή ότι µπορεί να συνεχίσει να στέλνει την αποστολή δεδοµένων. Οι συναρτήσεις start και stop µοιάζουν µε τις δύο προηγούµενες που είδαµε αλλά ο tty driver θα πρέπει να σταµατήσει να στέλνει δεδοµένα στη συσκευή και στη συνέχεια να συνεχίσει την αποστολή από εκεί που σταµάτησε. Η συνάρτηση hangup καλείται όταν ο tty driver πρέπει να διακόψει την επικοινωνία µε την tty συσκευή. Η συνάρτηση break_ctrl καλείται όταν ο tty driver πρόκειται να ενεργοποιήσει ή να απενεργοποιήσει την κατάσταση (state) BREAK της θύρας RS-232. Αν η κατάσταση έχει την τιµή -1, τότε η κατάσταση BREAK θα πρέπει να ενεργοποιηθεί. Αν έχει την τιµή µηδέν τότε θα πρέπει να συµβεί το αντίθετο δηλαδή να απενεργοποιηθεί. Αν η συνάρτηση break_ctrl είναι ενσωµατωµένη από τον tty driver τότε, το επίπεδο tty θα πρέπει να χειριστεί τα ioctls: TCSBRK, TCSBRKP, TIOCSBRK και TIOCCBRK. ∆ιαφορετικά θα πρέπει αυτά να σταλούν στην συνάρτηση ioctl του tty driver. Η συνάρτηση flush_buffer καλείται όταν ο tty driver θέλει να αδειάσει τα δεδοµένα που υπάρχουν ακόµα στους καταχωρητές εγγραφής του. Αυτό σηµαίνει ότι τα δεδοµένα που αποµένουν σε αυτούς τους καταχωρητές δε θα αποσταλούν στη συσκευή. Η συνάρτηση ser_ldisc καλείται όταν το επίπεδο tty αλλάζει την διαµόρφωση της σύνδεσης (line discipline) του tty driver. Παρ’ όλα αυτά η συνάρτηση αυτή δε χρησιµοποιείται πια και δεν θα πρέπει να την χρησιµοποιούµε ούτε εµείς. Η συνάρτηση wait_until_sent καλείται όταν το επίπεδο tty θέλει να ενηµερωθεί για όλα τα εναποµείναντα δεδοµένα που βρίσκονται σε εκκρεµότητα (pending data) αποστολής προς τη συσκευή από τον tty driver. Η συνάρτηση wait_until_sent δε θα πρέπει να επιστρέψει (return) καµία τιµή µέχρι να ολοκληρώσει την εργασία της και γι’ αυτό επιτρέπεται να τεθεί σε κατάσταση sleep αν χρειαστεί να περιµένει για κάποιο διάστηµα. Η συνάρτηση send_xchar χρησιµοποιείται για την αποστολή ενός χαρακτήρα υψηλής προτεραιότητας XON ή XOFF στη συσκευή tty. Οι συναρτήσεις read_proc και write_proc χρησιµοποιούνται όταν ο driver χρειάζεται να ενσωµατώσει µια καταχώρηση /proc/tty/driver/<onoma>. Το onoma θα εισαχθεί από το µέλος name που είδαµε νωρίτερα. Τέλος, τα µέλη next και prev, χρησιµοποιούνται από το επίπεδο tty για να διασυνδέσει όλους τους tty drivers µαζί. Στα µέλη αυτά δε θα πρέπει να έχει καµία πρόσβαση ο tty driver. Αφού είδαµε όλα τα παραπάνω µέλη αυτά τα οποία είναι άκρως απαραίτητα για τη δηµιουργία του ελάχιστου tty driver που όµως θα λειτουργεί και θα εκτελεί κάποιες βασικές εργασίες. Ο ακόλουθος κώδικας είναι ένα τέτοιο παράδειγµα: Ιστότοπος εργασίας: MyThesis.org 105 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) #define TINY_TTY_MAJOR 240 #define TINY_TTY_MINORS 255 /* experimental range */ /* use the whole major up */ static int tty_refcount; static struct tty_struct *tiny_tty[TINY_TTY_MINORS]; static struct termios *tiny_termios[TINY_TTY_MINORS]; static struct termios *tiny_termios_locked[TINY_TTY_MINORS]; static struct tty_driver tiny_tty_driver { magic: TTY_DRIVER_MAGIC, driver_name: "tiny_tty", #ifdef CONFIG_DEVFS_FS name: "tts/ttty%d", #else name: "ttty", #endif major: TINY_TTY_MAJOR, num: TINY_TTY_MINORS, type: TTY_DRIVER_TYPE_SERIAL, subtype: SERIAL_TYPE_NORMAL, flags: TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, refcount: table: termios: termios_locked: &tiny_refcount, tiny_tty, tiny_termios, tiny_termios_locked, open: close: write: write_room: tiny_open, tiny_close, tiny_write, tiny_write_room, }; static int __init tiny_init (void) { /* register the tty driver */ tiny_tty_driver.init_termios = tty_std_termios; tiny_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; if (tty_register_driver (&tiny_tty_driver)) { printk (KERN_ERR "failed to register tiny tty driver"); return -1; return 0; } static void __exit tiny_exit (void) { tty_unregister_driver (&tiny_tty_driver); } module_init (tiny_init); module_exit (tiny_exit); Για την καλύτερη κατανόηση της λειτουργίας του πυρήνα (core) του υποσυστήµατος TTY, πολύ χρήσιµη είναι και η περιγραφή ενός tty driver. Ένας tty driver είναι ένας character driver ο οποίος ελέγχει τη ροή και τη µορφή των δεδοµένων σε µια συσκευή tty. Για τον έλεγχο της ροής των δεδοµένων υπάρχει ένας αριθµός κανόνων ή ελέγχων σύνδεσης (line disciplines) ο οποίος µπορεί να ενσωµατωθεί εικονικά σε κάθε συσκευή tty. Αυτό επιτυγχάνεται µέσω διαΕπικοινωνία: [email protected] 106 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 φορετικών drivers ελέγχου σύνδεσης. Όπως βλέπουµε και στην επόµενη εικόνα, ο πυρήνας tty παίρνει τα δεδοµένα τα οποία θέλει ένας χρήστης να στείλει σε µια tty συσκευή και τα προωθεί στον driver ελέγχου σύνδεσης (tty line discipline) ο οποίος µε τη σειρά του τα προωθεί στον tty driver. Έπειτα ο τελευταίος, µετατρέπει τα δεδοµένα που έλαβε σε µια µορφή κατανοητή για το υλικό: Εικόνα 30. Γραφική αναπαράσταση της λειτουργίας του υποσυστήµατος TTY 3.5.2 RS-232 Εφόσον το RS232 είναι µια διεπαφή υλικού (chip), ο kernel δε χρειάζεται να το υποστηρίζει ο ίδιος. Έτσι περιλαµβάνει κυρίως drivers για ολοκληρωµένα που υλοποιούν την επικοινωνία RS232 (πχ: MAX232). Ο βασικός driver για την σειριακή (UART) επικοινωνία στον kernel είναι το αρχείο serial.c και βρίσκεται στον κατάλογο drivers. Κάποιες αρχιτεκτονικές όπως η AVR32 έχουν τους δικούς τους drivers (atmel_serial.c) για να υποστηρίξουν το υλικό τους. Παρ’ όλα αυτά, οι σειριακές συσκευές του Linux είναι προσβάσιµες σαν τερµατικές συσκευές, όπως συµβαίνει και στα συστήµατα Unix, ανεξάρτητα από το υποκείµενο υλικό και τα σχετικά προγράµµατα οδήγησης. Οι καταχωρήσεις των σειριακών συσκευών στο Linux ξεκινούν µε /dev/ttyS0 και µπορούν να φτάσουν µέχρι και /dev/ttyS0191 (δηλαδή τουλάχιστον 190 συσκευές). Στις περισσότερες των περιπτώσεων όµως οι συσκευές που υπάρχουν σε ένα σύστηµα είναι ελάχιστες. Τα βασικά για τη σειριακή επικοινωνία, τη διαµόρφωση, την εγκατάστασή της αλλά και τον προγραµµατισµό, σε ότι αφορά το Linux, περιλαµβάνονται στον αντίστοιχο online οδηγό του LDP (tldp.org/HOWTO/pdf/Serial-HOWTO.pdf). 3.5.3 GPIO Τα σήµατα εισόδου – εξόδου γενικού σκοπού, GPIO (General Purpose Input/Output) είναι ευέλικτα ψηφιακά σήµατα τα οποία ελέγχονται µέσω λογισµικού. Κάθε ένα τέτοιο σήµα αναπαριστά ένα bit το οποίο είναι αντιστοιχισµένο µε ένα συγκεκριµένο pin του µικροελεγκτή. Ανάλογα µε το υλικό, τα σήµατα GPIO µπορούν να επιτελούν διάφορες λειτουργίες. Για παράδειγµα, στον µικροελεγκτή της εργασίας µας παρέχονται τα εξής: Ιστότοπος εργασίας: MyThesis.org 107 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) ∆ιακοπή που ανιχνεύει µεταβολή εισόδου (input change interrupt) και µπορεί να κάνει δυνατή την ανίχνευση αλλαγής επιπέδου (level change detection) σε οποιαδήποτε είσοδο. Φίλτρο απόρριψης (glitch filter) παλµών που είναι χαµηλότεροι από το µισό ενός κύκλου ρολογιού, για την αποφυγή σφαλµάτων από απότοµες µεταβολές. Έλεγχο pull-up για γραµµές εισόδου – εξόδου. ∆ιαχείριση των εισόδων και των εξόδων Η υποστήριξη GPIO µπορεί να ρυθµιστεί από το γραφικό περιβάλλον παραµετροποίησης του Kernel (Device drivers GPIO Support). Στην εικόνα µπορούµε να δούµε τις δυνατότητες που παρέχονται: Εικόνα 31. Ενεργοποίηση υποστήριξης GPIO από τον Linux Kernel Το Linux παρέχει ένα επίπεδο λογισµικού το οποίο διαχειρίζεται την παραµετροποίηση και τις διεργασίες εισόδου – εξόδου των σηµάτων GPIO. Έτσι εξασφαλίζεται η ασφάλεια των ενεργειών που πραγµατοποιούνται, η µεταφερσιµότητα του κώδικα σε διαφορετικά συστήµατα, καθώς και ο ευκολότερος χειρισµός του υλικού που βρίσκεται από κάτω. Η πρόσβαση σε ένα σήµα GPIO είναι εφικτή από δύο σηµεία του Linux: Περιβάλλον χρήστη (user space) Περιβάλλον πυρήνα (Kernel space) Τα σήµατα GPIO θα πρέπει να καθορίζονται στον κώδικα που ρυθµίζει την πλακέτα µας (board setup code) ο οποίος βρίσκεται σε επίπεδο Kernel space. Αυτό είναι αναγκαίο γιατί από αυτό το σηµείο µπορεί να υπάρχει πλήρης έλεγχος για τις ρυθµίσεις όλων των ακροδεκτών του µικροελεγκτή µας. Για µια εφαρµογή επιπέδου user αλλά και για ένα module, δεν είναι δυνατή η τροποποίηση δύο εκ των τεσσάρων λειτουργιών που αναφέραµε αρχικά ότι επιτελούνται από τα σήµατα GPIO: Επικοινωνία: [email protected] 108 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 Ο έλεγχος pull-up για γραµµές εισόδου – εξόδου Η ρύθµιση του φίλτρου Όλες οι συναρτήσεις και οι λεπτοµέρειες που θα πρέπει να ρυθµιστούν για να ενεργοποιήσουµε τα σήµατα GPIO στην πλακέτα µας, βρίσκονται στο αρχείο κεφαλής portmux.h που υπάρχει στον κατάλογο του kernel include/asm/arch. Η συνάρτηση at32_select_gpio(unsigned int pin, unsigned long flags), είναι ότι χρειαζόµαστε για την παραµετροποίηση των GPIO. Ο ακέραιος αριθµός pin µπορεί βρεθεί χρησιµοποιώντας ειδικές µακροεντολές που υπάρχουν στο αρχείο at32ap7000.h. Για παράδειγµα, προκειµένου να επιλέξουµε την γραµµή εισόδου – εξόδου 0, του ελεγκτή (PIO controller) Β, χρησιµοποιούµε την µακροεντολή GPIO_PIN_PB(0). Τα διαθέσιµα flags (σηµαίες) που υπάρχουν για τη συνάρτηση at32_select_gpio και τα τα είδαµε στο µεγαλύτερο µέρος τους όταν ασχοληθήκαµε µε τον δίαυλο I2C-GPIO, στον πίνακα 2 (σελίδα 106). Υπάρχουν παραδείγµατα παραµετροποίησης των pins στον κώδικα διαµόρφωσης (setup code) που παρέχει η Atmel για τις πλακέτες της στον κατάλογο arch/avr32/boards του Linux Kernel. Σε αυτό τον κώδικα υπάρχουν δύο επιπλέον συναρτήσεις για την ρύθµιση GPIO pins: at32_select_periph(unsigned int pin, unsigned int periph, unsigned long flags) at32_reserve_pin(unsigned int pin) Η πρώτη µπορεί να ρυθµίσει κάποιο pin να είναι συνδεδεµένο µε ένα module που βρίσκεται ενσωµατωµένο (on-chip) στον µικροελεγκτή, όπως είναι για παράδειγµα το SPI και το I2C. Με λίγα λόγια τα σήµατα GPIO µπορούν να υποστηρίξουν επικοινωνία SPI και I2C. Αυτό βέβαια εξαρτάται από τον µικροελεγκτή. Εποµένως θα πρέπει να συµβουλευόµαστε κάθε φορά και το αντίστοιχο datasheet. Η δεύτερη συνάρτηση δεσµεύει ένα pin για µετέπειτα χρήση και αυτό κάνει το συγκεκριµένο pin µη τροποποιήσιµο. Και στις δύο συναρτήσεις, τα pins που δεσµεύονται δεν µπορούν να τροποποιηθούν από άλλους παράγοντες του λογισµικού. Αυτό καθιστά αδύνατη την κατά λάθος τροποποίηση ενός σήµατος GPIO και προσφέρει αξιοπιστία. Μέχρι αυτή τη στιγµή µελετήσαµε τον έλεγχο των σηµάτων GPIO από το Kernel space. Στη συνέχεια θα δούµε πως µπορούµε να χειριστούµε σήµατα GPIO µέσω εφαρµογών από το user space. Για επιτύχουµε κάτι τέτοιο µπορούµε να χρησιµοποιήσουµε τη διεπαφή /dev. Χρήση της διεπαφής /dev Η διεπαφή /dev κάνει χρήση του configfs προκειµένου να µπορεί να µπορεί να ρυθµιστεί ένα σήµα GPIO από το user space. Το configfs είναι ένα σύστηµα αρχείων το οποίο διαχειρίζεται αντικείµενα (objects) του Kernel. Αυτό το σύστηµα αρχείων υπάρχει µόνο στη RAΜ και είναι άδειο µέχρι να φορτωθούν modules που πιθανόν χρειάζονται κατά τη λειτουργία του συστήµατος. Ένας GPIO driver θα µπορούσε να είναι ένα τέτοιο module. Καταχωρεί τον εαυτό του στο configfs και στη συνέχεια είναι ορατό σαν ένας υποκατάλογος στον κατάλογο /configfs/gpio. Το αρχείο για το κάθε GPIO δηµιουργείται από το πρόγραµµα mdev. Εάν το πρόγραµµα αυτό δεν είναι διαθέσιµο θα δηµιουργηθεί από το πρόγραµµα mknod. Έτσι, µπορούµε εύκολα από το user space να δηµιουργούµε ή να καταργούµε σήµατα GPIO µε απλές εντολές UNIX, όπως είναι για παράδειγµα η mkdir και η rmdir. Βέβαια η κατάρ- Ιστότοπος εργασίας: MyThesis.org 109 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) γηση µπορεί να πραγµατοποιηθεί µόνο αν δεν υπάρχει κάποιο symlink προς το GPIO που θέλουµε να καταργήσουµε διαφορετικά κάτι τέτοιο θα αποτραπεί άµεσα από τον Kernel. Αφού δηµιουργήσουµε ένα σήµα GPIO µε την εντολή mkdir, αυτόµατα µέσα στον κατάλογό του θα δηµιουργηθούν επίσης και ορισµένα αρχεία. Τα αρχεία αυτά θα αποτελούν τις ιδιότητές (attributes) του σήµατος και θα µπορούν να τροποποιηθούν από εφαρµογές ή από εντολές shell, µε απλές αναγνώσεις και εγγραφές σαν να είναι απλά αρχεία κειµένου. Το σύστηµα αρχείων configfs και η δυνατότητα GPIO στο /dev θα πρέπει είτε να έχουν µεταγλωττιστεί µαζί µε τη µορφή ενός module, είτε να έχουν µεταγλωττιστεί µέσα στον Kernel, έτσι ώστε να είναι δυνατή η δηµιουργία σηµάτων GPIO από το user space. Επίσης θα πρέπει η δυνατότητα αυτή να έχει φορτωθεί κατά την εκκίνηση του συστήµατος από τα startup scripts. Εάν δεν έχει φορτωθεί θα πρέπει να το κάνουµε χειροκίνητα εκτελώντας σε γραµµή εντολών τις παρακάτω εντολές: mkdir /config mount –t configfs config /config Αν ο κατάλληλος driver GPIO για το /dev είναι διαθέσιµος στον Kernel, τότε στον κατάλογο /config θα βρούµε έναν υποκατάλογο gpio. Σε αυτό το σηµείο µπορούµε να δηµιουργήσουµε ένα νέο σήµα GPIO. Για παράδειγµα µπορούµε να χειριζόµαστε ένα LED στην πλακέτα µας για την προειδοποίηση του χρήστη όταν συµβαίνει κάποιο γεγονός. Ένα τέτοιο γεγονός σε ένα Router θα µπορούσε να είναι η ύπαρξη ή όχι, σύνδεσης µε κάποιο τοπικό δίκτυο (Local Network Status LED) ή η ένδειξη της κατάστασης της ασύρµατης δικτύωσης (WiFi Status LED). Με την παρακάτω εντολή δηµιουργούµε έναν κατάλογο στον οποίο θα υπάρχουν LEDs τα οποία θα ελέγχονται µέσω σηµάτων GPIO: mkdir /config/gpio/leds Τώρα όλες οι ιδιότητες του αντικειµένου GPIO που δηµιουργήσαµε θα βρίσκονται όπως είδαµε και νωρίτερα, µέσα στον κατάλογο leds µε τη µορφή αρχείων. Τα αρχεία αυτά δέονται ως είσοδο ακολουθίες χαρακτήρων (strings) ASCII και κατά την ανάγνωσή τους διαβάζονται ακολουθίες χαρακτήρων της ίδιας κωδικοποίησης. Οι αριθµοί µπορούν να γράφονται είτε σε δεκαδική (πχ: 255), είτε σε δεκαεξαδική µορφή (πχ: 0xFF). Τα αρχεία ρυθµίσεων που προκύπτουν είναι τα εξής: gpio_id – Επιλέγει τον παράλληλο ελεγκτή εισόδου – εξόδου (PIO – Parallel I/O controller). Γράφοντας µηδέν (0) στο αρχείο, επιλέγεται ο PIOA, γράφοντας 1, ο PIOB κλπ. pin_mask – Επιλέγει τα pins του PIO controller που αναφέρεται στο αρχείο gpio_id. Κάθε PIO controller έχει 32 προγραµµατιζόµενες γραµµές. Εποµένως τα pins του µικροελεγκτή που θέλουµε να συµπεριφέρονται σαν σήµατα GPIO, συµβολίζονται µε έναν δεκαεξαδικό αριθµό µήκους 32 bit. Για παράδειγµα αν θέλουµε η γραµµή 1 να λειτουργεί σαν GPIO θα πρέπει να γράψουµε την τιµή 0x00000001 (ή 0x1 ή απλά 1) στο αρχείο pin_mask. Πριν από αυτή την ενέργεια όµως θα πρέπει πρώτα να έχουµε ελέγξει µέσω κατάλληλης συνάρτησης αν η γραµµή 1 είναι ελεύθερη για χρήση. oe_mask – Καθορίζει το αν ένα σήµα GPIO θα λειτουργεί σαν είσοδος ή σαν έξοδος. Αφού έχουµε καταχωρήσει επιτυχώς µια γραµµή µέσω του αρχείου pin_mask, µπορούµε να κανονίσουµε την λειτουργία της µέσω του αρχείου oe_mask. Η ρύθµιση Επικοινωνία: [email protected] 110 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 που υπάρχει εξ αρχής (default) για ένα GPIO pin (γραµµή) είναι αυτή της εισόδου. Αν θέλουµε να λειτουργεί ως έξοδος, αποθηκεύουµε την τιµή 0x00000001 στο αρχείο oe_mask. enabled – Ενεργοποιεί το αντικείµενο gpio που είδαµε νωρίτερα όταν µιλήσαµε για τη διεπαφή /dev. Γράφοντας την τιµή 1 στο αρχείο enabled ενεργοποιούνται οι ρυθµίσεις παραµετροποίησης gpio_id, pin_mask και oe_mask. Αυτό θα έχει ως αποτέλεσµα τη δηµιουργία του αρχείου /dev/gpio0, δηλαδή της συσκευής gpio0. Βέβαια εάν πιο πριν έχουν δηµιουργηθεί και άλλες συσκευές GPIO το όνοµα µπορεί να διαφέρει (πχ: gpio3). Όταν επιλέγουµε να έχουµε πρόσβαση µέσω του userspace σε ένα σήµα GPIO, υπάρχουν τρεις τρόποι να το κάνουµε. Μέση εντολών shell Με ένα shell script Μέσω µιας εφαρµογής Ας δούµε ένα παράδειγµα πρόσβασης µέσω εντολών shell. Υποθέτοντας ότι θέλουµε τα pins του PIOB, 0-3 να λειτουργούν σαν έξοδοι και τα pins 4-7 ως είσοδοι. Θα πρέπει να εκτελέσουµε διαδοχικά τις παρακάτω εντολές: echo echo echo echo 1 255 15 1 > > > > /config/gpio/leds/gpio_id /config/gpio/leds/pin_mask /config/gpio/leds/oe_mask /config/gpio/leds/enabled # # # # ή 0x1 (χρήση του PIOB) ή 0xff (pins 0-7) ή 0x0f (έξοδοι:0-3,είσοδοι:4-7) δηµιουργία της συσκευής Αν οι παραπάνω εντολές αποθηκευτούν σε ένα αρχείο dimiourgia_syskevis_gpio.sh επιτυγχάνουµε τον δεύτερο τρόπο πρόσβασης, που είναι η πρόσβαση µέσω shell script. H εκτέλεσή του είναι πολύ απλή: chmod 755 dimiourgia_syskevis_gpio.sh ./ dimiourgia_syskevis_gpio.sh Πρέπει να πούµε ότι υπάρχει και η δυνατότητα να αντιστοιχίζουµε µια ολόκληρη συσκευή (gpio object) σε ένα και µόνο pin εφόσον απαιτείται µεγαλύτερη ανεξαρτησία. Αν θέλουµε να χειριστούµε σήµατα GPIO µέσω εφαρµογής και όχι µέσω των δύο άλλων τρόπων που περιγράψαµε, θα πρέπει να λάβουµε υπ’ όψιν τα εξής: Το σύστηµα αρχείων configfs απαιτεί µια κλήση εγγραφής (write call) προκειµένου να αποθηκεύσει ολόκληρο το αρχείο ιδιοτήτων (attribute file). Για το λόγο αυτό θα πρέπει πρώτα να διαβάζει ολόκληρο το αρχείο, να το τροποποιεί και στη συνέχεια να το αποθηκεύει. Το µέγιστο επιτρεπόµενο µέγεθος ενός τέτοιου αρχείου δεν θα πρέπει να υπερβαίνει το µέγεθος µιας σελίδας της µνήµης. Για να γνωρίζουµε κάθε φορά πιο είναι αυτό το µέγεθος µπορούµε να χρησιµοποιούµε τη συνάρτηση getpagesize() ή κάποια παρόµοια. Στην περίπτωση του Router NGW100, το µέγεθος της σελίδας ορίζεται από την αρχιτεκτονική AVR32 και είναι 4096 bytes (512 KB). Είσοδος και έξοδος σηµάτων GPIO Αφού έχουµε δηµιουργήσει τελικά το αντικείµενο gpio και έχουµε ολοκληρώσει την παραµετροποίησή του µπορούµε πλέον να το χρησιµοποιούµε για την είσοδο και έξοδο σηµάτων. Σε αυτή την περίπτωση δεν µπορούµε πλέον να στέλνουµε χαρακτήρες ASCII αλλά µόνο δυαδικά (binary) ψηφία. Φυσικά αυτό το αναλαµβάνει ο φλοιός. Έτσι χρησιµοποιώντας την Ιστότοπος εργασίας: MyThesis.org 111 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) εντολή echo σε κατάλληλη µορφή µπορούµε να εκτελέσουµε και πάλι τις εντολές που είδαµε νωρίτερα, µε τον εξής τρόπο: echo –ne "\x00\x00\x00\x0F" > /dev/gpio0 Ουσιαστικά µε την εντολή αυτή γράψαµε 4 bytes. Αν θέλουµε να διαβάσουµε τα 4 bytes χρησιµοποιούµε την εξής εντολή: dd bs=4 count=1 if=/dev/gpio0 2> /dev/zero | hexdump Το πρόγραµµα dd διαβάζει τα τέσσερα bytes (bs = αριθµός bytes, count = πόσες φορές να διαβαστούν τα bs bytes) από τη συσκευή gpio0, αποθηκεύει τυχόν σφάλµατα στο /dev/zero και περνά τα δεδοµένα στο πρόγραµµα hexdump. Το hexdump θα τυπώσει τα 4 bytes µε τη µορφή δεκαεξαδικής τιµής. Όσα είδαµε αφορούν το περιβάλλον του shell. Στη συνέχεια θα δούµε πως όλα αυτά επιτυγχάνονται και µέσω εφαρµογής. Συνήθως αυτή είναι και η συνηθέστερη περίπτωση ανάγνωσης και εγγραφής των GPIO. Σε µια εφαρµογή µπορούµε να χρησιµοποιήσουµε διάφορες µεθόδους εισόδου – εξόδου σε ένα αρχείο GPIO: Blocking – Γίνεται ανάγνωση του αρχείου της συσκευής GPIO ανεξάρτητα από το αν έχει τροποποιηθεί. Η µέθοδος αυτή είναι και η βασική µέθοδος ανοίγµατος αρχείων στο Linux. Non-Blocking – Γίνεται ανάγνωση του αρχείου µόνο εάν αυτό έχει αλλάξει. Η µέθοδος αυτή µπορεί να χρησιµοποιηθεί σε συνδυασµό µε τη διεπαφή Poll του Linux έτσι ώστε να υπάρχει η δυνατότητα όταν δεν χρησιµοποιείται, να πέφτει σε κατάσταση sleep. Asynchronous – Σε αυτή την περίπτωση η µέθοδος εισόδου – εξόδου βασίζεται στο σύστηµα σηµατοδοσίας (signaling) του Linux και στο GPIO framework που υπάρχει. Έτσι η εφαρµογή µας εναποθέτει στο λειτουργικό σύστηµα την παρακολούθηση των αλλαγών κατάστασης του GPIO που ελέγχουµε. Κάθε φορά που το Linux αντιλαµβάνεται µια αλλαγή, ενηµερώνει άµεσα την εφαρµογή µας. Έλεγχος GPIO µέσω module Η χρήση και ο έλεγχος των σηµάτων GPIO σε έναν driver είναι η πιο άµεση περίπτωση. Αν δηµιουργήσουµε µε τη µορφή module έναν driver χειρισµού GPIO τότε όπως γνωρίζουµε θα µπορούµε να τον φορτώνουµε κατά τη διάρκεια λειτουργίας του συστήµατος µας µε την εντολή insmod. Το Linux παρέχει µια εξειδικευµένη διεπαφή για ανάπτυξη τέτοιων drivers η οποία περιγράφεται στο αρχείο κεφαλής include/asm/arch/gpio.h. ∆εν θα πρέπει όµως να το εισάγουµε (include) όµως άµεσα στον κώδικά µας αλλά µέσω του αρχείου κεφαλής include/asm/gpio.h. Στο αρχείο Documentation/gpio.txt που υπάρχει στους καταλόγους του πηγαίου κώδικα του Linux, περιλαµβάνεται η απαραίτητη τεκµηρίωση. Linux LED Framework Το Linux παρέχει ειδικό κώδικα για την οδήγηση των LED. Το framework αυτό µπορεί όµως να χρησιµοποιηθεί και για τον έλεγχο άλλων περιπτώσεων µέσω GPIO. Για να είµαστε σε θέση να χρησιµοποιήσουµε το LED framework του Linux θα πρέπει να κάνουµε ορισµένες αλλαγές σε δύο σηµεία. Η µία αλλαγή θα πρέπει να γίνει στον εξειδικευµένο κώδικα που έχει γραφτεί για τον χειρισµό της πλακέτας µας (board specific code) και η δεύτερη στον Linux Επικοινωνία: [email protected] 112 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 Kernel. Στην εικόνα βλέπουµε τις επιλογές που υπάρχουν διαθέσιµες µέσω του γραφικού περιβάλλοντος παραµετροποίησης του Kernel (Device Drivers Led Devices): Εικόνα 32. Ενεργοποίηση του Linux LED Framework Όταν ένα LED συνδέεται µε ένα pin του µικροελεγκτή και θέλουµε να το ελέγχουµε µέσω του Linux LED Framework, τότε αυτό θα πρέπει να το δηλώσουµε στον εξειδικευµένο κώδικα που αφορά την πλακέτα µας. Η διεπαφή που θα πρέπει να χρησιµοποιηθεί περιγράφεται στο αρχείο κεφαλής include/linux/leds.h. Γενικά ένα LED έχει κάποια χαρακτηριστικά και αυτό σε επίπεδο υλικολογισµικού περιγράφεται στην παρακάτω δοµή δεδοµένων: struct gpio_led { const char *name; /* char *default_trigger; /* unsigned gpio; /* u8 active_low; /* }; /* Άνω του ενός LED απαιτείται η Led name */ Trigger name if used */ Number of the GPIO line */ Set to 1 if active low */ δηµιουργία πίνακα δοµών gpio_led */ Το επόµενο βήµα είναι να δηλώσουµε την δοµή gpio_led_platform_data η οποία θα έχει µέλη της τον αριθµό των LED και τη διεύθυνση µνήµης τους: struct gpio_led_platform_data { int num_leds; /* Number of LEDs in our array*/ struct gpio_led *leds; /* Start address of LED array */ }; Η τελευταία δοµή που χρειαζόµαστε είναι η platform_device ή οποία καταχωρεί όλα τα LED σαν µια συσκευή platform. Ο ορισµός αυτής της δοµής δεδοµένων υπάρχει στο αρχείο κεφαλής include/linux/platform_device.h: Ιστότοπος εργασίας: MyThesis.org 113 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) struct platform_device { const char *name; u32 id; struct device dev; u32 num_resources; struct resource *resource; }; Τα πεδία της platform_device τα οποία θα πρέπει να αρχικοποιηθούν από εµάς είναι το name, id και platform_data που ορίζεται στη δοµή device. Η περιγραφή αυτής της δοµής υπάρχει και στο αρχείο κεφαλής include/linux/device.h. Το όνοµα που θα επιλέξουµε θα εµφανίζεται στη συνέχεια στο σύστηµα αρχείων /sys σαν ένας απλός υποκατάλογος µέσα στον κατάλογο class/leds/ και θα πρέπει να είναι το gpio-leds. Αν θέλουµε να δηµιουργήσουµε µόνο ένα στιγµιότυπο της συσκευής (platform_device), δίνουµε την τιµή 0 στο id. Αν χρειαζόµαστε περισσότερα, το καθένα θα πρέπει να έχει την δική του µοναδική τιµή id. Το µέλος platform_data αρχικοποιείται µε τη διεύθυνση της δοµής gpio_led_platfrom_data. Πριν καταχωρήσουµε την συσκευή µας στο αντίστοιχο σύστηµα του Linux καλώντας τη συνάρτηση platform_device_register, πρέπει να σωστά τις γραµµές εισόδου – εξόδου. Στον σύστηµα της εργασίας µας, αυτό γίνεται στο αρχείο setup.c το οποίο ανήκει στον εξειδικευµένο κώδικα (board specific code) του Router NGW100 και βρίσκεται στον κατάλογο arch/avr32/boards/atngw100 του πηγαίου κώδικα του Linux. Χρήση του LED Framework στο user-space Μέχρι τώρα είδαµε πως µπορούµε µέσω του LED Framework να δηµιουργήσουµε τις συσκευές µας και ποιον εξειδικευµένο κώδικα θα πρέπει να παρέχουµε στο Linux έτσι ώστε να µπορεί να κατανοεί την πλακέτα µας. Στη συνέχεια θα εξετάσουµε τον τρόπο επικοινωνίας µε αυτές. Γενικά υπάρχουν κι εδώ δύο τρόποι να επικοινωνήσουµε. Είτε µέσω κάποιου driver από τον Kernel, είτε µέσω κάποιας εφαρµογής από το user-space. Στον Router NGW100 υπάρχουν τρία LED: a, b και sys (system). Βρίσκονται σε οµώνυµους υποκαταλόγους του κατάλογου /sys/class/leds. Τα a και b είναι βοηθητικά ενώ το sys είναι δεσµευµένο για να υποδεικνύει το πότε έχει ολοκληρωθεί µε επιτυχία η διαδικασία εκκίνησης. Μέσα σε καθέναν από τους υποκαταλόγους a, b και sys που προαναφέραµε, υπάρχουν δύο αρχεία τα οποία µπορούν επίσης να χρησιµοποιηθούν για τον έλεγχο των LED: brightness – Μπορούµε να αποθηκεύουµε µη µηδενικές τιµές στο εύρος 0 – 255 για να ρυθµίζουµε τη φωτεινότητα. Επειδή όµως τα περισσότερα LED δεν παρέχουν αυτό το χαρακτηριστικό, ουσιαστικά δίνοντας την τιµή 0 σβήνουµε το LED και µε οποιαδήποτε άλλη τιµή στο εύρος 1 – 255 ενεργοποιούµε το LED. trigger – Πρόκειται για µια λειτουργία η οποία ενεργοποιεί και απενεργοποιεί ένα LED µε συγκεκριµένο ρυθµό. Αυτό βοηθά στο να αναπαριστούµε µε εύκολο τρόπο στον χρήστη τη συσκευής µας το τι συµβαίνει σε µια δεδοµένη στιγµή σε κάποιο υποσύστηµα που έχει νόηµα γι’ αυτόν. Για παράδειγµα αν θέλουµε να δείξουµε ότι ο Router NGW100 έχει συνδεθεί επιτυχώς στο τοπικό δίκτυο θα µπορούσαµε να αναβοσβήνουµε ένα από τα βοηθητικά του LED µε ρυθµό καρδιακού παλµού: echo heartbeat > /sys/class/leds/a/trigger Επικοινωνία: [email protected] 114 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 Σε αυτό το σηµείο µπορούµε να πούµε ότι έχουµε αναφέρει τα σηµαντικότερα στοιχεία που αφορούν τα σήµατα GPIO. Η ανάλυση έπρεπε να είναι εκτενής µιας και τα σήµατα GPIO είναι πολύ σηµαντικά και ευέλικτα στο Linux σχεδόν για οποιαδήποτε συσκευή. Το Linux LED Framework αναλύεται περισσότερο και στον υποκατάλογο leds που υπάρχει στον κατάλογο documentation του Kernel. 3.6 Modem Τα modem στο Linux αντιµετωπίζονται σαν σειριακές συσκευές (οπότε ότι είπαµε πριν για τη σειριακή επικοινωνία ισχύει κι εδώ), κάτι που συµβαίνει και σε άλλα λειτουργικά συστήµατα. Κάθε σειριακή συσκευή έχει τη δική της καταχώρηση στον κατάλογο /dev και ελέγχεται από τον ίδιο driver που υπάρχει και για το κύκλωµα UART. Πολλές νεότερες συσκευές modem αποτελούνται από απλούστερα και πιο φθηνά κυκλώµατα. Τα modem αυτά είναι γνωστά ως Linmodems και περιέχουν µόνο το ελάχιστο υλικό που απαιτείται από µια συσκευή modem. Μπορούν να παρέχουν τις υπηρεσίες τους καθαρά λόγω της ύπαρξης του λειτουργικού συστήµατος, δηλαδή µέσω λογισµικού και όχι µέσω του υλικού που απαιτείται για να λειτουργήσει αυτόνοµα ένα modem. Για να παρέχεται υποστήριξη και γι’ αυτές τις συσκευές, η κοινότητα του Linux έχει αρχίσει να δηµιουργεί διάφορα projects ανάπτυξης των αναγκαίων πακέτων λογισµικού. Η κεντρική αρχή που ελέγχει και διαχειρίζεται αυτά τα project µπορεί να βρεθεί στον ιστότοπο linmodems.org. Επίσης, µια καλή θεωρητική ανάλυση για τα modems στο Linux, καθώς και για την παραµετροποίησή τους, γίνεται στον ιστότοπο του LDP (tldp.org). 3.7 Επικοινωνία µε τη µνήµη Σε ένα ενσωµατωµένο σύστηµα Linux αλλά και σε οποιοδήποτε άλλο υπολογιστικό σύστηµα το οποίο επεξεργάζεται δεδοµένα, υπάρχουν δύο βασικά τµήµατα που παίζουν τον σηµαντικότερο ρόλο από το οτιδήποτε άλλο. Ο µικροελεγκτής και η µνήµη. Σε αυτή την παράγραφο θα µας απασχολήσει η µνήµη. Αρχικά είναι χρήσιµο να εξηγήσουµε τι είναι ένα block. Πρόκειται για µια περιοχή µνήµης µε προκαθορισµένο (fixed) και σταθερό µέγεθος που διευθετείται από τον Kernel. Συνήθως το µέγεθός του είναι 4096 bytes αλλά η τιµή αυτή εξαρτάται κυρίως από την αρχιτεκτονική και το σύστηµα αρχείων που χρησιµοποιείται κάθε φορά. Σε προηγούµενη παράγραφο, όταν ασχοληθήκαµε µε τους drivers, τους κατηγοριοποιήσαµε σε τρία είδη. Ένα από αυτά ήταν και οι block drivers. Ένας block driver δηµιουργείται για να παρέχει πρόσβαση σε συσκευές που µεταφέρουν δεδοµένα τα οποία µπορεί να βρίσκονται αποθηκευµένα σε τυχαίες θέσεις. Αυτό συµβαίνει συχνά, µιας και όταν ένα αρχείο δεν µπορεί να χωρέσει σε µια συνεχή ελεύθερη περιοχή της µνήµης, το λειτουργικό σύστηµα το διασπά σε µικρότερα τµήµατα τα οποία στη συνέχεια µπορούν να αποθηκεύονται και σε µη γειτονικές περιοχές. Οι block drivers είναι το λογισµικό µέσο επικοινωνίας µεταξύ της κύριας και της δευτερεύουσας µνήµης. Για το λόγο αυτό µπορούν να θεωρηθούν και µέρος του υποσυστήµατος της εικονική µνήµης. Στον Router NGW100, όπως θα δούµε και στο πρακτικό µέρος της εργασίας, υπάρχουν διάφορα είδη µνήµης: Ιστότοπος εργασίας: MyThesis.org 115 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) SDRAM Parallel flash Serial DataFlash SD/MMC Η µνήµη που αφορά το user space στο οποίο υπάρχουν όλες οι εφαρµογές και τα δεδοµένα του Router NGW100, είναι η DataFlash. Σε αυτή την παράγραφο θα εξετάσουµε τη διαδικασία της ρύθµισης που πρέπει να γίνει στον Linux Kernel, την εγκατάσταση ενός καναλιού επικοινωνίας SPI, την φόρτωση τα απαραίτητων modules, τη δηµιουργία συστήµατος αρχείων και την φόρτωση της DataFlash στο σύστηµα αρχείων του Linux. Όπως γνωρίζουµε, το SPI υποστηρίζει σειριακή επικοινωνία µε εξωτερικές συσκευές (δηλαδή άλλα ολοκληρωµένα που συνδέονται στον µικροελεγκτή) και µνήµες, όπως είναι στην περίπτωσή µας και η DataFlash. Το SPI αποτελείται από δύο γραµµές δεδοµένων και δύο γραµµές ελέγχου: MOSI (Master Out Slave In) MISO (Master In Slave Out) SPCK (Serial Clock) NSS (Slave Select) Η DataFlash µπορεί να συνδεθεί στο SPI του µικροελεγκτή όπως φαίνεται στην εικόνα που ακολουθεί: Εικόνα 33. Σύνδεση DataFlash µε µικροελεγκτή µέσω SPI 3.7.1 ∆ηµιουργία του συστήµατος αρχείων root Η δηµιουργία του root filesystem απαιτεί να υπάρχει εγκατεστηµένο στο σύστηµα host το πρόγραµµα mtd-tools και το δυαδικό αρχείο mkfs.jffs2. Όπως γνωρίζουµε όµως, αυτό το αναλαµβάνει το αυτοµατοποιηµένο σύστηµα Buildroot. Παράµετροι µνήµης flash Όλες οι συσκευές flash έχουν καθορισµένο µέγεθος διαγραφής (delete size) και καθορισµένο µέγεθος σελίδας (page size). Οι τιµές των συγκεκριµένων µεγεθών θα πρέπει να είναι γνωστές καθώς το σύστηµα αρχείων θα δηµιουργηθεί σε blocks που θα εξαρτώνται από αυτές. Επικοινωνία: [email protected] 116 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 Πριν µπορέσει ένα block της µνήµης flash να προγραµµατιστεί, θα πρέπει πρώτα να σβηστεί. Το µέγεθος της διαγραφής δίνεται από το µέγεθος του block διαγραφής (erase block) το οποίο αντιπροσωπεύει τη µικρότερη δυνατή µονάδα διαγραφής. Η µικρότερη µονάδα εγγραφής είναι η σελίδα (page) και καθορίζεται από το µέγεθος σελίδας που αναφέραµε και πιο πάνω. Το σύστηµα αρχείων JFFS2 Σε αυτό το σηµείο µας δίνεται η δυνατότητα να πούµε λίγα πράγµατα για το σύστηµα αρχείων JFFS2 (Journaled Flash File System version2). Το JFFS2 έχει δηµιουργηθεί ειδικά για µνήµες flash σαν αυτές που χρησιµοποιούνται συνήθως στα ενσωµατωµένα συστήµατα. Το πλεονέκτηµά του είναι ότι χρησιµοποιείται ταυτόχρονα και σαν RAM. Οι κυριότεροι λόγοι για τους οποίους επιλέγουµε το JFFS για τα ενσωµατωµένα συστήµατα είναι: Είναι βελτιστοποιηµένο για µνήµες flash Μπορεί να φορτωθεί µέσω του U-Boot bootloader Επιτρέπει συµπίεση ∆εν χρειάζεται να φορτωθεί πρώτα σε κάποια RAM για να χρησιµοποιηθεί Φυσικά το JFFS2 έχει και περιορισµούς. Ο κυριότερος και κοινός για όλα τα παρεµφερή συστήµατα αρχείων, αφορά τη φύση των µνηµών flash. Όπως γνωρίζουµε ήδη, οι µνήµες αυτές είναι οργανωµένες σε blocks. Κάθε block έχει περιορισµένο αριθµό κύκλων εγγραφής / σβησίµατος. Εποµένως θα πρέπει σε περιπτώσεις που απαιτούνται τέτοιου είδους ενέργειες πολύ συχνά, να χρησιµοποιούµε και µια βοηθητική µνήµη. Το εργαλείο mkfs.jffs2 Το εργαλείο mkfs.jffs2 περιλαµβάνεται στο πακέτο λογισµικού mtd-utils. Χρησιµοποιείται για την δηµιουργία της εικόνας του κεντρικού συστήµατος αρχείων (rootfs.img), αλλά και για την εγγραφή του στην παράλληλη µνήµη (parallel flash) του Router NGW100. Για να λειτουργήσει σωστά θα πρέπει να του παρέχουµε τις εξής πληροφορίες: Όνοµα και διαδροµή καταλόγου για να αποθηκεύσει την εικόνα rootfs.img. ∆ιαδροµή προς τον κατάλογο root (/). Τύπος endian (little ή big). Η αρχιτεκτονική AVR32 χρησιµοποιεί big endian. Μέγεθος της σελίδας (pagesize) της µνήµης flash Μέγεθος block διαγραφής (eraseblock) ∆ιαµόρφωση και εγκατάσταση Ο Linux Kernel διαθέτει πολλούς drivers οι οποίοι µπορούν να φορτωθούν κατά την εκκίνηση ή να µεταγλωττιστούν σαν modules και να φορτωθούν κατά τη διάρκεια λειτουργίας του συστήµατος. Το τι απ’ όλα αυτά θα επιλέξουµε, εξαρτάται µόνο από τις ανάγκες µας. Για να ενεργοποιήσουµε στον Kernel την υποστήριξη για συσκευές MTD θα πρέπει να εισέλθουµε στο γραφικό περιβάλλον παραµετροποίησής του και από την ενότητα Device Drivers. Αφού ρυθµίσουµε την ενότητα SPI Support (έχουµε ήδη αναφερθεί σε αυτό όταν αναλύσαµε το SPI), στη συνέχεια περνάµε στην ρύθµιση της Memory Technology Devices (MTD): Ιστότοπος εργασίας: MyThesis.org 117 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 34. Ενεργοποίηση υποστήριξης συσκευών MTD από τον Linux Kernel Αµέσως µετά επιλέγουµε και ρυθµίζουµε την ενότητα RAM/ROM/Flash chip drivers: Επικοινωνία: [email protected] 118 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 Εικόνα 35. Ενεργοποίηση CFI driver για την ανάγνωση µνηµών Flash Και αφού επιστρέψουµε στο µενού Device Drivers, επιλέγουµε την Self-contained MTD device drivers: Εικόνα 36. Ενεργοποίηση υποστήριξης µνηµών AT45xxx της Atmel από τον Linux Kernel Ιστότοπος εργασίας: MyThesis.org 119 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Τέλος, από το αρχικό παράθυρο διαλόγου παραµετροποίησης του Linux Kernel, επιλέγουµε και ρυθµίζουµε την ενότητα File Systems Miscellaneous File Systems Journaling Flash File System v2 (JFFS2) Support, για να ενεργοποιήσουµε και να ρυθµίσουµε την υποστήριξη του συστήµατος αρχείων JFFS2: Εικόνα 37. Ενεργοποίηση υποστήριξης συστήµατος αρχείων JFFS2 από τον Linux Kernel SPI modules Για να επικοινωνήσουµε µε τη DataFlash του Router NGW100 χρειάζεται αρχικά να εγκατασταθεί ο SPI controller, να επιλεγεί η συσκευή slave (δηλαδή η DataFlash), να ρυθµιστεί η συχνότητα λειτουργίας του ρολογιού και να επιλεγεί ο driver στον οποίο θα στέλνονται τα δεδοµένα. Όπως ήδη έχουµε αναφέρει νωρίτερα κατά την ανάλυση του SPI, ο κώδικας ο οποίος είναι υπεύθυνος και πρέπει να διαµορφωθεί κατάλληλά βρίσκεται στο αρχείο setup.c στον κατάλογο arch/avr32/boards/avr32/atngw100. Για να ρυθµίσουµε τα χαρακτηριστικά του SPI αρκεί να τροποποιήσουµε τις µεταβλητές της παρακάτω δοµής δεδοµένων: Επικοινωνία: [email protected] 120 Το υλικό που υποστηρίζει το ενσωµατωµένο Linux – Κεφάλαιο 3 static struct spi_board_info spi0_board_info[] __initdata = { { .modalias = "mtd_dataflash", .max_speed_hz = 8000000, .chip_select = 0, }, }; Μεταγλώττιση και πρόσβαση Αφού έχουµε ολοκληρώσει όλες τις απαραίτητες τροποποιήσεις και προσθήκες στο σύστηµά µας θα πρέπει να µεταγλωττίσουµε τον Kernel έτσι ώστε να περιλαµβάνει τα νέα χαρακτηριστικά. Για να το κάνουµε αυτό, µέσα από τον κατάλογο του Buildroot εκτελούµε την παρακάτω εντολή: make ARCH=avr32-linux CROSS_COMPILE=avr32-linux- Αν ολοκληρωθεί µε επιτυχία, στον κατάλογο arch/avr32/boot/images θα έχουµε το νέο δυαδικό αρχείο uImage, το οποίο θα παρέχει υποστήριξη για την µνήµη DataFlash του Router NGW100. Προκειµένου να έχουµε πρόσβαση στη DataFlash και να επικοινωνούµε µε αυτή είναι απαραίτητο να δηµιουργηθούν ορισµένα αρχεία (ή αλλιώς, κόµβοι – nodes) που απαιτεί η τεχνολογία MTD και να υπάρχουν στον κατάλογο που θα αντιπροσωπεύει τη συσκευή. Αυτό γίνεται από το πρόγραµµα mdev κατά την εκκίνηση. Τέλος, για να είµαστε σε θέση να δηµιουργούµε, να διαβάζουµε και να διαγράφουµε αρχεία στην DataFlash, χρειάζεται πρώτα να φορτωθούν ένα ή περισσότερα διαµερίσµατα της. Στον Router NGW100 η συνολική µνήµη flash (Parallel flash + DataFlash) χωρίζεται σε τρία διαµερίσµατα (partitions) τα οποία περιλαµβάνουν συνήθως τα τρία κύρια µέρη ενός οποιουδήποτε ενσωµατωµένου συστήµατος Linux: Bootloader Root filesystem /usr Η διαµέριση επιτυγχάνεται στο αρχείο flash.c που υπάρχει αποθηκευµένο στον κατάλογο arch/avr32/boards/avr32/atngw100 και πιο συγκεκριµένα στην δοµή δεδοµένων. mtd_partition flash_parts[]. Η εντολή µε την οποία µπορεί να φορτωθεί το διαµέρισµα της DataFlash το οποίο χρειάζεται ένα χρήστης για να διαχειρίζεται τα δεδοµένα του είναι η εξής: mount -t jffs2 /dev/mtdblock0 /mnt-directory Μετά και από αυτό το βήµα, µπορούµε πια πολύ απλά να εκτελέσουµε για παράδειγµα την παρακάτω εντολή δηµιουργίας αρχείου κειµένου: touch rousis.txt 3.8 Υλικό δικτύωσης Ολόκληρη η εργασία είναι ένα σύνολο βηµάτων που οδηγούν στην ανάπτυξη ενός ενσωµατωµένου συστήµατος Linux το οποίο είναι αφοσιωµένο στην δροµολόγηση πακέτων αλλά και τη δικτύωση και ∆ιαδικτύωση υπολογιστικών συστηµάτων. Εποµένως είναι πολύ δύδκολο Ιστότοπος εργασίας: MyThesis.org 121 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) µέσα σε µία παράγραφο να περιγραφεί το οτιδήποτε αφορά την δικτύωσή του. Γι’ αυτό θα αφιερωθεί ένα ολόκληρο ξεχωριστό κεφάλαιο καθώς και ένα ειδικό παράρτηµα που θα αφορούν όλα όσα χρειάζεται ο Router NGW100 για να επικοινωνήσει µε τοπικά αλλά και µε αποµακρυσµένα δίκτυα. Επικοινωνία: [email protected] 122 ΚΕΦΑΛΑΙΟ 4 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux Εισαγωγή Οι βασικές λειτουργίες ενός Router είναι να επικοινωνεί µε δίκτυα και να δροµολογεί πακέτα δεδοµένων, όσο το δυνατόν πιο γρήγορα και πιο αξιόπιστα. Αυτή είναι µια αρκετά πολύπλοκη διαδικασία η οποία απαιτεί τη συνεργασία πολλών υποσυστηµάτων. Η πολυπλοκότητα αφορά τόσο το λογισµικό της συσκευής όσο και το υλικό, και δεν περιορίζεται µόνο σε ότι αφορά το πρωτόκολλο TCP/IP. Σε αυτό το κεφάλαιο θα γίνει µια πολύ συνοπτική περιγραφή των στοιχείων που αφορούν τη δικτύωση και τη δροµολόγηση πακέτων δεδοµένων στα ενσωµατωµένα συστήµατα Linux. Ο Router NGW100 που είναι και το αντικείµενο της παρούσας εργασίας, είναι ένα ενσωµατωµένο σύστηµα Linux, αφοσιωµένο στη δροµολόγηση πακέτων και πάνω στη λειτουργία του θα βασιστεί ολόκληρο το κεφάλαιο. Πιο συγκεκριµένα, τα θέµατα που θα µας απασχολήσουν είναι: Το TCP/IP στα ενσωµατωµένα συστήµατα Linux Linux Sockets ∆ροµολόγηση πακέτων Τα πρωτόκολλα ARP, ICMP και IGMP Οδηγοί δικτύου Απόδοση δικτύωσης του µικροελεγκτή AP7000 4.1 Το TCP/IP στα ενσωµατωµένα συστήµατα Linux Πριν χρησιµοποιηθεί ευρέως το TCP/IP (Transmission Control Protocol / Internet Protocol) στα ενσωµατωµένα συστήµατα, οι εφαρµογές που γραφόταν γι’ αυτά ήταν συνήθως αυτόνοµες και δεν εκτελούνταν επάνω από κάποιο λειτουργικό σύστηµα. Ακόµα και αν υπήρχε κάποιο ελάχιστο λειτουργικό σύστηµα, δε διέθετε καµία προγραµµατιστική διεπαφή. Το γεγονός αυτό, καθιστούσε αρκετά δύσκολη την ενσωµάτωση κάποιου πρωτοκόλλου επικοινωνίας. Κατά καιρούς υπήρξαν αρκετές αξιόλογες προσπάθειες ανάπτυξης στοιβών για ενσωµατωµένα συστήµατα των 8 αλλά και των 32 bit (πχ: uIP και lwIP). Οι στοίβες αυτές συνήθως κα- ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) λούνται µέσα από την main() κάποιας αυτόνοµης εφαρµογής η οποία αποτελεί και την µοναδική εφαρµογή του ενσωµατωµένου συστήµατος. Η σχεδίασή τους βασίζεται στο ότι, σπάνια σε ένα ενσωµατωµένο σύστηµα απαιτούνται όλες οι λειτουργίες και τα πρωτόκολλα του TCP/IP. Εποµένως αν αφαιρεθούν περιττά χαρακτηριστικά µπορεί να προκύψει µια πιο ανάλαφρη (lightweight) στοίβα η οποία θα έχει σηµαντικά λιγότερες απαιτήσεις µνήµης και επεξεργαστικής ισχύος. Οι αυτόνοµες στοίβες δεν θα µας απασχολήσουν περισσότερο σε αυτή την εργασία. Αναφέρθηκαν απλά για να γίνει κατανοητό ότι το TCP/IP µπορεί να εκτελείται και χωρίς την παρουσία κάποιου λειτουργικού συστήµατος. Στη συνέχεια θα εξετάσουµε το TCP/IP σε σχέση µε τα ενσωµατωµένα λειτουργικά συστήµατα και ειδικότερα σε σχέση µε το ενσωµατωµένο Linux. 4.1.1 Το TCP/IP και το µοντέλο αναφοράς OSI Είναι αρκετά σηµαντικό να διαχωρίσουµε την θεωρητική έννοια ενός πρωτόκολλου επικοινωνίας από την έννοια της εφαρµογής του πρωτοκόλλου αυτού στο υλικό και το λογισµικό ενός υπαρκτού υπολογιστικού συστήµατος. Θεωρητικά, το πρωτόκολλο επικοινωνίας είναι µια προδιαγραφή για µια καθορισµένη αλληλουχία ανταλλαγής δεδοµένων µεταξύ δύο ή περισσοτέρων υπολογιστικών συστηµάτων. ∆ηλαδή ένα θεωρητικό µοντέλο που περιγράφει κάποιες λειτουργίες και κάποια βήµατα. Ένα τέτοιο µοντέλο είναι και το µοντέλο αναφοράς Ανοικτής ∆ιασύνδεσης Συστηµάτων OSI (el.wikipedia.org/wiki/Μοντέλο_αναφοράς_OSI). Από την άλλη, η πρακτική εφαρµογή ενός πρωτοκόλλου καθορίζει το πώς το λογισµικό ή το υλικό εκτελεί τις λειτουργίες και τα βήµατα µέσα σε κάποιο σύστηµα. Ένα τέτοιο πρωτόκολλο είναι και το TCP/IP. Το πρωτόκολλο TCP/IP, όπως και το OSI, χωρίζεται σε λογικά επίπεδα έτσι ώστε να διευκολύνεται η ανάπτυξη του κώδικα του πρωτοκόλλου. Τα επίπεδα αυτά, αρχίζοντας από το ανώτερο και καταλήγοντας στο πιο χαµηλό, είναι τα παρακάτω: Εφαρµογής (Application Layer) Μεταφοράς (Transfer Layer) ∆ικτύου (Network ή Internet Layer) Φυσικό (Physical Layer) 4.1.2 Οι Απαιτήσεις του TCP/IP από το ενσωµατωµένο Linux Όπως γνωρίζουµε, τα περισσότερα πρωτόκολλα επικοινωνίας λειτουργούν ασύγχρονα. Το ίδιο ισχύει και για το TCP/IP. Η υλοποίηση του πρωτοκόλλου είναι γραµµική (linear) αλλά προσανατολισµένη σε συµβάντα (event oriented). Έτσι, η επεξεργασία ενός πακέτου αρχίζει µόνο τη στιγµή κατά την οποία αυτό παραλαµβάνεται. Κάθε πακέτο ταξιδεύει µεταξύ των επιπέδων µέσα στη στοίβα ως ένα ξεχωριστό και ανεξάρτητο, νήµα (thread) επεξεργασίας. Επιπροσθέτως, υπάρχουν παράλληλες δραστηριότητες οι οποίες θα πρέπει ταυτόχρονα µε την λειτουργία τους να παρακολουθούν και κάποια χρονικά όρια (timeouts) που προβλέπονται από τις απαιτήσεις του πρωτοκόλλου. Βασιζόµενοι σε όλες τις παραπάνω πληροφορίες, µπορούµε να παραθέσουµε µια λίστα µε τις ιδιότητες που θα πρέπει να έχει ένα ενσωµατωµένο λειτουργικό σύστηµα έτσι ώστε να είναι σε θέση να φιλοξενήσει µε επιτυχία την στοίβα TCP/IP: ∆υνατότητες χρονισµού (Timers): Σχεδόν σε όλα τα πρωτόκολλα επικοινωνίας απαιτούνται χρονιστές για τυχόν αναµεταδόσεις προβληµατικών πακέτων καθώς και για έλεγχο των χρονικών ορίων σύνδεσης. Σε πιο απλά λειτουργικά συστήµατα που χρη- Επικοινωνία: [email protected] 124 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux – Κεφάλαιο 4 σιµοποιούνται στα ενσωµατωµένα συστήµατα, αυτή η δυνατότητα είτε δεν υπάρχει, είτε δεν παρέχει κάποια σταθερή και εύχρηστη προγραµµατιστική διεπαφή (API). Ταυτοχρονισµό και πολυδιεργασία (Concurrency & Multitasking): Η προγραµµατιστική διεπαφή socket καθώς και άλλες παρόµοιες διεπαφές, θα πρέπει να επιτρέπουν την ύπαρξη πολλαπλών, ταυτόχρονα συνδεδεµένων χρηστών. Επίσης η στοίβα TCP/IP θα πρέπει να είναι πολυνηµατική (multithread). Φυσικά ο ταυτοχρονισµός απαιτεί κλείδωµα κάποιων πόρων και κάποιων σηµαντικών τµηµάτων του κώδικα έτσι ώστε να µην προσπαθούν δύο χρήστες ταυτόχρονα να τροποποιήσουν τα ίδια δεδοµένα ή δεδοµένα στα οποία δε θα έπρεπε να έχουν πρόσβαση. Η αντιµετώπιση αυτών των ζητηµάτων επιτυγχάνεται συνήθως, µέσω κατάλληλου κλειδώµατος σηµατοφόρων σε επίπεδο καταχωρητή. ∆ιαχείριση καταχωρητών (Buffer Management): Το πρωτόκολλο TCP/IP όπως και τα περισσότερα πρωτόκολλα είναι πιο αποτελεσµατικά όταν παρέχουν σύστηµα καταχωρητών σταθερού µήκους (fixed-length buffer system). Οι καταχωρητές εκχωρούνται δυναµικά όταν κάποιο νέο πακέτο παραληφθεί ή όταν δηµιουργείται ένα νέο εξερχόµενο πακέτο στο επίπεδο των sockets. Πρέπει να πούµε ότι οι καταχωρητές δικτύωσης του Linux ονοµάζονται και socket buffers. Οι socket buffers εκχωρούνται από µια µικρή περιοχή µνήµης, την slab cache η οποία επιλύει και θέµατα κατακερµατισµού (fragmentation) µεγάλων PDU (Protocol Data Unit) που ξεπερνούν το µέγιστο όριο µετάδοσης (MTU – Maximum Transmission Unit). Ένα άλλο πλεονέκτηµα της µνήµης αυτής είναι και η επεκτασιµότητά της καθώς δεν έχει κάποιο προκαθορισµένο ανώτατο όριο µεγέθους, αλλά µπορεί να µεγαλώνει δυναµικά ανάλογα µε τις τρέχουσες απαιτήσεις. ∆υνατότητα συνένωσης επιπέδων (Layer Linking): Σε περίπλοκες υλοποιήσεις πρωτοκόλλων, όπως είναι και αυτή του TCP/IP, απαιτείται ένας µηχανισµός προσθήκης επιπέδων (κάποιες φορές και ολόκληρων πρωτοκόλλων) κάτω από το επίπεδο IP και επάνω από την προγραµµατιστική διεπαφή του οδηγού δικτύου. Καλό είναι επίσης να υπάρχει και κάποιος µηχανισµός προσθήκης πρωτοκόλλων ο οποίος θα λειτουργεί έτσι ώστε να µην απαιτείται καµία τροποποίηση του IP κώδικα. Τέτοια πρωτόκολλα µπορεί να είναι για παράδειγµα το πρωτόκολλο NAT (Network Address Translation), το PPP (Point-to-Point Protocol), η γεφύρωση µέσω λογισµικού (software based bridging και η δυνατότητα µεταγωγής (switching). Χαµηλή καθυστέρηση (Low Latency): Το λειτουργικό σύστηµα δεν θα πρέπει να προσθέτει καµία επιπλέον καθυστέρηση από εκείνη που είναι ήδη απαραίτητη για την ελάχιστη επεξεργασία η οποία απαιτείται κατά τη διάρκεια µιας διακοπής (interrupt time) για την αποστολή ή την λήψη ενός πλαισίου στο φυσικό επίπεδο. Ο στόχος του λειτουργικού συστήµατος θα πρέπει να είναι η ελαχιστοποίηση των context switches (wikipedia.org/wiki/Context_switch) κατά τη διάρκεια επεξεργασίας ενός πακέτου από την στοίβα TCP/IP. Το Linux χρησιµοποιεί τα softirqs κατά το µεγαλύτερο µέρος της εσωτερικής του επεξεργασίας. Επίσης παρέχονται fast paths (wikipedia.org/wiki/Fast_path) για την αποστολή και λήψη πακέτων µε σκοπό την περεταίρω µείωση της καθυστέρησης. Ελάχιστη αντιγραφή δεδοµένων (Minimal Data Copying): Στα ενσωµατωµένα συστήµατα είναι αρκετά επιθυµητό να µειώνεται στο ελάχιστο το µέγεθος της αντιγραφής δεδοµένων κατά τη διάρκεια µεταφοράς ενός πακέτου από το επίπεδο εφαρµογής στο φυσικό επίπεδο της στοίβας. Τα ενσωµατωµένα συστήµατα και οι εφαρµογές τους, θα πρέπει να αναπτύσσονται µε γνώµονα την µέγιστη απόδοση και γι’ αυτό θα πρέπει η υλοποίηση της στοίβας δικτύωσής τους να µην επιτρέπει αντιγραφές καταχωρητών στο επίπεδο των drivers, παρέχοντας ταυτόχρονα την επιλογή εξάλειψης αντιγραφών στo επίπεδο των sockets. Ιστότοπος εργασίας: MyThesis.org 125 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Όλα τα παραπάνω ικανοποιούνται απόλυτα από το Linux. Το Linux υποστηρίζει επίσης και ορισµένα επιπλέον χαρακτηριστικά τα οποία αν και δεν απαιτούνται από το TCP/IP, βελτιώνουν σηµαντικά την απόδοση της επικοινωνίας. Ένα τέτοιο παράδειγµα είναι και η µείωση των προβληµάτων καθυστέρησης µέσω του µηχανισµού δηµιουργίας νηµάτων softirq που υπάρχει υλοποιηµένος στον kernel. Το Linux έχει την δυνατότητα αναβολής διακοπών που προκύπτουν από ορισµένες διεργασίες µε σκοπό την µείωση των καθυστερήσεων. Τα νήµατα softirq ουσιαστικά είναι ένας µηχανισµός διακοπών λογισµικού (software interrupts) που χρησιµοποιείται από τους οδηγούς συσκευών προκειµένου να είναι σε θέση να επεξεργαστούν κάτι που βρίσκεται εκτός του χειριστή διακοπής υλικού (hardware interrupt handler) όσο το δυνατόν ταχύτερα, ούτως ώστε να µπορούν να επανενεργοποιούνται και πάλι οι διακοπές. Με λίγα λόγια, είναι ένας µηχανισµός παύσης και επανέναρξης διακοπών. 4.1.3 Η στοίβα TCP/IP στο Linux Ο πηγαίος κώδικας της στοίβας TCP/IP που υπάρχει ενσωµατωµένη στον Linux Kernel, βρίσκεται στον κατάλογο net. ∆εν θα είχε κάποιο νόηµα στα πλαίσια αυτής της εργασίας να αναλύσουµε γραµµή προς γραµµή τον κώδικα αυτό καθώς υπάρχει πολύ καλή διαθέσιµη βιβλιογραφία για το σκοπό αυτό. Ούτως ή άλλως, οι εφαρµογές του Router NGW100 κάνουν χρήση του socket API όταν χρειάζεται να στείλουν ή να λάβουν δεδοµένα στο δίκτυο µέσω της στοίβας TCP/IP. Παρ’ όλα αυτά θα παρατεθούν κάποιες γενικές πληροφορίες που είναι καλό να γνωρίζουµε. Η επόµενη εικόνα παρουσιάζει σχηµατικά και µε µεγάλο επίπεδο αφαίρεσης, τη διαστρωµάτωση της στοίβας TCP/IP στο Linux: Εφαρµογής Μεταφοράς ∆ικτύου Φυσικό (PHY) Εικόνα 38. Η ενσωµατωµένη στοίβα TCP/IP του Linux Kernel Επικοινωνία: [email protected] 126 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux – Κεφάλαιο 4 Ουσιαστικά το πρωτόκολλο TCP/IP στο Linux, αποτελείται από τρία επίπεδα. Το Μεταφοράς το ∆ικτύου και το Φυσικό. Επάνω από το επίπεδο µεταφοράς δεν υπάρχει κανένα πρωτόκολλο δικτύωσης. Υπάρχει µόνο το επίπεδο της προγραµµατιστικής διεπαφής των sockets για τα οποία θα εξετάσουµε αναλυτικά. Κάθε επίπεδο λειτουργεί ανεξάρτητα από τα υπόλοιπα. Αυτό επιτυγχάνεται µέσω του µηχανισµού ενθυλάκωσης (encapsulation) αλλά και µε τη βοήθεια της προαπαιτούµενης υποδοµής που συσχετίζεται µε την στοίβα TCP/IP η οποία περιλαµβάνει συναρτήσεις και µακροεντολές για την εξαγωγή και την προσθήκη επικεφαλίδων (headers), αλλά και για τον συνδυασµό και τη συνένωση πακέτων. Επιπρόσθετα, ο Linux Kernel παρέχει κάποια επιπλέον υποδοµή προκειµένου η στοίβα TCP/IP να µπορεί να λειτουργήσει. Η υποδοµή αυτή αφορά τα εξής: Χρονιστές (timers) Νηµάτωση (threading) ∆ιεργασίες (tasks) Καταχώρηση πρωτοκόλλων (protocol registration) Προγραµµατιστική διεπαφή για οδηγούς δικτύου (network driver interface) Σύστηµα προσωρινής καταχώρησης δεδοµένων (buffering scheme) Αρχικοποίηση της στοίβας TCP/IP στον Router NGW100 Η αρχικοποίηση (initialization) της στοίβας TCP/IP στο Linux, είναι µια αρκετά πολύπλοκη διαδικασία η οποία περιλαµβάνει τα εξής: Αρχικοποίηση του επιπέδου των sockets Καταχώρηση πρωτοκόλλων ∆ηµιουργία εγγραφών στο σύστηµα αρχείων /proc Λόγω της φύσης της στοίβας TCP/IP του Linux, είναι δύσκολο να διαχωρίσουµε την βασική αρχικοποίησή της από την αρχικοποίηση του επιπέδου των sockets. Γενικά όµως η δεύτερη προηγείται καθώς το επίπεδο socket θα πρέπει να βρίσκεται στη θέση του όταν η AF_INET καταχωρηθεί σε αυτό, προκειµένου να µπορούν να λαµβάνονται και να αποστέλλονται πακέτα IP. Η καταχώρηση των πρωτοκόλλων χωρίζεται σε τρία µέρη. Το πρώτο αφορά την καταχώρηση των πρωτοκόλλων του επιπέδου δικτύου που είναι γνωστά και ως χειριστές πακέτων (packet handlers ή taps). Αυτό δίνει την δυνατότητα να λαµβάνονται αλλά και να αποστέλλονται πακέτα από και προς τους οδηγούς συσκευών δικτύου αντίστοιχα. ∆ηλαδή αυτό είναι το σηµείο όπου το υλικό δικτύωσης (drivers - PHY) συναντά το λογισµικό δικτύωσης (TCP/IP). Η καταχώρηση αλλά και κατάργηση (de-registration) των ορισµάτων των χειριστών πακέτων δηλώνονται στο αρχείο netdevice.h που βρίσκεται στον πηγαίο κώδικα του Kernel, στον κατάλογο include/linux/. Επίσης οι αντίστοιχες συναρτήσεις καθορίζονται στο αρχείο dev.c που βρίσκεται στον κατάλογο net/core/. Το δεύτερο, αφορά την καταχώρηση των πρωτοκόλλων του επιπέδου µεταφοράς µαζί µε το επίπεδο δικτύου, IP, έτσι ώστε να µπορούν να αποσταλούν δεδοµένα µέσω των πρωτοκόλλων TCP και UDP. Και το τρίτο την καταχώρηση των πρωτοκόλλων µαζί µε το επίπεδο των sockets. Η συνάρτηση inet_init ουσιαστικά φέρει εις πέρας όλη την αρχικοποίηση της στοίβας TCP/IP. Η συνάρτηση αυτή ορίζεται στο αρχείο af_inet.c το οποίο βρίσκεται στον κατάλογο net/ipv4/. Αφού η συνάρτηση ολοκληρώσει την καταχώρηση του επιπέδου socket, καλούνται οι συναρτήσεις αρχικοποίησης των πρωτοκόλλων του TCP/IP. Η πρώτη από αυτές είναι η arp_init η οποία αρχικοποιεί το πρωτόκολλο ARP. Στη συνέχεια καλείται η Ιστότοπος εργασίας: MyThesis.org 127 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) ip_init για την αρχικοποίηση του πρωτοκόλλου IP και ακολουθεί η tcp_v4_init η οποία δηµιουργεί την slab cache για το TCP. Η επόµενη κλήση αφορά την icmp_init για την αρχικοποίηση του πρωτοκόλλου ICMP. Αν στο σύστηµά µας έχουµε επιλέξει να υποστηρίζεται δροµολόγηση multicast τότε επιπρόσθετα καλείται και η συνάρτηση ip_mr_init. Αφού έχουν ολοκληρωθεί επιτυχώς όλες οι κλήσεις των συναρτήσεων αλλά και η αρχικοποίηση των επιθυµητών πρωτοκόλλων, γίνονται και οι απαραίτητες εγγραφές στο ψευδό-σύστηµα αρχείων /proc. Στο σηµείο αυτό είναι χρήσιµο να παραθέσουµε κάποιες πληροφορίες σχετικά µε τους όρους slab και /proc που αναφέρθηκαν νωρίτερα. Κάθε λειτουργικό σύστηµα, όπως και κάθε άλλο πρόγραµµα, είναι απαραίτητο να δεσµεύει µνήµη για τις διάφορες λειτουργίες του. Για το λόγο αυτό αναπτύσσεται εξειδικευµένος κώδικας ο οποίος χαρακτηρίζεται ως σύστηµα δέσµευσης µνήµης (allocation memory system). Στο Linux το σύστηµα αυτό ονοµάζεται “slab allocation” και χρησιµοποιείται για κάθε ανάγκη. Μια από τις χρήσεις του slab είναι και η δέσµευση µνήµης για δικτύωση (networking buffers). Το σύστηµα slab οργανώνεται σε caches οι οποίες αντιστοιχούν µια σε κάθε κύρια λειτουργία του Kernel. Για το TCP/IP δηµιουργούνται αρκετές caches, εκ των οποίων η σηµαντικότερη είναι η skbuff_head_cache και αφορά τα socket buffers. Τα ονόµατα των caches για το TCP/IP βρίσκονται στο σύστηµα αρχείων /proc στον κατάλογο slabinfo. Το procfs (processes file system) ή /proc, είναι ένα ειδικό σύστηµα αρχείων το οποίο αναπαριστά πληροφορίες που αφορούν τις διεργασίες που εκτελούνται σε ένα σύστηµα Linux και τις παρουσιάζει σε µια ιεραρχική δοµή όµοια µε αυτή που υπάρχει για την αναπαράσταση των αρχείων και των καταλόγων. Έτσι παρέχεται µια πιο βολική µέθοδος δυναµικής πρόσβασης στα δεδοµένα των διαφόρων διεργασιών, χωρίς να απαιτείται άµεση προσπέλαση της µνήµης του kernel από τις εφαρµογές που γράφουµε. Το σύστηµα αρχείων /proc στον Router NGW100, όπως και στα περισσότερα συστήµατα Linux, φορτώνεται κατά την εκκίνηση. 4.2 Linux Sockets Ουσιαστικά ένα socket είναι ο συνδυασµός µιας IP διεύθυνσης µε έναν αριθµό θύρας (port number) ή απλά πιο απλά, µε µια θύρα (port). Σε αυτή την ενότητα θα εξετάσουµε τα περισσότερα θέµατα που αφορούν τα sockets καθώς είναι ένα πάρα πολύ σηµαντικό χαρακτηριστικό για την λειτουργία του Router NGW100 αλλά και για κάθε συσκευή που απαιτείται να συνδέεται σε δίκτυο. 4.2.1 Τί είναι η διεπαφή socket Η διεπαφή (API) socket είναι ο συνδετικός κρίκος του επιπέδου µεταφοράς µε το επίπεδο εφαρµογής της στοίβας TCP/IP που βρίσκεται υλοποιηµένη µέσα στον Linux Kernel. Παρέχει µια εξειδικευµένη προγραµµατιστική διεπαφή την οποία χρησιµοποιούν οι προγραµµατιστές για να αναπτύξουν εφαρµογές που µπορούν να εκµεταλλευτούν τις δυνατότητες δικτύωσης και ∆ιαδικτύωσης του Kernel. Επίσης χρησιµοποιείται και από τον Kernel προκειµένου αυτός να είναι σε θέση να “ζητήσει” κάτι από µια εφαρµογή. Η διεπαφή socket είναι ανεξάρτητη πρωτοκόλλου. ∆ηλαδή ο προγραµµατιστής που δηµιουργεί µια δικτυακή εφαρµογή, δεν χρειάζεται να γνωρίζει τα πρωτόκολλα TCP/IP και τις εσωτερικές τους λεπτοµέρειες. Η διεπαφή socket µπορεί να χαρακτηριστεί ως το “παράθυρο” του TCP/IP προς τον έξω κόσµο. Ο σκοπός των sockets είναι να επιτύχουν τη µεταφορά δεδοµένων, την διαχείριση των συνδέσεων TCP και να ελέγχουν ή να βελτιώνουν την λειτουργία των πρωτοκόλλων του Επικοινωνία: [email protected] 128 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux – Κεφάλαιο 4 Kernel. Ο µηχανισµός των Linux sockets υποστηρίζει πολλές οικογένειες πρωτοκόλλων, µε πιο γνωστή την AF_INET στην οποία ανήκει το πρωτόκολλο TCP/IP. Η διεπαφή socket αποτελείται από δύο τµήµατα. Από ένα σύνολο συναρτήσεων που έχουν γραφτεί ειδικά για το δίκτυο και έναν µηχανισµό αποθήκευσης και χαρτογράφησης αιτηµάτων τα οποία στέλνουν οι εφαρµογές των χρηστών προκειµένου να φέρουν εις πέρας τις δικτυακές τους απαιτήσεις. 4.2.2 Οι βασικότερες δοµές διαχείρισης sockets Η βασικότερη δοµή δεδοµένων είναι η sk_buff ή οποία αντιπροσωπεύει ένα socket buffer και καθορίζεται στο αρχείο include/linux/sk_buff.h. Τα socket buffers είναι δοµές (δηλαδή ενιαίες περιοχές στη µνήµη) στις οποίες αποθηκεύονται τα δεδοµένα των πακέτων. Συνήθως ένα socket buffer είναι προσβάσιµο µέσω της µεταβλητής δείκτη skb. Η δεύτερη δοµή είναι η socket η οποία ορίζεται στο αρχείο include/linux/net.h. Σε αυτή τη δοµή καταγράφονται πληροφορίες για όλες τις ενεργές συνδέσεις µε τις διάφορες εφαρµογές που υπάρχουν έτσι ώστε να είναι διαθέσιµες όταν αυτό απαιτείται. Γενικά, κάθε στιγµιότυπο µιας δοµής socket, αντιστοιχεί σε ένα ανοικτό socket το οποίο έχει δηµιουργηθεί µέσω των κλήσεων που παρέχονται από τη διεπαφή επιπέδου socket. Συνήθως η δοµή socket προσπελαύνεται µέσω της µεταβλητής δείκτη, sock. Η τρίτη και αρκετά σηµαντική δοµή είναι η sock και ορίζεται στο αρχείο sock.h που υπάρχει στον κατάλογο include/net. Είναι αρκετά περίπλοκη δοµή η οποία διατηρεί πληροφορίες που αφορούν τις ανοικτές συνδέσεις. Είναι προσβάσιµη µέσω της στοίβας TCP/IP και κυρίως µέσω του πρωτοκόλλου TCP, ενώ συνήθως αναφέρεται µέσω µιας µεταβλητής δείκτη µε την ονοµασία sk. Όπως γίνεται εύκολα αντιληπτό, οι ονοµασίες στην υλοποίηση της διεπαφής των sockets στον Linux Kernel µοιάζουν αρκετά η µία µε την άλλη. Αυτό δηµιουργεί σύγχυση στον προγραµµατιστή και θα ήταν καλό σε νέες εκδόσεις του Kernel να διορθωθεί. 4.2.3 Οικογένειες πρωτοκόλλων Μια οικογένεια πρωτοκόλλων (protocol family) είναι απλά ένας αριθµός ο οποίος αντιστοιχεί στο όρισµα domain της κλήσης συστήµατος socket().κάποιες φορές µια οικογένεια πρωτοκόλλων µπορεί να αναφέρεται και ως οικογένεια διευθύνσεων (address family). Κάθε οικογένεια έχει ένα όνοµα. Για παράδειγµα το όνοµα της οικογένειας πρωτοκόλλων του TCP/IP είναι AF_INET και ο αριθµός που της αντιστοιχεί είναι το 2 (εποµένως domain = 2): #define AF_INET 2 /* Internet IP Protocol */ Εποµένως όταν λέµε AF_INET είναι σα να λέµε TCP/IP. Στον Linux Kernel (έκδοση 2.6) υπάρχουν 32 υποστηριζόµενες οικογένειες πρωτοκόλλων οι οποίες ορίζονται στο αρχείο include/linux/socket.h. Όπως συµβαίνει και µε τα περισσότερα στοιχεία λογισµικού σε ένα σύστηµα Linux, για να είµαστε σε θέση να χρησιµοποιήσουµε µια οικογένεια πρωτοκόλλων θα πρέπει πρώτα να την αρχικοποιήσουµε. Η αρχικοποίηση ουσιαστικά ενηµερώνει το επίπεδο των sockets για την ύπαρξη της οικογένειας αυτής. Ιστότοπος εργασίας: MyThesis.org 129 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Τέλος, αν κρίνεται αναγκαίο υπάρχει η δυνατότητα να γράψουµε το δικό µας πρωτόκολλο και να το φορτώσουµε κατά τη λειτουργία του συστήµατος µε τη µορφή module. Για να δηµιουργήσουµε όµως ένα νέο όνοµα οικογένειας πρωτοκόλλων (πχ AF_ROUSIS), θα πρέπει να υπερφορτώσουµε (override) κάποια ήδη υπάρχουσα η οποία δε χρησιµοποιείται από το σύστηµά µας: # ifndef AF_ROUSIS # define AF_ROUSIS AF_SNA /*override*/ # endif Θα πρέπει επίσης να αντιστοιχίσουµε την οικογένεια διευθύνσεων µε την οικογένεια πρωτοκόλλων έτσι ώστε να σηµαίνουν το ίδιο πράγµα: # ifndef PF_ROUSIS # define PF_ROUSIS AF_ROUSIS # endif Οι δύο παραπάνω προσθήκες κώδικα αφορούν το αρχείο socket.h που αναφέραµε νωρίτερα. Φυσικά υπάρχουν και άλλα βήµατα που πρέπει να γίνουν τα οποία όµως δεν κρίνεται αναγκαίο να αναφερθούν στα πλαίσια αυτής της εργασίας. 4.2.4 Αρχικοποίηση επιπέδου socket Αναφέραµε νωρίτερα σε αυτό το κεφάλαιο, ότι είναι δύσκολο να διαχωρίσουµε την αρχικοποίηση της στοίβας TCP/IP από την αρχικοποίηση του επιπέδου των sockets. Και ότι η δεύτερη, προηγείται, αφού το επίπεδο socket θα πρέπει να βρίσκεται στη θέση του όταν η οικογένεια πρωτοκόλλων AF_INET καταχωρηθεί σε αυτό, προκειµένου να µπορούν να λαµβάνονται και να αποστέλλονται πακέτα IP. Σε αυτή την παράγραφο θα παραθέσουµε πληροφορίες για τον τρόπο και τη σειρά που ακολουθείται έτσι ώστε να αρχικοποιηθεί σωστά το επίπεδο socket και η οικογένεια AF_INET. Η πρώτη συνάρτηση που καλείται είναι η sock_init. Η συνάρτηση αυτή ορίζεται στο αρχείο net/socket.c: static int __init sock_init(void) { sk_init(); /* Initialize sock SLAB cache */ skb_init(); /* Initialize skbuff SLAB cache */ /* Initialize the protocols module. */ init_inodecache(); register_filesystem(&sock_fs_type); sock_mnt = kern_mount(&sock_fs_type); #ifdef CONFIG_NETFILTER netfilter_init(); #endif return 0; } Καλείται πριν από την αρχικοποίηση του TCP/IP για τους λόγους που έχουµε αναφέρει ήδη. Εφόσον το επίπεδο socket αποτελείται από πολλές λειτουργίες και µπορεί να χρησιµοποιείται ταυτόχρονα από πολλά διαφορετικά πρωτόκολλα και δη οικογένειες πρωτοκόλλων, απαι- Επικοινωνία: [email protected] 130 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux – Κεφάλαιο 4 τείται η ύπαρξη ενός πίνακα. Ο σκοπός του θα είναι να αντιστοιχίζει τα επεξεργασµένα πακέτα των διάφορων πρωτοκόλλων µε τα sockets των εφαρµογών στις οποίες ανήκουν, και στις οποίες τελικά θα πρέπει να σταλούν τα επεξεργασµένα πακέτα. Αυτή η διαδικασία χαρακτηρίζεται ως αποπολύπλεξη (socket de-multiplexing). O κύριος µηχανισµός που παρέχει ο Linux Kernel για την αποπολύπλεξη των socket είναι ο πίνακας µεταγωγής πρωτοκόλλων (switching protocol table). Η αρχικοποίηση του πίνακα µεταγωγής πρωτοκόλλων γίνεται µέσω των συναρτήσεων inet_register_protosw που ορίζεται στο αρχείο net/ipv4/af_inet.c, ενώ η αντίθετη διαδικασία επιτυγχάνεται µέσω της inet_unregister_protosw που ορίζεται στο ίδιο αρχείο. Αφού αρχικοποιηθεί ο πίνακας θα πρέπει τα πρωτόκολλα που θα περιέχει να καταχωρηθούν στα πεδία του. Αυτό πραγµατοποιείται από δύο συναρτήσεις. Την inet_init και την inet_protosw, οι οποίες ορίζονται στο αρχείο net/ipv4/af_inet.c. Το τελευταίο βήµα που θα πρέπει να γίνει προκειµένου να ολοκληρωθεί η αρχικοποίηση του επιπέδου socket είναι η καταχώρηση των πρωτοκόλλων στο επίπεδο socket. Οι συναρτήσεις που θα αναφερθούν στη συνέχεια, βρίσκονται υλοποιηµένες στο αρχείο socket.c του καταλόγου net και δηλώνονται στο αρχείο κεφαλίδας include/linux/net.h. Η συνάρτηση που καταχωρεί µια οικογένεια πρωτοκόλλων στο επίπεδο socket είναι η sock_register. Αυτό γίνεται για κάθε οικογένεια η οποία θα χρειαστεί στο σύστηµά µας, ενώ η αντίθετη διαδικασία πραγµατοποιείται από την συνάρτηση sock_unregister. 4.2.5 Πρωτόκολλο IP και sockets Στην αρχή αυτής της ενότητας αναφέραµε ότι ένα socket είναι ο συνδυασµός µιας IP διεύθυνσης µε έναν αριθµό θύρας. Επειδή αυτός είναι ένας απλουστευµένος ορισµός, σε αυτή την παράγραφο θα προσθέσουµε κάποιες επιπλέον πληροφορίες. Γενικά, όταν χρησιµοποιείται η διεπαφή socket οι διευθύνσεις δικτύου αποθηκεύονται σε µια δοµή δεδοµένων µε όνοµα sockaddr_in η οποία ορίζεται στο αρχείο in.h και είναι η εξής: struct sockaddr_in { sa_family_t unsigned short int struct in_addr sin_family; /* Address family sin_port; /* Port number sin_addr; /* Internet address */ */ */ /* Pad to size of `struct sockaddr'. */ unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) sizeof(unsigned short int) - sizeof(struct in_addr)]; }; Το αρχείο κεφαλής in.h βρίσκεται στον κατάλογο include/linux και αποτελεί την υλοποίηση INET (linux.die.net/man/3/inet) του πρωτοκόλλου TCP/IP που είναι συµβατή µε τα BSD sockets (wikipedia.org/wiki/Berkeley_sockets): Τα µέλη της δοµής sin_family, sin_port, και sin_addr αφορούν την οικογένεια διευθύνσεων (AF_INET για το IP), τη θύρα, και τη διεύθυνση IP του συστήµατος στο οποίο εκτελείται η εφαρµογή. Ο πίνακας __pad αφορά τον υπολογισµό του µήκους της διεύθυνσης που χρησιµοποιείται κάθε φορά. Το µήκος πρέπει να υπολογίζεται εκ νέου κάθε φορά που αποστέλλονται δεδοµένα γιατί η διεπαφή socket υποστηρίζει πολλά διαφορετικά πρωτόκολ- Ιστότοπος εργασίας: MyThesis.org 131 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) λα και έτσι µια διεύθυνση δεν είναι πάντοτε IP. Εποµένως για το Linux, socket είναι ο συνδυασµός µιας οποιασδήποτε διεύθυνσης πρωτοκόλλου µε έναν αριθµό θύρας. 4.2.6 Η διεπαφή Socket Πριν προχωρήσουµε στην ανάλυσή µας θα αναφέρουµε ορισµένες βασικές λεπτοµέρειες. Η διεπαφή socket διαχειρίζεται κυρίως διευθύνσεις δικτύου και θύρες εφαρµογών. Επίσης, το πρωτόκολλο TCP/IP απαιτεί τα στοιχεία αυτά να περνιούνται µέσω της διεπαφής socket µε σειρά οκτάδων δικτύου (network byte order wikipedia.org/wiki/Endianness). Υπάρχουν δύο τύποι sockets, ο ένας είναι ο SOCK_DGRAM και είναι κατάλληλος για µεταφορά ανεξάρτητων πακέτων UDP και ο άλλος ονοµάζεται SOCK_STREAM και είναι κατάλληλος για ακολουθίες bytes του TCP. Σε αυτή την παράγραφο θα αναφέρουµε συναρτήσεις και κλήσεις προς το επίπεδο socket καθώς και εξειδικευµένα sockets που χρησιµοποιούνται από δικτυακές εφαρµογές τις οποίες γράφουµε για το Linux. Σε αυτούς τους ίδιους µηχανισµούς βασίζονται και οι εφαρµογές που έχουν αναπτυχθεί για τον Router NGW100. Για να είναι σε θέση ένας προγραµµατιστής να δηµιουργήσει µια εφαρµογή η οποία θα χρησιµοποιεί τη στοίβα TCP/IP του Router NGW100, θα πρέπει πρώτα να δηµιουργήσει ένα νέο socket, καλώντας την συνάρτηση socket, της διεπαφής socket: int socket(int domain, int type, int protocol); Ας δούµε ένα παράδειγµα δηµιουργίας νέου socket για σύνδεση εφαρµογής µέσω TCP: int socket(AF_INET, SOCK_STREAM, 0); /* protocol zero is for TCP or UDP */ Το δεύτερο βήµα που θα πρέπει να εκτελεστεί είναι η κλήση bind προκειµένου να αντιστοιχηθεί η τοπική διεύθυνση της εφαρµογής, που είναι ένας αριθµός θύρας (port number) και αποτελεί το όνοµα (name) του socket που δηµιουργήσαµε πριν. Η κλήση bind γράφεται ως εξής: int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen); Μέσω της bind, µια εφαρµογή η οποία λειτουργεί σαν server, ενηµερώνει το επίπεδο socket σχετικά µε το ποια θύρα θα χρησιµοποιεί για να λαµβάνει δεδοµένα. Με αυτό τον τρόπο δηµιουργείται ένα τερµατικό σηµείο (endpoint) που χαρακτηρίζεται από µια διεύθυνση IP και έναν αριθµό θύρας. Τα στοιχεία αυτά είναι προσβάσιµα µέσω του δείκτη myaddr ο οποίος δείχνει στη περιοχή µνήµης που βρίσκεται η δοµή sockaddr. Το τερµατικό σηµείο που δηµιουργήθηκε θα το χρησιµοποιούν εφαρµογές client προκειµένου να εξυπηρετούνται ανταλλάσοντας δεδοµένα. Για να µπορεί όµως µια εφαρµογή server να ικανοποιεί τα αιτήµατα των clients απαιτείται προηγουµένως να εκτελεστεί η κλήση listen. Αυτό γίνεται µέσω της SOCK_STREAM ή µέσω ενός TCP server, για να ενηµερωθεί το επίπεδο socket ότι η εφαρµογή είναι έτοιµη να λάβει αιτήµατα στο socket µε όνοµα, s: int listen(int s, int backlog); Το όρισµα backlog αντιπροσωπεύει το µήκος της ουράς των αιτηµάτων σύνδεσης που εκκρεµούν ενώ ο server περιµένει τη λήξη της εκτέλεσης της κλήσης accept. Η συγκεκριµένη κλήση γίνεται από την εφαρµογή όταν είναι έτοιµη να ικανοποιήσει αιτήµατα συνδέσεων. Επικοινωνία: [email protected] 132 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux – Κεφάλαιο 4 Η διεύθυνση IP του client που αιτείται τη σύνδεση τοποθετείται στο δοµή sockaddr και ταυτόχρονα περνιέται προς αυτή τη ο δείκτης addr. Ο δείκτης addrlen θα πρέπει να δείχνει σε µια µεταβλητή η οποία θα περιέχει το µέγεθος της δοµής sockaddr και αυτό θα πρέπει να γίνεται πριν κληθεί η accept. Αφού ολοκληρωθεί η εκτέλεση της accept ο δείκτης addrlen θα δείχνει στη µεταβλητή µήκους της νέας διεύθυνσης µνήµης του δείκτη addr: int accept(int s, struct sockaddr *addr, socklen_t *addrlen); Από την πλευρά της εφαρµογής client που θέλει να συνδεθεί στον server µέσω µιας αξιόπιστης σύνδεσης TCP χρησιµοποιείται η κλήση connect: int connect(int s const struct sockaddr *serv_addr, socklen_t addrlen); Ο δείκτης serv_addr καθορίζει τη διεύθυνση και τη θύρα του server µε τον οποίο ο client θέλει να συνδεθεί και η µεταβλητή addrlen περιέχει το µήκος της δοµής sockaddr. Στην εικόνα που ακολουθεί φαίνεται µε γραφικό τρόπο, το πως οι κλήσεις που αναφέραµε εκτελούνται σε ένα µοντέλο client – server: Εικόνα 39. Το µοντέλο επικοινωνίας client – server Συνεχίζοντας την ανάλυση της διεπαφής socket θα παραθέσουµε τρεις ακόµη συναρτήσεις, τις δοµές που συνδέονται µε αυτές καθώς και ορισµένες ακόµη κλήσεις socket που χρησιµοποιούνται από τους προγραµµατιστές δικτυακών εφαρµογών. Οι συναρτήσεις send, sendto και sendmsg χρησιµοποιούνται για την αποστολή αυτόνοµων πακέτων UDP (datagrams). Τα πρωτότυπά τους είναι τα παρακάτω: int send(int s, const void *msg, size_t len, int flags); int sendto(int s, const void *msg, size_t len, int flags const struct sockaddr *to, sockle_t tolen); int sendmsg(int s, const struct msghdr *msg, int flags); Ιστότοπος εργασίας: MyThesis.org 133 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Η sendmsg συχνά χρησιµοποιείται για την επικοινωνία µεταξύ του kernel και του περιβάλλοντος χρήστη. Επίσης χρησιµοποιείται συχνά στα netlink sockets που θα εξετάσουµε αργότερα. Το όρισµα flags παίρνει τιµές οι οποίες ορίζονται στο αρχείο socket.h: #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define MSG_OOB MSG_PEEK MSG_DONTROUTE MSG_TRYHARD MSG_CTRUNC MSG_PROBE MSG_TRUNC MSG_DONTWAIT MSG_EOR MSG_WAITALL MSG_FIN MSG_SYN MSG_CONFIRM MSG_RST MSG_ERRQUEUE MSG_NOSIGNAL MSG_MORE 1 2 4 4 /* Synonym for MSG_DONTROUTE for DECnet */ 8 0x10 /*Do not send.Only probe path f.e. for MTU*/ 0x20 0x40 /* Nonblocking io */ 0x80 /* End of record */ 0x100 /* Wait for a full request */ 0x200 0x400 0x800 /* Confirm path validity */ 0x1000 0x2000 /* Fetch message from error queue */ 0x4000 /* Do not generate SIGPIPE */ 0x8000 /* Sender will send more */ Το όρισµα msg στην κλήση sendmsg χρησιµοποιείται για να της επιτρέπει να περνά µηνύµατα προς τον kernel και ουσιαστικά είναι ένας δείκτης προς τη δοµή msghdr η οποία ορίζεται στο αρχείο socket.h. Η δοµή msghdr περιλαµβάνει µε τη σειρά της, άλλη µία σηµαντική δοµή, την iovec. Η δοµή αυτή ορίζεται στο αρχείο include/linux/uio.h και χρησιµοποιείται για να δεσµεύει µνήµη για την µετάφραση διευθύνσεων µεταξύ του Kernel και του περιβάλλοντος χρήστη. Κλείνοντας την ανάλυσή µας για τη διεπαφή socket θα περιγράψουµε τη λειτουργία ορισµένων ακόµη βασικών κλήσεων. Οι πρώτες τρεις που θα µας απασχολήσουν είναι η recv, η recvfrom και η recvmsg. Η κλήση recvmsg χρησιµοποιείται για να ληφθεί ένα µήνυµα από κάποιο socket ενός client. Όπως και µε την κλήση send, η recv χρησιµοποιείται για συνδεδεµένα socket σε TCP επικοινωνία, ενώ η recvfrom µπορεί να χρησιµοποιηθεί και µε επικοινωνία UDP ώστε να µπορεί να λαµβάνει πακέτα από οποιοδήποτε σύστηµα του δικτύου. Τα πρωτότυπά τους είναι τα ακόλουθα: int recv(int s, void *buf, size_t len, int flags); int recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); int recvmsg(int s, struct msghdr *msg, int *flags); Τα ορίσµατα from και fromlen, όταν ολοκληρωθεί επιτυχώς η εκτέλεση της κλήσης recvfrom, περιέχουν την διεύθυνση του αποστολέα και το µήκος της αντίστοιχα. Οι τρεις κλήσεις που µόλις αναφέραµε, συχνά χρησιµοποιούνται σε συνδυασµό µε άλλες έτσι ώστε να είναι πιο αποτελεσµατικές. Τέλος, θα αναφέρουµε δύο κλήσεις οι οποίες χρησιµοποιούνται για να επιτρέπουν στους προγραµµατιστές και στις εφαρµογές τους, την παραµετροποίηση των πρωτοκόλλων επικοινωνίας που βρίσκονται κάτω από το επίπεδο socket και τη διεπαφή socket. Οι κλήσεις αυτές είναι η getsockopt και setsockopt: int getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen); Επικοινωνία: [email protected] 134 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux – Κεφάλαιο 4 int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen); Το όρισµα level παίρνει τιµές (που καθορίζονται από τον οργανισµό iana.org) οι οποίες φαίνονται πιο κάτω: #define #define #define #define #define #define #define SOL_IP SOL_SOCKET SOL_TCP SOL_UDP SOL_IPV6 SOL_ICMPV6 SOL_RAW 0 1 6 17 41 58 255 Το όρισµα optname παίρνει τιµές οι οποίες για τον Router NGW100 καθορίζονται στο αρχείο arch/avr32/include/asm/socket.h. Το επίπεδο socket κάθε φορά που επιχειρείται η τροποποίηση τιµών, ελέγχει αν η εφαρµογή που αιτήθηκε τις τροποποιήσεις αυτές έχει τα απαραίτητα δικαιώµατα προκειµένου έτσι να παρέχεται και ένα επίπεδο ασφάλειας. Τα δικαιώµατα αυτά ονοµάζονται capabilities (παρόµοια µε τις λίστες ACL άλλων λειτουργικών) και για τον µηχανισµό αυτό υπάρχει αναλυτική τεκµηρίωση στον ιστότοπο linux.die.net/man/7/capabilities. 4.2.7 Socket buffers Ο Linux Kernel αποθηκεύει όλες τις πληροφορίες που αφορούν ένα πακέτο σε µια δοµή δεδοµένων µε όνοµα skbuff (ή SKB εν συντοµία) η οποία χαρακτηρίζεται ως ένα socket buffer. Η δοµή αυτή ορίζεται στο αρχείο include/linux/skbuff.h: struct skb_frag_struct { struct page *page; __u16 page_offset; __u16 size; }; 4.2.8 Τα είδη των sockets Αυτή η παράγραφος λειτουργεί συµπληρωµατικά µε την προηγούµενη. Εδώ θα εξετάσουµε τα ειδικά sockets που µας παρέχει το Linux. Τα sockets αυτά χρησιµοποιούνται για εσωτερική µεταφορά µηνυµάτων και για άµεση πρόσβαση σε πρωτόκολλα. Στην εικόνα µπορούµε να δούµε µια γραφική αναπαράσταση της χρήσης τους µέσω εφαρµογών στο Linux καθώς και της σχέσης τους µε το πρωτόκολλο TCP/IP το οποίο υπάρχει υλοποιηµένο στον Kernel: Ιστότοπος εργασίας: MyThesis.org 135 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 40. Γραφική αναπαράσταση της χρήσης Sockets από εφαρµογες Linux Packet Sockets Είναι προσβάσιµα από εφαρµογές όταν το πεδίο family της κλήσης socket τίθεται σε AF_PACKET: ps = socket(PF_PACKET, int type, int protocol); Το πεδίο type µπορεί να πάρει την τιµή SOCK_RAW ή SOCK_DGRAM. Το πεδίο Protocol παίρνει την ίδια τιµή µε το πρωτόκολλο που χρησιµοποιείται, η οποία µπορεί να είναι ίδια µε τον αριθµό πρωτοκόλλου που υπάρχει στην κεφαλίδα IP ή κάποιος άλλος έγκυρος αριθµός πρωτοκόλλου. Περισσότερες πληροφορίες για τα Packet Sockets µπορούν να βρεθούν και στη σελίδα linux.die.net/man/7/packet. Raw Sockets Επιτρέπουν σε εφαρµογές χρήστη να αποστείλουν ή να παραλάβουν πακέτα επιπέδου δικτύου: rs = socket(PF_INET, SOCK_RAW, int protocol); Το πεδίο protocol παίρνει την τιµή του πρωτοκόλλου µέσω του οποίου η εφαρµογή θέλει να επικοινωνήσει. Ένα παράδειγµα χρήσης Raw Socket είναι και η εντολή ping για την οποία µπορούµε να βρούµε πληροφορίες στη σελίδα linux.die.net/man/8/ping (Επί- Επικοινωνία: [email protected] 136 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux – Κεφάλαιο 4 σης: linux.die.net/man/7/raw). Κατά την εκτέλεση της εντολής ping το πεδίο protocol παίρνει την τιµή IPPROTO_ICMP. Netlink Sockets και πρωτόκολλο Netlink Το Linux προκειµένου να µπορεί να υποστηρίξει την πολυπλοκότητα των σύγχρονων δικτύων αλλά και τις απαιτήσεις παραµετροποίησης που έχει ένας router, παρέχει πολλαπλούς πίνακες δροµολόγησης και πολλαπλές µνήµες cache. Μπορεί επίσης να παρέχει τη δυνατότητα δηµιουργίας κανόνων δροµολόγησης (routing rules) οι οποίοι συνδέονται µε τους πίνακες δροµολόγησης και ταξινοµούνται σε µορφή βάσης δεδοµένων. Όλα αυτά συνήθως επιτυγχάνονται από εφαρµογές που γράφουµε ή που υπάρχουν ήδη έτοιµες και οι οποίες επικοινωνούν µε τον Kernel µέσω της διεπαφής Netlink Sockets: ns = socket(AF_NETLINK, int type, int netlink_family); Η παράµετρος type µπορεί να πάρει την τιµή SOCK_DGRAM, ή την τιµή SOCK_STREAM. Η παράµετρος netlink_family µπορεί να πάρει τιµές οι οποίες καθορίζονται στο αρχείο include/linux/netlink.h (πχ: NETLINK_FIREWALL). Τα Νetlink Sockets οφείλουν την ονοµασία τους στο εσωτερικό πρωτόκολλο επικοινωνίας Netlink. Σκοπός του πρωτοκόλλου αυτού, είναι η µεταφορά και η λήψη πακέτων µεταξύ των εφαρµογών και των διαφόρων πρωτοκόλλων που υπάρχουν στον Linux Kernel. Η οικογένεια διευθύνσεων του Netlink είναι η AF_NETLINK. Το Netlink υποστηρίζει τις περισσότερες συναρτήσεις της διεπαφής socket και η λειτουργικότητά του µπορεί να εµπλουτιστεί µέσω της επέκτασης Rtnetlink. Η πιο κοινή χρήση του Netlink είναι από εφαρµογές που θέλουν να ανταλλάξουν πληροφορίες δροµολόγησης µε τον εσωτερικό πίνακα δροµολόγησης του Kernel. Ένα Netlink Socket είναι προσβάσιµο όπως και οποιοδήποτε άλλο socket. Το πρωτόκολλο Netlink βρίσκεται υλοποιηµένο στο αρχείο af_netlink.c το οποίο υπάρχει στον κατάλογο net/netlink του πηγαίου κώδικα του Linux Kernel. Ουσιαστικά είναι σαν οποιοδήποτε άλλο πρωτόκολλο της στοίβας TCP/IP µε τη διαφορά ότι υπάρχει για την ανταλλαγή µηνυµάτων µεταξύ διεργασιών που εκτελούνται σε επίπεδο χρήστη (user-level ή user-space processes) και εσωτερικών οντοτήτων του Kernel. Επίσης είναι όµοιο µε το UDP και το TCP µε την έννοια ότι και αυτό πρέπει να καθορίσει τη δοµή δεδοµένων proto_ops προκειµένου να συνδέσει τις εσωτερικές κλήσεις µε τις κλήσεις των sockets οι οποίες πραγµατοποιούνται µέσω της AF_NETLINK. Περισσότερες πληροφορίες για το Netlink και τη δηµιουργία εφαρµογών που βασίζονται σε αυτό, υπάρχουν στις σελίδες: linux.die.net/man/7/netlink, linuxfoundation.org και infradead.org/~tgr/libnl. Routing Sockets Τα sockets δροµολόγησης καθορίζονται από την οικογένεια AF_ROUTE. Στο Linux τα sockets δροµολόγησης είναι πανοµοιότυπα µε τα netlink sockets. Ιστότοπος εργασίας: MyThesis.org 137 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Rtnetlink Sockets Τα sockets αυτά επεκτείνουν τα netlink sockets. Αυτό το επιτυγχάνουν προσθέτοντας κάποιους επιπλέον τύπους µηνυµάτων που έχουν περισσότερες ιδιότητες και είναι συµβατά µε τα netlink sockets. Τα Rtnetlink sockets χρησιµοποιούνται από εφαρµογές που πρέπει να έχουν πρόσβαση στους πίνακες δροµολόγησης του Kernel. Περισσότερες πληροφορίες υπάρχουν κι εδώ: linux.die.net/man/7/rtnetlink. Και ένα παράδειγµα χρήσης µακροεντολών Rtnetlink, εδώ: linux.die.net/man/3/rtnetlink. 4.2.9 Το πρόγραµµα netstat Το πρόγραµµα – εντολή netstat (wikipedia.org/wiki/Netstat), είναι ένα δωρεάν και στις περισσότερες περιπτώσεις, προεγκατεστηµένο εργαλείο γραµµής εντολών, το οποίο µπορούµε να χρησιµοποιούµε σε περιβάλλοντα Linux. Το πρόγραµµα netstat χρησιµεύει στην ανάπτυξη εφαρµογών (και κυρίως στο debugging) που η λειτουργία τους βασίζεται και στην χρήση sockets. Όταν εκτελείται χωρίς την προσθήκη παραµέτρων, εµφανίζει σε µια λίστα όλες τις πληροφορίες για τα συνδεδεµένα sockets των εφαρµογών που εκτελούνται την τρέχουσα χρονική στιγµή στο σύστηµα. 4.2.10 ∆ιεπαφή socket και IPv6 Η νέα έκδοση του πρωτοκόλλου IP, υποστηρίζεται πλήρως από τον Linux Kernel. Το πρωτόκολλο IPv6 ανήκει στην οικογένεια AF_INET6 η οποία ορίζεται στο αρχείο socket.h του καταλόγου include, µαζί µε τις υπόλοιπες διευθύνσεις που έχουµε αναφέρει ως τώρα. Για να υποστηρίζεται καλύτερα από τον kernel, το IPv6 διαθέτει τη δική του µορφή διεύθυνσης socket η οποία ονοµάζεται sockaddr_in6 και ορίζεται στο αρχείο in6.h του καταλόγου include/linux. Επίσης, για την ανεξαρτησία και τη διευκόλυνση των προγραµµατιστών από τα πολλά και διαφορετικά πρωτόκολλα, το Linux παρέχει µια βιβλιοθήκη συναρτήσεων, προκειµένου να είναι εφικτή η µετατροπή µεταξύ διευθύνσεων IPv4 και IPv6. Οι συναρτήσεις αυτές είναι η getaddrinfo, η freeaddrinfo και η getnameinfo. 4.3 ∆ροµολόγηση πακέτων Σύµφωνα µε το µοντέλο αναφοράς OSI, η δροµολόγηση και η διευθυνσιοδότηση των πακέτων πραγµατοποιείται από το επίπεδο ∆ικτύου δηλαδή από το πρωτόκολλο IP. Ο Router NGW100 είναι ένα ενσωµατωµένο σύστηµα στο οποίο έχουµε εγκαταστήσει το λειτουργικό σύστηµα Linux. Εποµένως οι λειτουργίες δροµολόγησής του βασίζονται στο πρωτόκολλο IP που υπάρχει υλοποιηµένο στην στοίβα TCP/IP του Kernel. Στο Linux το IP είναι υπεύθυνο για αρκετές διεργασίες. Μία από τις σηµαντικότερες είναι και η διαχείριση του πίνακα (ή των πινάκων) δροµολόγησης (routing table) που θα εξετάσουµε και στη συνέχεια. Επικοινωνία: [email protected] 138 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux – Κεφάλαιο 4 4.3.1 Πίνακες δροµολόγησης Σε έναν πίνακα δροµολόγησης αποθηκεύονται πληροφορίες που εξυπηρετούν τη λειτουργία του IP. Για να λειτουργούν όλα σωστά, ένας τέτοιος πίνακας θα πρέπει να παρέχει τις παρακάτω δυνατότητες: Λήψη και παροχή πληροφοριών από και προς ένα εξωτερικό πρωτόκολλο αντίστοιχα Ταχεία αναζήτηση στα πεδία που τον απαρτίζουν Υποστήριξη στατικής και δυναµικής δροµολόγησης Χρονιστής για την λήξη δροµολογήσεων που δεν ισχύουν πια Μετρητή χρήσης δροµολογήσεων ∆ιαφοροποίηση δροµολογήσεων ανάλογα µε το είδος τους Υποστήριξη πολλαπλών διεπαφών δικτύου Αποθήκευση επόµενου βήµατος πακέτων (next hop) αλλά και διευθύνσεων άλλων δροµολογητών Η υλοποίηση του πίνακα δροµολόγησης στο Linux περιλαµβάνει όλες τις δυνατότητες που αναφέρθηκαν. Ένας πίνακας δροµολόγησης στο Linux απαρτίζεται από την route cache και από την βάση δεδοµένων RPDB (Routing Policy Database). Route cache Tο Linux διαθέτει µια µνήµη δροµολόγησης γνωστή και ως route cache η οποία αποτελεί όπως αναφέραµε, το ένα από τα δύο κύρια µέλη του πίνακα δροµολόγησης και επιταχύνει τις αποφάσεις προώθησης των πακέτων χρησιµοποιώντας προ-αποθηκευµένες (cached) δροµολογήσεις που είναι ήδη γνωστές. Πιο συγκεκριµένα, όταν µια δροµολόγηση έχει αποφασιστεί, τοποθετείται στην route cache για µελλοντική, γρήγορη και εύκολη πρόσβαση. Αυτό σηµαίνει ότι τα πακέτα τα οποία στέλνονται µέσω της ίδιας δροµολόγησης, µπορούν να προωθούνται άµεσα και χωρίς καθυστερήσεις που θα προέκυπταν από περεταίρω ελέγχους στην βάση FIB της RPDB. Αυτό βελτιώνει την απόδοση του συστήµατος, ιδιαίτερα όταν υπάρχει µεγάλος όγκος πακέτων και πολλά ταυτόχρονα ανοικτά sockets. Η route cache θεωρείται ως το frontend της βάσης FIB που θα εξετάσουµε αργότερα. Ουσιαστικά κληρονοµεί την λειτουργικότητα της υλοποίησης cache που υπάρχει ήδη στον Linux Kernel. Αποτελείται µε τη σειρά της, από έναν πίνακα κατακερµατισµού (hash table) στον οποίο αποθηκεύονται εγγραφές δροµολόγησης. Ο πίνακας αυτός είναι σχεδιασµένος για ταχύτατη προσπέλαση και αναζήτηση µε ένα απλό κλειδί. Η υλοποίηση της route cache στον Linux Kernel αποτελείται από κάποιες δοµές δεδοµένων (rt_hash_table, rt_hash_bucket, rtable και flowi) και κάποιες συναρτήσεις (ipv4_dst_destroy, ipv4_dst_ifdown, ipv4_negative_advise, ip_rt_put, rt_binf_peer, ip_route_connect, rt_intern_hash και rt_set_nexthop) οι οποίες πραγµατοποιούν όλη τη λειτουργικότητα της θεωρίας που παραθέσαµε σε αυτή την παράγραφο. ∆εν κρίνεται όµως απαραίτητο να αναλυθεί εδώ ο κώδικάς τους καθώς µπορεί να βρεθεί αυτούσιος στο αρχείο net/ipv4/route.c. RPDB Η υλοποίηση του IP στο Linux ουσιαστικά διαχειρίζεται έναν πίνακα δροµολόγησης. Οι πληροφορίες δροµολόγησης καθώς και η απαραίτητη πολιτική δροµολόγησης, περιέχονται στην βάση RPDB. Τα δύο κυριότερα τµήµατα της RPDB είναι η βάση FIB (Forwarding Infor- Ιστότοπος εργασίας: MyThesis.org 139 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) mation Base) και οι κανόνες (rules) FIB. Η βάση FIB είναι ο πυρήνας της δροµολόγησης στο Linux. Σε αρκετές χρήσεις του Linux Kernel δεν υπάρχει ανάγκη δροµολόγησης που να είναι βασισµένη σε κανόνες. Αυτό ισχύει και για τα περισσότερα ενσωµατωµένα συστήµατα Linux. Η RPDB µπορεί να υποστηρίζει στατική αλλά και δυναµική δροµολόγηση ταυτόχρονα. Η απόφαση για το είδος της δροµολόγησης λαµβάνεται συνήθως από την κάθε εφαρµογή του χρήστη. Στη στατική δροµολόγηση η RPDB ενηµερώνεται χειροκίνητα από τον χρήστη µέσω ειδικών προγραµµάτων παραµετροποίησης. Στον Router NGW100 για παράδειγµα αυτό επιτυγχάνεται µέσω του προγράµµατος route το οποίο µπορεί να χρησιµοποιείται και σε συνδυασµό µε το πρόγραµµα ifconfig. Με µια γρήγορη µατιά µέσω της γραµµής εντολών που προσφέρει το BusyBox που υπάρχει στον Router NGW100 µπορούµε να πληροφορηθούµε για τις βασικές λειτουργίες του προγράµµατος route: Εικόνα 41. Η εντολή route στον Router NGW100 Το ίδιο µπορούµε να κάνουµε και για το πρόγραµµα ifconfig: Εικόνα 42. Η εντολή ifconfig στον Router NGW100 Από την άλλη, η δυναµική δροµολόγηση χρησιµοποιεί εξωτερικά πρωτόκολλα δροµολόγησης, όπως είναι τα OSPF και BGP, που βασίζονται σε περίπλοκους αλγόριθµους δροµολόγησης (πχ: αλγόριθµος Dijkstra), τα οποία ενηµερώνουν δυναµικά την RPDB αφού πρώτα επικοινωνήσουν και ανταλλάξουν ορισµένες απαραίτητες πληροφορίες δροµολόγησης µε γειτονικούς routers. Εποµένως είναι εύκολο κανείς να συµπεράνει ότι η στατική δροµολόγη- Επικοινωνία: [email protected] 140 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux – Κεφάλαιο 4 ση χρησιµοποιείται από οικιακούς routers ενώ η δυναµική, από τηλεπικοινωνιακούς παρόχους και εταιρείες µε προχωρηµένες απαιτήσεις δικτύωσης. Η βάση FIB αναλυτικά Στη βάση FIB αποθηκεύονται οι πληροφορίες δροµολόγησης, ενώ οι κανόνες FIB που αποτελούν και την πολιτική δροµολόγησης, χρησιµοποιούνται για την επιλογή ενός πίνακα δροµολόγησης όταν υπάρχουν περισσότεροι του ενός. Η βάση FIB είναι το βασικό εσωτερικό σηµείο αποθήκευσης και αναζήτησης ενός πίνακα δροµολόγησης. Μπορεί να χρησιµοποιηθεί για τη δροµολόγηση εισερχοµένων αλλά και εξερχοµένων πακέτων. Η βάση FIB είναι αυτή η οποία παρέχει τη δυνατότητα σε εφαρµογές που βρίσκονται εκτός του Kernel να ανακτήσουν πληροφορίες για δροµολογήσεις που πραγµατοποιούνται µέσα στον Kernel µέσω των sockets δροµολόγησης. Σε ένα σύστηµα Linux υπάρχει δυνατότητα υποστήριξης πολλαπλών πινάκων FIB αλλά από προεπιλογή υποστηρίζονται µόνο δύο. Ο τοπικός (local table) που περιέχει δροµολογήσεις τοπικών διευθύνσεων IP (και τη διεύθυνση loopback), και ο κύριος πίνακας (main table) περιέχει δροµολογήσεις προς εξωτερικά συστήµατα που βρίσκονται σε άλλα δίκτυα. Οι δύο αυτοί πίνακες είναι προσβάσιµοι µέσω δύο global δεικτών. Και οι δύο δηλώνονται στο αρχείο fib_frontend.c που βρίσκεται στον κατάλογο net/ipv4 του πηγαίου κώδικα του Linux Kernel. Οι δηλώσεις αυτές είναι οι παρακάτω: struct fib_table *ip_fib_local_table; struct fib_table *ip_fib_main_table; Σε περίπτωση που χρειάζεται να υπάρχουν πολλαπλοί πίνακες FIB, δηµιουργείται επίσης και ένας πίνακας δεικτών FIB µε αρίθµηση από το 1 έως το 256 (FIB index). Ο πίνακας αυτός λειτουργεί ως ευρετήριο. Μπορούµε να αναφερόµαστε σε κάθε πίνακα δροµολόγησης µέσω δύο διαφορετικών ID. Το ένα για τον πίνακα local και το άλλο για τον πίνακα main.Τα οποία αποθηκεύονται στις δύο τελευταίες θέσεις του πίνακα. Επίσης υπάρχει και ο πίνακας default για τον οποίο δεσµεύεται επίσης ένα ID. Αυτό µπορούµε να το δούµε µε µεγαλύτερη λεπτοµέρεια και στο τµήµα κώδικα που ακολουθεί /* Reserved table identifiers */ enum rt_class_t { RT_TABLE_UNSPEC = 0, /* User defined values */ RT_TABLE_COMPAT = 252, RT_TABLE_DEFAULT = 253, RT_TABLE_MAIN = 254, RT_TABLE_LOCAL = 255, RT_TABLE_MAX = 0xFFFFFFFF /* 256 */ }; Οι τοποθεσίες των πινάκων FIB καθορίζονται στο αρχείο include/linux/rtnetlink.h. Όταν κάποια εφαρµογή (πχ ένας daemon δροµολόγησης) δηµιουργεί δροµολογήσεις, αυτό που κάνει ουσιαστικά είναι να περνά µηνύµατα σε κάποια FIB µέσω των netlink sockets. Καθώς δηµιουργείται µια νέα FIB, θα παίρνει τιµές µεταξύ του 1 και του 252: struct fib_table *fib_tables[RT_TABLE_MAX+1] Οι εσωτερικές δοµές µιας FIB είναι η fib_table, η fib_info και η fib_nh. Ιστότοπος εργασίας: MyThesis.org 141 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Κλείνοντας την παράγραφο αυτή θα ασχοληθούµε λίγο περισσότερο µε την βάση FIB και τις ιδιαιτερότητές της. Γνωρίζουµε ότι οι κανόνες FIB (net/ipv4/fib_rules.c) είναι µέρος της FIB και όπως ήδη αναφέραµε, αποτελούν την πολιτική δροµολόγησης όταν υπάρχουν πολλαπλοί πίνακες FIB. Ουσιαστικά κάθε πίνακας FIB λειτουργεί σαν ένας εικονικός router. Πρακτικά λοιπόν, οι κανόνες FIB είναι ένας τρόπος επιλογής πινάκων FIB. Όταν έχουµε πολλαπλές FIB οι πίνακες main και local συνεχίζουν να υφίστανται αλλά η προσπέλαση των υπόλοιπων γίνεται µέσω πρόσβασης στους κανόνες FIB. Μια βάση FIB αποτελείται από την δοµή fib_rule, τα στιγµιότυπα (instances) της δοµής αυτής, και τις συναρτήσεις που χειρίζονται τη βάση των κανόνων. Τα στιγµιότυπα (default rule, main_rule και local_rule) αφορούν την υλοποίηση ενός από τους τρεις βασικούς πίνακες (default, main και local, αντίστοιχα). Επίσης, µερικές από τις συναρτήσεις είναι η inet_rtm_delrule, η inet_rtm_newrule, η fib_rules_tclass, η fib_lookup και η fib_select_default. Η βάση FIB όπως και οι περισσότεροι µηχανισµοί του Linux, πρέπει να αρχικοποιηθεί µαζί µε το πρωτόκολλο IPv4 πριν να είναι σε θέση να χρησιµοποιηθεί. Η αρχικοποίηση αυτή επιτυγχάνεται µέσω της συνάρτησης ip_fb_init (καθορίζεται στο αρχείο fib_frontend.c το οποίο βρίσκεται στον κατάλογο net/ipv4). Η αρχικοποίηση του κάθε ξεχωριστού πίνακα FIB γίνεται µέσω της συνάρτησης fib_hash_init. Οι προγραµµατιστικές διεπαφές της FIB Η βάση FIB παρέχει δύο προγραµµατιστικές διεπαφές για επικοινωνία µε τις εφαρµογές επιπέδου χρήστη αλλά και µε τον Kernel. Όπως αναφέραµε και νωρίτερα, η επικοινωνία των εφαρµογών χρήστη και των διάφορων daemons δροµολόγησης, επιτυγχάνεται µέσω της διεπαφής των netlink sockets. Γενικά στο Linux, όταν µια εφαρµογή δηµιουργεί ή καταργεί µια δροµολόγηση, χρησιµοποιεί την δοµή rtmsg προκειµένου να στείλει αντίστοιχο µήνυµα δηµιουργίας ή διαγραφής µέσω ενός rtnetlink socket στη βάση FIB, η οποία εκτελείται σε επίπεδο Kernel. Επίσης γίνεται χρήση της δοµής rtentry η οποία καθορίζεται στο αρχείο include/linux/route.h. Η διεπαφή της FIB προς τον Kernel έχει σκοπό να επιτρέψει σε κάποιες εσωτερικές συναρτήσεις του Kernel να προσπελαύνουν ορισµένες πληροφορίες δροµολόγησης που τους είναι απαραίτητες. Αυτό συµβαίνει για παράδειγµα όταν το πρωτόκολλο IP πρέπει να εκτελέσει κάποιες λειτουργίες δροµολόγησης ή όταν κάποια γνωστή δροµολόγηση πρέπει να µεταφερθεί στην route cache προκειµένου να µπορεί να επιταχυνθεί η ανάκτησή της. 4.3.2 ∆ροµολογητές και δροµολόγηση IP Όπως γνωρίζουµε, δροµολόγηση είναι η απόφαση η οποία αφορά την διεύθυνση IP πού θα προωθηθεί ένα πακέτο. Για το λόγο αυτό έχουν δηµιουργηθεί οι συσκευές router. Μια τέτοια συσκευή είναι και ο Router NGW100. Τα πακέτα που µπορεί να λάβει ο Router NGW100 χωρίζονται σε εισερχόµενα (incoming traffic) και σε εξερχόµενα (outgoing traffic). Με τους όρους εισερχόµενα και εξερχόµενα εννοούµε πακέτα που προέρχονται από το WAN ή από το LAN αντίστοιχα. Το πρωτόκολλο IP είναι υπεύθυνο επίσης και για µια σειρά άλλων ζητηµάτων όπως είναι για παράδειγµα ο κατακερµατισµός και η ανακατασκευή (re-assembly) πακέτων που ξεπερνούν το µέγιστο επιτρεπτό µήκος (MTU) το οποίο καθορίζεται κάθε φορά από τους φυσικούς περιορισµούς του υλικού µετάδοσης. Ένα άλλο ζήτηµα µε το οποίο ασχολείται το πρωτόκολ- Επικοινωνία: [email protected] 142 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux – Κεφάλαιο 4 λο IP είναι και ο χειρισµός του χρονικού ορίου µετάδοσης TTL (Time To Live) που εξασφαλίζει ότι ένα πακέτο δε θα δροµολογείται επ’ άπειρο µέσα στο Internet. Αν έχει επιλεγεί σε ένα σύστηµα να υποστηρίζεται multicast µετάδοση τότε το IP συνεργάζεται µε το πρωτόκολλο ICMP (ή το IGMP) προκειµένου αυτό να επιτευχθεί. Όπως είπαµε νωρίτερα, δροµολόγηση είναι η απόφαση που αφορά το που θα σταλεί ένα πακέτο. Αυτή η απόφαση βασίζεται στην διεύθυνση προορισµού (destination address) του πακέτου. Φυσικά στο σύγχρονο κόσµο του Internet η δροµολόγηση µπορεί ορισµένες φορές να είναι µια υπερβολικά περίπλοκη υπόθεση και να βασίζεται σε πολύ περισσότερες παραµέτρους από την διεύθυνση προορισµού. Οι παράµετροι αυτοί βρίσκονται στην κεφαλίδα (IP header) που προσθέτει το πρωτόκολλο IP όταν ένα πακέτο εξέρχεται από το επίπεδο ∆ικτύου της στοίβας TCP/IP προς το Φυσικό επίπεδο. ∆ροµολόγηση εισερχόµενων πακέτων (LAN WAN) Όταν το πρωτόκολλο TCP στον Router NGW100, λάβει κάποια κλήση µέσω των sockets accept ή connect για την παραλαβή ενός έγκυρου πακέτου, στέλνει µια αίτηση δροµολόγησης στο IP. Στη συνέχεια ο χειριστής εισερχοµένων πακέτων του IP αποφασίζει τι θα κάνει µε αυτό. Υπάρχουν δύο στάδια κατά την απόφαση της δροµολόγησης. Το πρώτο στάδιο χαρακτηρίζεται ως σύντοµη διαδικασία δροµολόγησης (fast path routing) ενώ το δεύτερο ως αργή διαδικασία δροµολόγησης (slow path routing). Κατά το πρώτο στάδιο γίνεται απλά ένας έλεγχος στη route cache για να διαπιστωθεί αν η ίδια δροµολόγηση για το πακέτο που λήφθηκε είναι ήδη φορτωµένη λόγω κάποιας πρόσφατης χρήσης της. Αν δεν βρεθεί κάτι στη route cache τότε εκτελείται το δεύτερο στάδιο που είναι πιο χρονοβόρο και κατά το οποίο ελέγχεται η βάση FIB. Η συνάρτηση που χρησιµοποιείται στο πρώτο στάδιο είναι η ip_route_input και ορίζεται στο αρχείο net/ipv4/route.c. Στο δεύτερο στάδιο, δηλαδή αν δεν βρεθεί εγγραφή στην route cache, χρησιµοποιείται η συνάρτηση ip_route_input_slow. Η συνάρτηση αυτή καλείται από την ip_route_input. ∆ροµολόγηση εξερχόµενων πακέτων (LAN WAN) Όταν ο Router NGW100 λάβει ένα πακέτο καλεί το πρωτόκολλο IP προκειµένου να αποφασίσει που θα το προωθήσει. Αν το πακέτο αφορά το τοπικό δίκτυο τότε όλα είναι απλά και το πακέτο προωθείται στην αντίστοιχη διεύθυνση υλικού MAC, που έχει χαρτογραφηθεί νωρίτερα από το πρωτόκολλο ARP (Address Resolution Protocol). Πρέπει να πούµε, ότι η προσθήκη της κεφαλίδας MAC σε κάθε πλαίσιο (frame) που δηµιουργείται, είναι ευθύνη του driver δικτύωσης (στην περίπτωσή µας ο driver ονοµάζεται MACB). Το πρωτόκολλο IP χωρίς να γνωρίζει τις πληροφορίες του κάθε πλαισίου απλά περνά ένα δείκτη προς τη διεύθυνση προορισµού η οποία καταχωρείται στην RPDB. Αν το πακέτο αφορά κάποια διεύθυνση που βρίσκεται εκτός LAN, καλείται και πάλι το πρωτόκολλο IP και στη συνέχεια το πακέτο αυτό προωθείται στο ανάλογο hardware interface (WAN) για να µεταδοθεί στο ∆ιαδίκτυο. Tο IP θα πρέπει επίσης να αποφασίσει ποιο θα είναι το επόµενο hop του πακέτου. Σε περίπτωση που το επιλεγµένο πρωτόκολλο επικοινωνίας είναι το TCP, πριν την αποστολή του πακέτου εγκαθίσταται µια σύνοδος (session) µεταξύ του Router NGW100 και του παραλήπτη. Αυτό επιτυγχάνεται µε την λεγόµενη τριπλή χειραψία (three way handshake) του TCP. Το TCP καλεί το IP για τη διαδικασία της δροµολόγησης µόνο όταν κληθεί το socket µε την ονοµασία connect, από κάποια εφαρµογή της περιοχής χρήστη. Το TCP κάνει χρήση µόνο της route cache γιατί η δροµολογήσεις του είναι προκαθορισµένες. Ιστότοπος εργασίας: MyThesis.org 143 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Αντίθετα το UDP δεν είναι βασισµένο στην σύνδεση. Έτσι ο Router NGW100 για κάθε πακέτο (datagram ή δεδοµενόγραµµα) UDP που έχει να στείλει, αιτείται εκ νέου την δροµολόγησή του από το IP. Ανεξάρτητα από το εάν χρησιµοποιείται το πρωτόκολλο TCP ή το UDP για την επικοινωνία, η κύρια συνάρτηση η οποία χειρίζεται και αποφασίζει για τις δροµολογήσεις των εξερχόµενων πακέτων, είναι η ip_route_connect και ορίζεται στο αρχείο include/net/route.h . Επίσης η συνάρτηση ip_route_output_flow που ορίζεται στο αρχείο route.c (βρίσκεται στον κατάλογο net/ipv4), φροντίζει για µετατροπές της δροµολόγησης υπό κάποιες συνθήκες (πχ αν γίνεται χρήση NAT). Για το fast path των εξερχόµενων πακέτων (δηλαδή για δροµολόγηση µέσω της route cache) χρησιµοποιείται η συνάρτηση __ip_route_output_key. Ενώ για το slow path (αναζήτηση στη FIB), η συνάρτηση ip_route_output_slow. Όπως και µε τις αντίστοιχες συναρτήσεις της δροµολόγησης εισερχοµένων πακέτων ο ορισµός των συναρτήσεων που αναφέραµε πραγµατοποιείται στο αρχείο net/ipv4/route.c. Η κεφαλίδα IP Στα πλαίσια αυτής της εργασίας, και µε τη βοήθεια του αναλυτή πακέτων Wireshark και της εφαρµογής Putty, έγινε καταγραφή ενός πακέτου που στάλθηκε από τον Router NGW100 µέσω της εφαρµογής ping που υπάρχει ενσωµατωµένη στο BusyBox. Η εφαρµογή ping βασίζεται στο πρωτόκολλο ICMP. Στην εικόνα που ακολουθεί µπορούµε να δούµε τα αιτήµατα ping καθώς και τα στατιστικά αποτελέσµατα: Εικόνα 43. Η εντολή ping στον Router NGW100 Στη συνέχεια, όπως φαίνεται και στην εικόνα που ακολουθεί, επιλέγοντας το “Internet Protocol” µέσα από το Wireshark µπορούµε να δούµε όλες τις πληροφορίες της κεφαλίδας IP ενός αιτήµατος (request) από τα τρία που στάλθηκαν συνολικά: Επικοινωνία: [email protected] 144 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux – Κεφάλαιο 4 Εικόνα 44. Ανάλυση κεφαλίδας IP µέσω του αναλυτή πακέτων Wireshark Αν και η όλη διαδικασία πραγµατοποιήθηκε σε τοπικό επίπεδο, µπορούµε να διακρίνουµε όλα τα σηµαντικά πεδία της κεφαλίδας IP. Η δροµολόγηση ενός πακέτου µπορεί να βασιστεί στα περισσότερα από αυτά. Όπως είναι για παράδειγµα η διεύθυνση αποστολέα (source IP) και το πεδίο ToS (Type of Service). Επίσης η δροµολόγηση µπορεί να βασιστεί στη διεπαφή υλικού (πχ Ethernet, WiFi, MODEM κλπ). Σε πολλές περιπτώσεις µπορεί να µεσολαβεί NAT (Network Address Translation) προκειµένου να αλλάζει δυναµικά η διεύθυνση των πακέτων και να επιτυγχάνεται ασφαλής προώθηση πακέτων χωρίς να απαιτείται το άνοιγµα κάποιας θύρας (port forwarding) του router. Εκτός από το Wireshark ένα άλλο πολύ χρήσιµο εργαλείο ανάλυσης πακέτων και πρωτοκόλλων είναι το tcpdump το οποίο λειτουργεί σε περιβάλλον γραµµής εντολών και τις περισσότερες φορές βρίσκεται προεγκατεστηµένο σε κάθε έκδοση Linux. Αποστολή και λήψη πακέτων µέσω του πρωτοκόλλου IP Σε αυτή την παράγραφο, όπως και σε άλλες παραγράφους αυτής της εργασίας, δεν περιγράφεται ολόκληρος ο πηγαίος κώδικας αλλά παρατίθενται απλά οι κυριότερες συναρτήσεις του καθώς και µια σύντοµη περιγραφή για την καθεµιά τους. Υπάρχουν πολύ αξιόλογα συγγράµµατα (στα αγγλικά) που αναλύουν λεπτοµερώς τον πηγαίο κώδικα ολόκληρης της στοίβας TCP/IP και οι τίτλοι κάποιων από αυτά, αναφέρονται και στη βιβλιογραφία αυτής της εργασίας. Εδώ, θα ασχοληθούµε απλά µε τις συναρτήσεις που χρησιµοποιούνται για την αποστολή πακέτων, την λήψη πακέτων µέσω του πρωτοκόλλου IP στο Linux. Επίσης θα α- Ιστότοπος εργασίας: MyThesis.org 145 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) ναφέρουµε και τις συναρτήσεις που χρησιµοποιούνται για την προώθηση των πακέτων σε υψηλότερα στρώµατα της στοίβας TCP/IP. Η αποστολή πακέτων µέσω IP επιτυγχάνεται ουσιαστικά µέσω δύο συναρτήσεων. Οι συναρτήσεις αυτές είναι η ip_append_data και η ip_append_page. Χρησιµοποιούνται για να ετοιµάσουν τα δεδοµένα που θα αποσταλούν καθώς και για την προώθησή των δεδοµένων αυτών στον driver δικτύωσης. Ορίζονται στο αρχείο net/ipv4/ip_output.c. Η λήψη πακέτων µέσω του IP επιτυγχάνεται από έναν αριθµό συναρτήσεων που φροντίζουν να παραλάβουν πλαίσια από τον driver δικτύωσης και αφού τα επεξεργαστούν (και δεν απορριφθούν) στην συνέχεια να τα προωθήσουν σε ανώτερα επίπεδα (αν αυτό κρίνεται απαραίτητο) µε την µορφή πακέτων. Για την λήψη χρησιµοποιούνται οι συναρτήσεις ip_rcv και ip_rcv_finish. Αφού ολοκληρωθεί και η λειτουργία της ip_rcv_finish, καλούνται διαδοχικά ακόµη δύο συναρτήσεις προκειµένου να αποφασιστεί, τα πακέτα που παρελήφθησαν, σε ποια ανωτέρα επίπεδα πρέπει να προωθηθούν. Οι συναρτήσεις αυτές όπως και όλες οι προηγούµενες, ορίζονται στο αρχείο net/ipv4/ip_output.c και είναι η ip_local_deliver που αποφασίζει το επίπεδο προώθησης, και η ip_local_deliver_finish φέρει εις πέρας όλα τα υπόλοιπα. 4.4 Τα πρωτόκολλα ARP, ICMP και IGMP Αν και δεν κρίνεται απαραίτητο για τους σκοπούς αυτής της εργασίας, είναι καλό να µιλήσουµε για τρία πρωτόκολλα που παίζουν σηµαντικό στην δικτύωση υπολογιστικών συστηµάτων και δη ενός ενσωµατωµένου συστήµατος Linux όπως είναι ο Router NGW100. Αυτό θα µας βοηθήσει να έχουµε µια πιο πλήρη εικόνα του τι πραγµατικά συµβαίνει στον πηγαίο κώδικα της δικτύωσης και δροµολόγησης που παρέχει το Linux. 4.4.1 ARP Γενικά το πρωτόκολλο ARP αντιστοιχίζει λογικές διευθύνσεις IP σε διευθύνσεις υλικού MAC και έχει δηµιουργηθεί για να χρησιµοποιείται από το πρωτόκολλο Ethernet σε τοπικά δίκτυα LAN. Όταν πρόκειται να µεταδοθεί ένα πακέτο η κατάλληλη ρουτίνα εξόδου, του προσθέτει την απαιτούµενη επικεφαλίδα η οποία αντιπροσωπεύει το φυσικό επίπεδο (ή το επίπεδο γραµµής δεδοµένων, στο OSI). Για να επιτευχθεί όµως κάτι τέτοιο, ο αποστολέας θα πρέπει να διαθέτει στη µνήµη του ένα πίνακα που θα περιλαµβάνει την IP του παραλήπτη η οποία θα είναι αντιστοιχισµένη µε την διεύθυνση MAC του παραλήπτη. Ο πίνακας αυτός ονοµάζεται ARP cache. Αν η αναζήτηση στην ARP cache δεν έχει αποτέλεσµα, τότε πιθανόν ο παραλήπτης δεν έχει χαρτογραφηθεί ακόµα. Έτσι, στέλνεται ένα αίτηµα ARP προς όλα (broadcast) τα συνδεδεµένα συστήµατα του LAN. Όταν επιστραφεί απάντηση τότε ενηµερώνεται αυτόµατα και η ARP cache. Η υλοποίηση του TCP/IP στο Linux απαιτεί να υπάρχουν και η ARP cache αλλά και ο πίνακας δροµολόγησης που αναφέραµε νωρίτερα. Ο πηγαίος κώδικας του ARP βρίσκεται στο αρχείο net/ipv4/arp.c. Η αρχικοποίηση του ARP αφορά κάποιες δοµές δεδοµένων οι οποίες αρχικοποιούνται κατά τη διάρκεια της µεταγλώττισης του Kernel και όχι κατά την εκκίνησή του. Οι δοµές αυτές είναι οι παρακάτω: arp_packet_type Επικοινωνία: [email protected] 146 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux – Κεφάλαιο 4 arp_netdev_notifier arp_init arp_table arp_generic_ops arp_hh_ops arp_direct_opts arp_broken_arps Όπως και κάθε άλλο πρωτόκολλο που ανήκει σε οικογένεια πρωτοκόλλων, το ARP διαθέτει µια συνάρτηση λήψης πακέτων. Η συνάρτηση αυτή είναι η arp_rcv. Επίσης διαθέτει και µια συνάρτηση για την επεξεργασία των πακέτων που λαµβάνει, την arp_process. 4.4.2 ICMP Το πρωτόκολλο ICMP (Internet Control Message Protocol) χειρίζεται πολλές λειτουργίες του IPv4 µε αποτέλεσµα να θεωρείται σε αρκετές περιπτώσεις, τµήµα του πρωτοκόλλου IP. Τα µηνύµατα ICMP µεταφέρονται µέσα σε πακέτα IP. Η πιο γνωστή χρήση του πρωτοκόλλου ICMP είναι µέσω της λειτουργίας ping (Packet Internet Groper) κατά την οποία στέλνονται πακέτα αιτηµάτων που λειτουργούν σαν την ηχώ (echo request) και επιστρέφουν στον αποστολέα τους πολλαπλές φορές µε την µορφή απαντήσεων (echo reply ή pong). Αυτό γίνεται για να είµαστε σε θέση να διαπιστώνουµε αν κάποιο σύστηµα είναι συνδεδεµένο και διαθέσιµο στο τοπικό δίκτυο ή στο Internet. Υπάρχουν διάφοροι τύποι πακέτων ICMP, καθένας εκ των οποίων χρειάζεται διαφορετική αντιµετώπιση και επεξεργασία. Το πρωτόκολλο ICMP στο Linux ορίζεται στο αρχείο net/ipv4/icmp.c. Για τον χειρισµό των πακέτων που λαµβάνονται το ICMP διαθέτει την δοµή icmp_control. Επίσης, στο αρχείο include/linux/icmp.h περιλαµβάνονται εκτός των άλλων και οι τύποι µηνυµάτων του ICMP. Το τµήµα αυτό είναι το ακόλουθο: #define #define #define #define #define #define #define #define #define #define #define #define #define ICMP_ECHOREPLY ICMP_DEST_UNREACH ICMP_SOURCE_QUENCH ICMP_REDIRECT ICMP_ECHO ICMP_TIME_EXCEEDED ICMP_PARAMETERPROB ICMP_TIMESTAMP ICMP_TIMESTAMPREPLY ICMP_INFO_REQUEST ICMP_INFO_REPLY ICMP_ADDRESS ICMP_ADDRESSREPLY 0 3 4 5 8 11 12 13 14 15 16 17 18 /* /* /* /* /* /* /* /* /* /* /* /* /* Echo Reply Destination Unreachable Source Quench Redirect (change route) Echo Request Time Exceeded Parameter Problem Timestamp Request Timestamp Reply Information Request Information Reply Address Mask Request Address Mask Reply */ */ */ */ */ */ */ */ */ */ */ */ */ Για τον χειρισµό και τη λειτουργία του ICMP υπάρχουν εξειδικευµένες συναρτήσεις οι οποίες ορίζονται επίσης στο αρχείο net/ipv4/icmp.c. Οι κυριότερες από αυτές είναι η icmp_rcv, η icmp_unreach, η icmp_redirect, η icmp_echo και η icmp_timestamp. Για την αποστολή πακέτων ICMP χρησιµοποιούνται οι συναρτήσεις icmp_reply και icmp_push_reply. 4.4.3 IGMP Το πρωτόκολλο IGMP (Internet Group Management Protocol) υπάρχει για να ανταλλάσσει µηνύµατα διαχείρισης δροµολόγησης πολλαπλών παραληπτών (multicast routing) και µετάδοσης µηνυµάτων. Ουσιαστικά ο σκοπός του είναι να αντιστοιχίσει µια οµάδα διευθύνσεων Ιστότοπος εργασίας: MyThesis.org 147 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) (unicast) σε µια διεύθυνση (multicast) κλάσης D. Η ανταλλαγή πληροφοριών που αφορούν αυτές τις διευθύνσεις γίνεται ανάµεσα σε συστήµατα που βρίσκονται συνδεδεµένα στο δίκτυο και σε routers. Υπάρχουν τρεις εκδόσεις του πρωτοκόλλου IGMP. Η έκδοση 1, η 2 και η 3. Κάθε έκδοση επιτυγχάνει το δικό της συγκεκριµένο σκοπό. Επειδή το Linux δεν υποστηρίζει αυτόµατη ενηµέρωση των πινάκων δροµολόγησης κατά τη λήψη αναφορών IGMP, έτσι ώστε να µπορεί να πραγµατοποιηθεί δροµολόγηση multicast, γράφονται ειδικές εφαρµογές προκειµένου κάτι τέτοιο να είναι δυνατό. Μία από τις πιο γνωστές είναι και η εφαρµογή mrouted. Για τη λειτουργία του πρωτοκόλλου IGMP στο Linux, γίνεται χρήση δύο δοµών δεδοµένων. Της ip_mreqn (αίτηση για multicast) που ορίζεται στο αρχείο include/linux/in.h και της ip_mc_socklist (multicast socket list) η οποία ορίζεται στο αρχείο igmp.h που βρίσκεται στον ίδιο κατάλογο. Επίσης χρησιµοποιούνται και κάποιες συναρτήσεις για την υλοποίηση συγκεκριµένων διεργασιών λήψης όπως η igmp_rcv (κύρια συνάρτηση λήψης), η igmp_heard_query (επεξεργασία εισερχόµενων αιτηµάτων), η igmp_heard_report (ελέγχει για εισερχόµενες αναφορές IGMP). Οι συναρτήσεις αυτές ορίζονται στο αρχείο net/ipv4/igmp.c. Τέλος, για την προσθήκη οµάδων διευθύνσεων αλλά και για την έξοδο από αυτές, υπάρχουν οι συναρτήσεις ip_mc_join_group και ip_mc_leave_group αντίστοιχα. 4.5 Οδηγοί δικτύου Μέχρι στιγµής έχουµε ασχοληθεί µε την υλοποίηση του TCP/IP στον Linux Kernel και τον τρόπο επικοινωνίας του επιπέδου εφαρµογής µε τα επίπεδα µεταφοράς και δικτύου µέσω της διεπαφής socket. Επίσης αναφέραµε ότι τα πρωτόκολλα επιπέδου δικτύου που είναι γνωστά ως χειριστές πακέτων (packet handlers), είναι το σηµείο “συγκόλλησης” του πρωτοκόλλου TCP/IP µε το υλικό δικτύωσης. ∆ηλαδή µε τους οδηγούς των ολοκληρωµένων που έχουν γραφτεί έτσι ώστε να µεταφέρονται πακέτα από το φυσικό επίπεδο στο επίπεδο δικτύου. Σε προηγούµενο κεφάλαιο είχαµε ασχοληθεί µε τις συσκευές για τις οποίες γράφονται οδηγοί στο Linux και τις είχαµε κατατάξει σε τρεις κλάσεις εκ των οποίων η µία είναι οι διεπαφές δικτύου (network interfaces). Επίσης είχαµε χωρίσει τους οδηγούς συσκευών σε τρία είδη, εκ των οποίων το ένα είναι οι οδηγοί δικτύου (network drivers). Αν συνδυάσουµε τις έννοιες διεπαφή δικτύου και οδηγός δικτύου, βγάζουµε έναν ακόµη πιο ακριβή όρο που είναι ο οδηγός διεπαφής δικτύου (network interface driver). Αυτό µας δείχνει ότι ουσιαστικά, εκτός από την διεπαφή socket που παρέχεται “πάνω” από τον Kernel για τις εφαρµογές, παρέχεται επίσης και µια διεπαφή “κάτω” από αυτόν, για τους οδηγούς δικτύου. Σε αυτή την ενότητα θα αναφερθούµε πολύ συνοπτικά στους οδηγούς δικτύου και στην διεπαφή για την ανάπτυξη οδηγών δικτύου που παρέχει ο Linux Kernel. Όλα αυτά θα βοηθήσουν στην καλύτερη κατανόηση του οδηγού δικτύου MACB, του Router NGW100. 4.5.1 H διεπαφή δικτύου Όπως έχουµε ήδη πει, η διεπαφή δικτύου είναι η τρίτη κλάση συσκευών του Linux. Ο ρόλος της είναι παρόµοιος µε εκείνον µιας συσκευής block η οποία καταχωρεί τις συσκευές αποθήκευσης για τις οποίες είναι υπεύθυνη, έτσι ώστε στη συνέχεια να στέλνει και να λαµβάνει δε- Επικοινωνία: [email protected] 148 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux – Κεφάλαιο 4 δοµένα µε τη µορφή blocks. Η διεπαφή δικτύου θα πρέπει αρχικά να είναι σε θέση να καταχωρήσει τον εαυτό της µέσω συγκεκριµένων δοµών δεδοµένων του Kernel, προκειµένου στη συνέχεια να είναι σε θέση να συµµετέχει στην λήψη και την αποστολή πακέτων στο τοπικό δίκτυο ή στο Internet. Η κυριότερη διαφορά µιας συσκευής αποθήκευσης µε µια διεπαφή δικτύου είναι ότι ενώ ένας σκληρός δίσκος για παράδειγµα, βρίσκεται καταχωρηµένος στον κατάλογο /dev, µια διεπαφή δικτύου δεν παρέχει τέτοιο σηµείο πρόσβασης. Εποµένως σε αυτή την περίπτωση δεν ισχύει και ο κανόνας, ότι τα πάντα στο Linux είναι αρχεία, καθώς σε µια διεπαφή δικτύου δεν υπάρχουν εγγραφές, αναγνώσεις ή άλλου είδους ενέργειες οι οποίες συνήθως χαρακτηρίζουν ένα αρχείο. Μια άλλη σηµαντική διαφορά είναι ότι ένας οδηγός block απαντά µόνο σε κλήσεις που προέρχονται από τον Kernel, ενώ ένας οδηγός δικτύου λαµβάνει (ασύγχρονα) πακέτα από το δίκτυο. 4.5.2 Ανάπτυξη οδηγού δικτύου Οι οδηγοί δικτύου θα πρέπει να είναι σχεδιασµένοι έτσι ώστε να µπορούν να υποστηρίξουν ένα σύνολο εργασιών διαχείρισης. Μερικές από αυτές είναι και ο καθορισµός της διεύθυνσης IP του συστήµατος, η τροποποίηση των παραµέτρων επικοινωνίας, και η διατήρηση στατιστικών (statistics) που αφορούν την κίνηση εισερχόµενων και εξερχόµενων πακέτων αλλά και τα σφάλµατα κατά τη µετάδοση ή τη λήψη. Όταν ξεκινάµε την ανάπτυξη ενός οδηγού δικτύου µπορούµε να χρησιµοποιήσουµε ορισµένα έτοιµα παραδείγµατα από ήδη υλοποιηµένους οδηγούς, όπως είναι ο εικονικός οδηγός loopback.c που ουσιαστικά δεν υποστηρίζει κάποια πραγµατική συσκευή αλλά τον προσαρµογέα localhost, και ο οδηγός e100.c που αφορά µια γενική υλοποίηση για το 100Mbps Ethernet. Τις περισσότερες φορές βέβαια οι οδηγοί είτε γράφονται από την κοινότητα ανοικτού κώδικα του Linux είτε από τους κατασκευαστές των µικροελεγκτών και των Ethernet PHY chips (η όποιου άλλου φυσικού πρωτοκόλλου επικοινωνίας χρησιµοποιείται). Αυτό ισχύει και για την περίπτωση του Router NGW100 και τον οδηγό µε την ονοµασία MACB ο οποίος έχει γραφτεί για τον µικροελεγκτή AP7000 και τους δύο ελεγκτές Ethernet (MACB0 και MACB1) που υπάρχουν στο σύστηµα οι οποίοι περιγράφονται στο πρακτικό µέρος της εργασίας καθώς και σε αντίστοιχο datasheet. 4.5.3 Ο οδηγός δικτύου MACB Ο οδηγός δικτύου MACB (ΠΑΡΑΡΤΗΜΑ B) αναπτύχθηκε ειδικά για το υποσύστηµα του ελεγκτή Ethernet (MACB Interface) που διαθέτει ο µικροελεγκτής AP7000. Μέσω του οδηγού MACB, ο AP7000 επικοινωνεί µε τη στοίβα TCP/IP του Kernel αλλά και µε το επίπεδο Socket. Στη συνέχεια, µέσω του ολοκληρωµένου DP83848I που χειρίζεται τις φυσικές απαιτήσεις (PHY) του πρωτοκόλλου IEEE 802.3, επικοινωνεί µε το δίκτυο, προκειµένου να δροµολογεί τα πακέτα των υπολογιστικών συστηµάτων µε τα οποία συνδέεται άµεσα ή έµµεσα (πχ µέσω switch). Ορισµένες πολύ πρακτικές υπηρεσίες οι οποίες παρέχονται στον συγκεκριµένο οδηγό δικτύου είναι και οι παρακάτω: NAPI (New API) ethtool API Στατιστικά Promiscuous mode Ιστότοπος εργασίας: MyThesis.org 149 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Έλεγχος σφαλµάτων NAPI Το NAPI είναι µια τροποποίηση του µηχανισµού επεξεργασίας πακέτων του οδηγού δικτύου MACB. Σχεδιάστηκε για να βελτιώνει την απόδοση του Router NGW100 σε δίκτυα υψηλών ταχυτήτων. Αυτό επιτυγχάνεται µέσω δύο µηχανισµών: Ελαχιστοποίηση διακοπών (interrupt mitigation): Στα δίκτυα υψηλών ταχυτήτων δηµιουργούνται χιλιάδες διακοπές λόγω λήψης ή αποστολής πακέτων. Εποµένως για κάθε ένα πακέτο θα µπορούσε να προκύπτει µια ξεχωριστή διαδικασία ενεργοποίησης και απενεργοποίησης διακοπής µε αποτέλεσµα να απαιτείται µεγάλη επεξεργαστική ισχύς και να δηµιουργούνται καθυστερήσεις που πολλές φορές οδηγούν ακόµη και στην αναγκαστική απόρριψη πακέτων. Το NAPI αποτρέπει την ύπαρξη αυτών των συνθηκών µε το να χειρίζεται µε πιο έξυπνο τρόπο τις διακοπές. Αυτό το πετυχαίνει χρησιµοποιώντας την µέθοδο Polling κατά την οποία ελέγχει περιοδικά για την έλευση νέων πακέτων. Η µέθοδος αυτή ενεργοποιείται µόνο αν προκύψει υπερφόρτωση, διαφορετικά χρησιµοποιείται ο βασικός µηχανισµός στον οποίο όπως γνωρίζουµε, για κάθε πακέτο προκύπτει και µια διακοπή. Μπλοκάρισµα πακέτων (packet throttling): Όταν το σύστηµα υπερφορτώνεται και πρέπει αναγκαστικά να απορρίψει πακέτα είναι προτιµότερο αυτό να συµβεί πριν αρχίσει καν η επεξεργασία τους ώστε να εξοικονοµηθούν κύκλοι επεξεργασίας του µικροελεγκτή. Το σύστηµα µπλοκαρίσµατος πακέτων κάνει ακριβώς αυτή την δουλειά. Εµποδίζει τα πακέτα από το να φτάσουν στη στοίβα TCP/IP όταν υπάρχει υπερφόρτωση και τα απορρίπτει άµεσα χωρίς καµία επεξεργασία. ethtool API Η διεπαφή ethtool χρησιµοποιείται σε επίπεδο εφαρµογής, µέσω γραµµής εντολών για την παρουσίαση ή την τροποποίηση των παραµέτρων του υποσυστήµατος υλικού δικτύωσης, γνωστό στους υπολογιστές και ως NIC (Network Interface Card ή Network adapter ή LAN adapter). Στην περίπτωση του Router NGW100 το αντίστοιχο υποσύστηµα δικτύωσης ονοµάζεται Ethernet controller και είναι ενσωµατωµένο στον µικροελεγκτή AP7000. Παρ’ όλα αυτά στον Router NGW100 χρησιµοποιείται το πρόγραµµα ifconfig καθώς υπάρχει ήδη ενσωµατωµένο στο BusyBox. Η εντολή ethtool δεν είναι ενεργοποιηµένη εξ αρχής αλλά µπορεί αν χρειαστεί να ενεργοποιηθεί εκ των υστέρων. Στατιστικά Ο οδηγός MACB κατά την αποστολή και τη λήψη πακέτων διατηρεί στατιστικά στοιχεία τα οποία ορίζονται ως ένα σύνολο πεδίων στη δοµή δεδοµένων macb_stats που υπάρχει στο αρχείο κεφαλής macb.h: rx_pause_frames tx_ok tx_single_cols tx_multiple_cols rx_ok rx_fcs_errors rx_align_errors Επικοινωνία: [email protected] 150 ∆ικτύωση και ∆ροµολόγηση στα ενσωµατωµένα συστήµατα Linux – Κεφάλαιο 4 tx_deferred tx_late_cols tx_excessive_cols tx_underruns tx_carrier_errors rx_resource_errors rx_overruns rx_symbol_errors rx_oversize_pkts rx_jabbers rx_undersize_pkts sqe_test_errors rx_length_mismatch tx_pause_frames Ο κώδικας του οδηγού, σχεδόν σε κάθε συµβάν που προκύπτει από την επικοινωνία του Router NGW100 µε το δίκτυο, τροποποιεί την τιµή του αντίστοιχου στατιστικού στοιχείου (πεδίου) και ταυτόχρονα παρέχει ένα µηχανισµό µετάδοσης αυτών των στοιχείων προς τις εφαρµογές του χρήστη. Έτσι, µπορεί για παράδειγµα µια εφαρµογή να ρωτήσει τον οδηγό δικτύου και να ενηµερωθεί για το ποσοστό των πακέτων που λήφθηκαν, που στάλθηκαν, που απορρίφθηκαν κλπ. Promiscuous mode Με αυτό τον µηχανισµό ο Router NGW100 µπορεί να αναλύει τα µη κρυπτογραφηµένα δεδοµένα όλων των πακέτων του δικτύου που δροµολογεί, είτε τον αφορούν είτε όχι (packet sniffing). Τα δεδοµένα αυτά µπορούν να επεξεργαστούν αργότερα και από κάποια εφαρµογή η οποία µπορεί να βρίσκεται ακόµα και σε κάποιο άλλο σύστηµα του δικτύου. Φυσικά το σύστηµα αυτό θα πρέπει να έχει τα απαραίτητα δικαιώµατα. Ο τύπος λειτουργίας Promiscuous mode µπορεί επίσης να φανεί χρήσιµος σε περιπτώσεις που θέλουµε να γεφυρώσουµε (bridge) δύο διαφορετικά δίκτυα η να κάνουµε διαγνωστικούς ελέγχους σύνδεσης. Έλεγχος σφαλµάτων Ο οδηγός MACB είναι σε θέση να ελέγχει για σφάλµατα στα πλαίσια που λαµβάνει µέσω ελέγχων CRC (Cyclic Redundancy Check), ελέγχων υπερβολικού µήκους πλαισίου, ακατάλληλων πλαισίων κλπ. Ό έλεγχος σφαλµάτων σε τόσο χαµηλό επίπεδο βοηθά στο να µην απασχολείται η CPU του µικροελεγκτή AP7000, αφού ένα πλαίσιο που παρουσιάζει σφάλµατα δεν θα προωθηθεί στα ανώτερα επίπεδα της στοίβας TCP/IP. Έτσι εξοικονοµείται χρόνος και αποφεύγονται περιττοί κύκλοι επεξεργασίας. Όλες οι παραπάνω υπηρεσίες του οδηγού χρησιµοποιούνται από εφαρµογές προκειµένου να δώσουν στον χρήστη τη δυνατότητα να διαχειρίζετια τις διεπαφές δικτύου Ethernet. Ένα τέτοιο παράδειγµα είναι και η εφαρµογή ethtool που θα εξετάσουµε στο πρακτικό µέρος αυτής της εργασίας. Ιστότοπος εργασίας: MyThesis.org 151 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) 4.6 Απόδοση δικτύωσης του µικροελεγκτή AP7000 Για να ελέγξουµε την απόδοση του Router NGW100 θα πρέπει να εξετάσουµε την απόδοση του µικροελεγκτή AP7000 κατά την επεξεργασία πακέτων. Για το λόγο αυτό χρειαζόµαστε ένα πρόγραµµα που θα εκτελείται στο επίπεδο εφαρµογής περιβάλλοντος χρήστη (user space), και το οποίο θα είναι σε θέση να επικοινωνεί µε τον οδηγό δικτύου που αναπτύξαµε. Ένα πολύ απλό και γνωστό πρόγραµµα είναι και το iperf. Το iperf είναι ουσιαστικά ένα εργαλείο µέτρησης εύρους ζώνης. Μπορεί να επικοινωνεί µέσω των πρωτοκόλλων TCP και UDP αλλά χρησιµοποιείται και για αξιολόγηση του φυσικού επιπέδου (που στην περίπτωσή µας είναι ο οδηγός δικτύου). Ο πιο συνηθισµένος τρόπος χρήσης του iperf είναι να εκτελείται ταυτόχρονα σε δύο διαφορετικά συνδεδεµένα υπολογιστικά συστήµατα και στο ένα να λειτουργεί ως server ενώ στο άλλο, ως client. Αν θέλουµε για παράδειγµα να ελέγξουµε την απόδοση λήψης πακέτων (Rx) του AP7000 και του οδηγού δικτύου MACB εκτελούµε στη γραµµή εντολών του Router NGW100 την παρακάτω εντολή: iperf –s Ενώ σε κάποιο άλλο σύστηµα (πχ το σύστηµα host) που βρίσκεται συνδεδεµένο µε τον Router NGW100, εκτελούµε την εντολή: iperf –c <IP του Router NGW100> Μετά από αυτή την εκτέλεση ο Router NGW100 που θα λειτουργεί σαν server θα λάβει για περίπου 10 δευτερόλεπτα, ένα σύνολο πακέτων από το άλλο σύστηµα και όταν αυτή η διαδικασία ολοκληρωθεί, θα εµφανιστούν τα στατιστικά αποτελέσµατα της επικοινωνίας τους. Αν θέλουµε να εξετάσουµε την απόδοση της αποστολής πακέτων απλά αντιστρέφουµε τις δύο εντολές και ο Router NGW100 συµπεριφέρεται πλέον ως server. Επικοινωνία: [email protected] 152 ΠΡΑΚΤΙΚΟ ΜΕΡΟΣ Εισαγωγή πρακτικού µέρους Στο θεωρητικό µέρος της εργασίας, παρουσιάστηκε το ελάχιστο θεωρητικό υπόβαθρο που αφορά τα ενσωµατωµένα συστήµατα Linux. Tα θέµατα που µας απασχόλησαν ήταν τα ακόλουθα: Ανάπτυξη ενσωµατωµένων συστηµάτων Linux Εργαλεία ανάπτυξης ενσωµατωµένων συστηµάτων Linux Το υλικό που υποστηρίζει το ενσωµατωµένο Linux Η δροµολόγηση στο Linux µέσω της ενσωµατωµένης στοίβας TCP/IP Όλα τα παραπάνω θα είναι χρήσιµα σαν σηµεία αναφοράς κατά τη διάρκεια του πρακτικού µέρους. Έτσι, θα υπάρχει η δυνατότητα να αναφέρονται κάποιες βασικές θεωρητικές έννοιες χωρίς να χρειάζεται να εξηγούνται εκ νέου. Στο πρακτικό µέρος της παρούσας εργασίας και στα κεφάλαια που θα ακολουθήσουν, θα αναλυθούν τα στοιχεία του υλικού και του λογισµικού από τα οποία αποτελείται ο Router NGW100. Τα θέµατα που θα µας απασχολήσουν είναι: Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 Προγραµµατισµός και εκκίνηση του Router NGW100 Οι εφαρµογές του Router NGW100 ΚΕΦΑΛΑΙΟ 5 Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 Εισαγωγή Σε αυτό το κεφάλαιο θα παρουσιαστεί το υλικό του Router NGW100 το οποίο απαρτίζεται από τρία κύρια µέρη: Ηλεκτρονικά στοιχεία Ηλεκτρικά στοιχεία Πλακέτα τυπωµένου κυκλώµατος PCB Ειδικότερα τα θέµατα που θα αναλυθούν είναι τα παρακάτω: Περιγραφή και λογικό διάγραµµα του Router NGW100 Ο µικροελεγκτής AP7000 ∆ιαθέσιµη µνήµη Ethernet PHY και MAC Συνδεσιµότητα RS232 Ελεγκτής πλακέτας – Board Controller ∆ιεπαφές προγραµµατισµού JTAG και NEXUS Σύστηµα χρονισµού Κύκλωµα επαναφοράς (reset) Σύστηµα τροφοδοσίας ∆ιαστάσεις πλακέτας Σχέδιο συναρµολόγησης Σχηµατικά και PCB του Router NGW100 Επέκταση δυνατοτήτων υλικού 5.1 Περιγραφή και λογικό διάγραµµα του Router NGW100 Ξεκινώντας µε την παρουσίαση των ηλεκτρικών και ηλεκτρονικών στοιχείων του Router NGW100 θα παραθέσουµε πρώτα τα βασικότερα χαρακτηριστικά του: Μικροελεγκτής AP7000 32bit ∆ύο θύρες Ethernet Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 – Κεφάλαιο 5 32MB SDRAM 16MB on-board flash ∆υνατότητα επέκτασης της µνήµης µέσω SD ή MMC καρτών µνήµης Θύρα USB JTAG για debugging και για προγραµµατισµό της µνήµης flash Υποδοχές επέκτασης (expansion connectors) µε 63 IOs γενικού σκοπού ή σύνδεσης περιφερειακών του µικροελεγκτή AP7000 Σύστηµα τροφοδοσίας και LED ενδείξεων κατάστασης του Router ∆ύο LED που µπορούν να είναι προσβάσιµα από τον προγραµµατιστή µέσω λογισµικού Σε αυτό το σηµείο θα πρέπει να πούµε ότι στο εξής όταν περιγράφουµε κάποιο στοιχείο του συστήµατός µας, όπου είναι απαραίτητο, θα προτιµάται έναντι της ελληνικής, η αγγλική ορολογία, µιας και αυτή έχει επικρατήσει και χρησιµοποιείται περισσότερο από τις εταιρείες και τους οργανισµούς παγκόσµια. Τόσο στην ανάπτυξη λογισµικού όσο και στην ανάπτυξη υλικού. Πριν παραθέσουµε το µπλοκ διάγραµµα του Router NGW100 ας πούµε µερικά πράγµατα για τη λίστα των χαρακτηριστικών που είδαµε πιο πριν. Ο Router NGW100 είναι ένα πλήρες ενσωµατωµένο υπολογιστικό σύστηµα. ∆ιαθέτει µονάδα τροφοδοσίας µε επιτρεπόµενη είσοδο τάση από 9 έως 15 Volt DC. Οι µετατροπείς DC/DC που βρίσκονται ενσωµατωµένοι (on board) στο PCB, δηµιουργούν την κατάλληλη τάση για την λειτουργία του µικροελεγκτή αλλά και για όλο το υπόλοιπο σύστηµα. Οπότε οποιοδήποτε τροφοδοτικό πρίζας, ικανοποιεί αυτά τα χαρακτηριστικά είναι κατάλληλο. Θα αναλύσουµε περεταίρω το σύστηµα τροφοδοσίας σε αντίστοιχη παράγραφο του κεφαλαίου αυτού. Ο Router NGW100 διαθέτει 16MB συνολική µνήµη Flash και 32MB SDRAM. Η µνήµη µπορεί να επεκταθεί µέσω της θύρας SD/MMC µε τη χρήση καρτών µνήµης SD (µέχρι και 2GB) ή MMC. Μέσω αυτών των µνηµών µπορούµε να µεταφέρουµε εύκολα δεδοµένα από το σύστηµα host (δηλαδή τον υπολογιστή µας) στο target σύστηµα (δηλαδή στον Router NGW100), χωρίς να χρειαζόµαστε κάποιο καλώδιο. Μπορούµε ακόµα και να ρυθµίσουµε τον bootloader να εκκινεί το Linux από µια τέτοια κάρτα. Οι διαθέσιµες διεπαφές επικοινωνίας (communication interfaces) του Router NGW100 είναι τρεις. Η σειριακή διεπαφή RS232 είναι κατάλληλη για χαµηλού επιπέδου (low level) debugging, όπως είναι για παράδειγµα η επικοινωνία µας µε τον bootloader. Η διεπαφή Ethernet που είναι και η πιο σηµαντική για τη λειτουργία του συστήµατος ως Router, δίνει δυνατότητες δροµολόγησης των πακέτων των υπολογιστών που συνδέονται στην θύρα LAN του Router, τόσο στο τοπικό δίκτυο όσο και σε αποµακρυσµένα δίκτυα, µέσω της θύρας WAN. Επίσης, µας δίνεται η δυνατότητα επικοινωνίας µε τον NGW100 µέσω FTP, SSH, TELNET και άλλων πρωτοκόλλων επικοινωνίας. Τέλος η θύρα USB δίνει επιπλέον δυνατότητες συνδεσιµότητας και επιτρέπει πιο εύκολη παραµετροποίηση του Router NGW100 από τους χρήστες του (στην παρούσα εργασία δεν αναπτύχθηκε η USB επικοινωνία αλλά θα µπορούσε να αποτελέσει µια µελλοντική προσθήκη). Πριν ξεκινήσουµε την ανάλυση του Router ας δούµε το µπλοκ διάγραµµα (block diagram) της πλακέτας του (τα αχνά σηµεία δεν έχουν συγκολληθεί): Ιστότοπος εργασίας: MyThesis.org 155 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 45. Μπλοκ διάγραµµα του υλικού του Router NGW100 Επικοινωνία: [email protected] 156 Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 – Κεφάλαιο 5 5.2 Ο µικροελεγκτής AP7000 Το κυριότερο στοιχείο του Router είναι η Κεντρική Μονάδα Επεξεργασίας του, δηλαδή ο µικροελεγκτής AP7000. Μέσα στο κείµενό µας όταν θα λέµε µικροελεγκτής ή microcontroller, ή CPU, ή µικροελεγκτής, θα εννοούµε πάντα τον AP7000. Τα κυριότερά του χαρακτηριστικά όπως αυτά αναφέρονται στο συνοδευτικό datasheet της κατασκευάστριας εταιρείας, είναι τα ακόλουθα: Interrupt Controller – Individually maskable Interrupts – Each interrupt request has a programmable priority and autovector address System Functions – Power and Clock Manager – Crystal Oscillator with Phase-Lock-Loop (PLL) – Watchdog Timer – Real-time Clock 6 Multifunction timer/counters – Three external clock inputs, I/O pins, PWM, capture and various counting capabilities 4 Universal Synchronous/Asynchronous Receiver/Transmitters (USART) – 115.2 kbps IrDA Modulation and Demodulation – Hardware and software handshaking 3 Synchronous Serial Protocol controllers – Supports I2S, SPI and generic frame-based protocols Two-Wire Interface – Sequential Read/Write Operations, Philips’ I2C© compliant Liquid Crystal Display (LCD) interface – Supports TFT displays – Configurable pixel resolution supporting QCIF/QVGA/VGA/SVGA configurations. Image Sensor Interface – 12-bit Data Interface for CMOS cameras Universal Serial Bus (USB) 2.0 High Speed (480 Mbps) Device – On-chip Transceivers with physical interface 2 Ethernet MAC 10/100 Mbps interfaces – 802.3 Ethernet Media Access Controller – Supports Media Independent Interface (MII) and Reduced MII (RMII) 16-bit stereo audio bitstream DAC – Sample rates up to 50 kHz On-Chip Debug System – Nexus Class 3 – Full speed, non-intrusive data and program trace – Runtime control and JTAG interface Package/Pins – AT32AP7000: 256-ball CTBGA 1.0 mm pitch/160 GPIO pins Power supplies – 1.65V to1.95V VDDCORE – 3.0V to 3.6V VDDIO Ο AP7000 είναι ένα πλήρες σύστηµα SoC (System on Chip) µε πυρήνα AVR32 RISC (Reduced Instruction Set Computing) και απόδοση 210 DMIPS (Dhrystone MIPS) στα 150 MHz. Ο πυρήνας AVR32 είναι ένας υψηλής απόδοσης, 32-bit RISC, µικροελεγκτής. Παρουσιάζει χαµηλή κατανάλωση, υψηλή πυκνότητα κώδικα (high code density – δηλαδή εκτελέσιµα µε µειωµένες απαιτήσεις χωρητικότητας) και µεγάλη απόδοση στην εκτέλεση εφαρµογών. Με αυτές τις δυνατότητες είναι σε θέση να εκτελεί ένα πλήρες, ενσωµατωµένο λειτουργικό σύ- Ιστότοπος εργασίας: MyThesis.org 157 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) στηµα Linux µε αρκετή άνεση. Στην εικόνα µπορούµε να δούµε το package του AP7000 στις δύο του απόψεις: κάτω όψη 256-ball CTBGA 1.0 mm pitch άνω όψη Εικόνα 46. Το το package του µικροελεγκτή AP7000 Ο AP7000 περιλαµβάνει µονάδα MMU (Memory Management Unit) και έναν ευέλικτο ελεγκτή διακοπών (interrupt controller), υποστηρίζοντας έτσι σύγχρονα λειτουργικά συστήµατα αλλά και λειτουργικά συστήµατα πραγµατικού χρόνου (RTOS – Real Time Operating System). Επίσης περιλαµβάνεται ένα πλούσιο σετ εντολών DSP (Digital Signal Processing) και SIMD (Single Instruction Multiple Data) για την υποστήριξη πολυµεσικών και δικτυακών εφαρµογών. ∆ιαθέτει ενσωµατωµένη 32 KB µνήµη SRAM (Static RAM) για γρήγορη και ασφαλή πρόσβαση σε καίρια δεδοµένα. Για εφαρµογές οι οποίες απαιτούν περισσότερη µνήµη, υπάρχει δυνατότητα για υποστήριξη εξωτερικής (in-chip) SRAM. Επιπρόσθετα, υποστηρίζεται µέσω SDRAM controller και εξωτερική (off-chip) επέκταση SDRAM µνήµης, η οποία µπορεί να είναι πτητική και µη-πτητική. Οι κυριότερες τεχνολογίες µη-πτητικών µνηµών που υποστηρίζονται είναι: Compact Flash, MMC (Multimedia Card), SDRAM (Secure Digital RAM), SmartCard, NAND Flash και Atmel DataFlash™. Ο DMA (Direct Memory Access) controller που υπάρχει ενσωµατωµένος στον AP7000 δίνει τη δυνατότητα σε κάθε σειριακό του περιφερειακό να µεταφέρει δεδοµένα σε µνήµες, χωρίς να απαιτείται η µεσολάβηση του ίδιου. Αυτό µειώνει τη συµφόρηση του όταν µεταφέρονται µεγάλες ακολουθίες δεδοµένων µεταξύ περιφερειακών εντός της MCU (Micro Controller Unit). Οι µονάδες MAC (Ethernet 10/100), MII (Media Independent Interface) και RMII (Reduced Media Independent Interface) του AP7000, παρέχουν µια ενσωµατωµένη (on-chip) λύση, για σύνδεση µε δικτυακές συσκευές. Επίσης, οι σύγχρονοι σειριακοί ελεγκτές (Synchronous Serial Controllers), παρέχουν εύκολη πρόσβαση σε σειριακά πρωτόκολλα επικοινωνίας και πρωτόκολλα πλαισίων (frame based protocols). Γενικά ο AP7000 είναι ένας πλούσιος σε χαρακτηριστικά µικροελεγκτής ο οποίος παρέχει ευελιξία συνδεσιµότητας και επικοινωνίας. Για παράδειγµα επιτρέπει τη σύνδεση έγχρωµης ή απλής LCD οθόνης, µπορεί να λειτουργήσει σαν δικτυακή κάµερα, σαν mp3 player και πολλά άλλα τα οποία στην παρούσα εργασία δεν κρίνεται αναγκαίο να αναπτύξουµε περισσότερο. Επικοινωνία: [email protected] 158 Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 – Κεφάλαιο 5 5.3 ∆ιαθέσιµη µνήµη Το Router NGW100 διαθέτει τρεις τύπους µνήµης. Την µνήµη SDRAM (Synchronous Dynamic Random Access Memory ή Synchronous Dynamic RAM), την παράλληλη µνήµη και την σειριακή. Στην εικόνα που ακολουθεί µπορούµε να αναγνωρίσουµε και οπτικά τις µνήµες αυτές όπως βρίσκονται συγκολληµένες στο PCB της πλακέτας: σειριακή (DataFlash) SDRAM παράλληλη (NOR Flash) Εικόνα 47. Οπτική αναγνώριση των µνηµών του Router NGW100 Ας εξετάσουµε τα χαρακτηριστικά και τον σκοπό της κάθε µιας µνήµης από αυτές ξεχωριστά. Αρχικά θα ασχοληθούµε µε την παράλληλη (parallel flash). Ο τύπος µνήµης αυτός, ανήκει στην κατηγορία µη-πτητικών (non volatile) µνηµών, πράγµα που σηµαίνει ότι σε αντίθεση µε ότι συµβαίνει στις πτητικές (volatile) µνήµες, µπορεί να συγκρατεί δεδοµένα ακόµα και όταν δεν τροφοδοτείται από κάποια τάση ρεύµατος. Στον Router NGW100 η µνήµη αυτή φιλοξενεί το λογισµικό εκκίνησης U-Boot (το οποίο είναι υπεύθυνο για την εκκίνηση του λειτουργικού συστήµατος αλλά και για τη διαχείριση του υλικού του NGW100) καθώς και ένα ενσωµατωµένο περιβάλλον Linux (Embedded Linux Environment) ή αλλιώς root filesystem (κεντρικό σύστηµα αρχείων). Η χωρητικότητά της είναι 8MB, ενώ ο δίαυλος επικοινωνίας µε τον µικροελεγκτή έχει εύρος 16Bit. Πιο συγκεκριµένα το ολοκληρωµένο που χρησιµοποιείται είναι το AT49BV642D-70TU (NOR flash) και κατασκευάζεται από την ATMEL. Η τροφοδοσία λειτουργίας της µνήµης είναι 2,7 Volt. Τα κυριότερα χαρακτηριστικά της είναι: Single Voltage Operation Read/Write: 2.65V - 3.6V 2.7V - 3.6V Read/Write Access Time – 70 ns Sector Erase Architecture – One Hundred Twenty-seven 32K Word Main Sectors with Individual Write Lockout – Eight 4K Word Sectors with Individual Write Lockout Fast Word Program Time – 10 µs Typical Sector Erase Time: 32K Word Sectors – 500 ms; 4K Word Sectors – 100 ms Suspend/Resume Feature for Erase and Program – Supports Reading and Programming Data from Any Sector by Suspending Erase of a Different Sector – Supports Reading Any Word by Suspending Programming of Any Other Word Low-power Operation Ιστότοπος εργασίας: MyThesis.org 159 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) – 10 mA Active – 15 µA Standby Data Polling and Toggle Bit for End of Program Detection VPP Pin for Write Protection and Accelerated Program Operations RESET Input for Device Initialization Sector Lockdown Support TSOP Package Top or Bottom Boot Block Configuration Available 128-bit Protection Register Common Flash Interface (CFI) Green (Pb/Halide-free) Packaging Ο όρος «παράλληλη µνήµη» έχει να κάνει µε τον τρόπο σύνδεσης και επικοινωνίας της µνήµης µε τον µικροελεγκτή. Η παράλληλη σύνδεση στα ηλεκτρονικά σχεδόν πάντα επιτυγχάνει µεγαλύτερες ταχύτητες διαµεταγωγής και επεξεργασίας δεδοµένων. κάτι που είναι απόλυτα επιθυµητό για το λογισµικό εκκίνησης και για το λειτουργικό σύστηµα (η root filesystem) ενός ενσωµατωµένου συστήµατος Linux. Στην παρακάτω εικόνα βλέπουµε την επάνω όψη του package της µνήµης: Εικόνα 48. Η παράλληλη µνήµη AT49BV642D-70TU Η σειριακή µνήµη (DataFlash) του Router NGW100 κατασκευάζεται και αυτή από την ίδια εταιρεία και η κωδική της ονοµασία είναι AT45DB642D. Τα κυριότερα χαρακτηριστικά όπως αυτά παρουσιάζονται στο αντίστοιχο datasheet της κατασκευάστριας εταιρείας είναι τα εξής: Single 2.7V - 3.6V Supply Dual-interface Architecture – RapidS Serial Interface: 66 MHz Maximum Clock Frequency SPI Compatible Modes 0 and 3 – Rapid8 8-bit Interface: 50 MHz Maximum Clock Frequency User Configurable Page Size – 1024 Bytes per Page – 1056 Bytes per Page – Page Size Can Be Factory Pre-configured for 1024 Bytes Page Program Operation – Intelligent Programming Operation – 8192 Pages (1024/1056 Bytes/Page) Main Memory Flexible Erase Options – Page Erase (1 Kbyte) – Block Erase (8 Kbytes) – Sector Erase (256 Kbytes) – Chip Erase (64 Mbits) Two SRAM Data Buffers (1024/1056 Bytes) – Allows Receiving of Data while Reprogramming the Flash Array Continuous Read Capability through Entire Array – Ideal for Code Shadowing Applications Επικοινωνία: [email protected] 160 Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 – Κεφάλαιο 5 Low-power Dissipation – 10 mA Active Read Current Typical – Serial Interface – 10 mA Active Read Current Typical – 8-bit Interface – 25 µA Standby Current Typical – 15 µA Deep Power Down Typical Hardware and Software Data Protection Features – Individual Sector Permanent Sector Lockdown for Secure Code and Data Storage – Individual Sector Security: 128-byte Security Register – 64-byte User Programmable Space – Unique 64-byte Device Identifier JEDEC Standard Manufacturer and Device ID Read 100,000 Program/Erase Cycles Per Page Minimum Data Retention – 20 Years Green (Pb/Halide-free/RoHS Compliant) Packaging Options Temperature Range – Industrial: -40°C to +85°C Ανήκει στην κατηγορία των µη-πτητικών µνηµών και βρίσκεται συνδεδεµένη στο SPI interface (CS0) του µικροελεγκτή. Στο σύστηµά µας, είναι δεσµευµένη για να φιλοξενεί το σύστηµα αρχείων του Linux στο οποίο υπάρχουν όλα τα δεδοµένα του χρήστη (userspace) και των προγραµµάτων που απαιτούνται για τη λειτουργία του Router. Το package της συσκευής φαίνεται στην επόµενη εικόνα: Εικόνα 49. Η σειριακή µνήµη AT45DB642D Η µνήµη SDRAM που χρησιµοποιήθηκε στον Router NGW100 κατασκευάζεται από την εταιρεία Micron και ανήκει στην κατηγορία των πτητικών (volatile) µνηµών, πράγµα που όπως γνωρίζουµε, σηµαίνει ότι µπορεί να συγκρατεί δεδοµένα µόνο όταν τροφοδοτείται από τάση ρεύµατος. Η µνήµη αυτή µπορεί να χρησιµοποιηθεί µε τον ίδιο τρόπο που χρησιµοποιείται και η µνήµη RAM ενός PC. Η κωδική της ονοµασία είναι MT48LC16M16A2 και το package της φαίνεται στην εικόνα: Εικόνα 50. Η µνήµη SDRAM MT48LC16M16A2 Η SDRAM στον Router NGW100 έχει χωρητικότητα 32MB, και 16bit δίαυλο επικοινωνίας (bus) µε τον AP7000. Οι παράµετροι της µνήµης SDRAM φαίνονται στον παρακάτω πίνακα: Ιστότοπος εργασίας: MyThesis.org 161 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) NGW100 SDRAM parameters Columns 512 (9 bit) Rows 8k (13 bit) Banks 4 (2 bit) Latency (CAS) 2 Refresh rate (RAS) 15.6 µs Recovery time (Rt) 2ck Πίνακας 4. Παράµετροι λειτουργίας της µνήµης SDRAM Αν και η σύνδεση της SDRAM µε τον µικροελεγκτή AP7000 παρουσιάζεται αρκετά καλά στο σχηµατικό του Router NGW100, πιο κάτω µπορούµε να πάρουµε µια πρώτη ιδέα για τη σύνδεση SDRAM µνήµης στην οικογένεια µικροελεγκτών AVR32 στην οποία φυσικά ανήκει και ο ίδιος: Εικόνα 51. ∆ιασύνδεση µνήµης SDRAM µε µικροελεγκτή αρχιτεκτονικής AVR32 Η ιεραρχία της µνήµης του Router NGW100 και τα σηµεία φόρτωσης του Linux (mount points) φαίνονται στο παρακάτω µπλοκ διάγραµµα. Όταν λέµε σηµεία φόρτωσης εννοούµε τα σηµεία στα οποία η κάθε µνήµη µπορεί να είναι ορατή από το Linux. Επικοινωνία: [email protected] 162 Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 – Κεφάλαιο 5 Εικόνα 52. Η ιεραρχία µνήµης του Router NGW100 και τα σηµεία φόρτωσης του Linux Ιστότοπος εργασίας: MyThesis.org 163 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) 5.3.1 Χάρτης φυσικών διευθύνσεων O χάρτης φυσικών διευθύνσεων ενός συστήµατος είναι συνήθως διαθέσιµος σε κάποιο datasheet της τεχνικής τεκµηρίωσης που συνοδεύει το υλικό του συστήµατός µας Στην περίπτωσή µας οι πληροφορίες αυτές βρίσκονται στη σελίδα 73 του datasheet του µικροελεγκτή AP7000 (doc32003.pdf) που µπορούµε να βρούµε στον ιστότοπο της ATMEL. Η µνήµη η οποία υποστηρίζεται από τον µικροελεγκτή µας είναι η παρακάτω: Πίνακας 5. Χάρτης φυσικών διευθύνσεων του AP7000 Οι φυσικές διευθύνσεις που φαίνονται στον πίνακα αντιστοιχούν σε διαφορετικές συσκευές µνήµης οι οποίες µπορούν να προσπελαστούν από τη CPU. Το ακρωνύµιο EBI (External Bus Interface) σηµαίνει διασύνδεση εξωτερικού διαύλου. Μπορούµε επίσης να δούµε ότι οι δύο εσωτερικές µνήµες SRAM (Static Random – Access Memory) των 16K, µπορούν να προσπελαστούν µέσω των φυσικών διευθύνσεων 0x2400_0000 και 0x2400_4000. Ο Router NGW100 δεν διαθέτει τόση πολλή µνήµη, γι’ αυτό ο µικροελεγκτής έχει κάποιες υποδοχές (slots) στις οποίες δεν έχει πρόσβαση αφού καµία συσκευή δεν είναι συνδεδεµένη σε αυτές. Έτσι στην πραγµατικότητα ηδιαθέσιµη µνήµη είναι η παρακάτω: Πίνακας 6. Η διαθέσιµη µνήµη του Router NGW100 Παρατηρούµε ότι θα µπορούσαµε να προσθέσουµε περισσότερη µνήµη flash ή SRAM στο δίαυλο εξωτερικής διασύνδεσης EBI. Ο Router NGW100 περιλαµβάνει επίσης 8 MB σειριακής flash όµως δεν είναι διαθέσιµη µέσω του EBI. Είναι συνδεδεµένη στον SPI (Serial Peripheral Interface). Ο Router NGW100 διαθέτει δύο διαύλους SPI, η σειριακή flash βρίσκεται Επικοινωνία: [email protected] 164 Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 – Κεφάλαιο 5 συνδεδεµένη στον δίαυλο 0 (SPI0). Έτσι προκειµένου να µπορούµε να προσπελάσουµε αυτή τη µνήµη θα πρέπει να γράψουµε έναν driver (ευτυχώς κάτι τέτοιο δεν ήταν απαραίτητο γιατί υπήρχε έτοιµος στο internet). Η σειριακή dataflash µπορεί να θεωρηθεί ως ο σκληρός δίσκος του συστήµατος σε αυτή την περίπτωση. 5.4 Ethernet PHY και MAC Εφόσον το λογισµικό και τα πρωτόκολλα επικοινωνίας (στοίβα TCP/IP) που απαιτούνται για την επίτευξη της ανταλλαγής πακέτων στο δίκτυο είναι διαθέσιµα και λειτουργούν σωστά, το τελευταίο τµήµα της επικοινωνίας (Φυσικό επίπεδο) που θα πρέπει να διευθετηθεί είναι η προσαρµογή στα ιδιαιτέρα χαρακτηριστικά που προβλέπονται από το πρότυπο Ethernet (IEEE 802.3). Ουσιαστικά στο στάδιο αυτό υλοποιούνται δύο βασικές λειτουργίες. Τα πακέτα µετατρέπονται σε πλαίσια (frames) και τα ηλεκτρικά σήµατα διαµορφώνονται κατάλληλα για την µετάδοσή τους στο φυσικό µέσο το οποίο είναι συνήθως ο χαλκός. Φυσικά υπάρχει και η περίπτωση κατά την οποία λαµβάνουµε πλαίσια από το δίκτυο στις θύρες Ethernet του συστήµατός µας, όπου και εκτελείται η ακριβώς αντίστροφη διαδικασία. Για τις λειτουργίες αυτές απαιτείται ειδικό υποσύστηµα το οποίο κάποιες φορές βρίσκεται ενσωµατωµένο στην µονάδα επεξεργασίας του συστήµατος που αναπτύσσεται, ενώ στις περισσότερες των περιπτώσεων, το υποσύστηµα αυτό υπάρχει σε ένα ξεχωριστό ολοκληρωµένο (Ethernet controller ή Ethernet PHY). Η κωδική ονοµασία (Device ID) του ολοκληρωµένου που χρησιµοποιήθηκε στο σύστηµά µας είναι DP83848I. Το package της συσκευής αυτής φαίνεται στην επόµενη εικόνα: Εικόνα 53. Το ολοκληρωµένο DP83848I (Ethernet PHY) Στο σύστηµά µας υπάρχουν δύο θύρες Ethernet, οπότε υπάρχουν και δύο Ethernet controllers. Η σύνδεσή τους µε τον µικροελεγκτή και η χρήση τους, φαίνεται στον πίνακα που ακολουθεί: AP7000 peripheral NGW schematic PHY ID Linux usage MACB0 ETH_0 00001 (01h) WAN MACB1 ETH_1 00011 (03h) LAN Πίνακας 7. Λεπτοµέρειες σύνδεσης των Ethernet PHYs Στην επόµενη εικόνα µπορούµε να δούµε τις δύο θύρες Ethernet, τους Ethernet controllers, καθώς και τον µικροελεγκτή, όπως αυτά βρίσκονται τοποθετηµένα στην πλακέτα του Router NGW100: Ιστότοπος εργασίας: MyThesis.org 165 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) AP7000 Θύρα Ethernet WAN WAN Ethernet controller Θύρα Ethernet LAN WAN Ethernet controller Εικόνα 54. θύρες LAN και WAN, Ethernet controllers και ο µικροελεγκτής AP7000 Οι δύο θύρες Ethernet κατασκευάζονται από την εταιρεία Pulse Electronics µε κωδικό προϊόντος J3026G01DNL, και διαθέτουν φωτεινές ενδείξεις κατάστασής µε πορτοκαλί και πράσινο led. Σε αντίστοιχο τεχνικό εγχειρίδιο που παρέχει η εταιρεία υποδεικνύονται τα τεχνικά χαρακτηριστικά στα οποία πρέπει να βασιστούµε προκειµένου να συγκολλήσουµε και να συνδέσουµε τις θύρες αυτές στο PCB της πλακέτας µας. Στην εικόνα που ακολουθεί βλέπουµε µια τέτοια θύρα: Πράσινο LED Κίτρινο LED Εικόνα 55. Η θύρα Ethernet J3026G01DNL Ο µικροελεγκτής AP7000 συνδέεται και επικοινωνεί µε τους δύο Ethernet controllers µέσω της διεπαφής MII που αναφέραµε και νωρίτερα όταν παραθέσαµε τα σηµαντικότερα χαρακτηριστικά του. Η διεπαφή MII υποστηρίζεται τόσο από τον µικροελεγκτή όσο και από τους Ethernet controllers. Στο διάγραµµα βλέπουµε ένα παράδειγµα σύνδεσης του controller (Ethernet PHY) µε τον µικροελεγκτή: Επικοινωνία: [email protected] 166 Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 – Κεφάλαιο 5 Εικόνα 56. Σύνδεση του ολοκληρωµένου DP83848I (Ethernet PHY) µε τον µικροελεγκτή AP7000 Τα pins που διατίθενται για σύνδεση MII περιγράφονται επίσης σε αντίστοιχα datasheets (του µικροελεγκτή και του Ethernet controller). Η διάταξη των ακροδεκτών (pin layout ή pinout) φαίνεται στο παρακάτω διάγραµµα: Ιστότοπος εργασίας: MyThesis.org 167 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 57. Η διάταξη των ακροδεκτών (pinout) του ολοκληρωµένου DP83848I (Ethernet PHY) Στο παράρτηµα που παρουσιάζεται το σχηµατικό διάγραµµα του συστήµατός µας µπορεί κανείς να δει ακριβώς τον τρόπο σύνδεσης των δύο Ethernet controllers µε τον µικροελεγκτή AP7000. Η φυσική διεύθυνση (MAC) των δύο Ethernet controllers προκύπτει από µια καταχωρηµένη από την εταιρεία, βάση MAC, που είναι η 00:00:04:25:1C:50:00 σε συνδυασµό µε το σειριακό αριθµό του κάθε controller. Oι φυσικές διευθύνσεις του συστήµατός µας στην εργασία αυτή είναι οι εξής: AP7000 peripheral NGW schematic MAC Linux usage MACB0 ETH_0 00:04:25:1C:82:7C WAN MACB1 ETH_1 00:04:25:1C:82:7D LAN Πίνακας 8. Οι MAC διευθύσεις του Router NGW100 Όπως αναφέραµε και πιο πάνω, περισσότερες και ακριβέστερες λεπτοµέρειες σχετικά µε την σύνδεση όλων των ηλεκτρικών και ηλεκτρονικών στοιχείων του Router NGW100 µπορούµε να βρούµε και στο παράρτηµα του πλήρους σχηµατικού του Router NGW100. 5.5 Συνδεσιµότητα RS232 Η σειριακή επικοινωνία όσον αφορά το υλικό του Router NGW100, επιτυγχάνεται µέσω των ακροδεκτών USART_1_TXD 1(PA18) και USART_1_RXD 1 (PA17) του µικροελεγκτή. Η µε- Επικοινωνία: [email protected] 168 Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 – Κεφάλαιο 5 τατροπή της TTL στάθµης σε στάθµη RS232 πραγµατοποιείται από το ολοκληρωµένο MAX3232ECAE+. Η συνδεσµολογία του µε τον µικροελεγκτή φαίνεται και στο αντίστοιχο παράρτηµα (σχηµατικό Router NGW100). Το package του MAX3232ECAE+ είναι το ακόλουθο: Εικόνα 58. Το ολοκληρωµένο MAX3232ECAE+ Επίσης, σε σχετικό παράρτηµα υπάρχουν οδηγίες και για την προσκόµιση του datasheet το οποίο διανέµεται από την κατασκευάστρια εταιρεία. Στην επόµενη εικόνα µπορούµε να δούµε το pinout της συσκευής: Εικόνα 59. Η διάταξη των ακροδεκτών του ολοκληρωµένου MAX3232ECAE+ 5.6 Ελεγκτής πλακέτας – Board Controller Στην πλακέτα του Router NGW100 υπάρχει κι ένας ελεγκτής πλακέτας (board controller). Σκοπός του είναι να κρατά πληροφορίες που αφορούν το ID του κατασκευαστή, το όνοµα της πλακέτας και άλλα στοιχεία. Οι πληροφορίες αυτές µπορούν να διαβάζονται και από άλλα συστήµατα που επικοινωνούν µε την πλακέτα. Για παράδειγµα σε ένα τοπικό δίκτυο οι υπολογιστές που θα συνδέονται µε τον Router NGW100 θα µπορούν να γνωρίζουν το όνοµά του, τον κατασκευαστή του και όποια άλλη πληροφορία κρίνεται χρήσιµο να διαµοιραστεί κάθε φορά. Επίσης, ο ελεγκτής πλακέτας είναι σε θέση να γνωρίζει τις τάσεις λειτουργίας και τη θερµοκρασία του συστήµατος και να αναφέρει τα αποτελέσµατα στον µικροελεγκτή. Έτσι µπορούν να γίνονται συνεχείς διαγνωστικοί έλεγχοι και να παίρνονται όπου κρίνεται απαραίτητο, οι ανάλογες αποφάσεις. Ουσιαστικά πρόκειται για τον µικροελεγκτή ATtiny24-20SSU της εταιρείας ATMEL. Οι µικροελεγκτές της οικογένειας tinyAVR προσφέρονται για περιπτώσεις που απαιτείται οικονοµία χώρου και τάσης λειτουργίας (µπορούν να λειτουργήσουν µε τάση 0.7 Volt!). Το package του ATtiny24 φαίνεται στην ακόλουθη εικόνα: Εικόνα 60. Ο µικροελεγκτής ATtiny24-20SSU (board controller) Ιστότοπος εργασίας: MyThesis.org 169 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Η επικοινωνία του board controller µε τον µικροελεγκτή επιτυγχάνεται µέσω του προτύπου PMbus (powersig.org). Το πρότυπο αυτό προτιµήθηκε λόγω του ότι χρησιµοποιείται σε αρκετά γνωστά εµπορικά προϊόντα (πχ PC motherboards) κι έτσι µπορούν να ευκολότερα να βρεθούν παραδείγµατα κώδικα που το χρησιµοποιούν. Στην επόµενη εικόνα µπορούµε να δούµε το pinout του ATtiny24: Εικόνα 61. Η διάταξη των ακροδεκτών του µικροελεγκτή ATtiny24-20SSU Επίσης, στο µπλοκ διάγραµµα που ακολουθεί, παρουσιάζεται η κυκλωµατική συνδεσµολογία του ATtiny24 µε τον µικροελεγκτή: Εικόνα 62. Η συνδεσµολογία του ATtiny24 µε τον µικροελεγκτή AP7000 Οι αντιστάσεις R82 και R83 είναι 4K η κάθε µια τους και λειτουργούν σαν pullup αφού ο δίαυλος TWI (Two Wires Interface) απαιτεί την ύπαρξή τους. Επικοινωνία: [email protected] 170 Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 – Κεφάλαιο 5 Η χαρτογράφηση του board controller στην µνήµη έχει την τιµή 0x0B. Τα µηνύµατα απαντήσεων του PMbus είναι της µορφής: <µήκος><databyte0><databyte1>...<databyteN> Στον πίνακα µπορούµε να δούµε ποια µηνύµατα υποστηρίζονται: Εντολή Τιµή Επιστρεφόµενη τιµή TWI_CMD_MFR_ID 0x99 atmel.no (ascii) TWI_CMD_MFR_MODEL 0x9A NGW100 (ascii) TWI_CMD_MFR_SERIAL 0x9E TWI_CMD_READ_TEMP_1 0x9D Serial number του NGW100 (ascii) Θερµοκρασία σε Kelvin (unsigned short int) Πίνακας 9. Μηνύµατα απόκρισης του PMbus Υπάρχει και άλλη µία εντολή, η TWI_CMD_MFR_REVISION (τύπου unsigned char), την οποία δεν θα αναλύσουµε εκτενώς. Απλά θα αναφέρουµε ότι πρόκειται για ένα σύστηµα διαχείρισης εκδόσεων – τροποποιήσεων του υλικού (hardware revision ή versioning). Όπως έχουµε δει και στο θεωρητικό µέρος της εργασίας, παρόµοιοι µηχανισµοί υπάρχουν και για τη διαχείριση εκδόσεων του λογισµικού (πχ: SVN, GIT κλπ). Ο προγραµµατισµός του ελεγκτή πλακέτας ATtiny24, προϋποθέτει ότι το ολοκληρωµένο έχει αποκολληθεί από την πλακέτα και έχει καθαριστεί πρώτα η µνήµη του (chip erase). Αυτό σηµαίνει επίσης ότι είναι δύσκολο κάποιος να αλλάξει την ψηφιακή ταυτότητα που εµείς ως κατασκευαστές, έχουµε δώσει στην πλακέτα µας. 5.7 ∆ιεπαφές προγραµµατισµού JTAG και NEXUS Ο Router NGW100 διαθέτει δύο διεπαφές για προγραµµατισµό και debugging, την διεπαφή JTAG και την διεπαφή NEXUS. Στην εργασία αυτή έγινε χρήση µόνο της πρώτης, δηλαδή της διεπαφής JTAG. Φυσικά όταν λέµε ότι ο Router NGW100 υποστηρίζει JTAG, εννοούµε ότι ο µικροελεγκτής AP7000 παρέχει αυτή τη δυνατότητα. Μέσω της διεπαφής µπορούµε να πάρουµε στα χέρια µας τον πλήρη έλεγχο του υλικού του Router NGW100 έτσι ώστε µέσω του συστήµατος host να µπορούµε να προγραµµατίζουµε την εξωτερική µνήµη Flash και να κάνουµε debugging στα προγράµµατα που τρέχουν στον AP7000 χωρίς να απαιτείται η ύπαρξη κάποιου bootloader στον Router. Στο PCB του Router υπάρχουν δύο θέσεις (footprints) για τη συγκόλληση ακροδεκτών JTAG. Η πρώτη η οποία και χρησιµοποιήθηκε σε αυτή την εργασία, υποστηρίζει trough hole headers και η απόσταση του ενός pin από το άλλο είναι 100 mil (ή 100 thou), ενώ η δεύτερη είναι επιφανειακής στήριξης (SMD) και 50 mil. Το 1 mil είναι το ένα χιλιοστό της ίντσας και πρόκειται για µονάδα µέτρησης µήκους που χρησιµοποιείται από τους µηχανικούς κατά την σχεδίαση πλακετών (PCB). Ιστότοπος εργασίας: MyThesis.org 171 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Η διάταξη των ακροδεκτών JTAG που υπάρχουν στον Router ακολουθεί το πρότυπο JTAG της ATMEL. Μπορούµε να δούµε τη διάταξη αυτών στην επόµενη εικόνα: Εικόνα 63. Η διάταξη των ακροδεκτών JTAG Το header που χρησιµοποιήθηκε (10 pin Header) είναι ένα απλό header 10 ακροδεκτών και µπορούµε να το δούµε πιο κάτω: Εικόνα 64. 10 pin Header για σύνδεση JTAG Επίσης πιο κάτω παρατίθεται ένα τµήµα του σχηµατικού του Router ώστε να φανεί όσο καλύτερα γίνεται και η σύνδεση του header µε τον µικροελεγκτή: Εικόνα 65. Σχηµατικό σύνδεσης του 10 pin Header µε τον µικροελεγκτή AP7000 Οι ονοµασίες AP7000_1 και AP7000_2 στο σχηµατικό του Router NGW100 υπάρχουν για να φαίνεται ότι η συνδεσµολογία του µικροελεγκτή δεν ολοκληρώνεται σε µια σελίδα Α4 του µηχανικού σχεδίου αλλά συνεχίζεται και σε δεύτερη. Αυτό είναι συχνό φαινόµενο στη σχεδίαση µεγάλων συστηµάτων όπου υπάρχουν µικροελεγκτές µε εκατοντάδες ακροδέκτες. Ο χώρος είναι περιορισµένος οπότε οι µηχανικοί αναγκάζονται να χωρίζουν την CPU σε λογικές µονάδες (στην περίπτωσή µας U1A, U1B και U1C) που περιγράφονται σε ξεχωριστές σελίδες. Με αυτό τον τρόπο προκύπτει πάντα ένα πιο ευανάγνωστο αποτέλεσµα. Επικοινωνία: [email protected] 172 Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 – Κεφάλαιο 5 5.8 Σύστηµα χρονισµού Ο µικροελεγκτής AP7000 διαθέτει τρεις πηγές χρονισµού (clocks). ∆ύο κρυστάλλους, τους XC1 και XC2, µε συχνότητες λειτουργίας 12MHz και 20MHz αντίστοιχα. Και τον κρύσταλλο XC3 µε συχνότητα λειτουργίας 32KHz που είναι κατάλληλος για συστήµατα πραγµατικού χρόνου. Πιο κάτω βλέπουµε το σχηµατικό σύνδεσης του εξωτερικού ρολογιού (external clock) µε τον µικροελεγκτή: Εικόνα 66. Σχηµατικό σύνδεσης του εξωτερικού ρολογιού (external clock) µε τον µικροελεγκτή AP7000 Μέσω των κρυστάλλων XC1 και XC2 µπορούµε να «οδηγήσουµε» την λειτουργία ορισµένων µονάδων του AP7000. Έτσι οι µονάδες PLL (Phase-locked loop), PLL0 και PLL1 «οδηγούνται» αντίστοιχα από τους δύο αυτούς κρυστάλλους. Με σκοπό να ορίσουµε την µέγιστη και την ελάχιστη συχνότητα λειτουργίας του Router NGW100. Ουσιαστικά λοιπόν πρόκειται για ένα σύστηµα φιλτραρίσµατος (PLL low-pass filter) συχνοτήτων. Πιο κάτω µπορούµε να δούµε αντιπροσωπευτικό διάγραµµα: Εικόνα 67. Χαµηλοπερατό σύστηµα φιλτραρίσµατος συχνοτήτων (PLL low-pass filter) Στο datasheet του AP7000 υπάρχει λεπτοµερής ανάλυση των µονάδων PLL. Στον πίνακα που ακολουθεί µπορούµε να δούµε τη διαµόρφωση των δύο φίλτρων PLL για την περίπτωσή του Router NGW100: Ιστότοπος εργασίας: MyThesis.org 173 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) DIV MUL Fn (PLL bandwidth) 20MHz 2 13 30KHz 130MHz R18=430R C7=1.8nF C14=18nF 20MHz 4 16 30KHz 80MHz R17=150R C6=5.6nF C15=56nF Filter Fosc PLL0 PLL1 Fout R17/R18 C6/C7 C14/C15 Πίνακας 10. Η διαµόρφωση των φίλτρων PLL Η συχνότητα εξόδου (Fout) υπολογίζεται ως εξής: Fout = (Fosc / DIV) * MUL 5.9 Κύκλωµα επαναφοράς (reset) Ο Router NGW100 διαθέτει δύο κυκλώµατα reset. Το ένα κύκλωµα χρησιµοποιείται για τον µικροελεγκτή και τον board controller, και το άλλο για τους Ethernet controllers και την παράλληλη µνήµη (DataFlash). Και τα δύο κυκλώµατα τίθενται σε χαµηλή στάθµη (low) από τον ίδιο διακόπτη (reset button). Κάθε φορά που γίνεται κάποιο reset, γίνεται πρώτα στη µνήµη και µετά στον µικροελεγκτή. Αυτό επιτυγχάνεται µε τη βοήθεια του κυκλώµατος καθυστέρησης το οποίο υλοποιείται από δύο αντιστάσεις (R15, R16) και δύο πυκνωτές (C1, C2) που έχουν διαφορετικά χαρακτηριστικά έτσι ώστε να επιτυγχάνονται και διαφορετικές καθυστερήσεις. Αυτό φαίνεται και στο διάγραµµα του κυκλώµατος reset που µπορούµε να δούµε παρακάτω: Εικόνα 68. Το κύκλωµα επαναφοράς (reset) του Router NGW100 Ο διακόπτης για το reset (SKHUAF010) στον Router NGW100 είναι επιφανειακής στήριξης (SMD) και µπορούµε να τον δούµε στην παρακάτω εικόνα: Εικόνα 69. Ο διακόπτης reset SKHUAF010 Επικοινωνία: [email protected] 174 Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 – Κεφάλαιο 5 5.10 Σύστηµα τροφοδοσίας Για να λειτουργήσει το Router και να εκτελέσει µε επιτυχία το λογισµικό του, θα πρέπει να τροφοδοτείται από ενέργεια. Την ενέργεια την παρέχει κάποια πηγή. Η παρεχόµενη τάση της πηγής (power supply) θα πρέπει να είναι 9-15 V DC και η ένταση 0.5 A, έτσι ώστε να υπάρχει αρκετή ισχύς στο κύκλωµα µετατροπής που υπάρχει εσωτερικά στον Router. Η τάση και η ένταση του οικιακού δικτύου (∆ΕΗ) είναι ακατάλληλές για τροφοδοσία οικιακών ηλεκτρονικών συσκευών και για το λόγο αυτό θα πρέπει να µεσολαβεί κάποιο κύκλωµα µετατροπής (power converter) ή πιο απλά, ένα κατάλληλο τροφοδοτικό. Το στοιχείο αυτό µπορεί να είναι είτε εσωτερικό, είτε εξωτερικό. Στην περίπτωσή µας είναι εξωτερικό. Για τις ανάγκες αυτής της εργασίας και για λόγους οικονοµίας, χρησιµοποιήθηκε ένα παροπλισµένο τροφοδοτικό από κάποιον ανενεργό router της εταιρείας Netgear που υπήρχε διαθέσιµο. Το βλέπουµε στην εικόνα: Εικόνα 70. Το τροφοδοτικό του Router NGW100 Τα χαρακτηριστικά του κρίθηκαν αποδεκτά για τις ανώτερες και κατώτερες επιτρεπτές στάθµες λειτουργίας του Router NGW100 και επιβεβαιώθηκαν και καθ’ όλη τη διάρκεια της ανάπτυξης του λογισµικού. Ας δούµε ποια είναι αυτά: input: AC 220 – 240V ~ 50/60Hz 0.2A (εναλλασσόµενο) output: DC 12V 1A (συνεχές) Το σύστηµα τροφοδοσίας (power system) αποτελείται από τρία κύρια µέρη. Το πρώτο το περιγράψαµε ήδη και δεν βρίσκεται συγκολληµένο στην πλακέτα του Router. Τα επόµενα δύο µέρη που θα περιγραφούν βρίσκονται ενσωµατωµένα στο PCB. Ουσιαστικά το ένα από αυτά τα δύο µέρη είναι παθητικό και το µόνο που κάνει είναι να ενώνει την έξοδο του µετατροπέα µε την είσοδο του συστήµατος µετατροπής του Router. Πρόκειται για µια θύρα (RASM722P) των 2.1 mm που είναι και το πρώτο στοιχείο το οποίο φέρνει σε επαφή την πλακέτα µε την παροχή ρεύµατος. Την βλέπουµε στην εικόνα: Εικόνα 71. Η θύρα τροφοδοσίας RASM722P Ιστότοπος εργασίας: MyThesis.org 175 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Το τρίτο και σηµαντικότερο µέρος του συστήµατος τροφοδοσίας του Router είναι το εσωτερικό κύκλωµα µετατροπής τάσης. Στο διάγραµµα που ακολουθεί µπορούµε να το δούµε περιληπτικά: Εικόνα 72. Το εσωτερικό κύκλωµα µετατροπής τάσης Το ολοκληρωµένο DF10S είναι µια πλήρης γέφυρα ανόρθωσης τεσσάρων διόδων η οποία αναλαµβάνει να µετατρέψει το σύνολο της κυµατοµορφής εισόδου (input) σε µία από τις δύο (θετική ή αρνητική) σταθερές πολικότητες. Στην εικόνα βλέπουµε το DF10S σαν συσκευή: Εικόνα 73. Η γέφυρα ανόρθωσης τεσσάρων διόδων DF10S Εφόσον γειώνουµε το πλην της γέφυρας στην είσοδο, επιλέγουµε την θετική πολικότητα στην έξοδό της. Στο σχήµα που ακολουθεί βλέπουµε µια θεωρητική γραφική αναπαράσταση: Εικόνα 74. θεωρητική γραφική αναπαράσταση της λειτουργίας µιας γέφυρας ανόρθωσης Το δεύτερο σηµαντικό βήµα που πραγµατοποιείται στο κύκλωµα µετατροπής του Router είναι η έξοδος της γέφυρας ανόρθωσης να γίνει είσοδος στο ολοκληρωµένο LM2717 το οποίο βλέπουµε και στην εικόνα: Επικοινωνία: [email protected] 176 Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 – Κεφάλαιο 5 Εικόνα 75. Το ολοκληρωµένο LM2717 (DC/DC converter) Πρόκειται για έναν DC/DC converter ο οποίος µετατρέπει την DC είσοδο των 12V που λαµβάνει από την γέφυρα ανόρθωσης DF10S και την υποβιβάζει σε δύο άλλα επίπεδα τάσης των 3.3 (τροφοδοσία CPU) και 1.8 Volt (ρυθµιζόµενη). Η διάταξη των ακροδεκτών του είναι η εξής: Εικόνα 76. Η διάταξη των ακροδεκτών του ολοκληρωµένου LM2717 Τα χαρακτηριστικά του, όπως αυτά υπάρχουν στο datasheet που παρέχει η κατασκευάστρια εταιρεία είναι τα ακόλουθα: Fixed 3.3V output buck converter with a 2.2A, 0.16Ω, internal switch Adjustable buck converter with a 3.2A, 0.16Ω, internal switch Operating input voltage range of 4V to 20V Input undervoltage protection 300kHz to 600kHz pin adjustable operating frequency Over temperature protection Small 24-Lead TSSOP package Από τη στιγµή που όλα έχουν γίνει σωστά, ανάβει το πράσινο LED (EL15-21UGC) που υποδεικνύει ότι η πλακέτα τροφοδοτείται σωστά και µπορεί να λειτουργήσει. Ιστότοπος εργασίας: MyThesis.org 177 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) 5.11 ∆ιαστάσεις πλακέτας Στο παρακάτω µηχανικό σχέδιο παρουσιάζονται λεπτοµέρειες όπως, το µέγεθος της πλακέτας, η τοποθέτηση και το µέγεθος των οπών στήριξης (mounting holes), και η τοποθέτηση των κεφαλών επέκτασης (expansion headers): Εικόνα 77. Το µηχανικό σχέδιο του PCB του Router NGW100 5.12 Σχέδιο συναρµολόγησης Το σχέδιο συναρµολόγησης (assembly drawing) είναι απαραίτητο για κάθε προϊόν το οποίο κατασκευάζεται και αποτελείται από πολλά µέρη. Είναι ένας πρόχειρος αλλά χρήσιµος οδηγός συναρµολόγησης: Επικοινωνία: [email protected] 178 Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 – Κεφάλαιο 5 Εικόνα 78. Το σχέδιο συναρµολόγησης του Router NGW100 Κάθε στοιχείο που υπάρχει επάνω στο σχέδιο συναρµολόγησης έχει µια κωδική ονοµασία η οποία υπάρχει και στο σχηµατικό της πλακέτας µας. Αυτό µας δίνει τη δυνατότητα να γνωρίζουµε τι θα συγκολληθεί σε κάθε σηµείο της. Φυσικά το ρόλο αυτό µπορεί να παίξει και το silkscreen (µεταξοτυπία) που θα έχει τυπωθεί όπως εµείς έχουµε υποδείξει στα µηχανικά σχέδια που στείλαµε στο εργοστάσιο το οποίο τύπωσε τις πλακέτες µας. Ιστότοπος εργασίας: MyThesis.org 179 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) 5.13 Σχηµατικά και PCB του Router NGW100 Όλα τα σχηµατικά της πλακέτας αυτής της εργασίας, όπως και όλα τα τεχνικά έγγραφα (datasheets) των ηλεκτρονικών στοιχείων που χρησιµοποιήθηκαν, περιλαµβάνονται στον ιστότοπο στο CD που συνοδεύει την εργασία. Έτσι αν κάτι είναι ελλιπές ή δυσνόητο στην ανάλυση του υλικού που παρατέθηκε σε αυτό το πρακτικό µέρος, µπορεί να βρεθεί εκεί. Στο παράρτηµα C υπάρχουν περισσότερες πληροφορίες. Το λογισµικό CAD (Computer Aided Design) που χρησιµοποιήθηκε για την ανάπτυξη των σχηµατικών αλλά και της πλακέτας του Router είναι το orCad. Μέσω του ίδιου λογισµικού δηµιουργήθηκαν και τα αντίστοιχα Gerber files τα οποία απαιτούνται κατά το στάδιο κατασκευής (PCB manufacturing) της πλακέτας. 5.14 Επέκταση δυνατοτήτων υλικού Σε αυτή την παράγραφο είναι πολύ χρήσιµο να ορίσουµε τι είναι και τι δεν είναι ο Router NGW100 προκειµένου να διαφανούν οι περεταίρω βελτιώσεις του υλικού που θα µπορούσαν να γίνουν. Αυτό ίσως να δείχνει περιττό αρχικά, εφόσον λέµε ότι πρόκειται για έναν δροµολογητή. Όµως σε πολλές περιπτώσεις η έννοια συγχέεται µε τους σύγχρονους δροµολογητές οι οποίοι ουσιαστικά είναι πολλές συσκευές σε µία. Ας δούµε όµως ποιες είναι αυτές οι συσκευές: Modem Router Switch Bridge Access Point Wireless κλπ Ο Router NGW100 υλοποιήθηκε κατά κύριο λόγο για την δροµολόγηση πακέτων. ∆εν διαθέτει κάποιο PSTN ή ISDN modem και έτσι χρειάζεται να συνδεθεί µέσω της θύρας WAN σε κάποιο, προκειµένου να είναι σε θέση να δροµολογήσει πακέτα στο Internet. Επίσης, διαθέτει µόνο µία θύρα LAN και εποµένως για να συνδεθούν περισσότεροι του ενός υπολογιστές, θα πρέπει να µεσολαβεί ένα switch. Ο Router NGW100 µπορεί να λειτουργήσει σαν γέφυρα (Bridge). ∆εν µπορεί να λειτουργήσει σαν Access Point ή να συνδεθούν σε αυτόν, υπολογιστές µέσω WiFi καθώς δε διαθέτει το απαιτούµενο υλικό. Οι βελτιώσεις που µπορούν να γίνουν µελλοντικά στον Router NGW100 είναι πολλές και θα επηρεάσουν εξίσου και το υλικό αλλά και το λογισµικό. Στο διάγραµµα της εικόνας που ακολουθεί, µπορούµε να δούµε σε µεγάλο επίπεδο αφαίρεσης τις βελτιώσεις αυτές: Επικοινωνία: [email protected] 180 Ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100 – Κεφάλαιο 5 Εικόνα 79. Επέκταση δυνατοτήτων υλικού του Router NGW100 Βέβαια, τώρα πια τα πράγµατα θα είναι ευκολότερα, καθώς υπάρχει υλοποιηµένος ο βασικός πυρήνας που είναι ο επεξεργαστής AP7000, το ειδικά διαµορφοµένο Buildroot και οι απαραίτητοι οδηγοί συσκευών. Ιστότοπος εργασίας: MyThesis.org 181 ΚΕΦΑΛΑΙΟ 6 Προγραµµατισµός και εκκίνηση του Router NGW100 Εισαγωγή Έχοντας αναλύσει το υλικό του Router NGW100, µπορούµε πλέον να ασχοληθούµε µε το λογισµικό του. Σε αυτό το κεφάλαιο θα παρουσιαστούν τα βήµατα που έγιναν έτσι ώστε Ο Router NGW100 να καταστεί µια πλήρως λειτουργική συσκευή, η οποία θα µπορεί να αλληλεπιδρά τόσο µε έναν προγραµµατιστή, όσο και µε τον απλό χρήστη. Τα θέµατα που θα αναλυθούν είναι τα ακόλουθα: Το σύστηµα ανάπτυξης host Το πακέτο υποστήριξης συστήµατος Atmel Linux BSP 3.0 ∆ηµιουργία της εικονικής µηχανής Ubuntu 9.04 Το περιβάλλον ανάπτυξης AVR32 Βοηθητικό λογισµικό ∆ιαµέριση µνήµης Η συσκευή JTAGICE mkII Σύνδεση JTAGICE mkII µε το σύστηµα host Εγγραφή U-Boot στην παράλληλη µνήµη Εγγραφή Linux Kernel στην παράλληλη µνήµη Εγγραφή συστήµατος αρχείων /usr ∆ιαδικασία εκκίνησης 6.1 Το σύστηµα ανάπτυξης host Για το σύστηµα ανάπτυξης host, στο οποίο αναπτύχθηκε και παραµετροποιήθηκε το λογισµικό του Router NGW100, χρησιµοποιήθηκε ένας προσωπικός υπολογιστής µε επεξεργαστή Intel Pentium 4, κύρια µνήµη RAM 2GB και σκληρό δίσκο 500GB. Σε αυτόν εγκαταστάθηκε µια καθαρή έκδοση των αγγλικών Windows XP Professional SP3, και η εφαρµογή VMware προκειµένου να µπορεί στη συνέχεια να δηµιουργηθεί µια εικονική µηχανή Linux. Οι κυριότεροι λόγοι για τους οποίους προτιµήθηκε αυτή η διαµόρφωση στο σύστηµα host, είναι: Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 Ευελιξία και µεταφερσιµότητα της εικονικής µηχανής: Όπως θα δούµε και στη συνέχεια, µια εικονική µηχανή αποτελείται ουσιαστικά από δύο αρχεία. Τα αρχεία αυτά µπορούµε να τα µεταφέρουµε από υπολογιστή σε υπολογιστή και να λειτουργούν µε τον ίδιο τρόπο, απλά αντιγράφοντάς τα. Αυτό δε θα µπορούσε να συµβεί τόσο εύκολα σε περίπτωση που το λειτουργικό σύστηµα ήταν εγκατεστηµένο εξ ολοκλήρου στον υπολογιστή µας. Προστασία της δουλειάς µας: Εφόσον µια εικονική µηχανή αποτελείται µόνο από απλά αρχεία, µπορούµε πολύ εύκολα να δηµιουργούµε αντίγραφα ασφαλείας. Αυτό µας βοηθά στο να µην χάνουµε την δουλειά µας σε περίπτωση αστοχίας υλικού αλλά και να µπορούµε να την επαναφέρουµε σε προγενέστερα σηµεία αναφοράς, αν εµείς οι ίδιοι, κάνουµε κάποιο λάθος. Περισσότερες δυνατότητες δοκιµών: ∆εδοµένου του ότι οι χωρητικότητες των σκληρών δίσκων είναι µεγάλες και το κόστος τους όλο και χαµηλότερο, έχουµε τη δυνατότητα να δηµιουργούµε πολλαπλές εικονικές µηχανές. Με αυτό τον τρόπο µπορούµε να κάνουµε δοκιµές σε διαφορετικές συνθήκες ώστε στο τέλος να επιλέξουµε την βέλτιστη περίπτωση. 6.2 Το πακέτο υποστήριξης συστήµατος Atmel Linux BSP 3.0 Ένα πακέτο BSP περιλαµβάνει, όπως αναφέραµε και σε αντίστοιχο κεφάλαιο του θεωρητικού µέρους της εργασίας αυτής, σχεδόν ότι χρειαζόµαστε για τη δηµιουργία του λογισµικού που απαιτείται για την ανάπτυξη ενός ενσωµατωµένου συστήµατος Linux. Το αντίστοιχο πακέτο για τον Router NGW100 είναι το Atmel Linux BSP 3.0 και παρέχεται δωρεάν µε µια απλή επίσκεψη στον αντίστοιχο σύνδεσµο λήψης, του επίσηµου ιστότοπου της εταιρείας Atmel: www.atmel.com/Images/AVR32_Linux_BSP_reduced_Image_3.0.0.zip. Μετά την εξαγωγή των περιεχοµένων του συµπιεσµένου αρχείου ZIP, προκύπτει το αρχείο AVR32_Linux_BSP_reduced_Image_3.0.0.iso το οποίο µε τη σειρά του περιλαµβάνει τα παρακάτω: Εικόνα 80. Τα περιεχόµενα του πακέτου Atmel Linux BSP 3.0 Οι ονοµασίες των καταλόγων είναι χαρακτηριστικές και αµέσως µας παρέχουν µια πρώτη πληροφόρηση για το περιεχόµενό τους. Ο κατάλογος appnotes (application notes) περιλαµβάνει οδηγίες για τη δηµιουργία του συστήµατος host και την εγκατάσταση των απαραίτητων εφαρµογών. Επίσης περιλαµβάνονται κάποιες οδηγίες για την ανάπτυξη και εκτέλεση εφαρµογών Linux. Ιστότοπος εργασίας: MyThesis.org 183 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Στον κατάλογο avr32studio υπάρχει το IDE AVR32 Studio το οποίο είναι βασισµένο σε eclipse. Μας παρέχει τη δυνατότητα να µεταγλωττίζουµε τις εφαρµογές που αναπτύσσουµε για τον Router NGW100 σε ένα πιο φιλικό και άνετο γραφικό περιβάλλον. Ο κατάλογος datasheets θεωρητικά θα έπρεπε να περιέχει όλα τα τεχνικά εγχειρίδια των ηλεκτρονικών στοιχείων. Όµως αυτό για άγνωστους λόγους, δε συνέβαινε. Στον συγκεκριµένο κατάλογο βρέθηκαν µόνο τα datasheets άλλων αναπτυξιακών πλακετών της Atmel, ενώ για την πλακέτα atngw100 υπήρχε απλά ένας κενός κατάλογος. Ο κατάλογος gnu_toolchain περιλαµβάνει την τοπική (native) αλυσίδα µεταγλώττισης GNU AVR32 που θα εγκατασταθεί στο σύστηµα host προκειµένου να µεταγλωττιστεί η cross toolchain του Buildroot αλλά και ότι άλλο ζητηθεί από τα makefiles του Buildroot. Επίσης περιλαµβάνει κάποιες χρήσιµες εφαρµογές της Atmel όπως είναι για παράδειγµα το πρόγραµµα avr32program το οποίο θα χρησιµοποιηθεί αργότερα για να επικοινωνήσουµε µε τη συσκευή programmer JTAGICE mkII. Ο κατάλογος buildroot περιλαµβάνει ένα κατάλληλα τροποποιηµένο αντίγραφο του αυτοµατοποιηµένου συστήµατος ανάπτυξης Buildroot και θα µας βοηθήσει µέσω της cross toolchain να δηµιουργήσουµε τα δυαδικά αρχεία (images) του λογισµικού εκκίνησης, του Linux Kernel και του συστήµατος αρχείων χρήστη /usr. Είναι ίσως το βασικότερο τµήµα λογισµικού του περιβάλλοντος ανάπτυξης AVR32 καθώς µέσω αυτού θα επιτυγχάνονται και µια σειρά άλλων σηµαντικών ενεργειών ανάπτυξης, όπως είναι η µεταγλώττιση εφαρµογών για τον Router NGW100 και η τροποποίηση των ρυθµίσεων του Linux Kernel, του BusyBox και της βιβλιοθήκης uClibc. Τέλος, στον κατάλογο documentation υπάρχει σε µορφή .html εγγράφων, µια πρόχειρη τεκµηρίωση η οποία αφορά το υλικό και το λογισµικό της αναπτυξιακής πλακέτας που έχουµε επιλέξει. 6.2.1 Τροποποίηση του πακέτου υποστήριξης Atmel Linux BSP 3.0 Αφού ολοκληρώθηκε η λήψη και η εξαγωγή του πακέτου BSP, σειρά είχε η δοκιµαστική εγκατάσταση της αλυσίδας GNU AVR32 και του αυτοµατοποιηµένου συστήµατος Buildroot σε µια προσωρινή εικονική µηχανή Linux. ∆ηλαδή η δοκιµαστική εγκατάσταση του περιβάλλοντος ανάπτυξης AVR32. Στον κατάλογο gnu_toolchain του BSP, υπήρχε υποστήριξη για τις διανοµές Linux, Fedora, SuSE και Ubuntu. Επιλέχθηκε η τελευταία λόγω προηγούµενης εµπειρίας. Η νεότερη έκδοση Ubuntu η οποία υποστηριζόταν από την αλυσίδα µεταγλώττισης GNU AVR32, ήταν η 8.04 (Ηardy Ηeron). Ακόµη όµως και για την έκδοση αυτή, η υποστήριξη ήταν ελλιπής. Χρειάστηκε να γίνουν αρκετές τροποποιήσεις και να βρεθούν ορισµένες εφαρµογές της αλυσίδας GNU AVR32, που είτε έλειπαν, είτε η έκδοσή τους δεν ήταν η απαιτούµενη. Για το λόγο αυτό ο κατάλογος gnu_toolchain χρησιµοποιήθηκε µόνο σαν σηµείο αναφοράς. Όλα τα παραπάνω οδήγησαν στο λογικό συµπέρασµα, ότι αφού θα έπρεπε να γίνει όλη αυτή η διαδικασία από την αρχή, θα ήταν καλύτερο να χρησιµοποιηθεί ταυτόχρονα, και µια νεότερη έκδοση της διανοµής Ubuntu. Επιλέχθηκε η 9.04 (Jaunty Jackalope). Για την ολοκλήρωση όλων των παραπάνω ελέγχων χρησιµοποιήθηκαν αρκετά προσωρινά περιβάλλοντα που δηµιουργήθηκαν σε εικονικές µηχανές (virtual machines) Linux. ∆εν θα αναφερθούν καθώς δεν κρίνεται σκόπιµο. Επικοινωνία: [email protected] 184 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 Τελικά η όλη διαδικασία τυποποιήθηκε προκειµένου να µπορεί να επαναλαµβάνεται όσες φορές χρειαστεί. Η τυποποίηση αυτή είχε ως αποτέλεσµα την δηµιουργία των αρχείων: Εικόνα 81. Τα αρχεία εγκατάστασης του περιβάλλοντος ανάπτυξης Ο κώδικας του αρχείου installer.sh θα εξεταστεί σε επόµενη παράγραφο. Το αρχείο rousis_buildroot.tar.gz αποτελεί απλά τη συµπιεσµένη µορφή του καταλόγου buildroot που υπάρχει στο BSP της Atmel. Ενώ τα περιεχόµενα του αρχείου rousis_toolchain.tar.gz τα οποία αποτελούν την τροποποιηµένη αλυσίδα GNU AVR32, φαίνονται στην εικόνα που ακολουθεί: Εικόνα 82. Τα περιεχόµενα του αρχείου rousis_toolchain.tar.gz Ο κατάλογος partial και το αρχείο lock είναι κενά, αλλά χρειάζεται να υπάρχουν κατά την εξαγωγή των περιεχοµένων του αρχείου rousis_toolchain.tar.gz στον κατάλογο της εικονικής µηχανής Ubuntu, /var/cache/apt/archives. 6.3 ∆ηµιουργία της εικονικής µηχανής Ubuntu 9.04 Υπάρχουν αρκετά προγράµµατα δηµιουργίας εικονικών µηχανών. Στην παρούσα εργασία επιλέχθηκε το VMware Player. Όπως ήδη αναφέραµε, η έκδοση της Ubuntu που εγκαταστάθηκε είναι η 9.04. Η επιλογή της έγινε µε γνώµονα την συµβατότητα των εκδόσεων των διαθέσιµων προγραµµάτων της αλυσίδας GNU AVR32 και των χαµηλών απαιτήσεων σε Ιστότοπος εργασίας: MyThesis.org 185 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) πόρους συστήµατος που παρουσιάζει. Για τον εικονικό δίσκο της µηχανής παραχωρήθηκαν 10GB και για την εικονική RAM, 512MB. Στην εικόνα που ακολουθεί βλέπουµε την εφαρµογή VMware µε εγκατεστηµένη την εικονική µας µηχανή: Εικόνα 83. Η εφαρµογή VMware µε εγκατεστηµένη την εικονική µηχανή Ubuntu 9.04 (jaunty) + AVR32 Ουσιαστικά τα αρχεία από τα οποία αποτελείται µια εικονική µηχανή στο VMware Player είναι δύο. Το αρχείο .vmx που περιέχει όλες τις ρυθµίσεις της µηχανής και το αρχείο .vmdk που είναι ο εικονικός σκληρός δίσκος. Στην εικόνα βλέπουµε τα δύο αυτά αρχεία που δηµιουργήθηκαν κατά την ανάπτυξη του συστήµατός µας: Εικόνα 84. Τα αρχεία από τα οποία αποτελείται µια εικονική µηχανή στο VMware Player Επίσης, στην εικόνα που ακολουθεί φαίνεται η επιφάνεια εργασίας του τελικού εικονικού συστήµατος host που δηµιουργήθηκε για τις απαιτήσεις ανάπτυξης αυτής της εργασίας: Επικοινωνία: [email protected] 186 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 Εικόνα 85. Η επιφάνεια εργασίας του τελικού εικονικού συστήµατος ανάπτυξης host Για να υπάρχει η δυνατότητα αντιγραφής αρχείων µε απλό drag n drop, από την εικονική µηχανή στα Windows και το αντίστροφο, χρειάστηκε να εγκατασταθούν τα VMware tools για Linux (google.gr/#q=vmware+tools+for+linux). 6.3.1 Login µε δικαιώµατα root Στο Linux υπάρχουν διάφοροι λογαριασµοί χρηστών (user accounts) ο καθένας µε τα δικά του δικαιώµατα πρόσβασης. Ο χρήστης που έχει τον υψηλότερο βαθµό πρόσβασης είναι ο root. Κάτι παρόµοιο µε τον Administrator των Windows. Ο χρήστης root τις περισσότερες φορές είναι κρυµµένος από τον κανονικό χρήστη, αλλά αρκετές λειτουργίες µπορούν να εκτελεστούν µε το βαθµό του, κάνοντας χρήση της εντολής sudo ή su. Η εντολή αυτή προϋποθέτει ότι γνωρίζουµε το username (όνοµα χρήστη) και το password (κωδικό πρόσβασης) του root. Για να µην είµαστε αναγκασµένοι συνεχώς να πληκτρολογούµε τα στοιχεία αυτά, µπορούµε να αποκτήσουµε δικαιώµατα root µόνιµα. Αρχικά εκτελούµε την παρακάτω εντολή: Ιστότοπος εργασίας: MyThesis.org 187 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) sudo passwd root ∆ίνουµε το password του root και εκτελούµε: sudo gdm setup Στο παράθυρο διαλόγου που θα ανοίξει, πηγαίνουµε στην καρτέλα “Security” και τσεκάρουµε το “Allow local administrator login”. Κλείνουµε το παράθυρο, κάνουµε logoff και ξανά login χρησιµοποιώντας τώρα πια, ως username το root και ως password τον αυτόν που δώσαµε µετά την εκτέλεση της πρώτης εντολής. 6.3.2 ∆ηµιουργία του κεντρικού κατάλογου εργασίας /rousis Για να υπάρχει ένα σηµείο αναφοράς στο οποίο θα αποθηκεύεται το λογισµικό και η τεκµηρίωση του περιβάλλοντος ανάπτυξης AVR32, αλλά και οτιδήποτε άλλο χρησίµευσε στην ανάπτυξη του Router NGW100 δηµιουργήθηκε στην εικονική µηχανή, ένα σύνολο υποκαταλόγων κάτω από ένα κεντρικό κατάλογο µε την ονοµασία /rousis: mkdir /rousis /rousis/buildroot /rousis/avr32studio /rousis/misc Η κάθετος στο Linux (/) συµβολίζει τον κεντρικό κατάλογο (root folder) κάτω από τον οποίο τοποθετούνται όλοι οι υπόλοιποι κατάλογοι του συστήµατος. Οι υποκατάλογοι των υποκαταλόγων buildroot, avr32studio και misc, θα αναφέρονται στη συνέχεια, όποτε και εφόσον αυτό κρίνεται απαραίτητο. 6.4 Το περιβάλλον ανάπτυξης AVR32 Στο κεφάλαιο “Εργαλεία ανάπτυξης ενσωµατωµένων συστηµάτων Linux” του θεωρητικού µέρους, έγινε η περιγραφή ορισµένων βασικών θεµάτων όπως είναι για παράδειγµα, η διαδικασία δηµιουργίας της αλυσίδας GNU, το αυτοµατοποιηµένο σύστηµα buildroot, το IDE AVR32 Studio της ATMEL και η εφαρµογή Putty. Σε αυτή την παράγραφο θα δούµε πως όλα αυτά εγκαταστάθηκαν στην εικονική µηχανή Ubuntu απαρτίζοντας το ολοκληρωµένο περιβάλλον ανάπτυξης AVR32, το οποίο δίνει την δυνατότητα δηµιουργίας εκτελέσιµου κώδικα και εφαρµογών για τον Router NGW100. 6.4.1 Εγκατάσταση Buildroot και αλυσίδας GNU AVR32 Αφού δηµιουργήθηκε η εικονική µηχανή Ubuntu και έγινε είσοδος σε αυτή µε µόνιµα δικαιώµατα root, σειρά είχε η εγκατάσταση του λογισµικού που προετοιµάστηκε κατά τη διαδικασία τροποποίησης του πακέτου υποστήριξης Atmel Linux BSP 3.0. Η πρώτη ενέργεια αφορούσε την αντιγραφή των αρχείων (µε απλό drag n drop) στον ριζικό κατάλογο της Ubuntu: Εικόνα 86. Τα αρχεία εγκατάστασης του περιβάλλοντος ανάπτυξης Επικοινωνία: [email protected] 188 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 Ο ριζικός κατάλογος στη διανοµή Ubuntu µπορεί να εντοπιστεί εύκολα µέσω του γραφικού περιβάλλοντος της Ubuntu, από την µπάρα εργασίας: “Places > Computer > Filesystem”. Έπειτα µε drag n drop αντιγράφουµε τα τρία αρχεία. Οι εντολές που έπρεπε να εκτελεστούν στη συνέχεια για να εκτελεστεί ο κώδικας του αρχείου installer.sh είναι: chmod 755 installer.sh ./installer.sh Το αρχείο installer.sh είναι ένα shell script που µπορεί να λειτουργεί σαν αυτοµατοποιηµένος εγκαταστάτης και αναπτύχθηκε ειδικά για τις ανάγκες αυτής της εργασίας. Ο πηγαίος του κώδικας είναι αρκετά απλός: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # INSTALLER FOR THE ROUSIS AVR32 DEVELOPMENT ENVIRONMENT # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #!/bin/sh directory="/rousis" if [ -d $directory ]; then echo Installation target directory is $directory else mkdir $directory echo Directory $directory just created! fi mv rousis_toolchain.tar.gz cd rousis rousis_buildroot.tar.gz /rousis echo echo echo Extracting Rousis Toolchain tar-ball to /var/cache/apt/archives sleep 3 echo tar xvzf rousis_toolchain.tar.gz ....... echo echo echo Unpacking deb files in /var/cache/apt/archives/ sleep 3 echo dpkg --unpack /var/cache/apt/archives/*deb echo echo echo Extracting Buildroot tar-ball to /buildroot sleep 3 echo tar xvzf rousis_buildroot.tar.gz ....... echo echo echo Installing AVR32 components ........ sleep 3 echo apt-get -y install avr32program avr32gdbproxy avr32trace avrfwupgrade libavr32ocd libavrtools libelfdwarfparser echo Ιστότοπος εργασίας: MyThesis.org 189 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) echo echo Configure buildroot for Router NGW100 ... sleep 3 echo cd /rousis/buildroot make ARCH=avr32 CROSS_COMPILE=avr32-linux- atngw100_defconfig echo echo echo Creating buildroot, this will take several hours!! echo sleep 3 echo # mporei na diarkesei mexri kai 3 ores analoga me to systima mas make echo echo echo Appending /rousis/buildroot/build_avr32/staging_dir/usr/bin to PATH sleep 3 echo echo "export PATH=$PATH:/buildroot-avr32-v2.3.0/build_avr32/staging_dir/usr/bin" >> /root/.bashrc export PATH=$PATH:/rousis/buildroot/build_avr32/staging_dir/usr/bin # Uncomment to backup original installation files #mkdir /rousis/misc/original-files #mv /rousis/rousis_toolchain.tar.gz /rousis/misc/original-files #mv /rousis/rousis_buildroot.tar.gz /rousis/misc/original-files #mv /rousis/installer.sh /rousis/misc/original-files echo echo Done. Please logout and login again. echo Με απλή ανάγνωση του κώδικα είναι πολύ εύκολο να καταλάβει κανείς την χρησιµότητα και το σκοπό του κάθε βήµατος που εκτελείται. Παρ’ όλα αυτά, η διαδικασία µπορεί να διαρκέσει µέχρι και τρεις ώρες (ανάλογα µε το σύστηµά µας). Αυτό συµβαίνει επειδή στην ουσία γίνονται πολύ περισσότερα πράγµατα από µια απλή εγκατάσταση. Οι βασικότερες ενέργειες που εκτελούνται είναι οι εξής: Εξαγωγή των πακέτων .dep στον κατάλογο archives. Εγκατάσταση των πακέτων .dep από τον κατάλογο archives. Παραµετροποίηση του Buildroot για το σύστηµά µας (atngw100_defconfig) και δηµιουργία cross toolchain, root filesystem, U-BOOT και target linux image. Ενηµέρωση των µεταβλητών περιβάλλοντος για τη θέση του cross compiler Όταν ολοκληρωθούν όλες οι διαδικασίες, στον κατάλογο rousis/buildroot/binaries θα έχουν δηµιουργηθεί όλα τα αρχεία που είναι απαραίτητα για να λειτουργήσει ο Router NGW100: Επικοινωνία: [email protected] 190 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 Εικόνα 87. Τα περιεχόµενα του καταλόγου binaries του συστήµατος Buidroot Τα αρχεία rootfs.avr32.jffs2-root και rootfs.avr32.jffs2-root, περιλαµβάνουν τον Linux Kernel και το περιβάλλον χρήστη (/usr) αντίστοιχα. Το αρχείο u-boot.bin περιλαµβάνει το λογισµικό εκκίνησης U-Boot. Το αρχείο rootfs.avr32.tar.bz2 περιλαµβάνει ένα πλήρες σύστηµα αρχείων αλλά δηµιουργείται για συστήµατα αρχείων που δεν είναι πλέον δηµοφιλή και δεν υποστηρίζονται επαρκώς όπως είναι για παράδειγµα το yaffs2. Για να δηµιουργηθεί το αρχείο rootfs.avr32.tar.bz2 θα πρέπει µέσα από το περιβάλλον του συστήµατος Buildroot να ενεργοποιήσουµε το “tar the root filesystem”. ∆ύο άλλοι λόγοι για να χρησιµοποιήσουµε το αρχείο αυτό είναι όταν θέλουµε να φορτώσουµε το root σύστηµα αρχείων µέσω δικτύου ή να εξάγουµε τα περιεχόµενά του σε ένα αποσπώµενο µέσο αποθήκευσης όπως είναι για παράδειγµα µια κάρτα SD/MMC. Αυτό ακριβώς έγινε και στην παρούσα εργασία κατά τη διαδικασία εγγραφής του συστήµατος αρχείων /usr στη σειριακή µνήµη Flash του Router NGW100. Ας δούµε όµως ποιοι είναι οι κατάλογοι που περιλαµβάνονται: Ιστότοπος εργασίας: MyThesis.org 191 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 88. Τα περιεχόµενα του συµπιεσµένου αρχείου rootfs.avr32.tar.bz2 Το αρχείο linuxrc θα χρησιµοποιηθεί κατά την εκκίνηση. Αφού φορτωθεί ένα τυπικό σύστηµα αρχείων ο kernel θα εκτελέσει το linuxrc σαν την πρώτη του διεργασία. Μόλις ολοκληρωθεί και επιστρέψει τιµή επιτυχούς εκτέλεσης ο kernel θα θεωρήσει ότι έχει φορτωθεί το ριζικό σύστηµα αρχείων και θα συνεχίσει µε την /sbin/init για να ξεκινήσει η διαδικασία εκκίνησης του περιβάλλοντος χρήστη (user space). Μετά την ολοκλήρωση των λειτουργιών του αρχείου installer.sh έγιναν κάποιες αλλαγές και κάποιες προσθήκες εφαρµογών. Οι εντολές παραµετροποίησης για το buildroot που χρησιµοποιήθηκαν και τις οποίες έχουµε δει και σε προηγούµενο κεφάλαιο είναι: make menuconfig Για τον Kernel: make linux26-menuconfig Για την βιβλιοθήκη uClibc: make uclibc-menuconfig Και για το BusyBox: make busybox-menuconfig Επικοινωνία: [email protected] 192 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 Οι εντολές αυτές θα χρειάζονται κάθε φορά που θα τροποποιούµε το λογισµικό συστήµατος και εφαρµογών του Router NGW100. Όταν όλες οι αλλαγές είναι έτοιµες εκτελούµε την εντολή make για να ξεκινήσει η µεταγλώττισή. 6.4.2 Το εξειδικευµένο λογισµικό του Router NGW100 Προηγουµένως αναφέρθηκε ότι τα ολοκληρωµένα του Router NGW100, πριν χρησιµοποιηθούν θα πρέπει πρώτα να αρχικοποιηθούν. Για να επιτευχθεί κάτι τέτοιο απαιτείται εξειδικευµένος κώδικας που θα είναι σε θέση να συνδέσει το λογισµικό εκκίνησης αλλά και τον Kernel µε το κάθε ολοκληρωµένο. Ο κατάλογος που αφορά τον εξειδικευµένο κώδικα του Router NGW100 είναι ο ακόλουθος: /rousis/buildroot/project_build_avr32/atngw100/linux-2.6.27.6/arch/avr32/boards/atngw100 Σε αυτό το σηµείο θα πρέπει να πούµε ότι ο πηγαίος κώδικας του Linux Kernel βρίσκεται στον παρακάτω κατάλογο του συστήµατος host: /rousis/buildroot/project_build_avr32/atngw100/linux-2.6.27.6/ Έτσι κάθε φορά που θα αναφερόµαστε σε ένα αρχείο που υπάρχει στον πηγαίο κώδικα του Linux Kernel που χρησιµοποιήθηκε, θα εννοείται ως πρόθεµα η πιο πάνω διαδροµή. Η διαδικασία αρχικοποίησης στον Router NGW100 πραγµατοποιείται από τον Kernel ο οποίος µε τη σειρά του παραµετροποιείται από το αρχείο setup.c που βρίσκεται στον κατάλογο arch/avr32/boards/atngw100. Εκτός από τα ηλεκτρονικά στοιχεία που βρίσκονται συγκολληµένα στο PCB του Router NGW100, υπάρχουν και άλλα που βρίσκονται ενσωµατωµένα στον µικροελεγκτή AP7000 και ονοµάζονται περιφερειακά (peripherals). Τα περιφερειακά του AP7000 πρέπει να αρχικοποιηθούν και αυτά από εξειδικευµένο κώδικα. Η Atmel για το σκοπό αυτό παρέχει το αρχείο arch/avr32/mach-at32ap/at32ap7000.c. Σε αυτό το αρχείο καθορίζονται τα ενσωµατωµένα περιφερειακά του µικροελεγκτή AP7000 τα οποία υποστηρίζονται από το Linux. Τέλος θα πρέπει να αναφέρουµε και έναν ακόµη κατάλογο του αυτοµατοποιηµένου συστήµατος Buildroot που περιλαµβάνει εξειδικευµένο κώδικα για το λογισµικό εκκίνησης U-Boot: /rousis/buildroot/project_build_avr32/atngw100/u-boot-1.3.4/board/atmel/atngw100 Στον κατάλογο αυτό υπάρχει το αρχείο atngw100.c αλλά και άλλος εξειδικευµένος κώδικας όπως οδηγοί συσκευών και βιβλιοθήκες. Ουσιαστικά το λογισµικό εκκίνησης U-Boot είναι ένας mini kernel. 6.4.3 Εγκατάσταση του AVR32 Studio Για την εγκατάσταση του AVR32Studio προαπαιτείται ύπαρξη του περιβάλλοντος ανάπτυξης της JAVA (JDK), µιας και πρόκειται για IDE που είναι βασισµένο στο eclipse. Στην εικόνα βλέπουµε το παράθυρο διαλόγου που προέκυψε µετά την εγκατάσταση: Ιστότοπος εργασίας: MyThesis.org 193 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 89. Παράθυρο διαλόγου µετά την εγκατάσταση της µηχανής JAVA Στον κατάλογο εργασίας, /rousis της εικονικής µηχανής, αντιγράφηκε ολόκληρος ο κατάλογος avr32studio που υπάρχει στο BSP. Για να είναι σε θέση να λειτουργεί το AVR32 Studio, αρχικά έπρεπε το εκτελέσιµο που το εκκινεί να έχει τα απαραίτητα δικαιώµατα: cd /rousis/avr32studio chmod 755 avr32studio Επίσης έπρεπε να συνδεθεί µε κάποιο τρόπο µε τον cross compiler avr32-linux-gcc. Αυτό είναι απαραίτητο καθώς το AVR32 Studio για Linux δεν περιλαµβάνει κάποιον ενσωµατωµένο compiler. Για να επιτευχθεί κάτι τέτοιο προστέθηκε αντίστοιχη εγγραφή στην ειδική µεταβλητή περιβάλλοντος (environment variable) PATH: export PATH=$PATH:/rousis/buildroot/build_avr32/staging_dir/bin Η εκτέλεση της εφαρµογής AVR32 Studio γίνεται απλά µε την παρακάτω εντολή. Η παράµετρος & είναι προαιρετική: ./avr32studio & Μετά την εκτέλεση της εντολής εµφανίζεται η οθόνη φόρτωσης: Εικόνα 90. Η αρχική οθόνη φόρτωσης του AVR32 Studio Επικοινωνία: [email protected] 194 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 Και στη συνέχεια εµφανίζεται ένα παράθυρο διαλόγου για να επιλέξουµε τον κατάλογο εργασίας του AVR32 Studio: Εικόνα 91. Παράθυρο διαλόγου για την επιλογή καταλόγου εργασίας του AVR32 Studio Τέλος εµφανίζεται το κεντρικό περιβάλλον εργασίας το οποίο είναι έτοιµο για την ανάπτυξη εφαρµογών που θα τρέχουν στον Router NGW100: Εικόνα 92. Tο κεντρικό περιβάλλον εργασίας του AVR32 Studio Ιστότοπος εργασίας: MyThesis.org 195 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Η εγκατάσταση του AVR32 Studio θα µπορούσε να ενσωµατωθεί σε εκείνη του Buildroot και της αλυσίδας GNU AVR32, προσθέτοντας απλά ένα ακόµη συµπιεσµένο αρχείο. Ουσιαστικά το αρχείο αυτό θα περιλάµβανε τα περιεχόµενα του καταλόγου avr32studio. Έπειτα θα έπρεπε να προστεθούν και οι αντίστοιχες γραµµές ενεργειών στον κώδικα του εγκαταστάτη installer.sh ώστε να γίνεται έλεγχος για την JAVA και να δηµιουργείται κατάλληλη µεταβλητή περιβάλλοντος ώστε να συνδέεται το AVR32 Studio µε τον cross compiler του συστήµατος Buildroot. 6.5 Βοηθητικό λογισµικό Στο σύστηµα host ήταν απαραίτητο να υπάρχουν διαθέσιµα ορισµένα βοηθητικά προγράµµατα προκειµένου να είµαστε σε θέση να φέρουµε εις πέρας βασικές διαδικασίες κατά την ανάπτυξη του συστήµατός µας. Τα κυριότερα εξ’ αυτών είναι: FileZilla FTP client, για µεταφορά αρχείων από και προς τον Router NGW100 Putty terminal, για διαχείριση και debugging του Router NGW100 Standard Linux terminal, για την εκτέλεση εντολών στον φλοιό της Ubuntu Τα προγράµµατα FileZilla και Putty, δε χρειάστηκε να εγκατασταθούν µόνο στην εικονική µηχανή Ubuntu αλλά και στα Windows. Αυτό έγινε γιατί το VMware ορισµένες φορές δηµιουργεί εικονικές θύρες και συσκευές, που υπό κάποιες συνθήκες δε λειτουργούν σωστά. Η διαδικασία εγκατάστασης στα Windows έγινε εκτελώντας τα αντίστοιχα εκτελέσιµα αρχεία (setup.exe), ενώ στην εικονική µηχανή της διανοµής Ubuntu µέσω της παρακάτω εντολής: apt-get install <onoma-programmatos> Το AVR32 Studio του οποίου η εγκατάσταση αναλύθηκε στην προηγούµενη παράγραφο, θεωρείται και αυτό έως ένα βαθµό, βοηθητικό πρόγραµµα. Οι εφαρµογές του Router NGW100 είναι δυνατό να αναπτύσσονται και σε έναν απλό κειµενογράφο, ενώ η µεταγλώττιση µπορεί να γίνει µέσω γραµµής εντολών. Παρ’ όλα αυτά ένα IDE όπως το AVR32 Studio θεωρείται πλέον βασικό λογισµικό κατά την ανάπτυξη κώδικα. Το AVR32 Studio δεν χρειάστηκε να εγκατασταθεί στα Windows µιας και σε αυτή την περίπτωση θα ήταν δυνατό να εξάγει µόνο ανεξάρτητα (standalone) προγράµµατα για τον µικροελεγκτή AP7000 τα οποία θα µπορούσαν να εκτελούνται µόνο αυτόνοµα και όχι σαν εφαρµογές Linux. 6.6 ∆ιαµέριση µνήµης Πριν περιγράψουµε τη διαδικασία µεταφοράς λογισµικού στον Router NGW100 είναι χρήσιµο να αναφερθούν σύντοµα ορισµένες λεπτοµέρειες που αφορούν την διαµέριση (partitioning) της µνήµης. Η µνήµη στον Router NGW100 αποτελείται από τρεις κατατµήσεις (partitions). Αυτό επιτυγχάνεται από το αρχείο arch/avr32/boards/atngw100/flash.c µέσω της δοµής δεδοµένων flash_parts. H δοµή αυτή για τον Router NGW100 έχει ως ακολούθως: static struct mtd_partition flash_parts[] = { { .name = "u-boot", .offset = 0x00000000, .size = 0x00020000, .mask_flags = MTD_WRITEABLE, Επικοινωνία: [email protected] /* mtd0 */ /* 128 KiB */ 196 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 }, { .name .offset .size = "root", = 0x00020000, = 0x007d0000, /* mtd1 */ .name .offset .size .mask_flags = = = = /* mtd2 */ }, { "env", 0x007f0000, 0x00010000, MTD_WRITEABLE, }, }; Στη δοµή flash_parts του αρχείου flash.c καθορίζονται οι κατατµήσεις /dev/mtd0, /dev/mtd1 και /dev/mtd2 οι οποίες αφορούν την παράλληλη µνήµη (AT49BV642D-70TU) και χρησιµοποιούνται για την αποθήκευση των εξής στοιχείων λογισµικού: mtd0 mtd1 mtd2 U-Boot Linux Kernel (root filesystem ή /root) ∆ιαµοιραζόµενες πληροφορίες ανάµεσα σε U-Boot και Kernel (IP, MAC κλπ) Το διαµέρισµα mtd2 ονοµάζεται και U-Boot environment (env) καθώς οι διαµοιραζόµενες µεταβλητές – πληροφορίες που υπάρχουν αποθηκευµένες σε αυτό, διαχειρίζονται από το λογισµικό εκκίνησης U-Boot. Στη δοµή flash_parts δεν καθορίζεται mtd3 καθώς το περιβάλλον χρήστη (userspace ή /usr) βρίσκεται στην σειριακή µνήµη (AT45DB642D). 6.7 Η συσκευή JTAGICE mkII Η συσκευή JTAGICE mkII προτιµήθηκε λόγω του ότι είναι πλήρως συµβατή µε την αλυσίδα εργαλείων ανάπτυξης GNU καθώς και µε τον µικροελεγκτή του Router NGW100, AP7000. Οι τύποι προγραµµατισµού του µικροελεγκτή και επικοινωνίας µε αυτόν, που υποστηρίζονται από τη συσκευή JTAGICE mkII, είναι οι ακόλουθοι: SPI (Serial Peripheral Interface) JTAG (Join Test Action Group) PDI (Passive Direct Interface) aWire Debugging µέσω των ακόλουθων interfaces: DebugWIRE (one wire) JTAG PDI aWire Τα κυριότερα χαρακτηριστικά που υποστηρίζει είναι: Τουλάχιστον 3 hardware breakpoints ή ένα data breakpoint (ανάλογα µε την µονάδα OCD του AVR µικροελεγκτή). Συµβολικό debugging σύνθετων τύπων δεδοµένων µε πληροφορίες για το εύρος (scope) τους. Μέχρι και 128 software (program) breakpoints. Πλήρης υποστήριξη για assembly αλλά και γλώσσες υψηλού επιπέδου (πχ C). Ιστότοπος εργασίας: MyThesis.org 197 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Emulation για όλες τις αναλογικές και ψηφιακές λειτουργίες του µικροελεγκτή. Περιλαµβάνει 512KB SRAM για γρήγορη βηµατική εκτέλεση (statement-level stepping). Μετατροπή επιπέδου τάσης από τα 1.8 έως τα 5.5 volt. Γρήγορο upload που φτάνει τα 256Kb κώδικα σε 30 sec. Συµβατότητα µε Full – speed USB 2.0 (12ΜΒ/s) και RS-232. Firmware upgradable (µέσω AVR32 Studio) για την υποστήριξη νέων µικροελεγκτών. Λειτουργεί µε εξωτερική (9 – 12 volt) ή µε USB τροφοδοσία. Λειτουργία NanoTrace (ανάλογα µε την µονάδα OCD του µικροελεγκτή). Περιλαµβάνει adaptors για να συνδέεται στα διάφορα διαθέσιµα hardware interfaces. Ελέγχεται από το IDE του AVR32 Studio. Η συσκευή JTAGICE mkII φαίνεται στην εικόνα: Εικόνα 93. Η συσκευή programmer - emulator JTAGICE mkII της Atmel 6.8 Σύνδεση JTAGICE mkII µε το σύστηµα host Μέχρι αυτή τη στιγµή έχει δηµιουργηθεί το σύστηµα host και το περιβάλλον ανάπτυξης AVR32 για τη µεταγλώττιση πηγαίου κώδικα C σε εκτελέσιµα προγράµµατα που θα είναι συµβατά µε τον µικροελεγκτή AP7000 και την αρχιτεκτονική AVR32. Επίσης, µετά την εκτέλεση του αρχείου installer.sh δηµιουργήθηκαν τα εκτελέσιµα του λογισµικού εκκίνησης U-Boot, του Linux Kernel και του συστήµατος αρχείων χρήστη. Το επόµενο που έπρεπε να γίνει, ήταν να µεταφερθεί στην παράλληλη µνήµη το λογισµικό εκκίνησης. Σε αυτό το εγχείρηµα χρησιµοποιήθηκε το πρόγραµµα avr32program το οποίο είναι ένα από τα προγράµµατα που εγκαταστάθηκαν νωρίτερα στο το περιβάλλον ανάπτυξης AVR32. Ο λόγος που επιλέχθηκε ο παράλληλος τύπος µνήµης είναι οι υψηλές ταχύτητες εγγραφής – ανάγνωσης που αυτή προσφέρει. Η εκκίνηση του Router NGW100 έπρεπε να είναι όσο το δυνατόν συντοµότερη προκειµένου να είναι άµεσα διαθέσιµος µετά από κάθε επανεκκίνηση ή ενεργοποίησή του. Το avr32program είναι ένα πρόγραµµα γραµµής εντολών το οποίο όταν χρησιµοποιηθεί σε συνδυασµό µε τον programmer JTAGICE mkII της Atmel, µας δίνει τη δυνατότητα να µεταφέρουµε το πρόγραµµά µας στην µνήµη του συστήµατός µας. Μέσω του προγράµµατος avr32program και του programmer JTAGICE mkII ανακτήθηκε ο πλήρης έλεγχος του µικροελεγκτή AP7000 καθώς υπήρχε η δυνατότητα έναρξης και τερµα- Επικοινωνία: [email protected] 198 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 τισµού εκτέλεσης, δυνατότητα reset αλλά και δυνατότητα ανάγνωσης των τιµών όλων των καταχωρητών του. Η συσκευή JTAGICE mkII, βασίζεται στην εξειδικευµένη λειτουργικότητα JTAG που βρίσκεται ενσωµατωµένη στην αρχιτεκτονική του µικροελεγκτή AP7000. Συνδέοντάς την µε τον AP7000 παίρνουµε τον πλήρη έλεγχό του στα χέρια µας. Αυτό σηµαίνει ότι είµαστε σε θέση να ελέγχουµε τα παρακάτω στοιχεία: Μνήµη Flash, EEPROM και SRAM Αρχείο καταχωρητών (register file) Μετρητή προγράµµατος (Program Counter) Fuse και lock bits Μονάδες εισόδου – εξόδου (I/O modules) Για τη φυσική σύνδεση του JTAGICE mkII µε τον Router NGW100, συνδέουµε το άκρο της καλωδιοταινίας στα ελεύθερα pins του JTAG hardware interface που υπάρχει στην πλακέτα. Η καλωδιοταινία σύνδεσης ονοµάζεται JTAG probe και φαίνεται στην παρακάτω εικόνα: JTAGICE mkII probe Εικόνα 94. Η καλωδιοταινία σύνδεσης JTAG probe Στο silkscreen (µεταξοτυπία) της πλακέτας µπορούµε να διακρίνουµε και το κείµενο jtag που µας ενηµερώνει ότι τα δέκα διαθέσιµα pins που υπάρχουν, προσφέρονται για σύνδεση JTAG: JTAG hardware interface Εικόνα 95. Η διεπαφή υλικού JTAG του Router NGW100 Στην εικόνα φαίνεται η σύνδεση της συσκευής JTAGICE mkII µε τον Router NGW100, µέσω του JTAG probe: Ιστότοπος εργασίας: MyThesis.org 199 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) JTAGICE mkII probe AP7000 Εικόνα 96. Σύνδεση της συσκευής JTAGICE mkII µε τον Router NGW100 Οι τρόποι επικοινωνίας µε υπολογιστή που υποστηρίζονται από την συσκευή JTAGICE mkII είναι: USB και RS232. Στην πρώτη περίπτωση δεν χρειάζεται να χρησιµοποιήσουµε έξτρα τροφοδοσία καθώς παρέχεται από τον δίαυλο, στην δεύτερη περίπτωση η εξωτερική πρόσθετη τροφοδοσία, είναι απαραίτητη. Στην εικόνα βλέπουµε τις δύο θύρες, USB και RS232 που υπάρχουν στο πίσω µέρος του JTAGICE mkII: RS232 POWER USB ON/OFF Εικόνα 97. Θύρες, USB, RS232 και τροφοδοσίας στο πίσω µέρος της συσκευής JTAGICE mkII ∆ιαγραµµατικά η σύνδεση του host συστήµατός µας µε τον Router NGW100, µέσω του JTAGICE mkII, θα µπορούσε να παρουσιαστεί διαγραµµατικά ως εξής: Εικόνα 98. ∆ιάγραµµα σύνδεσης του συστήµατός host µε τον Router NGW100 Επικοινωνία: [email protected] 200 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 Η σύνδεση του JTAGICE mkII µε το σύστηµα host έγινε µέσω USB. Για την αναγνώριση της συσκευής από το λειτουργικό σύστηµα δεν απαιτήθηκε η εγκατάσταση drivers µιας και αυτοί υπήρχαν διαθέσιµοι στο AVR32 Studio αλλά και στο avr32program. Για να γίνει διάγνωση της σωστής λειτουργίας της συσκευής JTAGICE mkII αλλά και της επικοινωνίας της µε το σύστηµα host, εκτελέστηκαν ορισµένες απλές εντολές µέσω του προγράµµατος avr32program. Η πρώτη εντολή που εκτελέστηκε είναι η cpuinfo –F η οποία επιστρέφει τα κυριότερα χαρακτηριστικά του µικροελεγκτή AP7000 και της συσκευής JTAGICE mkII: Εικόνα 99. Εκτέλεση της εντολής cpuinfo –F µέσω του προγράµµατος avr32program και του JTAGICE mkII Έπειτα έγινε εκτέλεση της readregs η οποία επέστρεψε µια λίστα µε καταχωρητές του AP7000 και τις αντίστοιχες διευθύνσεις της εσωτερικής του µνήµης: Ιστότοπος εργασίας: MyThesis.org 201 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 100. Εκτέλεση της εντολής readregs µέσω του προγράµµατος avr32program και του JTAGICE mkII 6.9 Εγγραφή U-Boot στην παράλληλη µνήµη Αφού επιβεβαιώθηκε ότι όλα λειτουργούν σωστά στη συνέχεια έπρεπε να αντιγραφεί στον Router NGW100 το πρώτο τµήµα λογισµικού που είναι το λογισµικό εκκίνησης U-Boot και πιο συγκεκριµένα το εκτελέσιµο αρχείο uboot.bin. Αρχικά έγινε εκκαθάριση της παράλληλης µνήµης Flash: avr32program erase -fcfi@0 Η εκκαθάριση έγινε υποχρεωτικά διότι σε διαφορετική περίπτωση η διαδικασία εγγραφής της, αποτύγχανε. Στην εικόνα βλέπουµε τα µηνύµατα που εµφανίστηκαν στην κονσόλα: Εικόνα 101. Eκκαθάριση της παράλληλης µνήµης Flash µέσω του JTAGICE mkII Ακολούθησε η µεταφορά και εγγραφή του αρχείου uboot.bin. χωρίς να καθοριστεί κάποια συγκεκριµένη διεύθυνση µνήµης, για να ξεκινήσει η εγγραφή από την πρώτη θέση που είναι η 0x00000000: cd /rousis/buildroot/binaries/atngw100 avr32program program -F bin -vfcfi@0 uboot.bin Στην εικόνα βλέπουµε τα µηνύµατα που εµφανίστηκαν στην κονσόλα: Εικόνα 102. Εγγραφή του λογισµικού εκκίνησης στην παράλληλη µνήµη µέσω του JTAGICE mkII Επικοινωνία: [email protected] 202 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 Σε αυτό το σηµείο η συσκευή JTAGICE mkII αποσυνδέθηκε και ο Router NGW100 απενεργοποιήθηκε για να συνδεθούµε µέσω κονσόλας στο περιβάλλον γραµµής εντολών του UBoot. Αυτό έγινε για να διαπιστωθεί ότι όλα έχουν γίνει σωστά µέχρι εκείνη τη στιγµή και ότι υπάρχει ένας λειτουργικός bootloader στο σύστηµά µας. Ουσιαστικά αυτή είναι και η πρώτη φορά που µπορούσε να γίνει έλεγχος ως προς τα αποτελέσµατα που έδωσε το αυτοµατοποιηµένο σύστηµα Buildroot στον κατάλογο /rousis/buildroot/binaries/atngw100. Επίσης σε αυτό το σηµείο ελέγχθηκε ταυτόχρονα και η ορθότητα της λειτουργίας των ηλεκτρονικών στοιχείων του Router NGW100. Η σύνδεση του Router NGW100 µε το σύστηµα host έγινε σειριακά (πρωτόκολλο RS232, βύσµα DB9) µέσω του καλωδίου που βλέπουµε στην εικόνα: Εικόνα 103. Καλώδιο σειριακής επικοινωνίας RS232 Από την πλευρά του συστήµατος host χρησιµοποιήθηκε η εφαρµογή Putty. Οι ρυθµίσεις της σειριακής επικοινωνίας φαίνονται στο παράθυρο διαλόγου παραµετροποίησης στην εικόνα που ακολουθεί: Εικόνα 104. Παράµετροι σειριακής επικοινωνίας της εφαρµογής PuTTY Ιστότοπος εργασίας: MyThesis.org 203 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Η συσκευή /dev/ttyS0 στο Linux είναι αντίστοιχη της θύρας COM1 στα Windows. Επιλέγοντας την κατηγορία Session Open και ενεργοποιώντας την τροφοδοσία του Router NGW100, εµφανίστηκαν τα πρώτα µηνύµατα του λογισµικού εκκίνησης U-Boot. Επειδή όµως το δυαδικό αρχείο του Kernel (uImage) δεν υπήρχε ακόµη στην µνήµη, το U-Boot εµφάνιζε ορισµένα αναµενόµενα (και επιθυµητά) µηνύµατα σφάλµατος. Τελικά, το command prompt του U-Boot εµφανίστηκε κανονικά: Εικόνα 105. Το command prompt του U-Boot Εκτελώντας την εντολή help στο U-Boot παρουσιάζεται µια λίστα µε τις διαθέσιµες εντολές και µια µικρή περιγραφή που αφορά την λειτουργία της κάθε µιας από αυτές: Επικοινωνία: [email protected] 204 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 Εικόνα 106. Εκτλελεση της εντολής help στο U-Boot Εκτελώντας την εντολή flinfo εµφανίζεται ο χάρτης της παράλληλης µνήµης όπου µε R0 συµβολίζονται οι χαρτογραφηµένες διευθύνσεις του αντίστοιχου καταχωρητή ο οποίος είναι ουσιαστικά και αυτός που χρησιµοποιείται πρώτος κατά την εκκίνηση του Router NGW100: Ιστότοπος εργασίας: MyThesis.org 205 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 107. Εκτλελεση της εντολής flinfo στο U-Boot Η µνήµη διαβάζεται από αριστερά προς τα δεξιά όπως ακριβώς και σε ένα κείµενο. Ο καταχωρητής R0 έχει µήκος 40 bytes, ενώ η πρώτη και τελευταία του διεύθυνση υποδηλώνει την αρχή και το τέλος αντίστοιχα, της παράλληλης µνήµης. Το µέγεθος του κάθε µπλοκ (block size ή bs) είναι 1024 bits (ή 128 bytes). Αυτή είναι η πρώτη φορά που ο Router NGW100 απέκτησε επαφή µε το περιβάλλον και παρείχε κάποιου είδους αλληλεπίδραση. Αυτό που αποµένει είναι η φόρτωση του συστήµατος αρχείων Linux root (Kernel) και του userspace. 6.10 Εγγραφή Linux Kernel στην παράλληλη µνήµη Με τον ίδιο τρόπο περίπου πραγµατοποιήθηκε και η εγγραφή του Linux Kernel: avr32program program -F bin -vfcfi@0 -O 0x20000 rootfs.avr32.jffs2-root Εφόσον οι πρώτες θέσεις µνήµης 0x00000000 έως 0x00010000 δεσµεύονται από τον καταχωρητή R0 και το λογισµικό εκκίνησης U-Boot, ο Kernel έπρεπε να ξεκινά από την αµέσως επόµενη θέση, 0x00020000 (ή αλλιώς 0x20000). Στην εικόνα βλέπουµε τα µηνύµατα που εµφανίστηκαν στην κονσόλα µετά την εκτέλεση της εντολής: Εικόνα 108. Εγγραφή Linux Kernel στην παράλληλη µνήµη µέσω του JTAGICE mkII Επικοινωνία: [email protected] 206 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 6.11 Εγγραφή συστήµατος αρχείων /usr Στο στάδιο αυτό µεταφέρθηκε και γράφτηκε στην µνήµη Flash του Router NGW100 το αρχείο rootfs.avr32.jffs2-usr (περιβάλλον χρήστη /usr). Η µεταφορά αυτή έγινε µε τη βοήθεια του λογισµικού εκκίνησης U-Boot: Το U-Boot υποστηρίζει πολλούς τρόπους µέσω των οποίων θα µπορούσε να ολοκληρωθεί η διαδικασία µεταφοράς: Με φορητή µνήµη SD-card Μέσω δικτύου Σειριακά (πρωτόκολλο Kermit) Επιλέχθηκε ο πρώτος τρόπος επειδή κρίθηκε πιο άµεσος και σύντοµος. Για το σκοπό αυτό χρησιµοποιήθηκε µια MMC/SD-card χωρητικότητας 2GB της εταιρείας SanDisk, η οποία φαίνεται και στην εικόνα: Εικόνα 109. SanDisk MMC/SD-card Αρχικά η SD-card συνδέθηκε µέσω ενός ειδικού προσαρµογέα (MEM-Flex, TMS-CRMF10R της εταιρίας takeMS) σε µια θύρα USB του συστήµατος host προκειµένου να διαµορφωθεί µε το σύστηµα αρχείων ext2. Θα µπορούσε να χρησιµοποιηθεί η θύρα USB σε συνδυασµό µε τον SD-card reader που υπάρχουν ενσωµατωµένα στον Router NGW100 αλλά δεν επιλέχθηκε αυτός ο τρόπος για λόγω ορισµένων δυσλειτουργιών των αντίστοιχων USB drivers. Οι εντολές που εκτελέστηκαν για τη διαµόρφωση της SD-card είναι οι παρακάτω: umount /dev/sdb1 /sbin/mke2fs /dev/sdb1 /sbin/e2fsck /dev/sdb1 mkdir /sd mount /dev/sdb1 /sd cd /sd Στη συνέχεια έπρεπε να γίνουν οι απαραίτητες ενέργειες για να µπορεί ο Router NGW100 να εκκινήσει από την SD-card. Αρχικά µεταφέρθηκε σε αυτή το δυαδικό αρχείο /rousis/buildroot/binaries/atngw100/rootfs.avr32.jffs2-usr, και έγινε εξαγωγή του αρχείου rootfs.avr32.tar.bz2 µέσω της εντολής: tar xjvf /rousis/buildroot/binaries/atngw100/rootfs.avr32.tar.bz2 Μετά την ολοκλήρωση της εξαγωγής, έπρεπε να γίνει µια µικρή τροποποίηση του αρχείου /sd/etc/fstab προκειµένου να µην γίνει εκκίνηση από λάθος σηµείο της µνήµης. Αυτό Ιστότοπος εργασίας: MyThesis.org 207 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) απαιτούσε την εισαγωγή µιας συγκεκριµένης γραµµής σε σχόλια (βάζοντας ως πρόθεµα το χαρακτήρα #), έτσι ώστε να µην είναι εκτελέσιµη κατά την εκκίνηση: #mtd3 /usr jffs2 defaults 0 0 Σε αυτό το σηµείο η SD-card αφαιρέθηκε από το σύστηµα host για να συνδεθεί στον Router NGW100. Για την ασφαλή κατάργηση της συσκευής εκτελέστηκαν οι πιο κάτω εντολές: cd / umount /sd Το επόµενο βήµα αφορούσε την εκκίνηση του Router NGW100 και την εισαγωγή µας σε περιβάλλον U-Boot. Το λογισµικό εκκίνησης πρέπει να γνωρίζει πως θα εκκινήσει το σύστηµα. ∆ηλαδή σε ποια διεύθυνση µνήµης βρίσκεται ο Kernel. Στην περίπτωσή µας η διεύθυνση αυτή βρισκόταν στην SD-card: Uboot> Uboot> Uboot> Uboot> set bootargs 'console=ttyS0 root=/dev/mmcblk0p1 rootwait' set bootcmd 'mmcinit; ext2load mmc 0:1 0x10400000 /boot/uImage; bootm' saveenv boot Η εκτέλεση των παραπάνω εντολών είχε ως αποτέλεσµα να αλλάξει το φυσικό µέσο εκκίνησης και ταυτόχρονα να γίνει εκκίνηση του Linux. Η σηµασία της κάθε εντολής έχει ως εξής: mmcinit Αρχικοποίηση της SD-card ext2load mmc 0:1 0x10300000 /boot/uImage Φόρτωση του αρχείου uImage από την SD-Card στην διεύθυνση µνήµης 0x10300000 bootm Εκκίνηση από την µνήµη Οι εντολές που ακολουθούν εκτελέστηκαν για να γίνει καθαρισµός της µνήµης flash του Router NGW100 και στη συνέχεια να εγγραφεί σε αυτή το δυαδικό αρχείο του περιβάλλοντος χρήστη /usr: ~# flash_eraseall /dev/mtd3 ~# dd if=/rootfs.avr32.jffs2-usr of=/dev/mtd3 bs=1056 ~# reboot Η εντολή flash_eraseall διαγράφει το διαµέρισµα mtd3 (δηλαδή όλη τη σειριακή Flash) και η dd αντιγράφει το δυαδικό αρχείο rootfs.avr32.jffs2-usr στο διαµέρισµα αυτό. Το µέγεθος block της µνήµης είναι 1056 bits (ή 132 bytes). Στην εικόνα βλέπουµε την εκτέλεση της εντολής dd: Εικόνα 110. Εγγραφή συστήµατος αρχείων /usr από την SD-card µέσω της εντολής dd Τέλος, σε περιβάλλον U-boot έγιναν οι αντίστροφες αλλαγές προκειµένου ο Router NGW100 να εκκινεί από την µνήµη flash και όχι από την SD-card: Uboot> set bootargs 'root=/dev/mtdblock1 rootfstype=jffs2 rootwait' Uboot> set bootcmd 'fsload /boot/uImage;bootm' Επικοινωνία: [email protected] 208 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 Uboot> saveenv Uboot> boot 6.12 ∆ιαδικασία εκκίνησης Σε αυτό το σηµείο είναι χρήσιµο να περιγράψουµε τη διαδικασία εκκίνησης του Router NGW100 ώστε να γίνει περισσότερο κατανοητός ο λόγος ύπαρξης όλων όσων έχουν πραγµατοποιηθεί έως τώρα. Η διαδικασία αυτή γίνεται καλύτερα αντιληπτή και µε τη βοήθεια του σχηµατικού του Router NGW100 που παρατίθεται και σε αντίστοιχο παράρτηµα. Επίσης, ακόµη µεγαλύτερη κατανόηση επιτυγχάνεται και µέσω των µηνυµάτων που εµφανίζονται στο τερµατικό Putty αν συνδεθούµε σειριακά στον Router NGW100 κατά τη διαδικασία εκκίνησης. Για το λόγο αυτό, σχεδόν σε κάθε βήµα που θα περιγράφεται παρακάτω, θα υπάρχει και η αντίστοιχη εικόνα (screenshot). Η διαδικασία εκκίνησης θα µπορούσε να συνοψιστεί στα 5 παρακάτω βασικά κύρια στάδια: Reset του µικροελεγκτή AP7000 και εκτέλεση του bootloader U-Boot Ο U-Boot φορτώνει και εκτελεί τον Kernel Ο Kernel εκτελεί και εκκινεί την διεργασία init Η διεργασία init εκτελεί και εκκινεί το script rcS Το script rcS καλεί ένα σύνολο άλλων scripts για να αρχικοποιήσει το σύστηµα και να εκκινήσει την γραµµή εντολών στο φλοιό του kernel (shell ή sh bash) Επειδή όµως η κατανόησή της διαδικασίας εκκίνησης µπορεί να µας δώσει µια πολύ καλή εικόνα για τη δοµή και τη λειτουργία του Router NGW100, είναι χρήσιµο να την περιγράψουµε πιο αναλυτικά. Ο βασικότερος παράγοντας που εµπλέκεται στη διαδικασία εκκίνησης είναι ο µικροελεγκτής AP7000, στους ακροδέκτες του οποίου βρίσκονται συνδεδεµένα, και ελέγχονται όλα τα ηλεκτρονικά υποσυστήµατα του Router NGW100. Όταν ο AP7000 τροφοδοτείται για πρώτη φορά µε την απαιτούµενη τάση και ένταση ρεύµατος, τίθεται σε κατάσταση POR (Power-On Reset). Σε αυτό το στάδιο επιβεβαιώνεται ότι όλα τα κρίσιµα υποσυστήµατά του µεταβαίνουν στην κατάλληλη κατάσταση λειτουργίας. Αµέσως µετά ο AP7000 χρησιµοποιεί τον ακροδέκτη XIN0 ως πηγή χρονισµού. Αφού ολοκληρωθεί το reset, ο AP7000 προσκοµίζει την πρώτη εντολή από την εσωτερική του µνήµη και πιο συγκεκριµένα από τη διεύθυνση 0xA000_0000 (reset address). Η διεύθυνση αυτή αντιστοιχεί στον τοµέα P2 που βρίσκεται µόνιµα χαρτογραφηµένος στην περιοχή διευθύνσεων 0x0000_0000 έως 0x2000_0000, της φυσικής µνήµης του AP7000. Αυτό σηµαίνει ότι η εντολή που προσκοµίζεται από την εικονική διεύθυνση 0xA000_0000, ουσιαστικά προσκοµίζεται από την φυσική διεύθυνση 0x0000_0000 (R1). Σε αυτή τη διεύθυνση βρίσκεται ο ακροδέκτης EBI SRAM CS0 στον οποίο συνδέεται η εξωτερική µνήµη AT49BV642D-70TU όπου βρίσκεται αποθηκευµένο το λογισµικό εκκίνησης U-Boot (partition mtd0). Το λογισµικό U-Boot αναζητά και φορτώνει τον Linux Kernel από το αρχείο uImage που βρίσκεται και αυτό στην παράλληλη µνήµη AT49BV642D-70TU. Τα µηνύµατα κατάστασης που εµφανίζονται στο τερµατικό Putty είναι: Ιστότοπος εργασίας: MyThesis.org 209 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 111. ∆ιαδικασία εκκίνησης στάδιο 1 Ο Linux Kernel του Router NGW100 ξεκινά µε την εκτέλεση της συνάρτησης start_kernel η οποία βρίσκεται στο αρχείο init/main.c. Το βασικότερο αρχείο πηγαίου κώδικα που φροντίζει για να συµβεί αυτό, είναι το αρχείο head.S (που είναι γραµµένο σε assembly), το οποίο βρίσκεται στον κατάλογο arch/avr32/boot/u-boot και επικοινωνεί µε τον U-Boot. Η συνάρτηση start_kernel αρχικοποιεί τους task schedulers και εµποδίζει να εκτελεστεί οποιαδήποτε άλλη λειτουργία, µέσω της συνάρτησης preempt_disable, µέχρις ότου να µπορέσουν να χαρτογραφηθούν οι απαραίτητες διακοπές του µικροελεγκτή AP7000. Τα µηνύµατα κατάστασης που εµφανίζονται στο τερµατικό Putty είναι: Επικοινωνία: [email protected] 210 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 Εικόνα 112. ∆ιαδικασία εκκίνησης στάδιο 2 Στη συνέχεια ο Kernel αρχικοποιεί και καταχωρεί τις υπηρεσίες του συστήµατος, όπως για παράδειγµα τη στοίβα TCP/IP, και τους οδηγούς των συσκευών για τα περιφερειακά USARTs, Ethernet controller, MMC (SD card controller), USB κλπ. Μετά την καταχώρηση και την αρχικοποίηση, αντιστοιχεί τις αναγκαίες διακοπές. Τα µηνύµατα κατάστασης που εµφανίζονται στο τερµατικό Putty είναι: Εικόνα 113. ∆ιαδικασία εκκίνησης στάδιο 3 Έπειτα αφού ελέγξει τον αριθµό των µνηµών CFI που υπάρχουν (µέσω του αρχείου drivers/mtd/maps/physmap_of.c), πραγµατοποιεί διαµέριση της παράλληλης µνήµης και φορτώνει το partition mtd1, µε το σύστηµα αρχείων root για να γίνει εκτέλεση του /sbin/init. Τα µηνύµατα κατάστασης που εµφανίζονται στο τερµατικό Putty είναι: Ιστότοπος εργασίας: MyThesis.org 211 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 114. ∆ιαδικασία εκκίνησης στάδιο 4 Σειρά έχει η χαρτογράφηση κάποιων επιπλέον καταχωρητών και διακοπών που αφορούν την επικοινωνία USB (που όµως δε µας αφορά στην παρούσα εργασία) το RTC (Real Time Clock) και το WDT (Watch Dog Timer) του AP7000, αλλά και τον ελεγκτή MCI της Atmel (Multimedia Card Interface). Επίσης γίνεται η καταχώρηση των σηµάτων GPIO των τριών LEDs SYS, A και B. Τα µηνύµατα κατάστασης που εµφανίζονται στο τερµατικό Putty είναι: Εικόνα 115. ∆ιαδικασία εκκίνησης στάδιο 5 Φορτώνεται και καταχωρείται το cubic TCP (wikipedia.org/wiki/CUBIC_TCP) και οι υπηρεσίες του. Και τέλος, γίνονται κάποιες ρυθµίσεις της συχνότητας λειτουργίας του AP7000 καθώς και της ζώνης ώρας του λειτουργικού συστήµατος Linux. Τα µηνύµατα κατάστασης που εµφανίζονται στο τερµατικό Putty είναι: Εικόνα 116. ∆ιαδικασία εκκίνησης στάδιο 6 Σε αυτό το σηµείο η φόρτωση του Linux Kernel και του root filesystem έχει ολοκληρωθεί. Η υπόλοιπη διαδικασία εκκίνησης πραγµατοποιείται από προγράµµατα του περιβάλλοντος χρήστη (userspace) τα οποία είναι υπεύθυνα και τη φόρτωση του συστήµατος αρχείων /usr. Η διεργασία Init είναι η πρώτη λειτουργία που εκτελείται σε ένα ενσωµατωµένο σύστηµα Linux και είναι ο γονέας (parent) όλων των υπόλοιπων διεργασιών που θα εκτελεστούν µετά από αυτή. ∆ηλαδή έχει PID (Process IDentifier) την µονάδα. Η Init διαβάζει το αρχείο Επικοινωνία: [email protected] 212 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 linuxrc το οποίο εκκινεί το πρόγραµµα BusyBox. Τα µηνύµατα κατάστασης που εµφανίζονται στο τερµατικό Putty είναι: Εικόνα 117. ∆ιαδικασία εκκίνησης στάδιο 7 Αµέσως µετά γίνεται ανάγνωση του αρχείου /etc/inittab προκειµένου να αποφασιστεί µέσω της οδηγίας ::sysinit: ποιες θα είναι οι επόµενες ενέργειες που θα εκτελεστούν: # Run the rcS script after kernel is booted. ::sysinit:/etc/init.d/rcS # Run a shell on the first serial port. Comment out if you want a getty instead. ttyS0::respawn:-/bin/sh # Run a shell on the g_serial port (USB gadget device)? This shell will spawn # error message if the device is not connected. #ttygserial::respawn:-/bin/sh # Uncomment this to run a getty on the first serial port. #ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100 # Run a script on shutdown. ::shutdown:/etc/init.d/rcK Οι ενέργειες αυτές περιλαµβάνονται συνήθως σε κάποια αρχεία κώδικα εκκίνησης (boot scripts ή startup scripts) τα οποία χαρακτηρίζονται και από έναν αύξων αριθµό προκειµένου να εκτελούνται µε την σωστή σειρά. Στον Router NGW100 το πρώτο από αυτά τα αρχεία που εκτελείται (λόγω της ::sysinit:) είναι το αρχείο rcS το οποίο βρίσκεται στον κατάλογο /etc/init.d του συστήµατος αρχείων root. Ο κώδικας του αρχείου εκκίνησης rcS (run commands Start) είναι ο ακόλουθος: #!/bin/sh for s in /etc/init.d/S*; do if [ -x $s ]; then $s start fi done echo echo echo echo echo echo echo " * * * * * * * * * * * * * * * * * * * " " * * " " * Rousis Network Gateway is Ready! * " " * * " " * * * * * * * * * * * * * * * * * * * " Ο παραπάνω κώδικας εκτελεί όλα τα εκτελέσιµα αρχεία του καταλόγου /etc/init.d, που περιέχουν κώδικα εκκίνησης και ξεκινούν από τον χαρακτήρα S. Το πρώτο από αυτά είναι το S00mountvirtfs που φορτώνει ορισµένα πολύ χρήσιµα εικονικά συστήµατα αρχείων. Τα µηνύµατα κατάστασης που εµφανίζονται στο τερµατικό Putty είναι: Ιστότοπος εργασίας: MyThesis.org 213 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 118. ∆ιαδικασία εκκίνησης στάδιο 8 Στη συνέχεια εκτελούνται άλλα επτά scripts: S01hotplug Ενεργοποιεί το σύστηµα /mdev το οποίο είναι υπεύθυνο για την φόρτωση και την εµφάνιση των συνδεδεµένων συσκευών αλλά και για τη δυναµική προσθήκη ή αφαίρεση συσκευών αποσπώµενων (πχ usb sticks) και εξωτερικών partitions µνήµης. Η επιτυχής εκτέλεση αυτού του script είναι πολύ σηµαντική! S02hostname Ενεργοποιεί τον loopback adaptor (127.0.0.1 ή localhost) και εντοπίζει το αρχείο hostname και ενηµερώνει τον DNS. S05avahi-setup.sh Ενεργοποιεί την υπηρεσία αυτόµατης αναγνώρισης σύνδεσης νέων συσκευών στο τοπικό δίκτυο (παρόµοιο µε το bonjour της Apple). S08syslog S09klog Ενεργοποιεί τα αρχεία καταγραφής κατάστασης συστήµατος. Ενεργοποιεί τα αρχεία καταγραφής κατάστασης του Kernel. S10modules-init etc/modules. Φορτώνει όλα τα modules που αναφέρονται στο αρχείο S13portmap Μετατρέπει τους αριθµούς RPC των προγραµµάτων, σε θύρες TCP και UDP (wikipedia.org/wiki/Portmap). Τα µηνύµατα κατάστασης που εµφανίζονται στο τερµατικό Putty είναι: Εικόνα 119. ∆ιαδικασία εκκίνησης στάδιο 9 Σε αυτό το σηµείο εκτελείται το αρχείο S15localfs και φορτώνεται το σύστηµα αρχείων /usr του περιβάλλοντος χρήστη, στην σειριακή µνήµη flash (AT45DB642D) που αντιπροσωπεύεται από το partition mtd3. Τo µήνυµα κατάστασης που εµφανίζεται στο τερµατικό Putty είναι: Επικοινωνία: [email protected] 214 Προγραµµατισµός και εκκίνηση του Router NGW100 – Κεφάλαιο 6 Εικόνα 120. ∆ιαδικασία εκκίνησης στάδιο 10 Μετά τη φόρτωση του συστήµατος αρχείων /usr εκτελείται το αρχείο S20network προκειµένου να ενεργοποιηθεί η δυνατότητα δικτύωσης του Router NGW100. Οι κυριότερες ενέργειες που πραγµατοποιούνται σε αυτό το στάδιο είναι η ενεργοποίηση των διεπαφών Ethernet, eth0 και eth1 (macb0 και macb1) για τα WAN και LAN αντίστοιχα (µε τη βοήθεια και του MACB driver). Σε αυτό το στάδιο αντιστοιχείται και µια στατική IP στη διεπαφή δικτύου WAN. Αυτό γίνεται µέσω του DHCP client udhcpc. Η εφαρµογή udhcp έπρεπε να εγκατασταθεί µιας και ο Router NGW100 δεν περιλαµβάνει κάποιο modem ώστε να εκτελεί το πρωτόκολλο (PPP) για να συνδεθεί στον ISP. Εποµένως η πρόσβασή του στο Internet εξαρτάται από την ύπαρξη κάποιου τοπικού modem gateway. Τα µηνύµατα κατάστασης που εµφανίζονται στο τερµατικό Putty είναι: Εικόνα 121. ∆ιαδικασία εκκίνησης στάδιο 11 Αφού η ενεργοποίηση της υποδοµής του δικτύου έχει ολοκληρωθεί µε επιτυχία, έχει έρθει πλέον η στιγµή να φορτωθούν οι υπηρεσίες δικτύου (σε αντίθετη περίπτωση αυτό είναι αδύνατον). ∆ηλαδή οι εφαρµογές που εκµεταλλεύονται το δίκτυο για λογαριασµό του χρήστη. Αυτές οι εφαρµογές βρίσκονται ενσωµατωµένες στο πολυεργαλείο BusyBox και για να λειτουργήσουν απαιτείται η εκτέλεση ενός ακόµη συνόλου αρχείων script τα οποία είναι τα εξής: S21dnsmasq DNS και DHCP server S22iptables Τείχος προστασίας (firewall) S40telnetd Telnet server S41inetd ∆ιαχείριση των υπηρεσιών δικτύου (Telnet, FTP κλπ) S42httpd Web Server µε υποστήριξη cgi-bin για δυναµικές σελίδες S43ntpdate , S49ntp Ρύθµιση ώρας και ηµεροµηνίας µέσω Internet S49netfs Σύστηµα αρχείων για παραµετροποίηση των ρυθµίσεων δικτύου S50dropbear SSH server S50proftpd FTP server Τα µηνύµατα κατάστασης που εµφανίζονται στο τερµατικό Putty είναι: Ιστότοπος εργασίας: MyThesis.org 215 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 122. ∆ιαδικασία εκκίνησης στάδιο 12 Εφόσον όλα τα παραπάνω εκτελέστηκαν επιτυχώς, η διαδικασία εκκίνησης του Router NGW100 έχει πλέον ολοκληρωθεί. Είναι πλέον έτοιµος να συνδέεται στο Internet, να δροµολογεί τα πακέτα των υπολογιστικών συστηµάτων του τοπικού δικτύου, να παραµετροποιείται µέσω γραµµής εντολών αλλά και µέσω του Web interface που διαθέτει. Στην εικόνα που ακολουθεί βλέπουµε την οθόνη καλωσορίσµατος, και το command prompt του BusyBox, που εµφανίζει ο Router NGW100 µετά την ολοκλήρωση της εκκίνησής του: Εικόνα 123. Ολοκλήρωση διαδικασίας εκκίνησης – έναρξη γραµµής εντολών BusyBox Επικοινωνία: [email protected] 216 ΚΕΦΑΛΑΙΟ 7 Οι εφαρµογές του Router NGW100 Εισαγωγή Οι περισσότερες ηλεκτρονικές συσκευές, όπως και ο Router NGW100, είναι παρόµοιες ως προς το υλικό τους. Έχουν µνήµη για να αποθηκεύουν δεδοµένα, µονάδα επεξεργασίας για να επεξεργάζονται δεδοµένα, και δίαυλους επικοινωνίας για να επικοινωνούν µεταφέροντας δεδοµένα. Επίσης, για να είναι όλα αυτά εφικτά, συνδέονται µε κάποια πηγή τροφοδοσίας. Αυτό όµως για το οποίο διαφοροποιείται πραγµατικά µια ηλεκτρονική συσκευή από µία άλλη, είναι η εφαρµογή ή οι εφαρµογές που περιλαµβάνει. Ο Router NGW100 είναι ένας δροµολογητής. Εποµένως οι κυριότερες διεργασίες που τον αφορούν και τον διαφοροποιούν από άλλες συσκευές είναι κυρίως η δροµολόγηση και η ασφαλής προώθηση πακέτων στο τοπικό δίκτυο και στα δίκτυα ευρείας περιοχής (πχ: Internet). Όπως έχουµε δει και στο θεωρητικό µέρος, οι διεργασίες αυτές βρίσκονται ενσωµατωµένες στον Linux Kernel. Για να τις εκµεταλλευτούµε δηµιουργούµε εφαρµογές οι οποίες επικοινωνούν µε το API των διεργασιών αυτών. Σε αυτό το τελευταίο κεφάλαιο θα παρουσιάσουµε τις εφαρµογές αυτές αλλά και τις δυνατότητες αλληλεπίδρασης του χρήστη µε τον Router NGW100. Πιο συγκεκριµένα θα ασχοληθούµε µε τα εξής: Ανάπτυξη δοκιµαστικής εφαρµογής Προσθήκη πακέτων λογισµικού στο Buildroot Οι εφαρµογές του Router NGW100 Είναι χρήσιµο να αναφέρουµε εδώ, ότι ως προς το πρωτόκολλο TCP/IP, το κεφάλαιο 7 ασχολείται µε το επίπεδο εφαρµογής. Στα προηγούµενα κεφάλαια παρουσιάστηκαν τα πιο χαµηλά επίπεδα, µε το χαµηλότερο να αφορά το φυσικό επίπεδο (µέρος αυτού του επιπέδου αποτελεί και το παράρτηµα B). 7.1 Ανάπτυξη δοκιµαστικής εφαρµογής Πριν αναφερθούµε στις εφαρµογές που εγκαταστάθηκαν στον Router NGW100 καλό θα είναι να δούµε ένα απλό παράδειγµα ανάπτυξης δοκιµαστικής εφαρµογής. Η εφαρµογή αυτή αναπτύχθηκε στα πλαίσια της παρούσας εργασίας καθαρά για διαγνωστικούς λόγους. Για την ανάπτυξη και τον έλεγχο επιτυχούς µεταγλώττισης της εφαρµογής, χρησιµοποιήθηκαν δύο τρόποι. Ο πρώτος περιλάµβανε έναν απλό κειµενογράφο και την γραµµή εντολών ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) της κονσόλας της Ubuntu, και ο δεύτερος, το IDE AVR32 Studio και τα προγράµµατα FileZilla και PuTTY. Αυτό έγινε για να δοκιµαστεί επίσης και η ορθή λειτουργία των βοηθητικών εφαρµογών αλλά και ορισµένων πρωτοκόλλων επικοινωνίας. Ο πηγαίος κώδικας της δοκιµαστικής εφαρµογής που αναπτύχθηκε, είναι ο ακόλουθος: #include <stdio.h> int main(int argc, char** argv) { printf("Hello World!\n"); return 0; } Το όνοµα του αρχείου που περιλαµβάνει τον πηγαίο κώδικα είναι το hello.c και βρίσκεται στον κατάλογο /rousis/misc/paradigm/application/hello.c. Επίσης το σύστηµα host συνδέθηκε µε τον Router NGW100 µέσω Ethernet (θύρα LAN) και µέσω της σειριακής θύρας. Για τη σειριακή σύνδεση χρησιµοποιήθηκε η εφαρµογή PuTTY. Για τη σύνδεση µέσω Ethernet κατασκευάστηκε αθωράκιστο καλώδιο συνεστραµµένων ζευγών UTP (Unshielded Twisted Pair) cat5e (wikipedia.org/wiki/Category_5_cable) µε συνδετήρες RJ45 (αρσενικούς) και διάταξη καλωδίων τύπου B. Στην εικόνα µπορούµε να δούµε το συγκεκριµένο καλώδιο: Εικόνα 124. Καλώδιο συνεστραµµένων ζευγών UTP cat5e Για την κατασκευή του καλωδίου χρησιµοποιήθηκε ειδική τανάλια η οποία δεν κρίνεται απαραίτητο να αναφερθεί εδώ. 7.1.2 Με χρήση κειµενογράφου και γραµµής εντολών Αρχικά, µε δεξί κλικ στον κατάλογο application, δηµιουργήθηκε ένα άδειο αρχείο (Empty File) το οποίο µετονοµάστηκε σε hello.c, όπως φαίνεται στην παρακάτω εικόνα: Επικοινωνία: [email protected] 218 Οι εφαρµογές του Router NGW100 – Κεφάλαιο 7 Εικόνα 125. ∆ηµιουργία άδειου αρχείου στην Ubuntu 9.04 Στη συνέχεια, µε διπλό κλικ, το αρχείο προσπελάστηκε προκειµένου να γραφτεί σε αυτό ο πηγαίος κώδικας της εφαρµογής. Αφού ολοκληρώθηκε η συγγραφή, αποθηκεύτηκαν οι αλλαγές και το αρχείο έκλεισε για να πραγµατοποιηθεί το επόµενο βήµα. ∆ηλαδή η µεταγλώττισή του µέσω της αλυσίδας cross και του cross compiler avr32-linux-gcc, που είχε ήδη δηµιουργηθεί νωρίτερα µέσω του αυτοµατοποιηµένου συστήµατος Buildroot. Αρχικά εκτελέστηκε (σε µια γραµµή) η παρακάτω εντολή, στην κονσόλα της Ubuntu για να δηµιουργηθεί το αρχείο object (αντικείµενος κώδικας): /rousis/buildroot/build_avr32/staging_dir/usr/bin/avr32-linux-gcc Wall -mcpu=ap7000 -D_GNU_SOURCE -c -o hello.o hello.c -pipe -O3 -g - Μετά την επιτυχή εκτέλεση της εντολής, στον κατάλογο application εµφανίστηκε ένα νέο αρχείο µε το όνοµα hello.o. Αυτό που έµενε είναι η σύνδεσή του (µε βιβλιοθήκες κλπ) µέσω του linker προκειµένου να προκύψει το εκτελέσιµο αρχείο: /rousis/buildroot/build_avr32/staging_dir/usr/bin/avr32-linux-gcc Wall -mcpu=ap7000 -o hello hello.o -pipe -O3 -g - Εφόσον όλα λειτούργησαν σωστά, δηµιουργήθηκε το εκτελέσιµο αρχείο hello το οποίο δεν περιλάµβανε καµία επέκταση. Στην εικόνα βλέπουµε τα αρχεία που δηµιουργήθηκαν τελικά στον κατάλογο application: Εικόνα 126. Τα αρχεία του καταλόγου application µετά τη µεταγλώττιση του hello.c Σε αυτό το σηµείο µπορούµε να διαπιστώσουµε και την έννοια του cross compiling (διασταυρούµενη µεταγλώττιση). Αν δοκιµάσουµε να εκτελέσουµε την εφαρµογή hello στην Ubuntu θα λάβουµε ως απάντηση το µήνυµα “bash: ./hello: cannot execute binary file”. Αυτό είναι απολύτως επιθυµητό καθώς το σύστηµα host που χρησιµοποιούµε είναι αρχιτεκτονικής i386 ενώ το αρχείο hello.c έχει µεταγλωττιστεί για την αρχιτεκτονική του µικροελεγκτή AP7000 που είναι η AVR32. Εποµένως για να εκτελεστεί η εφαρµογή που δηµιουργήσαµε θα πρέπει πρώτα να µεταφερθεί στη µνήµη του Router NGW100. Η µεταφορά της εφαρµογής στον Router NGW100 έγινε µέσω εντολών FTP (client) στην κονσόλα της Ubuntu. Φυσικά από την πλευρά του Router NGW100 υπάρχει ο ProFTPD Ιστότοπος εργασίας: MyThesis.org 219 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) server ο οποίος φρόντισε για την λήψη του αρχείου. Η εικόνα παρουσιάζει ολόκληρη την εν λόγω διαδικασία: Εικόνα 127. Μεταφορά της εφαρµογής hello στον Router NGW100 µέσω εντολών FTP Το επόµενο βήµα που έπρεπε να γίνει είναι η εκτέλεση της εφαρµογής hello µέσω της κονσόλας και του πρωτοκόλλου Telnet. Μετά την εκτέλεση εµφανίστηκε το µήνυµα Hello World!. Αυτό απέδειξε ότι όλα πήγαν καλά και ότι πλέον υπήρχε διαθέσιµο ένα ολοκληρωµένο περιβάλλον ανάπτυξης εφαρµογών για τον Router NGW100. Η διαδικασία εκτέλεσης µέσω Telnet φαίνεται στην εικόνα: Εικόνα 128. Εκτέλεση της δοκιµαστικής εφαρµογής hello µέσω Telnet Επικοινωνία: [email protected] 220 Οι εφαρµογές του Router NGW100 – Κεφάλαιο 7 Επειδή στον Router NGW100 το Telnet βρίσκεται ενσωµατωµένο στο BusyBox, αφού συνδεθούµε εµφανίζεται πρώτα η αντίστοιχη ενηµέρωση και αµέσως µετά µπορούµε να εκτελέσουµε τις εντολές µας. Για την ίδια διαδικασία θα µπορούσε να χρησιµοποιηθεί το ασφαλές πρωτόκολλο SSH που επίσης βρίσκεται ενσωµατωµένο στο BusyBox, ή το τερµατικό PuTTY. 7.1.3 Μέσω του AVR32 Studio Επειδή το AVR32 Studio παρουσίαζε κάποια δυσκολία ως προς τον εντοπισµό του cross compiler avr32-linux-gcc, δηµιουργήθηκε ένα ειδικό shell script µε την ονοµασία AVR32-Studio.sh. Ο κώδικάς του είναι ο ακόλουθος: #!/bin/sh export PATH=$PATH:/rousis/buildroot/build_avr32/staging_dir/bin /rousis/avr32studio/avr32studio Το αρχείο τοποθετήθηκε στη συνέχεια, στην επιφάνεια εργασίας της Ubuntu ώστε να είναι εύκολα προσβάσιµο. Με διπλό κλικ επάνω του προέκυψε το παρακάτω παράθυρο διαλόγου: Εικόνα 129. Παράθυρο διαλόγου µετά την εκτέλεση του αρχείου AVR32-Studio.sh µε διπλό κλικ Επιλέγοντας Run in Terminal, το AVR32 Studio ανοίγει και είναι έτοιµο για την ανάπτυξη εφαρµογών: Ιστότοπος εργασίας: MyThesis.org 221 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 130. Το κεντρικό περιβάλλον εργασίας του AVR32 Studio Για την δηµιουργία νέου project ακολουθείται η διαδροµή File New Project… και από το παράθυρο διαλόγου που εµφανίζεται επιλέγεται η επιλογή AVR32 C Project: Εικόνα 131. AVR32 Studio - ∆ηµιουργία νέου C project – Βήµα 1 Επικοινωνία: [email protected] 222 Οι εφαρµογές του Router NGW100 – Κεφάλαιο 7 Με κλικ στο Next εµφανίστηκε το τελευταίο βήµα στο οποίο καθορίστηκε το όνοµα του project dokimastiki-efarmogi, ο µικροελεγκτής AP7000 και ο τύπος project, AVR32 Linux Executable: Εικόνα 132. AVR32 Studio - ∆ηµιουργία νέου C project – Βήµα 2 Ο λόγος για τον οποίο επιλέχτηκε ο συγκεκριµένος τύπος project (AVR32 Linux Executable) και όχι ο AVR32 Standalone Executable, είναι ότι η προς ανάπτυξη εφαρµογή, θα έπρεπε µεν να εκτελείται στην αρχιτεκτονική του AP7000, αλλά ταυτόχρονα θα έπρεπε να υπακούει και στο ιδιαίτερο οικοσύστηµα του Linux Kernel. Αν επρόκειτο για µια ανεξάρτητη εφαρµογή η οποία δε θα εκτελούνταν επάνω από κάποιο λειτουργικό σύστηµα, τότε θα επιλέγαµε τον τύπο Standalone Executable. Κάτι αντίστοιχο ισχύει και σε περίπτωση ανάπτυξης βιβλιοθήκης, µε την εξαίρεση ότι σε µια ανεξάρτητη εφαρµογή δεν υπάρχει νόηµα να δηµιουργηθεί διαµοιραζόµενη βιβλιοθήκη (Linux Shared Library), αφού σε ένα τέτοιο σύστηµα (µονολιθικό), εκτελείται συνεχώς µία και µοναδική εφαρµογή. Αφού ολοκληρώθηκε η δηµιουργία νέου project, προστέθηκε – δηµιουργήθηκε στο project αυτό, ένα νέο αρχείο πηγαίου κώδικα C. Αυτό έγινε από το κεντρικό µενού του AVR32 Studio, επιλέγοντας File New Source File. Στην εικόνα βλέπουµε το παράθυρο διαλόγου για την δηµιουργία νέου αρχείου πηγαίου κώδικα: Ιστότοπος εργασίας: MyThesis.org 223 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 133. AVR32 Studio - ∆ηµιουργία νέου αρχείου πηγαίου κώδικα C Μετά την προσθήκη - δηµιουργία του αρχείου πηγαίου κώδικα C µε την ονοµασία hello.c, στο project dokimastiki-efarmogi, πληκτρολογήθηκε ο ίδιος κώδικας της δοκιµαστικής εφαρµογής που είδαµε και στην προηγούµενη παράγραφο. Αµέσως µετά από το κεντρικό µενού επιλέγηκε Project Build All για να δηµιουργηθεί το εκτελέσιµο αρχείο της εφαρµογής. Το AVR32 Studio δηµιουργεί για κάθε νέο project έναν οµώνυµο κατάλογο. Στην περίπτωσή µας αυτός ήταν ο /rousis/avr32studio/workspace/dokimastiki-efarmogi. Επίσης µετά από το Build του κάθε project, δηµιουργεί τον κατάλογο Debug (ή release) µέσα στον οποίο τοποθετείται τελικά το τελικό εκτελέσιµο αρχείο το οποίο είναι της µορφής .elf (wikipedia.org/wiki/Executable_and_Linkable_Format). Το αρχείο αυτό παίρνει αυτόµατα το όνοµα του project. Στην εικόνα βλέπουµε τα περιεχόµενα του κατάλογο Debug για το project dokimastiki-efarmogi: Εικόνα 134. AVR32 Studio - Περιεχόµενα καταλόγου Debug για το project "dokimastiki-efarmogi" Όπως και στην περίπτωση της προηγούµενης παραγράφου, έτσι κι εδώ, το εκτελέσιµο αρχείο έπρεπε να µεταφερθεί στον Router NGW100 για να επιβεβαιωθεί ότι όλα πήγαν καλά. Αυτό πραγµατοποιήθηκε µέσω της βοηθητικής εφαρµογής FileZilla FTP client που είδαµε και στο προηγούµενο κεφάλαιο. Στην εικόνα διακρίνεται ολόκληρη η διαδικασία: Επικοινωνία: [email protected] 224 Οι εφαρµογές του Router NGW100 – Κεφάλαιο 7 Εικόνα 135. Μεταφορά αρχείου dokimastiki-efarmogi.elf στον Router NGW100 µέσω του FileZilla FTP client Όπως εύκολα µπορεί να παρατηρήσει κανείς το αρχείο hello που προέκυψε από την µεταγλώττιση του hello.c µέσω γραµµής εντολών, υπάρχει ακόµα στον κατάλογο /home/rousis του Router NGW100. Επίσης η διαφορά του µεγέθους του (5,468 bytes), σε σχέση µε αυτό του dokimastiki-efarmogi.elf (29,032 bytes), είναι σχεδόν εξαπλάσια. Ολοκληρώνοντας την διαδικασία για την εκτέλεση της εφαρµογής dokimastikiefarmogi.elf στον Router NGW100 έπρεπε πρώτα να δοθούν τα απαραίτητα δικαιώµατα. Για το λόγο αυτό χρησιµοποιήθηκε το πρόγραµµα PuTTY και η σειριακή επικοινωνία. Οι εντολές που χρησιµοποιήθηκαν για την προσπέλαση του εκτελέσιµου αρχείου, την προσάρτηση κατάλληλων δικαιωµάτων και την εκτέλεσή του, είναι οι ακόλουθες: Ιστότοπος εργασίας: MyThesis.org 225 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 136. Εκτέλεση της δοκιµαστικής εφαρµογής hello µέσω του τερµατικού PuTTY 7.2 Προσθήκη πακέτων λογισµικού στο Buildroot Η δοκιµαστική εφαρµογή που αναπτύχθηκε στην προηγούµενη παράγραφο, ουσιαστικά δεν κάνει τίποτα χρήσιµο. Η επιτυχηµένη της εκτέλεση όµως, είναι µια βάσιµη ένδειξη ότι έχει πλέον δηµιουργηθεί µια πλήρης πλατφόρµα υλικού και λογισµικού. Σε αυτή θα µπορούν να αναπτύσσονται εφαρµογές οι οποίες θα είναι σε θέση να χρησιµοποιούν τους πόρους του Router NGW100, καθώς και τις διάφορες εισόδους και εξόδους του ώστε να επικοινωνούν µε το περιβάλλον. Ο Router NGW100, αναπτύχθηκε για να λειτουργεί ως δροµολογητής πακέτων υπολογιστικών συστηµάτων, εποµένως οι περισσότερες εφαρµογές του εξυπηρετούν µε τον ένα ή τον άλλο τρόπο το σκοπό αυτό. Οι εφαρµογές αυτές, συνήθως δεν αποτελούνται από ένα και µόνο ανεξάρτητο εκτελέσιµο, αλλά από πολλαπλά αρχεία που σε κάποιες περιπτώσεις χρειάζεται να βρίσκονται σε διαφορετικούς καταλόγους του συστήµατος αρχείων (πχ: /etc, /bin κλπ) του Router NGW100. Επίσης, µπορεί να απαιτούνται και κάποιες καταχωρήσεις σε αρχεία ρυθµίσεων του συστήµατος (πχ: /etc/passwd). Από όλα τα παραπάνω γίνεται εύκολα κατανοητό, το ότι κάποιες εφαρµογές δεν αλλάζουν συχνά και είναι βασικές για τη λειτουργία του συστήµατός µας. Εποµένως σε κάθε µεταγλώττιση που γίνεται µε σκοπό την δηµιουργία (ή τροποποίηση) των δυαδικών αρχείων rootfs.avr32.jffs2-root και rootfs.avr32.jffs2-usr, θα πρέπει να ενσωµατώνονται αυτόµατα έτσι ώστε να µεταφέρονται στη συνέχεια στον Router NGW100. Αυτό επιτυγχάνεται µέσω του αυτοµατοποιηµένου συστήµατος Buildroot και του ειδικού µηχανισµού προσθήκης πακέτων λογισµικού που διαθέτει. Η διαδικασία προσθήκης του πακέτου λογισµικού που περιλάµβανε την κάθε εφαρµογή του Router NGW100, στο Buildroot, αποτελείται από τα εξής βήµατα: ∆ηµιουργία οµώνυµου της εφαρµογής, καταλόγου, στον κατάλογο /rousis/buildroot/package Προσθήκη του πακέτου της εφαρµογής στο σύστηµα παραµετροποίησης του Buildroot µέσω δηµιουργίας κατάλληλου αρχείου Config.in ∆ήλωση εξαρτήσεων (dependencies) του πακέτου αυτού από την ύπαρξη άλλων υποστηρικτικών πακέτων µέσω των οδηγιών select και depends on Ενηµέρωση του global αρχείου /rousis/buildroot/package/Config.in για την ύπαρξη του πακέτου που προστέθηκε ∆ηµιουργία ενός αρχείου - εγκαταστάτη Makefile (*.mk) για το πακέτο Μετά από την ολοκλήρωση των παραπάνω βηµάτων και την εκτέλεση της εντολής make menuconfig στο Buildroot, η εφαρµογή ήταν διαθέσιµη για επιλογή. Η διαδικασία προσθήκης νέου πακέτου λογισµικού στο αυτοµατοποιηµένο σύστηµα Buildroot µπορεί να αυτοµατοποιηθεί και µέσω του ειδικού script add_new_package.wizard Επικοινωνία: [email protected] 226 Οι εφαρµογές του Router NGW100 – Κεφάλαιο 7 του λογισµικού autotools. Επίσης, σηµαντικός είναι και ο κατάλογος της κάθε εφαρµογής που προστίθεται, καθώς περιλαµβάνει αρχεία βοήθειας και οδηγιών εγκατάστασης και παραµετροποίησής της. Αυτά είναι συνήθως αρχεία κειµένου και έχουν συγκεκριµένες ονοµασίες, όπως README και INSTALL. 7.2.1 Παράδειγµα προσθήκης εφαρµογής στο σύστηµα Buildroot Σε αυτή την παράγραφο θα γίνει µια σύντοµη παρουσίαση της διαδικασίας προσθήκης µιας εκ των εφαρµογών – πακέτων, του Router NGW100 στο σύστηµα Buildroot. Το όνοµά της εφαρµογής είναι webif και είναι υπεύθυνη για την δηµιουργία του Web interface. Αρχικά δηµιουργήθηκε ο κατάλογος /rousis/buildroot/package/webif. και αντιγράφηκαν σε αυτόν οι κατάλογοι source και files του πακέτου webif από το BSP. Το αµέσως επόµενο βήµα που έπρεπε να γίνει (όπως είδαµε και στην προηγούµενη παράγραφο), ήταν να δηµιουργηθεί (µε τη βοήθεια του BSP) το αρχείο Config.in το οποίο ήταν απαραίτητο από το σύστηµα παραµετροποίησης του Buildroot: config BR2_PACKAGE_WEBIF bool "webif - Status Console" select BR2_PACKAGE_HASERL help A web interface for showing different network status. This package requires awk support on the system, either the one provided by Busybox or gawk. The default login on the status web pages are root/root and admin/admin. This can be changed in the etc/httpd.conf file. config BR2_PACKAGE_WEBIF_INSTALL_INDEX_HTML bool "instal index.html in /www which redirects to webif" depends on BR2_PACKAGE_WEBIF help Installs a /www/index.html which redirects to the status console cgi scripts. config BR2_PACKAGE_WEBIF_LANGUAGES bool "install language support" depends on BR2_PACKAGE_WEBIF help This option installs support for other languages than english. (Greek translation: Dimitrios A. Rousis) Supported languages: gr, ca, cz, de, dk, ee, es, fr, hr, hu, it, nl, no, pl, pt, ru and se. Η συγγραφή του κώδικα του αρχείου Config.in βασίζεται στο σύστηµα παραµετροποίησης του Linux Kernel (Kconfig) και εποµένως αναπτύχθηκε µε τους ίδιους συντακτικούς κανόνες (/rousis/buildroot/…/linux-2.6.27.6/Documentation/kbuild). Για να εµφανιστούν τα αποτελέσµατα του παραπάνω κώδικα, ενηµερώθηκε στη συνέχεια το global αρχείο Config.in του Buildroot (κάτω από το menu “Networking”) µε την προσθήκη της παρακάτω γραµµής: source "package/webif/Config.in" Ο κώδικας αυτός είχε ως αποτέλεσµα (µετά την εκτέλεση της εντολής make menuconfig) την εµφάνιση των αντίστοιχων επιλογών στο Buildroot (Package selection for tarNetworking webif – Status Console): get Ιστότοπος εργασίας: MyThesis.org 227 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 137. Buildroot – Πακέτο λογισµικού webif Όµως αυτό δεν είναι αρκετό, καθώς γιατί µεταγλώττιση της εφαρµογής webif θα έπρεπε να υπάρχει ένα ειδικό Makefile (webif.mk) του οποίου τον κώδικα βλέπουµε παρακάτω: ############################################################# # # webif # ############################################################# WEBIF_VERSION:=0.2 WEBIF_SOURCE:=package/webif WEBIF_SITE:=https://svn.openwrt.org/openwrt/tags/whiterussian_0.9/package/webif WEBIF_DIR:=$(BUILD_DIR)/webif-$(WEBIF_VERSION) $(WEBIF_DIR)/.unpacked: mkdir -p $(WEBIF_DIR) touch $@ $(WEBIF_DIR)/.built: $(WEBIF_DIR)/.unpacked $(TARGET_CC) $(TARGET_CFLAGS) -o $(WEBIF_DIR)/webif-page $(WEBIF_SOURCE)/src/webifpage.c $(TARGET_CC) $(TARGET_CFLAGS) -o $(WEBIF_DIR)/bstrip $(WEBIF_SOURCE)/src/bstrip.c $(STRIPCMD) $(STRIP_STRIP_UNNEEDED) $(WEBIF_DIR)/webif-page $(WEBIF_DIR)/bstrip touch $@ $(TARGET_DIR)/www/webif.css: $(WEBIF_DIR)/.built mkdir -p $(TARGET_DIR)/etc mkdir -p $(TARGET_DIR)/usr/bin mkdir -p $(TARGET_DIR)/usr/lib mkdir -p $(TARGET_DIR)/www cat $(WEBIF_SOURCE)/files/etc/httpd.conf >> $(TARGET_DIR)/etc/httpd.conf cp -dpfr $(WEBIF_SOURCE)/files/usr/lib/webif $(TARGET_DIR)/usr/lib/ ifneq ($(BR2_PACKAGE_WEBIF_LANGUAGES),y) rm -rf $(TARGET_DIR)/usr/lib/webif/lang endif $(INSTALL) -m0755 $(WEBIF_DIR)/webif-page $(TARGET_DIR)/usr/bin/ Επικοινωνία: [email protected] 228 Οι εφαρµογές του Router NGW100 – Κεφάλαιο 7 $(INSTALL) -m0755 $(WEBIF_DIR)/bstrip $(TARGET_DIR)/usr/bin/ ifeq ($(BR2_PACKAGE_WEBIF_INSTALL_INDEX_HTML),y) @if [ -f "$(TARGET_DIR)/www/index.html" ]; then echo; \ echo "webif WARNING:"; \ echo "There is already a $(TARGET_DIR)/www/index.html"; echo "webif might be replacing another package;" \ echo; \ echo "Sleeping for 10 seconds"; \ sleep 10; \ fi cp -dpf $(WEBIF_SOURCE)/files/www/index.html $(TARGET_DIR)/www/ endif cp -dpfr $(WEBIF_SOURCE)/files/www/cgi-bin $(TARGET_DIR)/www/ cp -dpfr $(WEBIF_SOURCE)/files/www/webif.* $(TARGET_DIR)/www/ @if [ ! -f $(TARGET_DIR)/etc/banner ]; then \ ln -sf issue $(TARGET_DIR)/etc/banner; \ fi touch $@ \ \ webif: busybox $(TARGET_DIR)/www/webif.css webif-clean: rm -rf $(TARGET_DIR)/www/cgi-bin/webif* $(TARGET_DIR)/www/webif.* rm -rf $(TARGET_DIR)/usr/lib/webif rm -f $(TARGET_DIR)/usr/bin/bstrip $(TARGET_DIR)/usr/bin/webif-page rm -r $(WEBIF_DIR)/bstrip $(WEBIF_DIR)/webif-page webif-source: webif-dirclean: rm -rf $(WEBIF_DIR) ############################################################# # # Toplevel Makefile options # ############################################################# ifeq ($(BR2_PACKAGE_WEBIF),y) TARGETS+=webif endif Ο κώδικας του Makefile webif.mk αναπτύχθηκε έτσι ώστε να περιλαµβάνει τους κανόνες για το κατέβασµα, την παραµετροποίηση, τη µεταγλώττιση και την εγκατάσταση της εφαρµογής webif στο σύστηµα αρχείων που δηµιουργεί το Buildroot για τον Router NGW100. Τελικά στον κατάλογο /rousis/buildroot/package/webif υπάρχουν τα εξής: Εικόνα 138. Buildroot - Τα περιεχόµενα του καταλόγου package/webif Μετά την εκτέλεση της εντολής make (και ενώ βρισκόµαστε µέσα τον κατάλογο του Buildroot) ο πηγαίος κώδικας του webif.mk εκτελείται εγκαθιστώντας το πακέτο webif στο σύστηµα αρχείων του Router NGW100. Με παρόµοιο τρόπο προστέθηκαν και οι υπόλοιπες εφαρµογές του Router NGW100. ∆εν κρίνεται σκόπιµο να αναφερθούν µία προς µία, µιας και το παράδειγµα που µόλις παρατέθηκε είναι αρκετό για την κατανόηση της όλης διαδικασίας. Ιστότοπος εργασίας: MyThesis.org 229 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) 7.3 Οι εφαρµογές του Router NGW100 Κατά την ανάλυση της διαδικασίας εκκίνησης του Router NGW100 σε προηγούµενο κεφάλαιο, αναφέρθηκαν ορισµένα script εκκίνησης τα οποία εκτελούνται προκειµένου να ενεργοποιήσουν την λειτουργία κάποιων εφαρµογών και κάποιων υπηρεσιών. Οι εφαρµογές αυτές είτε αποτελούν µέρος του λογισµικού BusyBox είτε έχουν εγκατασταθεί ανεξάρτητα, καθεµία ξεχωριστά. Κάτι αντίστοιχο ισχύει και για την εκκίνηση των εφαρµογών. Όσες ανήκουν στο BusyBox εκκινούνται από το script εκκίνησής του, ενώ όσες είναι ανεξάρτητες εκκινούν από ξεχωριστά scripts. Εποµένως οι εφαρµογές του Router NGW100 µπορούν να χωριστούν σε δύο κύριες κατηγορίες: Εφαρµογές που περιλαµβάνονται στο BusyBox Ανεξάρτητες εφαρµογές Για την εγκατάστασή τους, και στις δύο περιπτώσεις, ισχύει ότι είδαµε προηγουµένως στο παράδειγµα προσθήκης εφαρµογής στο σύστηµα Buildroot. Ο Router NGW100 είναι ένα ενσωµατωµένο σύστηµα Linux και όπως ήδη γνωρίζουµε έχει περιορισµένους πόρους. Για το λόγο αυτό όλες οι εφαρµογές που έχουµε αναφέρει και θα αναφέρουµε και στη συνέχεια, έχουν επιλεγεί µε κριτήρια το µικρό µέγεθος και τη µέγιστη δυνατή απόδοση και αξιοπιστία. Οι εφαρµογές που κρίθηκαν οι πιο σηµαντικές για ανάλυση στην παρούσα εργασία είναι οι ακόλουθες: busybox 1.13.1 inetd BusyBox httpd BusyBox webif 0.9 haserl 0.9.24 awk BusyBox iptables 1.4.1 bridge 1.4 dnsmasq 2.46 udhcpc BusyBox ifconfig BusyBox ifup BusyBox ifdown BusyBox route BusyBox dropbear 0.52 telnet BusyBox ethtool 6 ntp 4.2.4p5 portmap 5beta proFTPD 1.3.1 Οι εν λόγω εφαρµογές, αναφέρονται γιατί είναι κυρίως αυτές, οι οποίες βοηθούν στο να δοθεί στον Router NGW100 o χαρακτήρας µιας ολοκληρωµένης συσκευής δροµολογητή, όπως αυτή είναι γνωστή στην αγορά σήµερα. Βέβαια απαιτούνται ορισµένες επιπλέον προσθήκες για να καταστεί ο Router NGW100 εµπορικό προϊόν. Αυτό όµως δεν αποτελούσε σε καµία περίπτωση τον στόχο αυτής της εργασίας. Επικοινωνία: [email protected] 230 Οι εφαρµογές του Router NGW100 – Κεφάλαιο 7 Όσες εφαρµογές έχουν δεξιά τους το σύµβολο “ γράµµατα που είναι ενσωµατωµένα στο BusyBox. BusyBox “ σηµαίνει ότι αποτελούν προ- Η παραµετροποίησή σχεδόν όλων των εφαρµογών του Router NGW100 µπορεί να γίνει είτε µέσω γραµµής εντολών (command line) από κάποιο διαχειριστή δικτύων, είτε µέσω δυναµικών ιστοσελίδων (Web interface), από έναν οποιοδήποτε χρήστη. 7.3.1 Πακέτο εφαρµογών BusyBox Την εφαρµογή BusyBox (busybox.net) την εξετάσαµε και στο θεωρητικό µέρος της εργασίας στο δεύτερο κεφάλαιο. Εκεί παρουσιάσαµε την παραµετροποίησή του στο Buildroot και είδαµε ότι το menu επιλογών του χωρίζεται σε δύο κατηγορίες, τις ρυθµίσεις και τα applets. Επίσης, στο πρακτικό µέρος (κεφάλαιο 6) και κατά την ανάλυση της διαδικασίας εκκίνησης του Router NGW100, αναφέρθηκε ότι η εκκίνηση του BusyBox γίνεται µέσω της init και του αρχείου linuxrc. Το αρχείο αυτό ουσιαστικά είναι µια συντόµευση η οποία υπάρχει στο κεντρικό σύστηµα αρχείων και δείχνει προς το εκτελέσιµο του BusyBox (/bin/busybox). Οι ρυθµίσεις των εφαρµογών που βρίσκονται ενσωµατωµένες στο BusyBox βρίσκονται συνήθως στον κατάλογο /etc. Το BusyBox είναι πολύ ευέλικτο και µπορούµε κατά τη διαδικασία της µεταγλώττισής του να του αφαιρούµε και να του προσθέτουµε εφαρµογές κατά βούληση. Προκειµένου να δει κάποιος, ποιες εφαρµογές υποστηρίζονται από την τρέχουσα εγκατάσταση του Busybox στον Router NGW100 αρκεί να εκτελέσει στην γραµµή εντολών του την εντολή busybox. Επίσης θα εµφανιστεί και µια µικρή περιγραφή σχετικά µε το τι είναι το BusyBox. Το αποτέλεσµα φαίνεται στην εικόνα: Ιστότοπος εργασίας: MyThesis.org 231 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Εικόνα 139. Εκτέλεση της εντολής busybox στον Router NGW100 Παρατηρώντας την παραπάνω εικόνα, είναι µια πολύ καλή ευκαιρία να δει κανείς και τις υπόλοιπες εφαρµογές που είναι ενσωµατωµένες στο BusyBox στον Router NGW100. Για τον ίδιο λόγο, αλλά και για επιπλέον εφαρµογές που δεν βρίσκονται ενσωµατωµένες στο BusyBox, µπορούν να εκτελεστούν και οι παρακάτω εντολές: cd /bin ls –l cd /sbin ls –l Για επιπλέον µελέτη σε σχέση µε τις εφαρµογές που περιλαµβάνονται στο Busybox υπάρχει και η ιστοσελίδα busybox.net/downloads/BusyBox.html. Οι εφαρµογές του BusyBox εκκινούν χωρίς να χρειάζεται κάποιο script εκκίνησης. Αν όµως επιθυµούµε να έχουν συγκεκριµένες ρυθµίσεις, τότε θα πρέπει να γράψουµε ένα. Ένα τέτοιο παράδειγµα είναι και η εφαρµογή Webserver, httpd, την οποία θα εξετάσουµε και σε ειδική παράγραφο. Ο πηγαίος κώδικας του BusyBox και όλων των εφαρµογών που περιλαµβάνει, βρίσκεται στον κατάλογο /rousis/buildroot/project_build_avr32/atngw100/busybox-1.13.1 της εικονικής µηχανής Ubuntu. Στη συνέχεια όποτε θα αναφέρεται η τοποθεσία του πηγαίου Επικοινωνία: [email protected] 232 Οι εφαρµογές του Router NGW100 – Κεφάλαιο 7 κώδικα µιας εφαρµογής του BusyBox, θα εννοείται ως πρόθεµα και η διαδροµή του καταλόγου αυτού. 7.3.2 Εφαρµογή inetd Η εφαρµογή inetd (internet service daemon) στον Router NGW100, βρίσκεται ενσωµατωµένη στο BusyBox. Λειτουργεί σαν υπηρεσία και παίζει τον ρόλο του super server (wikipedia.org/wiki/Super-server). ∆ηλαδή εκκινεί άλλους servers όταν υπάρχει κάποιο αντίστοιχο αίτηµα προς τις υπηρεσίες τους. Ένας από αυτούς είναι και Webserver httpd που θα δούµε και στη συνέχεια. Το πλεονέκτηµα της inetd είναι ότι αφού είναι υπεύθυνη για την κλήση των υπόλοιπων servers, δεν χρειάζεται εκείνοι να δεσµεύουν πόρους του συστήµατος άσκοπα. Όταν προκύψει κάποιο αίτηµα θα ενηµερωθούν από την inetd. Στο αρχείο networking/inetd.c βρίσκεται ο πηγαίος κώδικας της εφαρµογής. Στην εσωτερική τεκµηρίωση του ίδιου αρχείου υπάρχει και µια µικρή ανάλυση. Η εκκίνησή της γίνεται µέσω του Busybox και οι ρυθµίσεις βρίσκονται στο αρχείο /etc/inetd.conf. Εφόσον λειτουργεί σαν υπηρεσία το όνοµά της θα πρέπει να υπάρχει στο /etc/services του συστήµατος αρχείων root. 7.3.3 Εφαρµογή httpd Η συντοµογραφία httpd σηµαίνει Hypertext Transfer Protocol Daemon. ∆ηλαδή ∆αίµονας Πρωτοκόλλου Μεταφοράς Υπερκειµένου. Ο όρος “δαίµονας” ή “daemon” µπορεί να γίνει κατανοητός κι από εδώ: wikipedia.org/wiki/Daemon_(computing). Στον Router NGW100 η εφαρµογή httpd αποτελεί τµήµα του BusyBox. Πρόκειται για έναν ελαφρύ και αποδοτικό Webserver, ο οποίος εκτελείται αδιάκοπα στο background του λογισµικού συστήµατος και σκοπός του είναι να φιλοξενεί το Web interface. O server httpd παρέχει δυνατότητα προστασίας των ιστοσελίδων του µε όνοµα χρήστη (username) και κωδικό πρόσβασης (password). Επίσης παρέχει υποστήριξη για δυναµικές σελίδες και τον κατάλογο cgi-bin. Μια δυναµική σελίδα µπορεί να είναι γραµµένη σε γλώσσα C/C++ (.cgi), σε Perl, Lua, AWK, να είναι κάποιο shell script, αλλά και οποιοδήποτε εκτελέσιµο το οποίο έχει µεταγλωττιστεί για την αρχιτεκτονική AVR32 του µικροελεγκτή AP7000. Εφόσον ο server httpd µπορεί να εξυπηρετεί µέσω του πρωτοκόλλου HTTP, αιτήµατα προς κάποιο εκτελέσιµο, δίνει τη δυνατότητα χειρισµού του υλικού του Router NGW100 µέσω Web. Έτσι µπορούµε για παράδειγµα να ανάβουµε και να σβήνουµε τα LED του PCB ή να προκαλούµε εγγραφές στη µνήµη, ή ακόµα και την επανεκκίνηση του Linux Kernel. Η εκκίνησή του server httpd (όπως είδαµε και στο προηγούµενο κεφάλαιο), γίνεται από το script εκκίνησης /etc/init.d/S42httpd και οι ρυθµίσεις του βρίσκονται στο αρχείο /etc/httpd.conf. Το εκτελέσιµο του αρχείο είναι το /usr/sbin/httpd. Ιστότοπος εργασίας: MyThesis.org 233 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) 7.3.4 Εφαρµογές για το Web interface Η διαχείριση ενός δροµολογητή από έναν ειδικό στα θέµατα των δικτύων, µπορεί να γίνει άνετα και αποτελεσµατικά, µέσω γραµµής εντολών, µε τη βοήθεια ενός τερµατικού (πχ: PuTTY). Σε έναν οικιακό δροµολογητή όµως, ο χρήστης διαθέτει µικρή ή και καθόλου εµπειρία γύρω από τα δίκτυα και τις ορολογίες τους. Σε αυτές τις περιπτώσεις ένα γραφικό περιβάλλον παραµετροποίησης είναι η µόνη λύση. Ένα τέτοιο περιβάλλον µπορεί να υλοποιηθεί συνήθως µε δύο τρόπους: Παραθυρική εφαρµογή σε PC και σύνδεση USB ∆υναµική ιστοσελίδα – Web Interface και σύνδεση Ethernet (ή WiFi) Η πρώτη περίπτωση αφορά πολύ αρχάριους χρήστες που δεν είναι σε θέση να συνδέσουν καλώδιο Ethernet και να αναζητήσουν την IP του δροµολογητή. Χρειάζεται όµως να υπάρχει κάποιος συνοδευτικός οπτικός δίσκος (CD/DVD), που θα περιέχει τους κατάλληλους USB drivers για τη σύνδεση του δροµολογητή µε το PC. Εποµένως η ανάπτυξη του συστήµατος µε αυτό τον τρόπο, ανεβάζει το κόστος παραγωγής. Άλλο ένα µειονέκτηµα είναι ότι ο υπολογιστής του χρήστη επιβαρύνεται µε µια επιπλέον εφαρµογή και επιπλέον drivers. Η δεύτερη περίπτωση αφορά τον µέσο χρήστη. Ο οποίος έχει κάποιες πολύ βασικές γνώσεις που του επιτρέπουν να φέρει εις πέρας µια τέτοια διαδικασία. Σε αυτή την περίπτωση δεν απαιτούνται ειδικοί drivers αφού η επικοινωνία γίνεται µέσω Ethernet και το περιβάλλον διαχείρισης είναι ενσωµατωµένο στον δροµολογητή. Το µόνο που χρειάζεται να γνωρίζει ο χρήστης, είναι την διεύθυνση IP του δροµολογητή έτσι ώστε να την πληκτρολογήσει σε κάποιο φυλλοµετρητή (Chrome, Firefox κλπ). Στον Router NGW100 το Web interface υλοποιείται κατά κύριο λόγο, µέσω των εξής εφαρµογών: awk haserl webif Εφαρµογή haserl Η εφαρµογή haserl (Html And Shell Embedded Runtime Language), είναι ένα πρόγραµµα cgi το οποίο εκτελεί διερµηνευµένα scripts. Θα µπορούσαµε να πούµε ότι είναι η PHP των ενσωµατωµένων συστηµάτων Linux. Συνδυάζει δύο πολύ σηµαντικά χαρακτηριστικά: Αναλύει αιτήµατα POST και GET τοποθετώντας τα στοιχεία της αντίστοιχης φόρµας σαν ζεύγη (πχ: name=value) έτσι ώστε να χρησιµοποιηθούν από κάποιο cgi script (πέρασµα παραµέτρων και τιµών). Εµφανίζει τα περιεχόµενα των αρχείων script σαν κώδικα HTML και υπό συγκεκριµένες περιστάσεις διερµηνεύει τον κώδικα – κείµενο που βρίσκεται ανάµεσα στα σύµβολα <%...%>. Εφαρµογή awk Η εφαρµογή awk είναι ουσιαστικά ο διερµηνευτής (interpreter) της γλώσσας scripting awk. Οι χρήσεις της γλώσσας αυτής αφορούν κυρίως σάρωση (parsing) και εξαγωγή δεδοµένων. Μπορεί επίσης να χρησιµοποιηθεί και σαν εργαλείο δηµιουργίας µορφοποιηµένων αναφορών (reports). Επικοινωνία: [email protected] 234 Οι εφαρµογές του Router NGW100 – Κεφάλαιο 7 Στο Linux οι ρυθµίσεις των διαφόρων προγραµµάτων χρήστη, βρίσκονται σε αρχεία κειµένου µέσα στα οποία συνήθως υπάρχει και κάποια ορισµένη δοµή (pattern) διάταξης. Κάθε φορά λοιπόν που θέλουµε να αλλάξουµε τις ρυθµίσεις ενός προγράµµατος θα πρέπει πρώτα να ανοίξουµε και να διαβάσουµε το αντίστοιχο αρχείο ρυθµίσεων. Επειδή συνήθως η γραµµή που θα θέλουµε να τροποποιήσουµε δεν θα είναι πάντα η πρώτη και δε θα χρειάζεται πάντα να την αλλάξουµε ολόκληρη, ο κώδικάς µας θα πρέπει να µπορεί να κάνει αναζήτηση µέσα στο κείµενο και να εντοπίζει µόνο τους χαρακτήρες ή τα σύµβολα που µας ενδιαφέρουν. Σε ορισµένες περιπτώσεις επίσης, θα χρειάζεται ακόµα και η διαγραφή γραµµών ή η εισαγωγή τους σε σχόλια ούτως ώστε να µην είναι πλέον εκτελέσιµες. Όλα τα παραπάνω µπορούν να γίνουν γράφοντας ένα πρόγραµµα C αλλά επειδή η γλώσσα αυτή έχει αναπτυχθεί για άλλο σκοπό, θα χρειαστούν πολλές γραµµές κώδικα και πολύς χρόνος. Εποµένως η γλώσσα awk είναι το κατάλληλο εργαλείο γι’ αυτές τις περιπτώσεις. Στον Router NGW100 η γλώσσα awk εγκαθίσταται µαζί µε το BusyBox αφού αποτελεί µια από τις ενσωµατωµένες του εφαρµογές. Μια ανεξάρτητη και πιο πλήρης, εναλλακτική λύση, είναι και η εφαρµογή gawk η οποία δεν κρίθηκε αναγκαία για τις ανάγκες της παρούσας εργασίας. Εφαρµογή webif Η εφαρµογή webif είναι ένα σύστηµα διαχείρισης περιεχοµένου (CMS - Content Management System) και αποτελεί µέρος του OpenWrt (openwrt.org). Θα µπορούσαµε να πούµε ότι είναι το Joomla των ενσωµατωµένων συστηµάτων Linux. Το σύστηµα webif µας δίνει τη δυνατότητα να δηµιουργήσουµε ένα Web interface το οποίο θα βασίζεται σε ένα σύνολο shell και awk scripts. Η επεξεργασία των φορµών µπορεί να γίνεται µέσω της εφαρµογής haserl. Για να είναι όλα αυτά διαθέσιµα στον χρήστη, το σύστηµα webif χρησιµοποιεί τον httpd Webserver ο οποίος όπως είδαµε και σε αντίστοιχη παράγραφο, είναι µέρος του BusyBox. Σχεδόν όλες οι εφαρµογές του Router NGW100 µπορούν να παραµετροποιούνται µέσω του Web interface που διαθέτει ο Router NGW100. 7.3.5 Eφαρµογές διαχείρισης των διεπαφών δικτύου Στον Router NGW100 οι θύρες Ethernet, LAN και WAN, ονοµάζονται διεπαφές δικτύου (network interfaces: wikipedia.org/wiki/Network_card) eth1 και eth0 αντίστοιχα. Τα χαρακτηριστικά που συνήθως τις αφορούν, είναι η IP, η µάσκα δικτύου και η ενεργοποίηση ή η απενεργοποίησή τους. Σε αυτή την παράγραφο θα παρουσιαστούν οι εφαρµογές που µας βοηθούν να διαχειριστούµε αυτά τα χαρακτηριστικά. Εφαρµογή ifconfig Η εφαρµογή ifconfig (interface configuration) βρίσκεται ενσωµατωµένη στο BusyBox. Εκτελώντας την χωρίς παραµέτρους στον Router NGW100 παίρνουµε ως αποτέλεσµα όλες τις λεπτοµέρειες των θυρών LAN, WAN καθώς και εκείνη του loopback adaptor. Αυτό φαίνεται και στην εικόνα που ακολουθεί: Ιστότοπος εργασίας: MyThesis.org 235 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Για να εµφανίσουµε κάθε διεπαφή δικτύου χωριστά εκτελούµε αντίστοιχα τις παρακάτω εντολές: ifconfig eth0 ifconfig eth1 ifconfig lo Για την απενεργοποίηση ή την ενεργοποίηση µιας συγκεκριµένης διεπαφής δικτύου του Router NGW100, όπως για παράδειγµα της διεπαφής του τοπικού LAN eth1, µπορούµε να χρησιµοποιήσουµε την ifconfig ως εξής: ifconfig eth1 down Επίσης, µέσω της ίδιας εντολής µπορούµε να αποδώσουµε µια στατική IP: ifconfig eth1 192.168.1.1 netmask 255.255.255.0 up Και για να ισχύει η αλλαγή και µετά την επανεκκίνηση του Router NGW100: ifconfig –a eth1 Ο πηγαίος κώδικας της εφαρµογής βρίσκεται στο αρχείο networking/ifconfig.c. Εφαρµογές ifup και ifdown Στον Linux Kernel οι διεπαφές δικτύου έχουν τη δυνατότητα να ενεργοποιούνται και να απενεργοποιούνται δυναµικά. Αυτό επιτυγχάνεται µέσω των εφαρµογών του BusyBox, ifup και Επικοινωνία: [email protected] 236 Οι εφαρµογές του Router NGW100 – Κεφάλαιο 7 ifdown. Κατά την εκκίνηση του Router NGW100 το script S20network φροντίζει για την ενεργοποίησή τους εκτελώντας την εντολή /sbin/ifup: #! /bin/sh IFUP=/sbin/ifup echo -n "Network interfaces: " if ${IFUP} -a; then echo "done" else echo "failed" exit 1 fi Ενώ κατά τον τερµατισµό (εντολές poweroff, halt ή reboot) το script K85network φροντίζει για τον τερµατισµό τους εκτελώντας την εντολή /sbin/ifdown: #!/bin/sh IFDOWN=/sbin/ifdown echo -n "Stopping networking: " if ${IFDOWN} -a; then echo "done" else echo "failed" exit 1 fi Οι παραπάνω εντολές µπορούν να εκτελούνται και µέσω γραµµής εντολών οποιαδήποτε στιγµή είναι απαραίτητο. 7.3.6 Eφαρµογή route Ο Router NGW100 είναι ένας δροµολογητής. Εποµένως η σηµαντικότερη λειτουργία που επιτελεί είναι η δροµολόγηση πακέτων. Σε προηγούµενο κεφάλαιο του θεωρητικού µέρους αναλύσαµε σε αρκετό βάθος τους µηχανισµούς δροµολόγησης που υπάρχουν ενσωµατωµένοι στον Linux Kernel. Επίσης αναλύσαµε και τη διεπαφή που παρέχεται για τη διαχείριση µηχανισµών αυτών από τις εφαρµογές του επιπέδου χρήστη (user space apps). Σε αυτή την παράγραφο θα παρουσιάσουµε συνοπτικά µια τέτοια εφαρµογή ή οποία ονοµάζεται route και είναι µέρος του BusyBox. Ο πηγαίος κώδικας της εφαρµογής route βρίσκεται στο αρχείο network/route.c. Tα βασικά µέρη του πηγαίου κώδικα που περιλαµβάνεται στο αρχείο route.c, είναι οι οδηγίες συµπερίληψης κεφαλίδων (#include): #include <net/route.h> #include <net/if.h> /* katalogos ... include/net */ #include "libbb.h" #include "inet_common.h" /* ston katalogo tou arxeiou route.c */ Και η κύρια συνάρτηση main() η οποία περιλαµβάνει όλη τη λογική του αλγόριθµου δροµολόγησης του Router NGW100: int route_main(int argc UNUSED_PARAM, char **argv) { unsigned opt; Ιστότοπος εργασίας: MyThesis.org 237 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) int what; char *family; char **p; /* First, remap '-net' and '-host' to avoid getopt problems. */ p = argv; while (*++p) { if (strcmp(*p, "-net") == 0 || strcmp(*p, "-host") == 0) { p[0][0] = '#'; } } opt = getopt32(argv, "A:ne", &family); if ((opt & ROUTE_OPT_A) && strcmp(family, "inet") != 0) { #if ENABLE_FEATURE_IPV6 if (strcmp(family, "inet6") == 0) { opt |= ROUTE_OPT_INET6; /* Set flag for ipv6. */ } else #endif bb_show_usage(); } argv += optind; /* No more args means display the routing table. */ if (!*argv) { int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0; #if ENABLE_FEATURE_IPV6 if (opt & ROUTE_OPT_INET6) INET6_displayroutes(); else #endif bb_displayroutes(noresolve, opt & ROUTE_OPT_e); fflush_stdout_and_exit(EXIT_SUCCESS); } /* Check verb. At the moment, must be add, del, or delete. */ what = kw_lookup(tbl_verb, &argv); if (!what || !*argv) { /* Unknown verb or no more args. */ bb_show_usage(); } #if ENABLE_FEATURE_IPV6 if (opt & ROUTE_OPT_INET6) INET6_setroute(what, argv); else #endif INET_setroute(what, argv); return EXIT_SUCCESS; } Για να δούµε τις επιλογές που µας δίνονται αρκεί να πληκτρολογήσουµε στην γραµµή εντολών του Router NGW100 την παρακάτω εντολή: route --help Για να δούµε τον πίνακα δροµολόγησης του Kernel εκτελούµε την εντολή route χωρίς παραµέτρους: route Επικοινωνία: [email protected] 238 Οι εφαρµογές του Router NGW100 – Κεφάλαιο 7 Αν θέλουµε να προσθέσουµε έναν κανόνα δροµολόγησης στον πίνακα παρακάµπτοντας την αυτόµατη διαδικασία, χρησιµοποιούµε την παράµετρο add. Για να καταργήσουµε τον κανόνα χρησιµοποιούµε την παράµετρο del. Φυσικά όλα τα παραπάνω αποτελούν µια απλή σύνοψη της εφαρµογής route. Υπάρχουν πολλές επιπλέον λειτουργίες, καθώς και πολλές δυνατότητες συνδυασµών µε άλλες εφαρµογές. 7.3.7 Εφαρµογή iptables Στον Router NGW100 το ρόλο του Firewall και του NAT (Network Address Translator) παίζει η εφαρµογή iptables (wikipedia.org/wiki/Iptables). Η εφαρµογή iptables εκκινεί από το αρχείο S22iptables. Το ίδιο αρχείο µπορεί να χρησιµοποιηθεί και για την παραµετροποίηση της εφαρµογής, αλλά θα πρέπει κάθε φορά να γίνεται επανεκκίνηση για να ισχύουν οι νέες ρυθµίσεις. Ο κώδικας του script εκκίνησης S22iptables είναι ο παρακάτω: #!/bin/sh IPTABLES=`which iptables` echo "Enable NAT:" echo -n " IPv4 forwarding: " if echo 1 > /proc/sys/net/ipv4/ip_forward; then echo "done" else echo "failed" return 1 fi echo -n " iptables postrouting: " if [ ! -x "${IPTABLES}" ]; then echo "missing" exit 1 fi if ${IPTABLES} -t nat -A POSTROUTING -o eth0 -j MASQUERADE; then echo "done" else echo "failed" exit 1 fi echo -n " iptables incoming trafic: " if ${IPTABLES} -A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT; then echo "done" else echo "failed" exit 1 fi echo -n " iptables outgoung trafic: " if ${IPTABLES} -A FORWARD -i eth1 -o eth0 -j ACCEPT; then echo "done" else echo "failed" exit 1 fi exit 0 Ιστότοπος εργασίας: MyThesis.org 239 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Ουσιαστικά δίνει την δυνατότητα στο διαχειριστή του δικτύου να ρυθµίζει τους κανόνες φιλτραρίσµατος πακέτων (packet filtering ruleset) του Linux Kernel. Αυτό το επιτυγχάνει επικοινωνώντας µε το API του ενσωµατωµένου Firewall – Framework που υπάρχει στον Kernel (Netfilter). Το φιλτράρισµα των πακέτων αφορά τόσο την εισερχόµενη, όσο και την εξερχόµενη κίνηση. Όταν το NAT είναι ενεργοποιηµένο, η εφαρµογή iptables αναλαµβάνει να φιλτράρει και αυτά τα πακέτα. Το πακέτο λογισµικού iptables περιλαµβάνει υποστήριξη και για το IPv6 µέσω του προγράµµατος ip6tables. Στον Router NGW100 το Firewall iptables περιορίζει τις θύρες της διεπαφής δικτύου WAN, ενώ αφήνει ανοικτές όλες τις πόρτες στη διεπαφή LAN. Οι κανόνες του Firewall εµφανίζονται µε την εντολή: iptables -v -L --line-numbers 7.3.8 Εφαρµογές για DNS και DHCP server Ο Router NGW100 διαθέτει DNS server, DHCP server και DHCP client. Όλα αυτά επιτυγχάνονται µέσω των εφαρµογών dnsmasq και udhcp. Εφαρµογή dnsmasq Η εφαρµογή dnsmasq είναι ένας ελαφρύς και εύκολα παραµετροποιήσιµος DNS server και DHCP server. Είναι σχεδιασµένη για µικρά δίκτυα αλλά µπορεί να υποστηρίξει ταυτόχρονα µέχρι και 1000 συνδεδεµένους υπολογιστές. Τα σηµαντικότερα χαρακτηριστικά που διαθέτει είναι τα παρακάτω: Η παραµετροποίηση DNS των συστηµάτων που βρίσκονται πίσω από Firewall είναι απλή και δεν εξαρτάται από τους DNS server του ISP. Οι clients που προσπαθούν να διαβάσουν τις καταχωρήσεις DNS ενώ δεν υπάρχει σύνδεση στο Internet, δεν καθυστερούν περιµένοντας άσκοπα, αφού προκαλείται άµεσα timeout. Υποστηρίζει δυναµικές αλλά και στατικές αποδόσεις IP. Περιλαµβάνει τα πρωτόκολλα BOOTP και TFTP για συστήµατα που δεν διαθέτουν αποθηκευτικό µέσο και χρειάζεται να εκκινούν µέσω δικτύου. Αποθηκεύει προσωρινά (caching) σε ειδική περιοχή της µνήµης, τις διευθύνσεις Internet (A και AAAA records – wikipedia.org/wiki/List_of_DNS_record_types) καθώς και τις διευθύνσεις που είναι αντιστοιχισµένες µε ονόµατα (address to name mappings ή PTR records). Αυτό έχει ως αποτέλεσµα την µείωση των αιτηµάτων προς εξωτερικούς servers και την βελτίωση της απόδοσης. Μπορεί να ρυθµιστεί ώστε να λαµβάνει αυτόµατα τις διευθύνσεις των upstream name servers (wikipedia.org/wiki/Upstream_server) από τα αρχεία παραµετροποίησης της εφαρµογής ppp ή από dhcp. Αλλά και να ενηµερώνεται αυτόµατα κάθε φορά που γίνονται αλλαγές σε αυτά τα αρχεία. Υποστηρίζει IPv6 και προώθηση πακέτων IPv4 προς IPv6 και το αντίστροφο. Επικοινωνία: [email protected] 240 Οι εφαρµογές του Router NGW100 – Κεφάλαιο 7 Υποστηρίζει ιδιωτική λειτουργία DNS στέλνοντας αιτήµατα στους upstream servers τα οποία αφορούν µόνο συγκεκριµένα domains (wikipedia.org/wiki/Domain_name). Τέλος, υποστηρίζει εγγραφές MX και SRV. Επίσης µπορεί να ρυθµιστεί ώστε να επιστρέφει µόνο εγγραφές MX για µεµονωµένα ή για όλα τα τοπικά συστήµατα. Στον Router NGW100 o DHCP server εκτελείται µόνο για τη θύρα LAN. Οι ρυθµίσεις βρίσκονται στο αρχείο /etc/dnsmasq.conf. Το script που τον εκκινεί είναι το S21dnsmasq και το εκτελέσιµο της εφαρµογής το /usr/sbin/dnsmasq. Ο πηγαίος κώδικας βρίσκεται στον κατάλογο /rousis/buildroot/build_avr32/dnsmasq-2.46/src. Εφαρµογή udhcpc Ο DHCP client udhcpc (wikipedia.org/wiki/Udhcpc), είναι ενσωµατωµένος στο BusyBox και εκτελείται µόνο για τη θύρα WAN. Αυτό συµβαίνει καθώς, όπως έχουµε αναφέρει και στο κεφάλαιο 5, ο Router NGW100 χρειάζεται κάποιο modem για να είναι σε θέση να δροµολογεί πακέτα στο Internet. Εποµένως θα πρέπει να λαµβάνει µια IP για τη θύρα WAN. Η διαδικασία αυτή γίνεται κατά την εκκίνηση. Αν δε βρεθεί DHCP server τότε αυτόµατα αποδίδεται η προκαθορισµένη IP. Στον κατάλογο networking/udhcpc βρίσκεται ο πηγαίος κώδικας της εφαρµογής. Ο udhcp έχει τη δυνατότητα να ελέγχει µέσω του πρωτοκόλλου ARP αν η IP που του προσφέρεται είναι ίδια µε κάποια άλλη στο δίκτυο. Αν ισχύει κάτι τέτοιο, τότε η διεύθυνση αυτή απορρίπτεται και η διαδικασία αρχίζει και πάλι από την αρχή. Αν δεν βρεθεί κανένας DHCP server στην πρώτη αναζήτηση (Sending discover…), τότε γίνονται άλλες δύο προσπάθειες και αν δεν υπάρξει απόκριση αποδίδεται αυτόµατα µια διεύθυνση. Στον Router NGW100 η εφαρµογή udhcpc εκκινείται από το script εκκίνησης S20network, µέσω της εφαρµογής ifup, που είδαµε και νωρίτερα σε αντίστοιχη παράγραφο. 7.3.9 Εφαρµογή telnetd Ο Router NGW100 µπορεί να παραµετροποιηθεί όπως γνωρίζουµε και µέσω δικτύου. Για το σκοπό αυτό απαιτείται εκτός από τη φυσική σύνδεση, και µια εφαρµογή Telnet server η οποία θα εκτελείται σε αυτόν και θα εξυπηρετεί αντίστοιχα αιτήµατα σύνδεσης. Στην περίπτωσή µας η εφαρµογή αυτή βρίσκεται ενσωµατωµένη στο BusyBox και το όνοµά της είναι telnetd. Η εφαρµογή telnetd (server – client) εκκινείται από το script S40telnetd του οποίου ο κώδικας είναι ο παρακάτω: #!/bin/sh TELNETD=/usr/sbin/telnetd echo -n "Starting telnetd: " if [ ! -x "${TELNETD}" ]; then echo "missing" exit 1 fi if ${TELNETD} -l /bin/sh; then Ιστότοπος εργασίας: MyThesis.org 241 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) echo "done" else echo "failed" exit 1 fi Το Telnet δεν παρέχει κάποιο αλγόριθµο κρυπτογράφησης και εποµένως έχει κριθεί µη ασφαλές για δίκτυα µε κρίσιµα δεδοµένα. Επίσης δεν είναι ασφαλές και για αποµακρυσµένη διαχείριση µέσω Internet. Σε τέτοιες περιπτώσεις είναι προτιµότερο να χρησιµοποιηθεί κάποια εφαρµογή που έχει αναπτυχθεί για το πρωτόκολλο SSH. Μια τέτοια εφαρµογή θα εξετάσουµε και στην αµέσως επόµενη παράγραφο. Σε έναν δροµολογητή της αγοράς συνήθως το Telnet είναι απενεργοποιηµένο. Παρ’ όλα σε περιβάλλοντα ανάπτυξης το Telnet προτιµάται γιατί δεν απαιτεί σύνδεση µε όνοµα χρήστη και κωδικό. 7.3.10 Εφαρµογή dropbear Η εφαρµογή dropbear (matt.ucc.asn.au/dropbear/dropbear.html) είναι ένας µικρός SSH client – server ο οποίος εκτελείται σε γραµµή εντολής και στον Router NGW100 προσφέρει ασφαλή διαχείριση µέσω δικτύου. Επίσης υποστηρίζει τον ασύµµετρο αλγόριθµο κρυπτογράφησης RSA. Αυτό σηµαίνει ότι υπάρχει δηµόσιο και ιδιωτικό κλειδί. Εποµένως κατά την εκκίνηση του server θα πρέπει να δηµιουργηθούν τα κλειδιά RSA και DSS. Η εκκίνηση του dropbear γίνεται από το script S50dropbear: #!/bin/sh # # Starts dropbear sshd. # # Make sure the dropbearkey progam exists [ -f /usr/bin/dropbearkey ] || exit 0 start() { echo -n "Starting dropbear sshd: " # Make sure dropbear directory exists if [ ! -d /etc/dropbear ] ; then mkdir -p /etc/dropbear fi # Check for the Dropbear RSA key if [ ! -f /etc/dropbear/dropbear_rsa_host_key ] ; then echo -n "generating rsa key... " /usr/bin/dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key > /dev/null 2>&1 fi # Check for the Dropbear DSS key if [ ! -f /etc/dropbear/dropbear_dss_host_key ] ; then echo -n "generating dsa key... " /usr/bin/dropbearkey -t dss -f /etc/dropbear/dropbear_dss_host_key > /dev/null 2>&1 fi umask 077 start-stop-daemon -S -q -p /var/run/dropbear.pid --exec /usr/sbin/dropbear echo "OK" } stop() { echo -n "Stopping dropbear sshd: " start-stop-daemon -K -q -p /var/run/dropbear.pid echo "OK" } Επικοινωνία: [email protected] 242 Οι εφαρµογές του Router NGW100 – Κεφάλαιο 7 restart() { stop start } case "$1" in start) start ;; stop) stop ;; restart|reload) restart ;; *) echo $"Usage: $0 {start|stop|restart}" exit 1 esac exit $? Τα κλειδιά RSA (dropbear_rsa_host_key) και DSS (dropbear_dss_host_key) που θα δηµιουργηθούν θα βρίσκονται στον κατάλογο /etc/dropbear του συστήµατος αρχείων του Router NGW100 προκειµένου να χρησιµοποιούνται όταν εγκαθιδρύεται µία σύνδεση SSH. 7.3.11 Εφαρµογή ProFTPD Στα πλαίσια ανάπτυξης της εργασίας αυτής, χρησιµοποιήθηκε αρκετά συχνά ο FTP client Filezilla. Η εφαρµογή αυτή ήταν εγκατεστηµένη στην εικονική µηχανή Ubuntu, του συστήµατος host. ∆ιευκόλυνε σε µεγάλο βαθµό την µεταφορά αρχείων από και προς τον Router NGW100 αλλά ταυτόχρονα χρησίµευε και για ταχύτατη επισκόπηση των περιεχοµένων του συστήµατος αρχείων. Φυσικά, για να είµαστε σε θέση να το κάνουµε αυτό έπρεπε και από το άλλο άκρο της επικοινωνίας να εκτελείται µια εφαρµογή FTP server. Το όνοµα της εφαρµογής αυτής είναι ProFTPD (wikipedia.org/wiki/ProFTPD). Η εκκίνησή της γίνεται από το script S50proftpd. Ο κώδικάς του είναι ο εξής: #!/bin/sh DAEMON=/usr/sbin/proftpd trap "" 1 trap "" 15 test -f $DAEMON || exit 0 [ ! -d /var/run/proftpd ] && mkdir /var/run/proftpd [ ! -f /var/log/wtmp ] && touch /var/log/wtmp start() { echo -n "Starting ProFTPD: " $DAEMON if [ $? != 0 ]; then echo "FAILED" exit 1 else echo "done" fi } stop() { echo -n "Stopping ProFTPD: " Ιστότοπος εργασίας: MyThesis.org 243 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) killall proftpd echo "done" } case "$1" in start) start ;; stop) stop ;; restart) stop start ;; *) echo "Usage: /etc/init.d/S50proftpd {start|stop|restart}" exit 1 ;; esac exit 0 Οι ρυθµίσεις του ProFTPD server στον Router NGW100 βρίσκονται στο αρχείο /etc/proftpd.conf. Ο πηγαίος κώδικας της εφαρµογής βρίσκεται στον κατάλογο /rousis/buildroot/build_avr32/proftpd-1.3.1. του συστήµατος host. 7.3.12 Εφαρµογή ethtool Η εφαρµογή ethtool δίνει τη δυνατότητα διαχείρισης των διεπαφών δικτύου Ethernet, eth0 και eth1. Οι σηµαντικότεροι λόγοι για να την χρησιµοποιήσουµε είναι οι εξής: Αναγνώριση και διαγνωστικός έλεγχος των eth0 και eth1 Στατιστικά Έλεγχος ταχύτητας µετάδοσης, αµφίδροµη µετάδοση duplex, αυτόµατη διαπραγµάτευση (auto-negotiation) και έλεγχος ροής Έλεγχος σφαλµάτων (cheksum) σε χαµηλό επίπεδο Έλεγχος µήκους DMA δακτυλίων και τροποποίηση διακοπών ∆ιαχείριση ουράς λήψης πλαισίων Αναβάθµιση του firmware του Router NGW100 Για να είναι δυνατά όλα τα παραπάνω φυσικά, απαιτείται η ύπαρξη του οδηγού MACB τον οποίο αναλύσαµε ενδελεχώς και σε αντίστοιχο παράρτηµα. Ο πηγαίος κώδικας της εφαρµογής ethtool βρίσκεται στον κατάλογο της εικονικής Ubuntu, /rousis/buildroot/build_avr32/ethtool-6. Εκτελώντας την εντολή ethtool eth1, µπορούµε να δούµε τις λεπτοµέρειες της θύρας LAN του Router NGW100: Επικοινωνία: [email protected] 244 Οι εφαρµογές του Router NGW100 – Κεφάλαιο 7 Εικόνα 140. Εκτέλεση της εντολής ethtool για τη διεπαφή eth1 7.3.13 Εφαρµογή portmap Η εφαρµογή portmap στον Router NGW100 χρησιµεύει για να διατηρεί µε τις θύρες που χρησιµοποιούν οι εφαρµογές (κυρίως daemons) που προσφέρουν υπηρεσίες RPC (Remote Procedure Call - wikipedia.org/wiki/Remote_procedure_call). Η εφαρµογή εκκινείται µετά τον Linux Kernel. Ο πηγαίος κώδικας της εφαρµογής βρίσκεται στον κατάλογο /rousis/buildroot/build_avr32/portmap_5beta. 7.3.14 Εφαρµογή bridge Ο Router NGW100 µπορεί να λειτουργήσει και ως γέφυρα µεταξύ των διεπαφών δικτύου eth0 και eth1. Αυτό επιτυγχάνεται µέσω της εφαρµογής bridge. Στον κατάλογο /rousis/buildroot/build_avr32/bridge-1.4/doc υπάρχει αρκετά καλή τεκµηρίωση της εφαρµογής. Εποµένως εδώ θα παρατεθεί µόνο η βοήθεια που εµφανίζεται στη γραµµή εντολών όταν εκτελέσουµε την εντολή brctl (bridge control) µε την παράµετρο --help: Εικόνα 141. Βοήθεια για την εντολή brctl της εφαρµογής bridge Ιστότοπος εργασίας: MyThesis.org 245 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Ένα σηµαντικό χαρακτηριστικό της εφαρµογής bridge είναι ότι επιτρέπει να εκτελείται και τείχος προστασίας κατά τη λειτουργία της. Κάτι όµως που όπως αναφέρει η τεκµηρίωση είναι ταυτόχρονα χρήσιµο αλλά και εντελώς αντίθετο µε τη λογική του µοντέλου OSI. 7.3.15 Εφαρµογές ενηµέρωσης – εµφάνισης ώρας και ηµεροµηνίας Η ρύθµιση και η ενηµέρωση της ώρας και της ηµεροµηνίας του Router NGW100 επιτυγχάνεται µέσω του πρωτοκόλλου NTP (Network Time Protocol) και των εφαρµογών ntpd και ntpdate. Για την εκκίνησή τους εκτελούνται δύο scripts. To script S43ntpdate: #!/bin/sh NTPDATE=/usr/bin/ntpdate if [ -f /etc/default/ntpdate ]; then . /etc/default/ntpdate else echo "WARNING: missing /etc/default/ntpdate" exit 1 fi echo -n "Running ntpdate: " if [ ! -x ${NTPDATE} ]; then echo "missing" echo -n " WARNING: could not syncronize clock, " echo "edit NTPSERVERS in /etc/default/ntpdate." exit 1 fi if ${NTPDATE} $NTPOPTIONS $NTPSERVERS; then echo "done" else echo "failed" echo -n " WARNING: could not syncronize clock, " echo "edit NTPSERVERS in /etc/default/ntpdate." exit 1 fi Και το script S49ntp: PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DESC="network time protocol daemon" NAME=ntpd DAEMON=/usr/sbin/$NAME NTPDATE_BIN=/usr/bin/ntpdate # Gracefully exit if the package has been removed. test -x $DAEMON || exit 0 # Read config file if it is present. if [ -r /etc/default/$NAME ] then . /etc/default/$NAME fi case "$1" in start) if test x$NTPDATE = xyes ; then echo -n "Getting initial time via ntp" $NTPDATE_BIN $NTPDATE_OPTS $NTPSERVERS > /dev/null 2>&1 echo "." Επικοινωνία: [email protected] 246 Οι εφαρµογές του Router NGW100 – Κεφάλαιο 7 fi if test x$NTPD = xyes ; then echo -n "Starting $DESC: $NAME" start-stop-daemon -S -q -x $DAEMON echo "." fi ;; stop) echo -n "Stopping $DESC: $NAME" start-stop-daemon -K -q -n $NAME echo "." ;; reload|force-reload) echo -n "Reloading $DESC configuration..." start-stop-daemon -K -q -n $NAME -s 1 echo "done." ;; restart) echo "Restarting $DESC: $NAME" $0 stop sleep 1 $0 start ;; *) echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 exit 1 ;; esac exit 0 Ο πηγαίος κώδικας των εφαρµογών ntp και ntpdate βρίσκεται στον κατάλογο /rousis/buildroot/build_avr32/ntp-4.2.4p5 του συστήµατος host. Οι ρυθµίσεις βρίσκονται στο αρχείο /etc/ntp.conf του συστήµατος αρχείων του Router NGW100. Ιστότοπος εργασίας: MyThesis.org 247 ΠΑΡΑΡΤΗΜΑΤΑ ΠΑΡΑΡΤΗΜΑ A Το σχηµατικό του Router NGW100 Εισαγωγή Ένα από τα σηµαντικότερα στάδια ανάπτυξης ενός ενσωµατωµένου συστήµατος είναι η δηµιουργία του σχηµατικού διαγράµµατος (schematic diagram), ή απλά του σχηµατικού. Κατά τη διάρκεια υλοποίησης της παρούσας εργασίας υπήρξε η ευκαιρία να συγκεντρωθούν αρκετές σχετικές πληροφορίες. Αυτό έγινε τόσο µέσω του ∆ιαδικτύου, όσο και µε προσωπική προσπάθεια. Οι πληροφορίες αυτές είναι προαιρετικές για την κατανόηση του παραρτήµατος και µπορούν να παρακαµφθούν. Παραθέτονται εδώ κυρίως για εγκυκλοπαιδικούς λόγους. Η ανάπτυξη του σχηµατικού Το σχηµατικό πρέπει να σχεδιαστεί έτσι ώστε να δίνει κάθε λεπτοµέρεια µε ευκρίνεια, έχοντας πάντα υπ’ όψιν µας το σχέδιο PCB (Printed Circuit Board). Πριν αρχίσουµε να σχεδιάζουµε το PCB, πρέπει να έχουµε ένα πλήρες και ακριβές σχηµατικό διάγραµµα. Εάν δεν υπάρχει ένα ακριβές σχηµατικό, το PCB θα καταλήξει πιθανότατα να σχεδιαστεί λάθος. Ένα σχέδιο PCB είναι µια κατασκευασµένη έκδοση του σχηµατικού και είναι φυσικό να επηρεάζεται από αυτό. Αν το σχηµατικό µας είναι τακτοποιηµένο λογικά, και σαφώς σχεδιασµένο, τότε η δηµιουργία του σχεδίου PCB γίνεται ευκολότερη. Η ορθή πρακτική, θα έχει τα σήµατα να ρέουν από τις εισόδους στα αριστερά µε φορά προς τις εξόδους στα δεξιά. Αφού σχεδιαστούν σωστά τα σηµαντικά ηλεκτρικά και ηλεκτρονικά στοιχεία, το PCB που προκύπτει, είναι κατά πάσα πιθανότητα, σωστό. Οι σηµειώσεις στο σχηµατικό βοηθούν στην σωστή απεικόνιση (layout) του PCB. Ακόµα κι αν είµαστε εµείς που σχεδιάσαµε το κύκλωµα και τη σχηµατική αναπαράστασή του, οι σηµειώσεις όχι µόνο θα µας υπενθυµίζουν διάφορες λεπτοµέρειες, αλλά θα είναι χρήσιµες και για τους ανθρώπους που θα αναθεωρήσουν το σχέδιο. Κλείνοντας θα πρέπει να πούµε ότι το σχηµατικό ενός ενσωµατωµένου συστήµατος, ανάλογα µε την πολυπλοκότητα του, µπορεί να εκτείνεται σε περισσότερες της µίας, σελίδες. Σε αυτή την περίπτωση ένα ολοκληρωµένο µπορεί να βρίσκεται σε διαφορετική σελίδα µε κάποιο άλλο. Εποµένως η σύνδεσή τους δεν είναι δυνατό να φαίνεται άµεσα. Σε τέτοιες περιπτώσεις γίνεται χρήση της ονοµασίας της γραµµής που τα συνενώνει. Αυτό ισχύει και για το σχηµατικό του Router NGW100. ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Οι γραµµές του σχηµατικού, που ταξιδεύουν από µία σελίδα σε µια άλλη, έχουν στο ελεύθερο άκρο τους ένα διπλό βέλος. Τα βέλη που έχουν αριστερή διεύθυνση αφορούν τις γραµµές που πρέπει να συνδεθούν σε κάποιο άλλο στοιχείο. Τα βέλη που έχουν δεξιά διεύθυνση αφορούν τις γραµµές που προέρχονται από άλλα ολοκληρωµένα και συνδέονται εκεί που καταλήγουν. Αυτό γίνεται ευκολότερα αντιληπτό και µε µια πιο προσεκτική παρατήρηση. Το σχηµατικό του Router NGW100 Το σχηµατικό του Router NGW100 αποτελείται από αρκετές σελίδες. Λόγω του µεγέθους του έχει γίνει µια προσπάθεια, κάθε στοιχείο ή κάθε οµοειδής οµάδα στοιχείων, να καταλαµβάνει ξεχωριστή σελίδα. Αυτό έχει ως αποτέλεσµα να µειώνεται η πολυπλοκότητα και να διευκολύνεται η ανάγνωση. Οι σελίδες αυτές είναι οι εξής: AP7000_1 Το πρώτο τµήµα ακροδεκτών του µικροελεγκτή AP7000 AP7000_2 Το δεύτερο τµήµα ακροδεκτών του µικροελεγκτή AP7000 ETHERNET PORT 0 LAN Ethernet PHY controller ETHERNET PORT 1 WAN Ethernet PHY controller FLASH Μνήµες Flash, παράλληλη (NOR) και σειριακή (NAND) SDRAM Η µνήµη SDRAM SD/MMC Η θύρα (slot) για κάρτες µνήµης SD (SDCARD reader) RS-232 / USB Θύρες RS-232 και USB BOARD CONTROL Ο ελεγκτής της πλακέτας GPIO Τα σήµατα GPIO JTAG_NEXUS Η διεπαφές υλικού JTAG και NEXUS POWER Το κύκλωµα τροφοδοσίας NON Electrical Μηχανικές λεπτοµέρειες οπών Επικοινωνία: [email protected] 250 Σχηµατικά του Router NGW100 – Παράρτηµα Α Ιστότοπος εργασίας: MyThesis.org 251 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Επικοινωνία: [email protected] 252 Σχηµατικά του Router NGW100 – Παράρτηµα Α Ιστότοπος εργασίας: MyThesis.org 253 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Επικοινωνία: [email protected] 254 Σχηµατικά του Router NGW100 – Παράρτηµα Α Ιστότοπος εργασίας: MyThesis.org 255 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Επικοινωνία: [email protected] 256 Σχηµατικά του Router NGW100 – Παράρτηµα Α Ιστότοπος εργασίας: MyThesis.org 257 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Επικοινωνία: [email protected] 258 Σχηµατικά του Router NGW100 – Παράρτηµα Α Ιστότοπος εργασίας: MyThesis.org 259 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Επικοινωνία: [email protected] 260 Σχηµατικά του Router NGW100 – Παράρτηµα Α Ιστότοπος εργασίας: MyThesis.org 261 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Επικοινωνία: [email protected] 262 Σχηµατικά του Router NGW100 – Παράρτηµα Α Ιστότοπος εργασίας: MyThesis.org 263 ΠΑΡΑΡΤΗΜΑ Β Τεκµηρίωση ανάπτυξης του οδηγού δικτύου MACB Εισαγωγή Επειδή δεν είναι δυνατό να παρουσιάσουµε γραµµή – γραµµή την λειτουργία του driver MACB θα περιγράψουµε βήµα – βήµα τα στάδια ανάπτυξής του, και όπου είναι χρήσιµο θα παραθέτουµε τµήµατα του πηγαίου κώδικα που βρίσκεται στα αρχεία macb.c και macb.h. Η ανάλυση θα γίνει µε τέτοιο τρόπο ώστε να µπορεί να εφαρµοστεί και σε άλλες περιπτώσεις ανάπτυξης οδηγών δικτύου. Τα κυριότερα βήµατα που ακολουθήθηκαν για την ανάπτυξη του οδηγού δικτύου MACB, και τα οποία µπορούν να εφαρµοστούν για οποιαδήποτε άλλη παρόµοια περίπτωση ανάπτυξης, είναι τα εξής Τεχνικά εγχειρίδια Λήψη αποφάσεων ανάπτυξης Το αρχείο κεφαλής drivers/net/macb.h Το αρχείο macb.c Καταχώρηση διεπαφής δικτύου ∆ηµιουργία συσκευής πλατφόρµας Χαρτογράφηση των καταχωρητών εισόδου – εξόδου Καταχώρηση IRQ Ρύθµιση και ενεργοποίηση ρολογιού Ανάκτηση της διεύθυνση υλικού MAC Πρόσβαση στο ολοκληρωµένο DP83848I (PHY) µέσω του διαύλου MDIO Συναρτήσεις πρόσβασης του διαύλου MDIO Σύνδεση και επίβλεψη κατάστασης στο ολοκληρωµένο DP83848I ∆έσµευση των DMA buffers, αρχικοποίηση και καθαρισµός Reset και αρχικοποίηση υλικού Εκκίνηση και τερµατισµός λειτουργιών Κλείδωµα οδηγού Αποστολή πακέτων Ολοκλήρωση της αποστολής Λήψη πακέτων Τεκµηρίωση ανάπτυξης του οδηγού δικτύου MACB – Παράρτηµα Β Τεχνικά εγχειρίδια Καθ’ όλη τη διάρκεια της ανάπτυξης, θα πρέπει να συµβουλευόµαστε συνέχεια το datasheet του µικροελεγκτή και των Ethernet controllers που θα χρησιµοποιηθούν καθώς και το σχηµατικό διάγραµµα της πλακέτας. Αυτό θα µας βοηθά να γνωρίζουµε τον τρόπο µε τον οποίο συνδέονται τα σήµατα µεταξύ τους αλλά και τις ιδιότητες του κάθε σήµατος. Βάσει αυτών θα είµαστε σε θέση να γράψουµε το αρχείο κεφαλής (mac.h) στο οποίο καθορίζονται (#define) οι λεπτοµέρειες του υλικού που θέλουµε να “οδηγήσουµε”. Λήψη αποφάσεων ανάπτυξης Ένα από τα πρώτα πράγµατα που πρέπει να αποφασίσουµε όταν αναπτύσσουµε έναν οδηγό δικτύου είναι αν θέλουµε να φορτώνεται στο σύστηµά µας ως module κατά την εκκίνηση ή αν θα είναι ενσωµατωµένος στον Kernel. Στην περίπτωση του οδηγού MACB ισχύει το πρώτο. Για την λειτουργία των modules έχουµε µιλήσει σε προηγούµενο κεφάλαιο. Εποµένως δεν χρειάζεται να γίνει κάποια επιπλέον ανάλυση εδώ. Το αρχείο κεφαλής drivers/net/macb.h Το αρχείο κεφαλής macb.h περιλαµβάνει πληροφορίες που θα χρειαστούν κατά την ανάπτυξη του οδηγού δικτύου. Για παράδειγµα, περιλαµβάνει οδηγίες #define για τον καθορισµό των λεπτοµερειών που αφορούν την αρχιτεκτονική του µικροελεγκτή AP7000 καθώς επίσης, και τη δοµή macb που αντιπροσωπεύει την διεπαφή του δικτύου που θέλουµε να υλοποιήσουµε και να καταχωρίσουµε στον Kernel έτσι ώστε να µπορεί ο Router NGW100 να επικοινωνεί µέσω των θυρών LAN (macb1) και WAN (macb0): struct macb { void __iomem *regs; unsigned int struct dma_desc void rx_tail; *rx_ring; *rx_buffers; unsigned int struct dma_desc struct ring_info tx_head, tx_tail; *tx_ring; *tx_skb; spinlock_t struct platform_device struct clk struct clk struct net_device struct napi_struct struct net_device_stats struct macb_stats lock; *pdev; *pclk; *hclk; *dev; napi; stats; hw_stats; dma_addr_t dma_addr_t dma_addr_t rx_ring_dma; tx_ring_dma; rx_buffers_dma; unsigned int rx_pending, tx_pending; struct mii_bus mii_bus; Ιστότοπος εργασίας: MyThesis.org 265 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) struct phy_device unsigned int unsigned int unsigned int *phy_dev; link; speed; duplex; }; Οι δοµές δεδοµένων που χρησιµοποιούνται από την macb ορίζονται µέσα σε διάφορα αρχεία του πηγαίου κώδικα C, του Linux Kernel: dma_desc ring_info platform_device clk net_device napi_struct net_device_stats macb_stats mii_bus phy_device drivers/net/macb.h drivers/net/s2io.h include/linux/platform_device.h include/linux/clk.h include/linux/netdevice.h include/linux/netdevice.h include/linux/netdevice.h drivers/net/macb.h include/linux/phy.h include/linux/phy.h Η ανάπτυξη της δοµής macb θα αναλυθεί παράλληλα µε την ανάλυση της ανάπτυξης του οδηγού δικτύου. Το αρχείο macb.c Σε αυτό το αρχείο περιέχεται η λογική και οι µηχανισµοί στους οποίους βασίζεται ο µικροελεγκτής AP7000 για να επικοινωνήσει µε τον Kernel και µε το ολοκληρωµένο DP83848I. προκειµένου ο Router NGW100 να είναι σε θέση να επικοινωνήσει µε το δίκτυο. Φυσικά ο κώδικας βασίζεται και σε έτοιµη υποδοµή που υπάρχει ήδη στον Kernel. Αυτό επιτυγχάνεται κυρίως µε την συµπερίληψη των αρχείων κεφαλής που πραγµατοποιείται στις πρώτες γραµµές του κώδικα και τις οποίες βλέπουµε πιο κάτω: #include #include #include #include #include #include #include #include #include #include #include #include <linux/clk.h> <linux/module.h> <linux/moduleparam.h> <linux/kernel.h> <linux/types.h> <linux/slab.h> <linux/init.h> <linux/netdevice.h> <linux/etherdevice.h> <linux/dma-mapping.h> <linux/platform_device.h> <linux/phy.h> #include <mach/board.h> #include <mach/cpu.h> #include "macb.h" Στις επόµενες παραγράφους γίνεται ανάλυση του οδηγού δικτύου που αναπτύχθηκε για τον Router NGW100. Η ανάλυση δεν γίνεται γραµµή – γραµµή αλλά τµήµα – τµήµα, για να βασίζεται στη φυσική ροή των διαδικασιών που πρέπει να ολοκληρωθούν ώστε το σύστηµά µας να είναι σε θέση να ανταλλάξει δεδοµένα µε το δίκτυο και τις εφαρµογές του. Επικοινωνία: [email protected] 266 Τεκµηρίωση ανάπτυξης του οδηγού δικτύου MACB – Παράρτηµα Β Καταχώρηση διεπαφής δικτύου Εφόσον όλα τα βασικά εργαλεία ανάπτυξης είναι στη θέση τους, µπορούµε πλέον να δηµιουργήσουµε τον πηγαίο κώδικα του οδηγού δικτύου, macb.c. Αρχικά, καταχωρούµε µια διεπαφή δικτύου (network interface registration). Αυτό πρέπει να γίνει µέσα στην συνάρτηση αρχικοποίησης του module. Επίσης όπως είδαµε και σε προηγούµενο κεφάλαιο όταν αναλύαµε τα modules στο Linux, είναι απαραίτητο να γράψουµε και µια συνάρτηση κατάργησης του module ώστε να απελευθερώνονται πόροι όταν δεν το χρειαζόµαστε πια. Αρχικά χρησιµοποιούµε την συνάρτηση alloc_etherdev η οποία ουσιαστικά θα δηµιουργήσει τη δοµή net_device. Η δοµή αυτή καθορίζεται στο αρχείο netdevice.h που βρίσκεται στον κατάλογο include/linux και είναι υπεύθυνη για τη δηµιουργία µιας νέας διεπαφής δικτύου: err = -ENOMEM; dev = alloc_etherdev(sizeof(*bp)); if (!dev) { dev_err(&pdev->dev, "etherdev alloc failed, aborting.\n"); goto err_out; } SET_NETDEV_DEV(dev, &pdev->dev); Στη συνέχεια δηµιουργούµε και αρχικοποιούµε ένα στιγµιότυπο της δοµής ethtool_ops µε όνοµα macb_ethtool_ops: static struct ethtool_ops macb_ethtool_ops = { .get_settings = macb_get_settings, .set_settings = macb_set_settings, .get_drvinfo = macb_get_drvinfo, .get_link = ethtool_op_get_link, }; Η καταχώρησή της διεπαφής που δηµιουργήσαµε επιτυγχάνεται µέσω της συνάρτησης register_netdev και η κατάργησή της µέσω της macb_remove και των περιεχόµενων σε αυτή, κλήσεων συναρτήσεων, unregister_netdev και free_netdev. Αν όλα έχουν γίνει σωστά, µετά τη φόρτωση του module θα δηµιουργηθεί µια νέα διεπαφή δικτύου µε το όνοµα eth0 (macb0) για το WAN. την οποία θα µπορούµε να δούµε στο σύστηµά µας άµεσα Στον Router NGW100 υπάρχει και eth1 (macb1) για το LAN. Πράγµα που σηµαίνει ότι υπάρχουν δύο διεπαφές δικτύου. Συγκεκριµένα, οι διεπαφές αυτές χαρακτηρίζονται ως WAN και LAN αντίστοιχα. Χρησιµεύουν για σύνδεση στο τοπικό δίκτυο και το Internet. Φυσικά αν αναπτύσσαµε ένα Router όπως αυτά που κυκλοφορούν στην αγορά, θα έπρεπε να δηµιουργήσουµε και τα eth2, eth3 (αν ο µικροελεγκτής το υποστηρίζει) προκειµένου να ενσωµατώσουµε µε αυτό τον τρόπο στον Router µας, ένα switch 4 θυρών. Στη συνάρτηση κατάργησης του module που αναφέραµε πιο πάνω, προσθέτουµε τις συναρτήσεις unregister_netdev και free_netdev, ούτως ώστε όταν το module καταργηθεί να συµβεί το ίδιο και στη διεπαφή δικτύου που δηµιουργήθηκε από αυτό. Ιστότοπος εργασίας: MyThesis.org 267 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) ∆ηµιουργία συσκευής πλατφόρµας Αν θέλουµε ο οδηγός που αναπτύσσουµε να µπορεί να ενσωµατωθεί στο δέντρο του πηγαίου κώδικα (source tree) του Kernel ως οδηγός πλατφόρµας, θα πρέπει να προσθέσουµε επιπλέον κώδικα δηµιουργίας οδηγού πλατφόρµας και ενεργοποίησης της αντίστοιχης συσκευής πλατφόρµας. Αυτό γίνεται ούτως ώστε κατά την µεταγλώττιση του Kernel ο οδηγός να µπορεί να ενσωµατωθεί στατικά σε αυτόν. Για να το πετύχουµε αυτό θα πρέπει ο οδηγός µας να καταχωρηθεί ως συσκευή πλατφόρµας (platform device). Αυτό γίνεται ως εξής: Καθορίζουµε µια δοµή platform_driver µε όνοµα macb_driver: static struct platform_driver macb_driver = { .remove = __exit_p(macb_remove), .suspend = macb_suspend, .resume = macb_resume, .driver = { .name = "macb", .owner = THIS_MODULE, }, }; ∆ηµιουργούµε τη συνάρτηση macb_init: static int __init macb_init(void) { return platform_driver_probe(&macb_driver, macb_probe); } Και την συνάρτηση macb_exit: static void __exit macb_exit(void) { platform_driver_unregister(&macb_driver); } Εννοείται βέβαια ότι η συνάρτηση platform_driver_probe καθώς και η συνάρτηση platform_driver_unregister, βρίσκονται ήδη υλοποιηµένες στον Kernel προκειµένου να αρχικοποιούν και να καταργούν αντίστοιχα, κάποιον οδηγό πλατφόρµας που θα τους ζητηθεί. Στην περίπτωσή µας αυτός είναι ο macb_driver (στιγµιότυπο της δοµής platform_driver). ∆ηµιουργούµε τη συνάρτηση macb_probe η οποία θα πραγµατοποιήσει µε τη σειρά της άλλες δύο ενέργειες. Η πρώτη θα είναι να συνδέσει την συσκευή δικτύου µε την συσκευή πλατφόρµας, µέσω της συνάρτησης SET_NETDEV_DEV: SET_NETDEV_DEV(dev, &pdev->dev); Όπου το όρισµα dev είναι ένα στιγµιότυπο της δοµής net_device και το pdev ένας δείκτης που δείχνει στη δοµή platform_device. και το οποίο περνιέται και σαν όρισµα στην συνάρτηση macb_probe. Και η δεύτερη θα είναι να αρχικοποιήσει τον δείκτη της συσκευής πλατφόρµας ώστε να δείχνει στη συσκευή δικτύου: Επικοινωνία: [email protected] 268 Τεκµηρίωση ανάπτυξης του οδηγού δικτύου MACB – Παράρτηµα Β platform_set_drvdata(pdev, dev); Υλοποιούµε την συνάρτηση macb_remove η οποία λειτουργεί παρόµοια µε την προηγούµενη συνάρτηση κατάργησης. Χαρτογράφηση των καταχωρητών εισόδου – εξόδου Η χαρτογράφηση των καταχωρητών εισόδου – εξόδου την µονάδας δικτύωσης του µικροελεγκτή, όπως είναι φυσικό υλοποιείται στη µνήµη του συστήµατος. Εποµένως πρέπει να δηµιουργηθεί µια περιοχή στην µνήµη όπου θα αποθηκεύουµε από ‘δω και στο εξής τέτοιου είδους πληροφορίες. Αυτό µπορεί να γίνει δηµιουργώντας µια δοµή η οποία προς το παρόν θα περιέχει µόνο ένα δείκτη προς τους καταχωρητές εισόδου – εξόδου: struct macb { void __iomem *regs; ... }; Αφού δηµιουργηθεί η δοµή macb µπορούµε να πραγµατοποιήσουµε την χαρτογράφηση µε τη βοήθεια της συνάρτησης ioremap. Φυσικά χρειάζεται να κάνουµε µια προσθήκη στη συνάρτηση macb_probe για να γνωρίζει το µέγεθος της δοµής macb: dev = alloc_etherdev(sizeof(*bp)); Καταχώρηση IRQ Η καταχώρηση IRQ (el.wikipedia.org/wiki/Αίτηση_διακοπής) είναι παραπλήσια µε την χαρτογράφηση των καταχωρητών εισόδου – εξόδου. Γίνεται από την συνάρτηση macb_probe µέσω της κλήσης των συναρτήσεων: platform_get_irq – Ανακτά τον αριθµό διακοπής IRQ του Ethernet controller. Πρέπει να αποθηκεύεται στο πεδίο irq της δοµής net_device. Για να το κάνουµε όµως αυτό θα πρέπει να χρησιµοποιήσουµε το στιγµιότυπο dev της δοµής net_device που έχουµε δηµιουργήσει δυναµικά για τον οδηγό µας: dev -> irq = platform_get_irq(pdev, 0); request_irq – Προκειµένου να ολοκληρωθεί η διαδικασία καταχώρησης αριθµού IRQ απαιτείται η δηµιουργία ενός χειριστή διακοπών (interrupt handler). err = request_irq(dev->irq, macb_interrupt, IRQF_SAMPLE_RANDOM, dev->name, dev); Φυσικά όπως και στις περισσότερες περιπτώσεις δέσµευσης πόρων, είναι καλό να αποδεσµεύουµε ότι δε χρειάζεται πια. Αυτό στην περίπτωση της κατάργησης IRQ επιτυγχάνεται µέσω της συνάρτησης free_irq η οποία θα πρέπει να βρίσκεται µέσα στη συνάρτηση macb_remove: free_irq(dev->irq, dev); Ιστότοπος εργασίας: MyThesis.org 269 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Ρύθµιση και ενεργοποίηση ρολογιού Για την ρύθµιση του ρολογιού θα χρειαστούµε πρώτα ορισµένες διευθύνσεις και τιµές καταχωρητών του µικροελεγκτή AP7000. Όλα αυτά τα στοιχεία υπάρχουν στο αντίστοιχο datasheet (doc32003.pdf) και πιο συγκεκριµένα στην παράγραφο 31.7.2 (Network Configuration Register). Έτσι κατά την ανάπτυξη του αρχείο κεφαλής macb.h, θα πρέπει να προσθέσουµε την εξής οδηγία: #define MACB_NCFGR 0x0004 Επίσης προσθέτουµε τις τιµές: /* Constants for CLK */ #define MACB_CLK_DIV8 #define MACB_CLK_DIV16 #define MACB_CLK_DIV32 #define MACB_CLK_DIV64 0 1 2 3 Ο διαιρέτης του ρολογιού (clock divider ή MACB_CLK_DIV) καθορίζεται στο δέκατο και ενδέκατο bit του καταχωρητή NCFGR. Εποµένως χρειαζόµαστε άλλη µια οδηγία για το λόγο αυτό: #define MACB_CLK_OFFSET 10 Σε αυτό το σηµείο, και µέσω της συνάρτησης macb_probe, µπορούµε πλέον να χρησιµοποιήσουµε τη διεπαφή ρολογιού (clock API) του Kernel η οποία περιλαµβάνει τις συναρτήσεις clk_get, clk_put, clk_enable, clk_disable και clk_get_rate. Οι χρονισµοί του µικροελεγκτή AP7000 βρίσκονται στο αρχείο arch\avr32\mach-at32ap. Ο δείκτης που επιστρέφεται από την συνάρτηση clk_get θα αποθηκευτεί στη συνέχεια στη δοµή macb µέσω του δείκτη bp: Αφού ενεργοποιηθεί το ρολόι, απλά πρέπει να ρυθµίσουµε τον διαιρέτη του Ethernet controller και πάλι µε βάση το datasheet του µικροελεγκτή: bp->pclk = clk_get(&pdev->dev, "pclk"); if (IS_ERR(bp->pclk)) { dev_err(&pdev->dev, "failed to get pclk\n"); goto err_out_free_dev; } bp->hclk = clk_get(&pdev->dev, "hclk"); if (IS_ERR(bp->hclk)) { dev_err(&pdev->dev, "failed to get hclk\n"); goto err_out_put_pclk; } clk_enable(bp->pclk); clk_enable(bp->hclk); Και πάλι στη συνάρτηση macb_remove θα πρέπει να προσθέσουµε κώδικα που να καταργεί το ρολόι που χρησιµοποιήσαµε: clk_disable(bp->pclk); clk_put(bp->pclk) Επικοινωνία: [email protected] 270 Τεκµηρίωση ανάπτυξης του οδηγού δικτύου MACB – Παράρτηµα Β Ανάκτηση της διεύθυνση υλικού MAC Το επόµενο βήµα αρχικοποίησης που είναι και το πρώτο το οποίο αφορά άµεσα τη δυνατότητα δικτύωσης, είναι να διαβάσουµε τη διεύθυνση MAC του υλικού και να ενηµερώσουµε για την ύπαρξή της, την στοίβα TCP/IP. Σύµφωνα µε το datasheet η διεύθυνση MAC µπορεί να διαβαστεί από δύο καταχωρητές: #define MACB_SA1B #define MACB_SA1T 0x0098 0x009c Ο πρώτος καταχωρητής περιέχει τα 4 λιγότερο κατώτερα bytes και τα 2 ανώτερα, δηµιουργώντας έτσι τα 6 bytes που είναι απαραίτητα για την διεύθυνση MAC. Για να µπορέσουµε να διαβάσουµε τη διεύθυνση MAC από τους καταχωρητές και στη συνέχεια να την περάσουµε στη στοίβα TCP/IP, απαιτείται η δηµιουργία µιας συνάρτησης η οποία θα εκτελεί τα εξής: ∆ιαβάζει τη διεύθυνση MAC µέσω της συνάρτησης __raw_readl (ορίζεται στο αρχείο macb.h). Αρχικοποιεί έναν πίνακα των 6 bytes µε τη MAC Ελέγχει αν η MAC είναι έγκυρη, µέσω της συνάρτησης is_valid_ether_addr που παρέχεται από τον Kernel. Αν είναι έγκυρη την αντιγράφει στο πεδίο dev_addr της δοµής net_device. Αν δεν είναι, δηµιουργεί µια τυχαία διεύθυνση µέσω της συνάρτησης eth_hw_addr_random. η οποία παρέχεται επίσης από τον Kernel. Όλα τα παραπάνω εκτελούνται από την συνάρτηση macb_get_hwaddr: static void __init macb_get_hwaddr(struct macb *bp) { u32 bottom; u16 top; u8 addr[6]; bottom = macb_readl(bp, SA1B); top = macb_readl(bp, SA1T); addr[0] addr[1] addr[2] addr[3] addr[4] addr[5] = = = = = = bottom & 0xff; (bottom >> 8) & 0xff; (bottom >> 16) & 0xff; (bottom >> 24) & 0xff; top & 0xff; (top >> 8) & 0xff; if (is_valid_ether_addr(addr)) { memcpy(bp->dev->dev_addr, addr, sizeof(addr)); } else { dev_info(&bp->pdev->dev, "invalid hw address, using random\n"); random_ether_addr(bp->dev->dev_addr); } } Στη συνέχεια, µέσα στην συνάρτηση macb_probe καλούµε την συνάρτηση ανάγνωσης διεύθυνσης MAC: macb_get_hwaddr(bp); Ιστότοπος εργασίας: MyThesis.org 271 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Αν όλα πάνε καλά το αποτέλεσµα θα αποθηκευτεί στο πεδίο dev_addr της δοµής net_device. Πρόσβαση στο ολοκληρωµένο DP83848I (PHY) µέσω του διαύλου MDIO Σε αυτό το στάδιο θέλουµε να επικοινωνήσει ο Ethernet controller του µικροελεγκτή AP7000 µε τον Ethernet controller του ολοκληρωµένου DP83848I. Το ολοκληρωµένο αυτό περιγράφεται αναλυτικότερα στο κεφάλαιο που αναλύονται τα ηλεκτρικά και ηλεκτρονικά χαρακτηριστικά του Router NGW100. Η επικοινωνία αυτή µπορεί να γίνει µέσω του διαύλου MDIO (Management Data Input – Output) ο οποίος υποστηρίζεται και από ειδικό framework που υπάρχει υλοποιηµένο στον Kernel. Η πρώτη ενέργεια που πρέπει να γίνει λοιπόν είναι η αρχικοποίηση του MDIO. Αυτό επιτυγχάνεται µε την συνάρτηση macb_mii_init. Η συνάρτηση αυτή εκτελεί τις παρακάτω λειτουργίες: ∆εσµεύει µνήµη για τη δοµή mii_bus µέσω της συνάρτησης mdiobus_alloc Αρχικοποιεί όλα τα µέλη της δοµής mii_bus: bp->mii_bus.name = "MACB_mii_bus"; bp->mii_bus.read = &macb_mdio_read; bp->mii_bus.write = &macb_mdio_write; bp->mii_bus.reset = &macb_mdio_reset; snprintf(bp->mii_bus.id, MII_BUS_ID_SIZE, "%x", bp->pdev->id); bp->mii_bus.priv = bp; bp->mii_bus.dev = &bp->dev->dev; pdata = bp->pdev->dev.platform_data; if (pdata) bp->mii_bus.phy_mask = pdata->phy_mask; bp->mii_bus.irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); if (!bp->mii_bus.irq) { err = -ENOMEM; goto err_out; } for (i = 0; i < PHY_MAX_ADDR; i++) bp->mii_bus.irq[i] = PHY_POLL; platform_set_drvdata(bp->dev, &bp->mii_bus); Το πεδίο read είναι ένας δείκτης προς τη συνάρτηση macb_mdio_read η οποία χρησιµοποιείται για την επικοινωνία µέσω του διαύλου MDIO. Κάτι παρόµοιο ισχύει και για το πεδίο write, µε τη διαφορά ότι η συνάρτηση macb_mdio_write στην οποία δείχνει, δεν γράφει στον δίαυλο MDIO. Η συνάρτηση snprintf αρχικοποιεί το πεδίο id. Επίσης, ο δείκτης priv δείχνει στη δοµή macb προκειµένου να µπορούµε να έχουµε πρόσβαση στις read και write. Τέλος, το πεδίο irq της δοµής mii_bus θα πρέπει να αρχικοποιηθεί µε τέτοιο τρόπο ώστε να ενηµερώνει ολόκληρη την υποδοµή MDIO του Kernel ότι δεν θα χρησιµοποιούνται διακοπές µεταξύ του ολοκληρωµένου DP83848I και του Ethernet controller. Καταχωρεί την mii_bus µέσω της συνάρτησης mdiobus_register. Αν εκτελεστεί µε επιτυχία, ο δίαυλος MDIO θα ζητήσει το id του ολοκληρωµένου DP83848I και θα εντοπίσει τον κατάλληλο οδηγό. Επειδή στην περίπτωσή µας δεν υπάρχει, θα χρησι- Επικοινωνία: [email protected] 272 Τεκµηρίωση ανάπτυξης του οδηγού δικτύου MACB – Παράρτηµα Β µοποιήσει τελικά τον γενικό οδηγό drivers/net/phy/phy_device.c ο οποίος υποστηρίζει τις βασικές λειτουργίες (generic ή core driver). Συναρτήσεις πρόσβασης του διαύλου MDIO Αφού έχουµε καταφέρει να επικοινωνεί ο Ethernet controller του µικροελεγκτή AP7000 µε το ολοκληρωµένο DP83848I, πρέπει να υλοποιήσουµε τις συναρτήσεις πρόσβασης στο δίαυλο MDIO: static int macb_mdio_read(struct mii_bus *bus, int mii_id, int regnum) { struct macb *bp = bus->priv; int value; macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_SOF) | MACB_BF(RW, MACB_MAN_READ) | MACB_BF(PHYA, mii_id) | MACB_BF(REGA, regnum) | MACB_BF(CODE, MACB_MAN_CODE))); /* wait for end of transfer */ while (!MACB_BFEXT(IDLE, macb_readl(bp, NSR))) cpu_relax(); value = MACB_BFEXT(DATA, macb_readl(bp, MAN)); return value; } Οι εγγραφές και οι αναγνώσεις επιτυγχάνονται διαµέσου του καταχωρητή διαχείρισης φυσικού επιπέδου (PHY Maintenance Register) MACB_MAN: ο οποίος καθορίζεται και στο αρχείο κεφαλίδας macb.h: #define MACB_MAN 0x0034 Οι πληροφορίες κατάστασης του διαύλου MDIO παρέχονται από τον καταχωρητή κατάστασης δικτύου (Network Status Register) MACB_NSR: #define MACB_NSR 0x0008 Φυσικά στο αρχείο macb.h θα πρέπει να γραφτούν όλες οι οδηγίες που αφορούν τους αντίστοιχους καταχωρητές του AP7000 που χρησιµοποιούνται στις δυο συναρτήσεις που παραθέσαµε. Αφού όλα είναι στη θέση τους και µπορούµε πια να γράφουµε και να διαβάζουµε στον δίαυλο MDIO θα πρέπει να προχωρήσουµε στην κύρια αρχικοποίηση. Αυτό θα πρέπει να γίνει και πάλι µέσα από την συνάρτηση mac_probe του οδηγού µας. Αρχικά ενεργοποιούµε το ρολόι και καθορίζουµε το αν θα χρησιµοποιήσουµε σύνδεση RMII ή MII µε το ολοκληρωµένο DP83848I. Αυτό γίνεται µέσω του καταχωρητή MACB_USRIO. #define MACB_USRIO 0x00c0 Έπειτα καλούµε την συνάρτηση macb_mii_init την οποία αναφέραµε και νωρίτερα κατά την αρχικοποίηση του διαύλου MDIO. Φυσικά, κατά την εκτέλεση του driver η συνάρτηση αυτή εκτελείται µία φορά και φέρει εις πέρας όλες τις λειτουργίες που της έχουµε αναθέσει. Ιστότοπος εργασίας: MyThesis.org 273 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Η επιλογή για RMII ή MII γίνεται κυρίως βασιζόµενη στα δεδοµένα της πλατφόρµας του µικροελεγκτή που χρησιµοποιούµε κάθε φορά. Στην περίπτωσή µας είναι η AVR32. Τα δεδοµένα αυτά µεταφέρονται µε τη µορφή δοµής δεδοµένων στον οδηγό που αναπτύσσουµε. Η δοµή αυτή είναι η macb_platform_data και την τοποθετούµε µέσα στη συνάρτηση macb_probe: struct eth_platform_data *pdata; pdata = pdev->dev.platform_data; Σε αυτό το σηµείο θέτουµε το bit, CLKEN του καταχωρητή MACB_USRIO και το bit RMII αν το πεδίο is_rmii από τα δεδοµένα πλατφόρµας, είναι αληθές. Τέλος καλούµε τη συνάρτηση macb_mii_init. Σύνδεση και επίβλεψη κατάστασης στο ολοκληρωµένο DP83848I Εφόσον ο δίαυλος MDIO έχει πια αρχικοποιηθεί, είµαστε πλέον σε θέση να συνδεθούµε µε το ολοκληρωµένο DP83848I. Αυτό θα µας επιτρέψει να ενηµερωνόµαστε για κάθε αλλαγή που γίνεται. Οι αλλαγές συνήθως αφορούν την κατάσταση της γραµµής, δηλαδή αν είναι ενεργή ή αν δεν αποκρίνεται, αν η µετάδοση είναι ηµι-αµφίδροµη ή πλήρως αµφίδροµη, αν η ταχύτητα µετάδοσης είναι 100 ή 1000 Mbps κλπ. Για να πραγµατοποιήσουµε όλα αυτά θα πρέπει να εισάγουµε επιπλέον κώδικα στη συνάρτηση macb: struct phy_device unsigned int unsigned int unsigned int *phy_dev; link; speed; duplex; Με τον παραπάνω κώδικα έχουµε καταφέρει να περάσουµε ένα δείκτη στη δοµή phy_device που αναπαριστά το ολοκληρωµένο DP83848I το οποίο και θα αναζητήσουµε µέσω της συνάρτησης macb_mii_probe µε τον κώδικα: που ακολουθεί: /* find the first phy */ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { if (bp->mii_bus.phy_map[phy_addr]) { phydev = bp->mii_bus.phy_map[phy_addr]; break; } } if (!phydev) { printk (KERN_ERR "%s: no PHY found\n", dev->name); return -1; } Η δοµή phy_device ορίζεται στο αρχείο include/linux/phy.h. Συνδέουµε τον Ethernet controller του AP7000 µε το ολοκληρωµένο DP83848I: και περνάµε µια την κλήση macb_handle_link_change (callback) η οποία θα ενεργοποιείται όταν αλλάζει η κατάσταση της γραµµής: /* attach the mac to the phy */ if (pdata && pdata->is_rmii) { phydev = phy_connect(dev, phydev->dev.bus_id, Επικοινωνία: [email protected] 274 Τεκµηρίωση ανάπτυξης του οδηγού δικτύου MACB – Παράρτηµα Β &macb_handle_link_change, 0, PHY_INTERFACE_MODE_RMII); } else { phydev = phy_connect(dev, phydev->dev.bus_id, &macb_handle_link_change, 0, PHY_INTERFACE_MODE_MII); } Ενηµερώνουµε την δοµή phy_device µέσω του δείκτη phydev για τα διαθέσιµα χαρακτηριστικά και τις λειτουργίες του DP83848I και αρχικοποιούµε τα πεδία speed και duplex µε τις σωστές τιµές που αφορούν την περίπτωσή µας: /* mask with MAC supported features */ phydev->supported &= PHY_BASIC_FEATURES; phydev->advertising = phydev->supported; bp->link = 0; bp->speed = 0; bp->duplex = -1; bp->phy_dev = phydev; Σε αυτό το σηµείο η συνάρτηση macb_mii_probe έχει ολοκληρωθεί και την καλούµε µέσω της macb_mii_init: if (macb_mii_probe(bp->dev) != 0) { goto err_out_unregister_bus; } Το τελευταίο βήµα που πρέπει να κάνουµε για να επιτύχουµε την σύνδεση και την επίβλεψη µε το ολοκληρωµένο DP83848I , το οποίο φροντίζει για τη διαµόρφωση της σύνδεσης στις απαιτήσεις του πρωτοκόλλου Ethernet (802.3), είναι να υλοποιήσουµε τη συνάρτηση macb_handle_link_change. Η πρώτη περίπτωση που πρέπει να χειριστούµε µέσω του κώδικα της συνάρτησης αυτής είναι η ενηµέρωση του καταχωρητή NCFGR αν η γραµµή είναι ενεργή. Για να ελέγξουµε αν είναι ενεργή, ελέγχουµε αν οι phydev->link, phydev->speed και phydev->duplex έχουν διαφορετικές τιµές από αυτές που υπάρχουν στην δοµή macb. if (phydev->link) { if ((bp->speed != phydev->speed) || (bp->duplex != phydev->duplex)) { u32 reg; reg = macb_readl(bp, NCFGR); reg &= ~(MACB_BIT(SPD) | MACB_BIT(FD)); if (phydev->duplex) reg |= MACB_BIT(FD); if (phydev->speed == SPEED_100) reg |= MACB_BIT(SPD); macb_writel(bp, NCFGR, reg); bp->speed = phydev->speed; bp->duplex = phydev->duplex; status_change = 1; } } Ιστότοπος εργασίας: MyThesis.org 275 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) ∆ιαφορετικά, απλά επαναφέρουµε (reset) τις τιµές των πεδίων speed και duplex έτσι ώστε όταν η γραµµή ενεργοποιηθεί, να πάρουν τις σωστές τιµές, απελευθερώνουµε το spinlock µέσω της spin_unlock_irqrestore και ενηµερώνουµε τις πληροφορίες κατάστασης του Kernel: if (phydev->link != bp->link) { if (!phydev->link) { bp->speed = 0; bp->duplex = -1; } bp->link = phydev->link; status_change = 1; } spin_unlock_irqrestore(&bp->lock, flags); if (status_change) { if (phydev->link) printk(KERN_INFO "%s: link up (%d/%s)\n", dev->name, phydev->speed, DUPLEX_FULL == phydev->duplex ? "Full":"Half"); else printk(KERN_INFO "%s: link down\n", dev->name); } ∆έσµευση των DMA buffers, αρχικοποίηση και καθαρισµός Σε αυτό το σηµείο γράφουµε τρεις βοηθητικές συναρτήσεις οι οποίες θα χρησιµοποιηθούν αργότερα. Η πρώτη είναι η macb_alloc_consistent και χρησιµοποιείται για δέσµευση DMA buffers, η δεύτερη είναι η macb_free_consistent που κάνει ακριβώς το αντίθετο και η τρίτη είναι η macb_init_rings, η οποία αρχικοποιεί δακτυλίους DMA. Αφού συµβουλευτούµε το datasheet του AP7000 σχετικά µε τη συνεργασία DMA και Ethernet controller, δηµιουργούµε δύο δακτυλίους DMA, ο ένας για τους buffers µετάδοσης και ο άλλος για τους buffers λήψης. Το µήκος ρους πρέπει να είναι 8 bytes. 4 για τη διεύθυνση του DMA buffer και 4 για τις διάφορες σηµαίες ελέγχου. Αυτό επιτυγχάνεται µε την παρακάτω δοµή η οποία προστίθεται στο αρχείο κεφαλίδας macb.h: struct dma_desc { u32 addr; u32 ctrl; }; Για την λήψη χρειάζεται επίσης να δεσµεύσουµε τους καταχωρητές DMA. Σύµφωνα µε το datasheet το µέγεθός τους είναι 128 bytes, ενώ για τον δακτύλιο λήψης αποφασίζουµε στην τύχη να έχει µήκος 512 bytes: #define RX_BUFFER_SIZE #define RX_RING_SIZE 128 512 Εποµένως το µέγεθος της µνήµης που πρέπει να δεσµευθεί είναι: #define RX_RING_BYTES Επικοινωνία: [email protected] (sizeof(struct dma_desc) * RX_RING_SIZE) 276 Τεκµηρίωση ανάπτυξης του οδηγού δικτύου MACB – Παράρτηµα Β Για την µετάδοση, τα buffers θα δεσµευθούν από τον Kernel αφού γεµίζονται µέσω των εφαρµογών µε τα δεδοµένα που στέλνουν (payload): #define TX_RING_SIZE #define DEF_TX_RING_PENDING #define TX_RING_BYTES 128 (TX_RING_SIZE - 1) (sizeof(struct dma_desc) * TX_RING_SIZE) Επειδή θέλουµε να ξέρουµε ποιο πακέτο αποστέλλεται κάθε φορά δηµιουργούµε στο αρχείο macb.h τη δοµή ring_info η οποία δεν αφορά το υλικό: struct ring_info { struct sk_buff dma_addr_t }; *skb; mapping; Με τη βοήθειά της ring_info θα υπολογίσουµε αργότερα και τη µεταβλητή size της συνάρτησης macb_alloc_consistent. Η δοµή sk_buff είναι ένας δείκτης προς το πακέτο που µεταδίδεται και η dma_addr_t είναι η διεύθυνση DMA στην οποία βρισκόταν χαρτογραφηµένα τα περιεχόµενα του πακέτου πριν ξεκινήσει η µετάδοση. Στη συνέχεια χρειαζόµαστε ορισµένα ακόµα πεδία τα οποία θα προσθέσουµε στην δοµή macb. Οι τρεις µεταβλητές που ακολουθούν αποθηκεύουν τιµές που αφορούν την κίνηση των δακτυλίων DMA: unsigned int tx_head, rx_tail, tx_tail; Η δοµή dma_desc αφορά τα περιεχόµενα του δακτυλίου λήψης: struct dma_desc *rx_ring; Ο δείκτης προς το πεδίο rx_buffers της δοµής ring_info, δείχνει στη διεύθυνση µνήµης που βρίσκονται οι ίδιοι οι δακτύλιοι λήψης: void *rx_buffers; Οι διευθύνσεις DMA του δακτυλίου λήψης, του δακτυλίου αποστολής και των buffers λήψης αντίστοιχα: dma_addr_t dma_addr_t dma_addr_t rx_ring_dma; tx_ring_dma; rx_buffers_dma; Σε αυτό το σηµείο και αφού οι απαιτούµενες δοµές βρίσκονται στη θέσης τους, δηµιουργούµε τη συνάρτηση macb_alloc_consistent προκειµένου να δεσµεύσουµε τη µνήµη που απαιτείται. Στον κώδικά της θα πραγµατοποιούνται ορισµένες δεσµεύσεις µνήµης: Για τον πίνακα της δοµής ring_info µέσω της kmalloc (εφόσον οι πληροφορίες αυτές δεν θα χρησιµοποιηθούν από τον Ethernet controller) Για τους DMA descriptors (αποστολής και λήψης), αλλά και για τους DMA buffers, µέσω της dma_alloc_coherent Μετά από τις παραπάνω δεσµεύσεις µνήµης θα έχουν αποκτήσει τιµές τα πεδία της δοµής macb, tx_skb, tx_ring, rx_ring, rx_buffers, rx_ring_dma, tx_ring_dma και rx_buffers_dma. Ιστότοπος εργασίας: MyThesis.org 277 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Παρόµοια γράφουµε και την macb_free_consistent που κάνει ακριβώς τις αντίθετες ενέργειες χρησιµοποιώντας τις συναρτήσεις kfree και dma_free_coherent. Τέλος, γράφουµε την macb_init_rings προκειµένου να αρχικοποιήσουµε τους δακτυλίους µε βάση το datasheet του AP7000. Για τον δακτύλιο λήψης αρχικοποιούµε κάθε DMA descriptor me την διεύθυνση του αντίστοιχου buffer. Ο τελευταίος χαρακτηρίζεται από το γεγονός ότι το WRAP bit του πεδίου addr: είναι ενεργοποιηµένο: addr = bp->rx_buffers_dma; for (i = 0; i < RX_RING_SIZE; i++) { bp->rx_ring[i].addr = addr; bp->rx_ring[i].ctrl = 0; addr += RX_BUFFER_SIZE; } bp->rx_ring[RX_RING_SIZE - 1].addr |= MACB_BIT(RX_WRAP); Για τον δακτύλιο αποστολής αρχικοποιούµε όλες τις διευθύνσεις µε την τιµή µηδέν αφού δεν υπάρχουν ακόµα πακέτα προς αποστολή. Επίσης ενεργοποιούµε το bit USED του πεδίου ctrl για να υποδηλώσουµε ότι αυτοί οι descriptors ανήκουν στον AP7000. Οµοίως ενεργούµε και για τους descriptors λήψης for (i = 0; i < TX_RING_SIZE; i++) { bp->tx_ring[i].addr = 0; bp->tx_ring[i].ctrl = MACB_BIT(TX_USED); } bp->tx_ring[TX_RING_SIZE - 1].ctrl |= MACB_BIT(TX_WRAP); Ολοκληρώνουµε την συνάρτηση αρχικοποίησης των δακτυλίων DMA µε τον επανακαθορισµό (reset) των πεδίων tx_head, tx_tail και rx_tail: bp->rx_tail = bp->tx_head = bp->tx_tail = 0; Reset και αρχικοποίηση υλικού Για την αρχικοποίηση του υλικού χρειαζόµαστε να προστεθεί ένα σύνολο διευθύνσεων και οδηγιών #define στο αρχείο macb.h: #define #define #define #define #define #define #define MACB_TSR MACB_RBQP MACB_TBQP MACB_RSR MACB_ISR MACB_IER MACB_IDR 0x0014 0x0018 0x001c 0x0020 0x0024 0x0028 0x002c /* /* /* /* /* /* /* Transmit Status Register */ Receive Buffer Queue Pointer */ Transmit Buffer Queue Pointer */ Reception Status Register */ Interrupt Status Register */ Interrupt Enable Register */ Interrupt Disable Register */ Επιπροσθέτως χρειάζονται και οι ακόλουθες οδηγίες: Για τον καταχωρητή δικτυακής διαµόρφωσης (Network Configuration Register) θέλουµε τα ψηφία του να ενεργοποιούν την λήψη και την µετάδοση: #define MACB_RE_OFFSET #define MACB_TE_OFFSET Επικοινωνία: [email protected] 2 3 278 Τεκµηρίωση ανάπτυξης του οδηγού δικτύου MACB – Παράρτηµα Β Για τον καταχωρητή ενεργοποίησης διακοπών (Interrupt Enable Register) θέλουµε τα ψηφία του να ενεργοποιούν διακοπές κατά την ολοκλήρωση της αποστολής και της λήψης: #define MACB_RCOMP_OFFSET #define MACB_TCOMP_OFFSET 1 7 Τώρα είµαστε σε θέση να γράψουµε την συνάρτηση macb_reset_hw η οποία θα φροντίζει για το reset και την αρχικοποίηση του υλικού. Οι λειτουργίες της εξηγούνται στα σχόλια της εσωτερικής τεκµηρίωσης της συνάρτησης: static void macb_reset_hw(struct macb *bp) { /* Make sure we have the write buffer for ourselves */ wmb(); /* * Disable RX and TX (XXX: Should we halt the transmission * more gracefully?) */ macb_writel(bp, NCR, 0); /* Clear the stats registers (XXX: Update stats first?) */ macb_writel(bp, NCR, MACB_BIT(CLRSTAT)); /* Clear all status flags */ macb_writel(bp, TSR, ~0UL); macb_writel(bp, RSR, ~0UL); /* Disable all interrupts */ macb_writel(bp, IDR, ~0UL); macb_readl(bp, ISR); } Επίσης χρειαζόµαστε µια ακόµη συνάρτηση macb_init_hw: static void macb_init_hw(struct macb *bp) { u32 config; macb_reset_hw(bp); __macb_set_hwaddr(bp); config = macb_readl(bp, NCFGR) & MACB_BF(CLK, -1L); config |= MACB_BIT(PAE); /* PAuse Enable */ config |= MACB_BIT(DRFCS); /* Discard Rx FCS */ if (bp->dev->flags & IFF_PROMISC) config |= MACB_BIT(CAF); /* Copy All Frames */ if (!(bp->dev->flags & IFF_BROADCAST)) config |= MACB_BIT(NBC); /* No BroadCast */ macb_writel(bp, NCFGR, config); /* Initialize TX and RX buffers */ macb_writel(bp, RBQP, bp->rx_ring_dma); macb_writel(bp, TBQP, bp->tx_ring_dma); /* Enable TX and RX */ macb_writel(bp, NCR, MACB_BIT(RE) | MACB_BIT(TE) | MACB_BIT(MPE)); Ιστότοπος εργασίας: MyThesis.org 279 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) /* Enable interrupts */ macb_writel(bp, IER, (MACB_BIT(RCOMP) | MACB_BIT(RXUBR) | MACB_BIT(ISR_TUND) | MACB_BIT(ISR_RLE) | MACB_BIT(TXERR) | MACB_BIT(TCOMP) | MACB_BIT(ISR_ROVR) | MACB_BIT(HRESP))); } Οι συναρτήσεις που παραθέσαµε θα κληθούν αργότερα µέσα από τον κώδικα του οδηγού δικτύου MACB, που αναπτύσσουµε. Εκκίνηση και τερµατισµός λειτουργιών Οι συναρτήσεις εκκίνησης και τερµατισµού µιας λειτουργίας καλούνται αντίστοιχα όταν κάποια διεπαφή δικτύου ενεργοποιείται ή απενεργοποιείται. Αυτό µπορεί να συµβεί για παράδειγµα, όταν καλούµε την εντολή ifconfig από την γραµµή εντολών στο επίπεδο εφαρµογής του χρήστη. Για την υλοποίηση των συναρτήσεων εκκίνησης και τερµατισµού, αρχικά δηµιουργούµε δύο άδειες συναρτήσεις οι οποίες θα επιστρέφουν µια ακέραια τιµή και θα παίρνουν σαν όρισµα έναν δείκτη dev προς τη δοµή net_device: static int macb_open(struct net_device *dev) { return 0; } static int macb_close(struct net_device *dev) { return 0; } Στη συνέχεια τις προσθέτουµε στην συνάρτηση macb_probe: dev->open = macb_open; dev->stop = macb_close; Τώρα είµαστε έτοιµοι να αναπτύξουµε τις συναρτήσεις εκκίνησης και τερµατισµού. Ας αρχίσουµε πρώτα να περιγράφουµε τα βήµατα που έγιναν για την ανάπτυξη της macb_open: ∆εσµεύουµε µνήµη µέσω της συνάρτησης macb_alloc_consistent για τους DMA buffers Αρχικοποιούµε τους DMA buffers µέσω της συνάρτησης macb_init_rings Αρχικοποιούµε το υλικό µέσω της macb_init_hw Εκκινούµε την επικοινωνία µε το ολοκληρωµένο DP83848I (PHY) µέσω της συνάρτησης phy_start ούτως ώστε να ελέγχουµε την κατάσταση της γραµµής: /* schedule a link state check */ phy_start(bp->phy_dev); Καλούµε την netif_start_queue προκειµένου να ενηµερώσουµε τον Kernel ότι η διεπαφή δικτύου που δηµιουργήσαµε είναι έτοιµη να λάβει πακέτα: Επικοινωνία: [email protected] 280 Τεκµηρίωση ανάπτυξης του οδηγού δικτύου MACB – Παράρτηµα Β netif_start_queue(dev); Ακριβώς την αντίστροφη διαδικασία υλοποιούµε στην συνάρτηση macb_close: Καλούµε την netif_stop_queue για να ενηµερώσουµε τον Kernel ότι η διεπαφή δικτύου που δηµιουργήσαµε δεν θα λαµβάνει πλέον πακέτα ∆ιακόπτουµε τη λειτουργία του µέσω της phy_stop Κάνουµε reset µέσω της macb_reset_hw για να απενεργοποιήσουµε τις διακοπές αλλά και οποιονδήποτε άλλο πόρο έχει δεσµευθεί Τέλος αποδεσµεύουµε τους DMA buffers µέσω της macb_free_consistent Κλείδωµα οδηγού Για να διασφαλίσουµε ότι δεν υπάρχει ανεπιθύµητη πρόσβαση στα δεδοµένα που µεταφέρονται πρέπει να υλοποιήσουµε κάποια µέθοδο κλειδώµατος του οδηγού. Αυτό επιτυγχάνεται µε ένα απλό spinlock το οποίο υλοποιούµε µε την δηµιουργία του στιγµιότυπου lock της δοµής spinlock_t, στο αρχείο macb.h: spinlock_t lock; Η αρχικοποίηση του spinlock που δηµιουργήσαµε γίνεται µε µια κλήση της συνάρτησης spin_lock_init την οποία εισάγουµε στην συνάρτηση macb_probe: spin_lock_init(&bp->lock); Στη συνέχεια θα πρέπει να χρησιµοποιήσουµε το spinlock που δηµιουργήσαµε στα παρακάτω σηµεία: Στη συνάρτηση macb_handle_link_change µαζί µε την spin_lock_irqsave και την spin_unlock_irqrestore για την αποφυγή ταυτόχρονης εκτέλεσης του χειριστή διακοπών Στη συνάρτηση netdrv_close, και πάλι µαζί µε την spin_lock_irqsave και την spin_lock_irqrestore για την αποφυγή ταυτόχρονης εκτέλεσης των διακοπών και της διαδικασίας τερµατισµού της διεπαφής δικτύου. Όλα αυτά πρέπει να γίνουν πριν τον τερµατισµό της ουράς πακέτων και του ολοκληρωµένου DP83848I Αποστολή πακέτων Πριν την υλοποίηση της αποστολής πακέτων απαιτούνται κάποιες οδηγίες #define: Το bit TSTART του καταχωρητή ρύθµισης δικτύου NCR (Network Configuration Register) µέσω του οποίου γίνεται η εκκίνηση της µετάδοσης των πακέτων που βρίσκονται στην ουρά µετάδοσης (Transmission Queue): #define MACB_TSTART_OFFSET 9 Το bit ολοκλήρωσης µετάδοσης, του καταχωρητή TSR (Transmit Status Register): #define MACB_COMP_OFFSET Ιστότοπος εργασίας: MyThesis.org 5 281 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Το bit µετάδοσης DMA που στην περίπτωσή µας συµβολίζει ότι κάθε πακέτο µεταδίδεται σε έναν δικό του DMA buffer: #define MACB_TX_LAST_OFFSET 15 Μια µακροεντολή η οποία υπολογίζει τον αριθµό των buffers που βρίσκονται στην ουρά για µετάδοση: #define TX_BUFFS_AVAIL(bp) (((bp)->tx_tail <= (bp)->tx_head) ? (bp)->tx_tail + (bp)->tx_pending - (bp)->tx_head : (bp)->tx_tail - (bp)->tx_head - TX_RING_GAP(bp)) \ \ \ Μια µακροεντολή η οποία παίρνει ως είσοδό ένα δείκτη που δείχνει σε κάποιο DMA buffer που βρίσκεται στην ουρά και επιστρέφει έναν δείκτη που δείχνει στο επόµενο: #define NEXT_TX(n) (((n) + 1) & (TX_RING_SIZE - 1)) Το σηµείο εκκίνησης µετάδοσης στον οδηγό µας, είναι η συνάρτηση macb_start_xmit. Σε αυτή πρέπει να υλοποιήσουµε µια σειρά λειτουργιών που θα επιτυγχάνουν την µετάδοση πακέτων. Η συνάρτηση αυτή θα χειρίζεται και θα ελέγχει την ουρά των DMA buffer descriptors η οποία µε τη σειρά της θα τροποποιείται από τον χειριστή διακοπών. Για το λόγο αυτό είναι απαραίτητη η χρήση κλειδώµατος που υλοποιήσαµε νωρίτερα ώστε µόνο µία λειτουργία να έχει πρόσβαση κάθε φορά στην ουρά. Εποµένως µπορούν να χρησιµοποιηθούν οι συναρτήσεις spin_lock_irq και spin_unlock_irq. Από τη στιγµή που έχουµε δηµιουργήσει κάποιο κλείδωµα, το πρώτο πράγµα που πρέπει να ελέγξουµε, είναι εάν υπάρχει έστω και ένας DMA buffer descriptor για την αποστολή πακέτου. Αυτό µπορεί να γίνει µέσω της µακροεντολής TX_BUFFS_AVAIL. Αν δεν υπάρχει θα πρέπει να διακόψουµε την ουρά µέσω της συνάρτησης netif_stop_queue, να απελευθερώσουµε το κλείδωµα και να επιστρέψουµε την τιµή 1 ώστε ο Kernel να ενηµερωθεί για το σφάλµα. Αν υπάρχει έστω και ένας DMA buffer descriptor διαθέσιµος, ο επόµενος µπορεί να βρεθεί από τον δείκτη tx_head ο οποίος βρίσκεται στη δοµή macb του αρχείου κεφαλής macb.h. Το επόµενο βήµα είναι να χαρτογραφήσουµε το πακέτο ώστε να µπορεί να σταλθεί µέσω DMA. Αυτό µπορεί να γίνει µε την συνάρτηση dma_map_single η οποία παίρνει σαν όρισµα έναν δείκτη προς τη δοµή net_device, την περιοχή µνήµης που θα χαρτογραφηθεί (skb->data), το µήκος (skb->length) και την κατεύθυνση της µεταφοράς DMA (στην περίπτωσή µας DMA_TO_DEVICE). Η συνάρτηση επιστρέφει µια διεύθυνση DMA του τύπου dma_addr_t. Στη συνέχεια ενηµερώνουµε τον εσωτερικό πίνακα tx_skb µε τη διεύθυνση DMA και τον δείκτη προς την SKB. Αυτό θα µας χρειαστεί κατά την ολοκλήρωση της µετάδοσης. Σε αυτό το σηµείο ας υπολογίσουµε την τιµή του πεδίου ctrl του DMA buffer descriptor: Θα πρέπει να περιέχει το µήκος των δεδοµένων που θα αποσταλούν skb->len Το bit MACB_TX_LAST_OFFSET θα πρέπει να ενεργοποιηθεί αφού όλα µας τα πακέτα θα στέλνονται σε ένα απλό buffer Αν το buffer που χρησιµοποιούµε είναι το τελευταίο της ουράς, δηλαδή αν η τιµή του tx_head είναι ίση µε την τιµή TX_RING_SIZE – 1, τότε αντίστοιχα, και το bit MACB_TX_WRAP_OFFSET θα πρέπει να ενεργοποιηθεί Επικοινωνία: [email protected] 282 Τεκµηρίωση ανάπτυξης του οδηγού δικτύου MACB – Παράρτηµα Β Έπειτα αρχικοποιούµε και το πεδίο addr της δοµής ring_info µε την διεύθυνση DMA του DMA descriptor που βρήκαµε πιο πριν. Επίσης, αρχικοποιούµε το πεδίο ctrl της ίδιας δοµής µε την τιµή που βρήκαµε νωρίτερα. Για να εµποδίσουµε την αλλαγή της σειράς αυτών των εγγραφών µε την εγγραφή που θα εκκινήσει τη µετάδοση, τοποθετούµε ένα όριο εγγραφής µνήµης wmb(); (write memory barrier) µετά την ενεργοποίηση του DMA buffer descriptor. Το επόµενο βήµα είναι να ενηµερώσουµε την tx_head µε το επόµενο διαθέσιµο buffer το οποίο βρίσκεται στην ουρά προς µετάδοση. Για το σκοπό αυτό χρησιµοποιούµε την µακροεντολή NEXT_TX. Αφού έχουν ολοκληρωθεί όλα τα παραπάνω, είµαστε έτοιµοι να εκκινήσουµε την πραγµατική µετάδοση ενεργοποιώντας το bit TSTART του καταχωρητή NCR: macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART)); Πριν το κλείσιµο της συνάρτησης macb_start_xmit θα πρέπει να ενηµερώσουµε τον Kernel για το αν έχουµε άλλους DMA buffer descriptors να λάβουµε. Αν ισχύει κάτι τέτοιο, θα πρέπει να χρησιµοποιήσουµε τη συνάρτηση netif_stop_queue: if (TX_BUFFS_AVAIL(bp) < 1) netif_stop_queue(dev); Τέλος, θα πρέπει να προβούµε σε κάποιες τυπικές διαδικασίες οι οποίες φαίνονται και στον κώδικα που ακολουθεί: spin_unlock_irq(&bp->lock); dev->trans_start = jiffies; return 0; Ολοκλήρωση της αποστολής Η ολοκλήρωση της µετάδοσης θα επισηµαίνεται συνήθως µέσω κάποιας διακοπής. Εποµένως όταν προκύπτει µια διακοπή ελέγχουµε αν οφείλεται σε ολοκλήρωση κάποιας µετάδοσης. Αν συµβαίνει κάτι τέτοιο, απελευθερώνουµε τον DMA buffer που η είχαµε χαρτογραφήσει γι’ αυτή την µετάδοση έτσι ώστε να είναι πλέον ξανά διαθέσιµος προς χρήση. Επίσης, ενηµερώνουµε τον Kernel ότι είµαστε και πάλι έτοιµοι για µια νέα µετάδοση αν χρειαστεί. Εποµένως το πρώτο πράγµα που κάνουµε είναι να χρησιµοποιήσουµε τη συνάρτηση macb_interrupt. Αρχικά εξετάζουµε αν η διακοπή προέκυψε από τη συσκευή µας διαβάζοντας τον καταχωρητή διακοπών MACB_ISR (Interrupt Status Register) και αν είναι µηδέν τότε απλά επιστρέφουµε στον Kernel την τιµή IRQ_NONE: status = macb_readl(bp, ISR); if (unlikely(!status)) return IRQ_NONE; Ιστότοπος εργασίας: MyThesis.org 283 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) Σε διαφορετική περίπτωση, δηµιουργούµε ένα spinlock ούτως ώστε να αποτρέψουµε την ταυτόχρονη πρόσβαση στον κώδικα που χειρίζεται τη διακοπή που προέκυψε. Αυτό γίνεται µέσω των συναρτήσεων spin_lock και spin_unlock. Έπειτα θα πρέπει να δηµιουργήσουµε ένα βρόχο ο οποίος να εκτελείται µέχρι ο καταχωρητής MACB_ISR να πάρει την τιµή µηδέν. Ο καταχωρητής αυτός µετά από κάθε ανάγνωση που του γίνεται, παίρνει την τιµή µηδέν. Αυτό σηµαίνει ότι δε χρειάζεται κάθε φορά να εκτελούµε εµείς αυτό τον µηδενισµό αλλά ταυτόχρονα σηµαίνει ότι πρέπει να αποθηκεύουµε και να χρησιµοποιούµε την αρχική τιµή που είχε ο καταχωρητής πριν συµβεί η διακοπή και η ανάγνωση από τον χειριστή διακοπών. Επίσης, µέσα στο βρόχο χρειάζεται να ελέγχουµε µέσω της µακροεντολής MACB_BIT αν το bit TCOMP του καταχωρητή IER είναι ενεργοποιηµένο γιατί αυτό είναι που σηµατοδοτεί και την ολοκλήρωση µιας µετάδοσης. Αν ισχύει κάτι τέτοιο τότε καλούµε την συνάρτηση macb_tx για να αναλάβει τον τερµατισµό της µετάδοσης αυτής: if (status & (MACB_BIT(TCOMP) | MACB_BIT(ISR_TUND))) macb_tx(bp); Η υλοποίηση της συνάρτησης macb_tx αφορά την ικανοποίηση των παρακάτω λειτουργιών: Επιβεβαίωση της ολοκλήρωσης µιας µετάδοσης µέσω του καταχωρητή TSR: Ανάγνωση του καταχωρητή MACB_TSR και εγγραφή της τιµής που διαβάστηκε: status = macb_readl(bp, TSR); macb_writel(bp, TSR, status); Έλεγχος του bit UND του καταχωρητή TSR για κατάσταση buffer underrun: if (status & MACB_BIT(UND)) . . . Έλεγχος όλων των DMA buffer descriptors. από την κεφαλή (head) µέχρι την ουρά τους (tail). Για τον υπολογισµό της θέσης του κάθε επόµενου DMA descriptor χρησιµοποιούµε την NEXT_TX. Για κάθε descriptor κάνουµε τα εξής: Χρησιµοποιούµε ένα εµπόδιο ανάγνωσης µνήµης (memory read barrier) ώστε να εξασφαλίσουµε ότι αυτό που θα διαβάσουµε είναι αυτό που η συσκευή τοποθέτησε στον DMA descriptor: rmb(); Ελέγχουµε το bit TX_USED του DMA descriptor. Αν δεν είναι ενεργοποιηµένο θα πρέπει να διακόψουµε τον βρόχο ελέγχου των DMA descriptors γιατί σηµαίνει ότι εντοπίσαµε έναν DMA descriptor του οποίου η µετάδοση δεν έχει ολοκληρωθεί ακόµα: if (!(bufstat & MACB_BIT(TX_USED))) break; Επικοινωνία: [email protected] 284 Τεκµηρίωση ανάπτυξης του οδηγού δικτύου MACB – Παράρτηµα Β Αποχαρτογραφούµε, απελευθερώνουµε και επανακαθορίζουµε τον δείκτη SKB ενώ ταυτόχρονα ενηµερώνουµε και κάποια στατιστικά στοιχεία της δοµής macb: dev_dbg(&bp->pdev->dev, "skb %u (data %p) TX complete\n", tail, skb->data); dma_unmap_single(&bp->pdev->dev, rp->mapping, skb->len, DMA_TO_DEVICE); bp->stats.tx_packets++; bp->stats.tx_bytes += skb->len; rp->skb = NULL; dev_kfree_skb_irq(skb); Ενηµέρωση της tx_tail ώστε να δείχνει στον επόµενο DMA descriptor που θα αναλυθεί στην επόµενη διακοπή ολοκλήρωσης κάποιας µετάδοσης bp->tx_tail = tail; Τελικά εάν η ουρά έχει σταµατήσει και εφόσον έχουµε αρκετούς διαθέσιµους buffers αποστολής, ενηµερώνουµε τον Kernel µέσω της συνάρτησης netif_wake_queue για να αρχίσει νέα αποστολή: if (netif_queue_stopped(bp->dev) && TX_BUFFS_AVAIL(bp) > MACB_TX_WAKEUP_THRESH) netif_wake_queue(bp->dev); Με την ολοκλήρωση της διαδικασίας ολοκλήρωσης µετάδοσης µπορούµε µέσω του Wireshark και ενώ κάνουµε ping από τον υπολογιστή host προς το ενσωµατωµένο σύστηµα Linux που αναπτύσσουµε, να δούµε τις αιτήσεις ARP που θα προέρχονται από αυτό. Φυσικά επειδή ακόµη δεν έχουµε ολοκληρώσει την διαδικασία ολοκλήρωσης λήψης, τα αιτήµατα ping θα λήξουν. Λήψη πακέτων Το τελευταίο κοµµάτι του οδηγού δικτύου που αναπτύσσουµε είναι η υλοποίηση της λειτουργίας λήψης πακέτων η οποία και σε αυτή την περίπτωση γίνεται αντιληπτή από µία διακοπή. Εποµένως στον χειριστή διακοπών θα πρέπει να προσθέσουµε µια κλήση προς τη συνάρτηση rx_macb. Η συνάρτηση αυτή, ελέγχει τους DMA descriptors και βρίσκει τα εύρη αυτών που αντιστοιχούν σε κάποιο πακέτο. Για κάθε τέτοιο εύρος καλείται η συνάρτηση παραλαβής πλαισίου macb_rx_frame. Σε αυτό το σηµείο θα πρέπει να τονίσουµε µια διαφορά που υπάρχει µεταξύ της αποστολής και της λήψης πακέτων. Κατά τη µετάδοση κάθε πακέτο στέλνεται αποκλειστικά µέσω ενός DMA buffer και ενός descriptor. Ενώ στη λήψη οι DMA buffers έχουν το όριο των 128 bytes µε αποτέλεσµα για την λήψη ενός και µόνο πακέτου να χρειάζονται συνήθως πολλαπλοί buffers. Συνεχίζοντας θα ασχοληθούµε µε τις επιπλέον οδηγίες και µακροεντολές που θα πρέπει να προστεθούν στο αρχείο macb.h και macb.c έτσι ώστε να µπορούµε να λαµβάνουµε πακέτα: Οδηγίες για τους DMA buffer descriptors Το bit MACB_RX_USED_OFFSET παίρνει από την συσκευή, την τιµή 1 στο πεδίο διεύθυνσης του DMA descriptor, όταν το DMA buffer γεµίσει µε δεδοµένα Ιστότοπος εργασίας: MyThesis.org 285 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) #define MACB_RX_USED_OFFSET 0 Το bit MACB_RX_SOF_OFFSET παίρνει από την συσκευή την τιµή 1 στο πεδίο ελέγχου του DMA descriptor, όταν τα δεδοµένα του DMA buffer αποτελούν την αρχή ενός πακέτου (SOF – Start Of Frame): #define MACB_RX_SOF_OFFSET 14 Το bit MACB_RX_EOF_OFFSET παίρνει από την συσκευή την τιµή 1 στο πεδίο ελέγχου του DMA descriptor, όταν τα δεδοµένα του DMA buffer αποτελούν το τέλος ενός πακέτου (EOF – End Of Frame): #define MACB_RX_EOF_OFFSET 15 Καθώς η κεφαλίδα Ethernet έχει µήκος 14 bytes και για λόγους απόδοσης, είναι καλό να την ευθυγραµµίζουµε (word aligned). Αυτό γίνεται προσθέτοντας δύο επιπλέον byte σε κάθε πακέτο και µετακινώντας έτσι την κεφαλίδα κατά δύο bytes επίσης: #define MACB_RX_OFFSET_SIZE 2 Η µακροεντολή NEXT_RX που είναι η αντίστοιχη της NEXT_TX για την αποστολή πακέτων: #define NEXT_RX(n) (((n) + 1) & (RX_RING_SIZE - 1)) Αφού φροντίσουµε για όλα τα παραπάνω προσθέτουµε κώδικά στον χειριστή διακοπών ούτως ώστε µετά από τον έλεγχο που κάνει για την ολοκλήρωση κάποιας µετάδοσης, να κάνει το ίδιο και για την ολοκλήρωση λήψης (µέσω του bit MACB_RCOMP_OFFSET) και στη συνέχεια καλούµε την συνάρτηση macb_rx. Στη συνάρτηση macb_rx προσπελαύνουµε µε τη σειρά όλους τους DMA descriptors ξεκινώντας από την ουρά λήψης (rx_tail) και εκτελούµε τις παρακάτω ενέργειες: Καλούµε την rmb() ώστε να εξασφαλίσουµε ότι η ανάγνωσή µας θα είναι έγκυρη Αν το bit MACB_RX_USED_OFFSET στον τρέχον DMA descriptor δεν είναι ενεργοποιηµένο τότε σηµαίνει ότι έχουµε φτάσει στο τελευταίο DMA buffer που λήφθηκε και εποµένως µπορούµε να διακόψουµε την επανάληψη Αν το bit MACB_RX_SOF_OFFSET είναι ενεργοποιηµένο στο πεδίο ελέγχου (ctrl) του τρέχοντος DMA descriptor, τότε αυτός περιέχει την αρχή ενός πακέτου. Εποµένως αποθηκεύουµε τον δείκτη του descriptor σε µια µεταβλητή ώστε να θυµόµαστε ότι είναι ο πρώτος DMA buffer descriptor του τρέχοντος πακέτου. Αν το bit MACB_RX_EOF_OFFSET είναι ενεργοποιηµένο στο πεδίο ελέγχου του τρέχοντος DMA descriptor, τότε το τρέχον buffer είναι το τέλος του τρέχοντος πακέτου. Εποµένως τώρα έχουµε τον δείκτη της αρχής του πακέτου που βρήκαµε πριν και τον δείκτη του τέλους του πακέτου. Με αυτές τις δύο πληροφορίες είµαστε σε θέση να καλέσουµε τη συνάρτηση macb_rx_frame η οποία θα χειριστεί τη λήψη ενός ολόκληρου πακέτου. Επίσης µετά το βρόχο θα πρέπει να ενηµερώσουµε την rx_tail. Κλείνοντας την ανάλυση του οδηγού δικτύου MACB θα εξετάσουµε την συνάρτηση macb_rx_frame. Αυτή η συνάρτηση είναι υπεύθυνη για τη δέσµευση ενός SKB από την Επικοινωνία: [email protected] 286 Τεκµηρίωση ανάπτυξης του οδηγού δικτύου MACB – Παράρτηµα Β στοίβα δικτύου (TCP/IP) για το γέµισµά της µε δεδοµένα και την αποστολή της προς ανάλυση και πάλι από τη στοίβα δικτύου. Στην αρχή της συνάρτησης υπολογίζουµε το µέγεθος του πακέτου που παραλαµβάνεται. Το µήκος αυτό αποθηκεύεται στα 11 χαµηλότερης τάξεως bits του πεδίου ελέγχου του τελευταίου DMA descriptor που περιέχει το πακέτο. Στη συνέχεια ζητάµε από τον Kernel να δεσµεύσει µια SKB χρησιµοποιώντας την συνάρτηση dev_alloc_skb. Η συνάρτηση αυτή παίρνει ως όρισµα ένα µήκος το οποίο πρέπει να είναι το µήκος του πακέτου που παραλήφθηκε συν το RX_OFFSET που χρησιµοποιείται για την ευθυγράµµιση (σε bytes) που αναφέραµε και νωρίτερα. Φυσικά πρέπει να κάνουµε και έναν έλεγχο για το αν η δέσµευση ήταν επιτυχής. Αν δεν ήταν επιτυχής το πακέτο απλά απορρίπτεται. Μετά από ένα τέτοιο συµβάν θα πρέπει να χαρακτηρίσουµε τους DMA descriptors του πακέτου αυτού ως ελεύθερους θέτοντας σε µηδέν το bit MACB_RX_USED_OFFSET από το πεδίο της διεύθυνσης. Σε αυτό το σηµείο πρέπει να διαµορφώσουµε τo SKB ενηµερώνοντας τον Kernel για τα εξής: Τα δύο πρώτα bytes του πακέτου πρέπει να αγνοηθούν: skb_reserve(skb, RX_OFFSET) ∆εν έχει γίνει κάποιος έλεγχος checksum στα πακέτα: skb->ip_summed = CHECKSUM_NONE; Μέσω της συνάρτησης skb_put και µε παράµετρο το µήκος του πακέτου, ότι τα πακέτα θα τοποθετηθούν στο SKB: skb_put(skb, len); Αφού το SKB έχει διαµορφωθεί θα πρέπει να χειριστούµε όλους τους DMA buffers που περιέχουν τα πακέτα µας και να πραγµατοποιήσουµε σε καθέναν τους, τις παρακάτω ενέργειες: Να υπολογίσουµε το µήκος των δεδοµένων που περιλαµβάνουν. Συνήθως το µήκος αυτό ισούται µε το µέγεθος του buffer RX_BUFFER_SIZE (εκτός από το τελευταίο): unsigned int frag_len = RX_BUFFER_SIZE if (offset + frag_len > len) { BUG_ON(frag != last_frag); frag_len = len - offset; } Να αντιγράψουµε τα δεδοµένα από τον DMA buffer στο SKB χρησιµοποιώντας τη συνάρτηση skb_copy_to_linear_data_offset. Τα ορίσµατα της συνάρτησης αυτής είναι ο δείκτης SKB, το εύρος του SKB στο οποίο τα δεδοµένα θα αντιγραφούν, η διεύθυνση µνήµης από την οποία θα αντιγραφούν τα δεδοµένα και το µήκος των δεδοµένων που θα αντιγραφούν: skb_copy_to_linear_data_offset(skb, offset, (bp->rx_buffers + (RX_BUFFER_SIZE * frag)), Ιστότοπος εργασίας: MyThesis.org 287 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) frag_len); Να θέσουµε σε µηδέν την τιµή του bit MACB_RX_USED_OFFSET του DMA descriptor ώστε να φαίνεται και πάλι διαθέσιµος για µελλοντικές λήψεις πακέτων: bp->rx_ring[frag].addr &= ~MACB_BIT(RX_USED); Στο τέλος της συνάρτησης macb_rx_frame βρίσκουµε το πρωτόκολλο του πακέτου που λήφθηκε και το αποθηκεύουµε στο SKB: skb->protocol = eth_type_trans(skb, bp->dev); Αµέσως µετά ενηµερώνουµε τα στατιστικά της δοµής macb αποστέλλουµε στον Kernel το πακέτο που λάβαµε χρησιµοποιώντας τη συνάρτηση netif_receive_skb: bp->stats.rx_packets++; bp->stats.rx_bytes += len; bp->dev->last_rx = jiffies; dev_dbg(&bp->pdev->dev, "received skb of length %u, csum: %08x\n", skb->len, skb->csum); netif_receive_skb(skb); Σε αυτό το σηµείο η ανάπτυξη του οδηγού δικτύου MACB έχει ολοκληρωθεί και ο Ethernet controller του µικροελεγκτή AP7000 είναι σε θέση να επικοινωνεί µε τον Kernel αλλά και µε το ολοκληρωµένο DP83848I που παίζει το ρόλο του Ethernet PHY. Έτσι ο Router NGW100 είναι πια σε θέση να ανταλλάσσει πληροφορίες µε το δίκτυο δροµολογώντας ταυτόχρονα τα πακέτα των υπολογιστικών συστηµάτων που βρίσκονται συνδεδεµένα σε αυτόν. Επικοινωνία: [email protected] 288 ΠΑΡΑΡΤΗΜΑ C Το πακέτο RBSP και o ιστότοπος MyThesis.org Εισαγωγή Καθ’ όλη τη διάρκεια συγγραφής αυτής της εργασίας αποκόµισα κάποια γνώση και εµπειρία την οποία νιώθω την ανάγκη να εµπλουτίσω ακόµα περισσότερο και να την µοιραστώ µε όσους ενδιαφέρονται για το ίδιο αντικείµενο. Έτσι δηµιούργησα τον ιστότοπο Mythesis.org. Ο ιστότοπος MyThesis.org Στο MyThesis.org θα υπάρχει διαθέσιµο όλο το υλικό της παρούσας εργασίας αλλά και επιπλέον χρήσιµο περιεχόµενο που δεν ήταν δυνατόν να παρατεθεί εδώ. Από εκεί θα είναι δυνατόν να προσκοµιστεί (download) και το πακέτο υποστήριξης, RBSP. Το πακέτο υποστήριξης RBSP Το πακέτο υποστήριξης RBSP (Rousis BSB), είναι το αποτέλεσµα της τροποποίησης και προσαρµογής του πακέτου Atmel Linux BSP 3.0 ειδικά για τον Router NGW100, αλλά και της προσθήκης επιπλέον χρήσιµων εργαλείων και λογισµικού. Περιλαµβάνει τα εξής: Το αρχείο AVR32_Linux_BSP_reduced_Image_3.0.0.iso Το λογισµικό Vmware Player Αρχείο εικόνας iso της διανοµής Ubuntu 9.04 Τα αρχεία rousis_buildroot.tar.gz, rousis_toolchain.tar.gz και installer.sh Datasheets που αφορούν το υλικό του Router NGW100 Αρχεία CAD του PCB του Router NGW100 Το πακέτο RBSP είναι ένας οπτικός δίσκος DVD µε γραφικό περιβάλλον (menu) το οποίο είναι πολύ φιλικό προς το χρήστη. Παρέχει εύκολη πρόσβαση στο διαθέσιµο περιεχόµενο καθώς και ορισµένες πληροφορίες για τη χρησιµότητα του κάθε στοιχείου. Ουσιαστικά αποτελεί µια offline έκδοση του ιστότοπου MyThesis.org. Ιστότοπος εργασίας: MyThesis.org 289 ∆ηµήτριος Α. Ρούσης – Υλοποίηση δροµολογητη (Router Implementation) ΒΙΒΛΙΟΓΡΑΦΙΑ [1]. Christopher Hallinan, “Embedded Linux Primer: A Practical Real-World Approach (2nd Edition)”, Prentice Hall, 2010. [2]. Robert Love, “Linux Kernel Development (3rd Edition)”, Pearson education, 2010. [3]. Daniel P. Bovet, Marco Cesati, “Understanding the Linux Kernel, Third Edition”, O'Reilly, 2005. [4]. Michael Kerrisk, “The Linux Programming Interface: A Linux and UNIX System Programming Handbook”, No Starch Press, 2010. [5]. Sreekrishnan Venkateswaran, “Essential Linux Device Drivers”, Prentice Hall, 2008. [6]. Christian Benvenuti, “Understanding Linux Network Internals”, O'Reilly, 2006. [7]. Cameron Newham, “Learning the bash Shell: Unix Shell Programming (Third Edition)”, O'Reilly, 2005. [8]. Richard Blum, “Linux Command Line and Shell Scripting Bible, Second Edition”, Wiley Publishing Inc., 2011. [9]. www.atmel.com. Επίσηµη ιστοσελίδα της εταιρείας Atmel. [10]. www.avrfreaks.net. Η µεγαλύτερη κοινότητα γύρω από τους µικροελεγκτές AVR της εταιρείας Atmel. Επικοινωνία: [email protected] 290 Ονοµάζοµαι ∆ηµήτριος Ρούσης και είµαι απόφοιτος του τµήµατος Πληροφορικής και Τεχνολογίας Υπολογιστών του ΑΤΕΙ ∆υτικής Μακεδονίας. Αγαπώ την επιστήµη των υπολογιστών και µε ενδιαφέρουν ταυτόχρονα τόσο το λογισµικό όσο και το υλικό. Πιστεύω ότι αυτά τα δύο είναι αλληλένδετα. Όπως η σκέψη και το σώµα. Τα τελευταία χρόνια ασχολούµαι µε την ανάπτυξη ενσωµατωµένων συστηµάτων Linux. Πρόκειται για ένα άκρως απαιτητικό αντικείµενο µε απεριόριστες δυνατότητες. Κατά τη διάρκεια της φοίτησής µου δεν περιορίστηκα µόνο στο πρόγραµµα σπουδών της σχολής. Προσπάθησα να αποκοµίσω γνώσεις που αφορούν πραγµατικές ανάγκες και έχουν άµεσο πρακτικό αποτέλεσµα. Οι κυριότερες εξ’ αυτών είναι: Γλώσσες προγραµµατισµού: C, VB.NET, PHP, MYSQL, JAVASCRIPT, HTML Ανάπτυξη ενσωµατωµένων συστηµάτων µε µικροελεγκτή 8 ή 32 bit Ανάπτυξη ιστοσελίδων µε CMS: Joomla, Drupal, Blogger Βελτιστοποίηση ιστότοπων για τις µηχανές αναζήτησης (SEO) Εγκατάσταση και παραµετροποίηση λειτουργικών συστηµάτων Εγκατάσταση και παραµετροποίηση συστηµάτων βιντεοεπιτήρησης IP (CCTV) Εγκατάσταση και παραµετροποίηση ηλεκτρονικών επιγραφών LED Ανάπτυξη installer εφαρµογής και συστήµατος Internet update Ανάπτυξη αλληλεπιδραστικής διαφηµιστικής παρουσίασης (CD,DVD ή USB stick) Ανάπτυξη τεκµηρίωσης (documentation, wikis, video tutorials κλπ) Προβολή προϊόντων µέσω κοινωνικών δικτύων Επίσης µελέτησα αρκετά την αγορά της υψηλής τεχνολογίας σε παγκόσµιο επίπεδο και προσπάθησα να διδαχθώ και να παραδειγµατιστώ από τους καλύτερους. Σε αυτό µου το εγχείρηµα κύριο ρόλο έπαιξε το ∆ιαδίκτυο. Άµεση επικοινωνία: 693 204 16 96
© Copyright 2024 Paperzz