-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdevices.c
150 lines (115 loc) · 3.16 KB
/
devices.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include <linux/cdev.h>
#include <linux/semaphore.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include "scull.h"
int create_dev(void)
{
int result = -1;
dev_t dev;
if(scull_major){
dev = MKDEV(scull_major, scull_minor);
result = register_chrdev_region(dev, scull_nr_devs, "scull");
}else{
result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");
scull_major = MAJOR(dev);
}
if(result < 0){
/* clean up */
printk(KERN_WARNING "scull: Can't get major device number %d\n", scull_major);
}
return result;
}
struct scull_qset *scull_follow(struct scull_dev *dev, int n)
{
struct scull_qset *qs = dev->data;
/* Allocate first qset explictly if needed */
if(!qs){
qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if(qs == NULL)
return NULL; /* Don't care */
memset(qs,0,sizeof(struct scull_qset));
}
/* Follow the list */
while(n--){
if(!qs->next){
qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if(qs->next == NULL)
return NULL; /* Never mind */
memset(qs->next,0,sizeof(struct scull_qset));
}
qs = qs->next;
continue;
}
return qs;
}
/* seq_file proc methods */
static void *scull_seq_start(struct seq_file *s, loff_t *pos) /* pos is an index to a scull_devices array */
{
if (*pos >= scull_nr_devs)
return NULL; /* no more to read */
return scull_devices + *pos;
}
static void *scull_seq_next(struct seq_file *s, void *v, loff_t *pos) /* *v is the iterator returned by previous call to start or next */
{
(*pos)++;
if (*pos >= scull_nr_devs){
return NULL;
}
return scull_devices + *pos;
}
static void scull_seq_stop(struct seq_file *s, void *v)
{
}
static int scull_seq_show(struct seq_file *s, void *v)
{
struct scull_dev *dev = (struct scull_dev *) v;
struct scull_qset *d;
int i;
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
seq_printf(s, "\nDevice %i: qset %i, q %i, sz %li\n",
(int) (dev - scull_devices), dev->qset,
dev->quantum, dev->size);
for(d = dev->data; d; d = d->next) { /* scan the list */
seq_printf(s, " item at %p, qset at %p\n", d, d->data);
if(d->data && !d->next) /*dump only the last item */
for(i=0; i < dev->qset; i++){
if(d->data[i])
seq_printf(s, " % 4i: %8p\n",
i, d->data[i]);
}
}
up(&dev->sem);
return 0;
}
static struct seq_operations scull_seq_ops = {
.start = scull_seq_start,
.next = scull_seq_next,
.show = scull_seq_show,
.stop = scull_seq_stop
};
static int scull_proc_open(struct inode *inode, struct file *file)
{
return seq_open(file, &scull_seq_ops);
}
static struct file_operations scull_proc_ops = {
.owner = THIS_MODULE,
.open = scull_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
void procfile_setup(void)
{
struct proc_dir_entry *entry;
entry = create_proc_entry("scullseq", 0, NULL);
if(entry)
entry->proc_fops = &scull_proc_ops;
}
/* No clean up work to do, no stop method then */