Hoi mede-techneuten,
ik ben vanochtend maar eens begonnen met het schrijven van mijn eerste device driver onder linux. Ik gebruik Ubuntu op het moment, maar het idee is dat deze driver straks op een embedded linux versie gaat draaien.
De driver zou een Analog Devices ADV7180 aan moeten gaan sturen. Deze chip is een simpele videodecoder welke aanstuurbaar is via I2C. Hij stuurt zijn beelden over een aparte databus en werkt met een externe klok en geeft deze klok door aan mijn ARM11. Maar dat gaat al veel te ver, want ik krijg de simpele functionaliteit al niet aan de praat.
Het scenario:
Mijn driver zal een character-device aan gaan sturen en gebruik gaan maken van de file-operaties. Aangezien de ADV7180 via I2C bestuurbaar is, wil ik een shadowmap gaan maken van zijn registers. Dit zijn maximaal 256 registers van elk 8 bits. Enkele registers zijn alleen writeable, anderen alleen readable.
Wat ik wilde doen is met seek de offset voor de I2C actie te bepalen en dan daar de read of write op uitvoeren als de gebruiker read of write naar de file descriptor.
Mijn driver code op dit moment om te testen of ik vanuit userspace mijn driver kan benaderen:
Nu compileert dit gewoon zonder errors en krijg ik netjes een kernel module.
Dan insmod ik de kernel module zodat deze geinitialiseerd wordt.
Kijk in de dmseg om te bepalen wat zijn major en minor nummers zijn.
Deze nummers gebruik ik dan om te mknod-en.
Dan om te testen of ik de open, write, read en release functies aan kan roepen doe ik simpel weg een echo naar de /dev/adv7180 en een cat van de /dev/adv7180.
Dan krijg ik als error:
Dit is zover ik gekomen ben, en nu ben ik even de kluts kwijt.
ik ben vanochtend maar eens begonnen met het schrijven van mijn eerste device driver onder linux. Ik gebruik Ubuntu op het moment, maar het idee is dat deze driver straks op een embedded linux versie gaat draaien.
De driver zou een Analog Devices ADV7180 aan moeten gaan sturen. Deze chip is een simpele videodecoder welke aanstuurbaar is via I2C. Hij stuurt zijn beelden over een aparte databus en werkt met een externe klok en geeft deze klok door aan mijn ARM11. Maar dat gaat al veel te ver, want ik krijg de simpele functionaliteit al niet aan de praat.
Het scenario:
Mijn driver zal een character-device aan gaan sturen en gebruik gaan maken van de file-operaties. Aangezien de ADV7180 via I2C bestuurbaar is, wil ik een shadowmap gaan maken van zijn registers. Dit zijn maximaal 256 registers van elk 8 bits. Enkele registers zijn alleen writeable, anderen alleen readable.
Wat ik wilde doen is met seek de offset voor de I2C actie te bepalen en dan daar de read of write op uitvoeren als de gebruiker read of write naar de file descriptor.
Mijn driver code op dit moment om te testen of ik vanuit userspace mijn driver kan benaderen:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
| #include <linux/module.h> #include <linux/fs.h> #include <linux/fcntl.h> #include <linux/cdev.h> /* Defines */ #define DRIVER_DESC "ADV7180 Driver" #define ADV7180_MAJOR 0 #define ADV7180_MINOR 0 #define ADV7180_DEVS 1 /* Declaration of adv7180 functions */ static int adv7180_open ( struct inode *inode, struct file *filp ); static int adv7180_release( struct inode *inode, struct file *filp ); static loff_t adv7180_seek ( struct file *filp, loff_t off, int whence ); static ssize_t adv7180_read ( struct file *filp, char *buf, size_t count, loff_t *f_pos ); static ssize_t adv7180_write ( struct file *filp, const char *buf, size_t count, loff_t *f_pos ); /* Struct which declares which of the file operations are applicable to this driver */ struct file_operations adv7180_fops = { .owner = THIS_MODULE, .open = adv7180_open, .llseek = adv7180_seek, .read = adv7180_read, .write = adv7180_write, .release = adv7180_release }; struct adv7180_cdev { unsigned char mutex; struct cdev cdev; } adv7180_dev; static int adv7180_major = 0; static int adv7180_minor = 0; static int __init adv7180_init( void ) { int ret; int dev; printk(KERN_INFO "Init " DRIVER_DESC "\n"); if (ADV7180_MAJOR) { dev = MKDEV( ADV7180_MAJOR, ADV7180_MINOR ); ret = register_chrdev_region( dev, ADV7180_DEVS, DRIVER_DESC ); } else { ret = alloc_chrdev_region( &dev, ADV7180_MINOR, ADV7180_DEVS, DRIVER_DESC ); } adv7180_major = MAJOR( dev ); adv7180_minor = MINOR( dev ); if (ret < 0) { printk( KERN_ERR "ADV7180: Can't get major %d (err %d)\n", adv7180_major, ret ); return ret; } else { printk( KERN_INFO "ADV7180 located at %d %d\n", adv7180_major, adv7180_minor ); } cdev_init( &(adv7180_dev.cdev), &adv7180_fops ); adv7180_dev.cdev.owner = THIS_MODULE; adv7180_dev.cdev.ops = &adv7180_fops; ret = cdev_add( &(adv7180_dev.cdev), adv7180_major, ADV7180_DEVS ); if (ret < 0) { printk( KERN_ERR "ADV7180: Can't add device (err %d)\n", ret ); return ret; } return 0; } static void __exit adv7180_exit( void ) { printk(KERN_INFO "Exit " DRIVER_DESC "\n"); unregister_chrdev_region( adv7180_major, ADV7180_DEVS ); } static int adv7180_open( struct inode *inode, struct file *filp ) { printk(KERN_INFO "OPEN\n"); return 0; } static int adv7180_release( struct inode *inode, struct file *filp ) { printk(KERN_INFO "RELEASE\n"); return 0; } static loff_t adv7180_seek( struct file *filp, loff_t off, int whence ) { printk(KERN_INFO "SEEK\n"); return 0; } static ssize_t adv7180_read( struct file *filp, char *buf, size_t count, loff_t *f_pos ) { printk(KERN_INFO "READ\n"); return 0; } static ssize_t adv7180_write( struct file *filp, const char *buf, size_t count, loff_t *f_pos ) { printk(KERN_INFO "WRITE\n"); return 0; } MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Jaap Koekkoek"); MODULE_LICENSE("GPL"); module_init(adv7180_init); module_exit(adv7180_exit); |
Nu compileert dit gewoon zonder errors en krijg ik netjes een kernel module.
Dan insmod ik de kernel module zodat deze geinitialiseerd wordt.
Kijk in de dmseg om te bepalen wat zijn major en minor nummers zijn.
Deze nummers gebruik ik dan om te mknod-en.
code:
1
2
3
4
5
6
7
| sudo insmod adv7180.ko dmesg | tail [177865.245290] Init ADV7180 Driver [177865.245294] ADV7180 located at 244 0 mknod /dev/adv7180 c 244 0 |
Dan om te testen of ik de open, write, read en release functies aan kan roepen doe ik simpel weg een echo naar de /dev/adv7180 en een cat van de /dev/adv7180.
Dan krijg ik als error:
code:
1
| bash: /dev/adv7180: No such device or address |
Dit is zover ik gekomen ben, en nu ben ik even de kluts kwijt.
[ Voor 0% gewijzigd door moto-moi op 07-02-2010 11:32 . Reden: even highlighting aangezet voor je code :) ]
A byte walks into a bar and orders a pint. Bartender asks him "What's wrong?" Byte says "Parity error." Bartender nods and says "Yeah, I thought you looked a bit off."