View Single Post
Old 14-01-2005, 14:53   #18
ilsensine
Senior Member
 
L'Avatar di ilsensine
 
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
Organizzazione degli oggetti nel kernel: kobject e sysfs. Il nuovo Device Model.

Una delle novità maggiori introdotti nel kernel 2.6 è la generalizzazione di concetti quali "driver", "dispositivo", "classe di dispositivi". E' stata introdotta una entità, kobject, che rappresenta in maniera astratta ogni oggetto del kernel. Potete pensare al kobject come alla classe base astratta in un toolkit c++, ad esempio. I vari kobject sono raggruppati gerarchicamente in insiemi (kset), raggruppabili a loro volta in altre gerarchie. La struttura è alquanto sofisticata.
L'organizzazione gerarchica interna del kernel viene esportata in userspace tramite il "sysfs". Questo file system virtuale mostra la gerarchia degli oggetti, consentendone di modificare o esaminare eventuali attributi (non usate più /proc nei vostri driver!).
Una descrizione generale e più esauriente del sysfs è pubblicata da lwn, che riporto qui per comodità:
http://lwn.net/Articles/driver-porting/

Qui parleremo solo degli oggetti device e driver, che sono ciò che maggiormente interessano al programmatore.

Un "device" è un oggetto connesso fisicamente (o logicamente) a un qualche "bus". E' descritto a livello astratto dalla "struct device" (v. linux/device.h), che è la base per la descrizione di altre classi di device (ad es. pci_dev, usb_device...). Descrivere un "device" vuol dire descrivere anche l'oggetto bus (struct bus_type) a cui è collegato o può essere collegato.
Facciamo un esempio. Un tipo di bus presente ovunque è il "platform_bus_type". Questo bus è pensato per contenere gli oggetti intrinsecamente presenti nella piattaforma (ad es. controller dma, controller floppy ecc.) che non possono essere pensati come connessi ad altri bus. Altri esempi di bus sono il bus pci, il bus usb, il bus i2c...
Descrivere un dispositivo connesso al platform_bus_type è semplice; questo modulo ad esempio dichiara e registra nel sistema due dispositivi virtuali:

Codice:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>

static void dev_release(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	printk(KERN_INFO "%s: releasing %s:%d\n",
		__func__, pdev->name, pdev->id);
}

static struct platform_device pdev0 = {
	.name = "dummy_dev",
	.id = 0,
	.dev = {
		.release = dev_release
	}
};

static struct platform_device pdev1 = {
	.name = "dummy_dev",
	.id = 1,
	.dev = {
		.release = dev_release
	}
};

static int __init dev_init(void)
{
	platform_device_register(&pdev0);
	platform_device_register(&pdev1);
	return 0;
}

static void __exit dev_exit(void)
{
	platform_device_unregister(&pdev1);
	platform_device_unregister(&pdev0);
}

module_init(dev_init);
module_exit(dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ilsensine");
MODULE_DESCRIPTION("dummy (platform) device");
Un platform_device "è anche" una "struct device" (campo dev), più alcune altre cose specifiche per il tipo di bus cui è connesso. Tra queste cose è necessario specificare delle informazioni per identificare univocamente il device (ad es. il pci id per i device pci) e, opzionalmente, le risorse assegnate (irq, iomem...). I platform device sono molto semplici, non hanno un id univoco come il pci_id, ma sono distinti dal campo "name". Notate che la registrazione di una struct device non implica affatto che il dispositivo sia gestito dal sistema (abbia un driver), ma semplicemente che "esiste". Ad esempio ogni qual volta inserite un winmodem usb, viene dinamicamente generato e registrato un device usb che lo rappresenta, anche se il dispositivo in realtà è un paperweight.
La (de)registrazione può essere fatta dinamicamente: il driver model prevede intrinsecamente l'hotplug.
Potete compilare e inserire il driver, notando come vengano creati i device dummy_dev0 e dummy_dev1 in /sys/bus/platform/device.

Accanto ai device esistono i device_driver. Sono anch'essi specifici per tipo di bus (dialogare con un dispositivo usb è per forza diverso dal dialogare con un dispositivo pci). Questo modulo è lo skull di un driver per il dispositivo creato nel modulo precedente:

Codice:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>

static int __devinit drv_probe(struct device * dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	printk(KERN_INFO "%s: probing device %s:%d\n", __func__, pdev->name, pdev->id);
	return 0;
}

static int __devexit drv_remove(struct device * dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	printk(KERN_INFO "%s: removing device %s:%d\n", __func__, pdev->name, pdev->id);
	return 0;
}

static void drv_shutdown(struct device * dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	printk(KERN_INFO "%s: shutting down device %s:%d\n", __func__,
		pdev->name, pdev->id);
}

static int drv_suspend(struct device * dev, u32 state, u32 level)
{
	struct platform_device *pdev = to_platform_device(dev);
	printk(KERN_INFO "%s: suspending device %s:%d state=%d lev=%d\n",
		__func__, pdev->name, pdev->id, state, level);
	return 0;
}

static int drv_resume(struct device * dev, u32 level)
{
	struct platform_device *pdev = to_platform_device(dev);
	printk(KERN_INFO "%s: resuming device %s:%d lev=%d\n", __func__,
		pdev->name, pdev->id, level);
	return 0;
}

static struct device_driver drv = {
	.name 		= "dummy_dev",
	.bus	 	= &platform_bus_type,
	.probe 		= drv_probe,
	.remove		= __devexit_p(drv_remove),
	.shutdown	= drv_shutdown,
	.suspend	= drv_suspend,
	.resume		= drv_resume
};

static int __init drv_init(void)
{
	return driver_register(&drv);
}

static void __exit drv_exit(void)
{
	driver_unregister(&drv);
}

module_init(drv_init);
module_exit(drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ilsensine");
MODULE_DESCRIPTION("dummy (platform) device driver");
All'atto dell'inserimento del driver, il kernel controlla tutti i device su quel bus non gestiti da un driver, verifica che l'identificativo del device coincida con quello specificato nel driver (per i platform_device l'identificativo univoco è semplicemente il "nome"), e in caso di match chiama la funzione "probe" del driver. Il driver può quindi verificare la gestibilità del device, allocare le risorse necessarie ecc, oppure restituire un codice d'errore nel caso che il dispositivo non sia gestibile per qualche motivo.
*importante*: come un "device" può esistere senza un "driver", è vero anche il viceversa: il modulo "driver" può essere presente in memoria anche se il "device" non esiste, ed essere invocato solo quando il "device" compare. Questo comportamento è fondamentale per supportare l'hotplug. Potete ad esempio provare a caricare il secondo modulo, e verificare che compare in /sys/bus/platform/drivers.
Potete giocare con i due moduli illustrati, controllando i messaggi stampati alla rimozione/caricamento degli stessi.
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al
andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12

Ultima modifica di ilsensine : 16-01-2005 alle 18:55.
ilsensine è offline