Δημιουργία usb driver - Kernel space (χωρίς άχρηστες λεπτομέρειες)

th4n0z | Τετ, 04/22/2009 - 01:32 | 6' | 8

Αυτο το tutorial εχει σαν σκοπο την δημιουργία ενος usb driver σε Kernel space που θα διαβαζει οτι στελνει η συσκευη και θα τα περναει μεσο μιας συναρησης στο user space.

Πρώτο βήμα: usb sniffing

Πρώτα απ' ολα θα πρέπει να μάθουμε τι μας στέλνει η συσκευούλα μας κατι που γίνεται πολύ απλά με τις παρακάτω εντολες στο terminal ως root:

mount -t debugfs none_debugs /sys/kernel/debug
modprobe usbmon
cat /sys/kernel/debug/usbmon/1u

Στην 3η ο αριθμός 1 παραπεμπει σε ποιο bus του υπολογιστή είναι εντοπισμένη η συσκευή κατι που μαθαίνουμε μεσω της εντολής lsusb.
Tωρα αρκεί να καταλάβουμε την τελικη εξοδο π.χ.
(το παράδειγμα βρίσκεται στο http://groups.google.com/group/microdia/web/usbmon-user-guide, εδω αναλύονται μονο τα απαραιτητα )

Εξοδος

cd419d80 2772257915 S Co:3:003:0 s 41 08 10c0 0000 0008 8 = 90211c00 00000010
cd419d80 2772258146 C Co:3:003:0 0 8 >
cd419e40 2772261950 S Ci:3:003:0 s c1 00 10c0 0000 0001 1 <
cd419e40 2772262145 C Ci:3:003:0 0 1 = 94
cd4193c0 2772269961 S Co:3:003:0 s 41 08 10c0 0000 0008 8 = 92210000 00000010
cd4193c0 2772270270 C Co:3:003:0 0 8 >
cd419780 2772274005 S Ci:3:003:0 s c1 00 10c0 0000 0001 1 <
cd419780 2772274266 C Ci:3:003:0 0 1 = 96
cd419780 2772280186 S Ci:3:003:0 s c1 00 10c2 0000 0005 5 <
cd419780 2772280516 C Ci:3:003:0 0 5 = 00001c00 7f
cd419780 2772288066 S Co:3:003:0 s 41 08 10c0 0000 0008 8 = 90211d00 00000010
cd419780 2772288392 C Co:3:003:0 0 8 >
cd419780 2772296047 S Ci:3:003:0 s c1 00 10c0 0000 0001 1 <
cd419780 2772296278 C Ci:3:003:0 0 1 = 94
cd419780 2772304018 S Co:3:003:0 s 41 08 10c0 0000 0008 8 = 92210000 00000010

Συμπεράσματα:

Στήλες:

1η & 2η ..... δεν μας ενδιαφέρουν

3η.................Transfer type: S submission-αποστολή στη συσκευούλα
                                     C callback-απάντηση
                                     E Submission Error-οταν υπάρχει s χωρις c

4η................Address specification: δεν μας ενδιαφέρει(η στηλη αποτελειται απο πχ Ci:3:003:0)

5η..................URB specific data: s - αντιστιχει σε αποστολη δεδομενων (βλ. Παραπάνω)

0 – αντιστιχει σε διαβασμα δεδ.

Οι σειρές που μας ενδιαφέρουν ειναι αυτες με Transfer type c & URB specific data 0.
Σ'αυτες μετα το 0 υπάρχει ενας αριθμός (τα byte που διαβάστηκαν) μετα ενα = και στη συνέχεια τα byte που διαβάστηκαν.
Αυτο που μας ενδιέφερε απο την αρχή δηλ. Να ανακαλύψουμε τι μας στέλνει η συσκευή.

 

Δεύτερο βήμα : κατασκευη του driver σε C

Δημιουργία της δομής :

static struct usb_driver skel_driver = {

.name = "skeleton", //ονoμα του driver

.probe = skel_probe, //η συναρτηση που θα κληθει οταν συνδεθει η συσκ.

.disconnect = skel_disconnect, // η συναρτηση που θα κληθει οταν αποσυνδεθει

.fops = &skel_fops,

.minor = USB_SKEL_MINOR_BASE,

.id_table = skel_table,

};

Register του driver :

Unregister του driver:

<code lang="bash">
static void __exit usb_skel_exit(void)
{
  usb_deregister(&skel_driver);
}

module_exit(usb_skel_exit);

 

Οταν συνδεθεί η συσκευη περναν καποια στοιχεια στην skel_probe η οπαια αν επιστρέψει 0 ολα πανε καλα αλλιως εχουμε προβληματάκια :

static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id)

 

Ωρα να διαβάσουμε απο την συσκευη:

θα χρησιμοποιησουμε την συναρτηση usb_bulk_msg η οποια εχει σαν ορισμα ενα buffer στο οποιο καταχωρείται η τιμη που διαβαζεται απο την συσκευη.
Προσοχη αν δεν διαβαστει κατι σύντομα θα επιστρέψει λάθος:

retval = usb_bulk_msg (skel->dev, usb_rcvbulkpipe (skel->dev, skel->bulk_in_endpointAddr), skel->bulk_in_buffer, skel->bulk_in_size, &count, HZ*10);

if (!retval) {
  if (copy_to_user (buffer, skel->bulk_in_buffer, count))
  retval = -EFAULT; //είδος error
else
  retval = count; // o αριθμος των δεδομένων που διαβαστηκαν

Η συναρτηση copy_to_user() περναει το περιεχόμενο του buffer απο το kernel space στο user space πχ γραφει το buffer σε ενα αρχειο και η εφαρμογη μας (στο user space) το διαβαζει.
Το περιεχόμενο της δεν δινεται καθώς διαφερει σε καθε περιπτωση αναλογα με της ανάγκες μας.

Δώσε αστέρια!

MO: (ψήφοι: 0)

Σχόλια

Πώπω, με πήγες πολλά χρόνια πίσω! Στη διπλωματική μου είχα φτιάξει κάποτε ένα driver για ένα USB host controller. Να'σαι καλά (κ καλώς όρισες)!

Θα διαφωνήσω όμως ότι οι υπόλοιπες λεπτομέρειες είναι άχρηστες (βλ. τίτλο άρθρου). Οι περισσότεροι εδώ μέσα δεν έχουν ιδέα τι είναι αυτά που γράφεις (πχ "kernel space"), κ κυρίως πώς μπορούν να τα δοκιμάσουν, κάνοντας compile κ προσθέτοντας το δικό τους driver στον πυρήνα.

Όπως κ να έχει η ιδέα ενός tutorial για το πώς γράφεται κ δοκιμάζεται ένας driver είναι καλή. Για αρχή, για όποιον θέλει να καταλάβει λίγο καλύτερα "τι παίχτηκε" παραπάνω, υπάρχει η βίβλος:

http://lwn.net/Kernel/LDD3/

-- gnu_labis

Το Linux ΔΕΝ είναι Windows!!!

Μπράβο βρε th4n0z!

 

Έκανα ένα ρετουσάρισμα στο κείμενο για να φαίνεται καλύτερα (hint: καλύτερα να κάνεις copy/paste απευθείας από OOorg σε plaintext mode αντί για τον editor, και μετά να μορφοποιείς το κείμενο με code tags).

 

--ΔΚ

Για τον πρώτο usb driver είναι μάλλον καλύτερο να μείνει κανείς εξ ολοκλήρου σε userspace. Αυτό μπορεί να γίνει είτε

κατευθείαν μέσω usbfs (read/write/ioctl) είτε μέσω της libusb.

 

Αν επιλέξετε την τελευταία, υπάρχει έτοιμος κώδικας που θα μετατρέψει ένα log από usb snoopy (windows) ή

usbmon (linux) σε ένα "σκελετό" driver οπότε συνεχίζουμε από εκεί. Αν ο οδηγός σας χρειάζεται kernel interface

(π.χ., πρόκειται για κάρτα τηλεόρασης και θέλετε να είναι προσβάσιμη μέσω /dev/video0 με V4L2 API, ή αν

η συσκευή είναι κάπως περίεργη (renumeration, zerocd κλπ) ή αν μεταφέροντας τον οδηγό σας στον πυρήνα

γλιτώνετε πολύ κώδικα τότε συμφέρει η μεταφορά. Αλλιώς είναι καλύτερα ο οδηγός να μείνει userspace

(π.χ., οι οδηγοί για scanners στις μέρες μας έτσι δουλεύουν).

 

 

Ναι, πράγματι, οι userspace drivers έχουν πάρει τα πάνω τους τελευταία στο Linux.

Κάποιο link ίσως για το εργαλείο που μετατρέπει τα log σε ένα πρώτο σκελετό driver?

-- gnu_labis

Το Linux ΔΕΝ είναι Windows!!!

Ειδικά το usb πρότυπο βολεύει για userspace drivers, επειδή υλοποιείται σε μεγάλο βαθμό όπως και στα windows

με message-passing. Η απόδοση δεν είναι πρόβλημα, με λίγη προσοχή στον κώδικα μπορείς να φτάσεις στα 29-42MB/s

(ανάλογα με τον controller και τη συσκευή. ~42MB/s έχω πιάσει μόνο με intel controller στη M/B και cypress FX2 με "πειραγμένο"

firmware).

 

Links για εργαλεία:

http://mcentral.de/wiki/index.php5/Usbreplay (ncurses interface για replay των logs).

http://iki.fi/lindi/darcs/usbsnoop2libusb/usbsnoop2libusb.pl (φτιάχνει ένα σκελετό με βάση τα logs)

και άλλα (google is your friend)

 

(Δυστυχώς δεν έχω δοκιμάσει ο ίδιος τα παραπάνω εργαλεία οπότε δεν είμαι σίγουρος για την ποιότητά τους).