?? ldd ——scull(main.c).txt
字號:
1 /*
2 * main.c -- the bare scull char module
3 *
4 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
5 * Copyright (C) 2001 O'Reilly & Associates
6 *
7 * The source code in this file can be freely used, adapted,
8 * and redistributed in source or binary form, so long as an
9 * acknowledgment appears in derived source files. The citation
10 * should list that the code comes from the book "Linux Device
11 * Drivers" by Alessandro Rubini and Jonathan Corbet, published
12 * by O'Reilly & Associates. No warranty is attached;
13 * we cannot take responsibility for errors or fitness for use.
14 *
15 */
16
17 /*
18 #include <linux/config.h>
19 */
20 #include <linux/module.h>
21 #include <linux/moduleparam.h>
22 #include <linux/init.h>
23
24 #include <linux/kernel.h> /* printk() */
25 #include <linux/slab.h> /* kmalloc() */
26 #include <linux/fs.h> /* everything... */
27 #include <linux/errno.h> /* error codes */
28 #include <linux/types.h> /* size_t */
29 #include <linux/proc_fs.h>
30 #include <linux/fcntl.h> /* O_ACCMODE */
31 #include <linux/seq_file.h>
32 #include <linux/cdev.h>
33
34 #include <asm/system.h> /* cli(), *_flags */
35 #include <asm/uaccess.h> /* copy_*_user */
36
37 #include "scull.h" /* local definitions */
38
39 /*
40 * Our parameters which can be set at load time.
41 */
42
43 int scull_major = SCULL_MAJOR;
44 int scull_minor = 0;
45 int scull_nr_devs = SCULL_NR_DEVS; /* number of bare scull devices */
46 int scull_quantum = SCULL_QUANTUM;
47 int scull_qset = SCULL_QSET;
48
49 module_param(scull_major, int, S_IRUGO);
50 module_param(scull_minor, int, S_IRUGO);
51 module_param(scull_nr_devs, int, S_IRUGO);
52 module_param(scull_quantum, int, S_IRUGO);
53 module_param(scull_qset, int, S_IRUGO);
54
55 MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet");
56 MODULE_LICENSE("Dual BSD/GPL");
57
58 struct scull_dev *scull_devices; /* allocated in scull_init_module */
59
60
61 /*
62 * Empty out the scull device; must be called with the device
63 * semaphore held.
64 */
65 int scull_trim(struct scull_dev *dev)
66 {
67 struct scull_qset *next, *dptr;
68 int qset = dev->qset; /* "dev" is not-null */
69 int i;
70
71 for (dptr = dev->data; dptr; dptr = next) { /* all the list items */
72 if (dptr->data) {
73 for (i = 0; i < qset; i++)
74 kfree(dptr->data[i]);
75 kfree(dptr->data);
76 dptr->data = NULL;
77 }
78 next = dptr->next;
79 kfree(dptr);
80 }
81 dev->size = 0;
82 dev->quantum = scull_quantum;
83 dev->qset = scull_qset;
84 dev->data = NULL;
85 return 0;
86 }
87 #ifdef SCULL_DEBUG /* use proc only if debugging */
88 /*
89 * The proc filesystem: function to read and entry
90 */
91
92 int scull_read_procmem(char *buf, char **start, off_t offset,
93 int count, int *eof, void *data)
94 {
95 int i, j, len = 0;
96 int limit = count - 80; /* Don't print more than this */
97
98 for (i = 0; i < scull_nr_devs && len <= limit; i++) {
99 struct scull_dev *d = &scull_devices[i];
100 struct scull_qset *qs = d->data;
101 if (down_interruptible(&d->sem))
102 return -ERESTARTSYS;
103 len += sprintf(buf+len,"\nDevice %i: qset %i, q %i, sz %li\n",
104 i, d->qset, d->quantum, d->size);
105 for (; qs && len <= limit; qs = qs->next) { /* scan the list */
106 len += sprintf(buf + len, " item at %p, qset at %p\n",
107 qs, qs->data);
108 if (qs->data && !qs->next) /* dump only the last item */
109 for (j = 0; j < d->qset; j++) {
110 if (qs->data[j])
111 len += sprintf(buf + len,
112 " % 4i: %8p\n",
113 j, qs->data[j]);
114 }
115 }
116 up(&scull_devices[i].sem);
117 }
118 *eof = 1;
119 return len;
120 }
121
122
123 /*
124 * For now, the seq_file implementation will exist in parallel. The
125 * older read_procmem function should maybe go away, though.
126 */
127
128 /*
129 * Here are our sequence iteration methods. Our "position" is
130 * simply the device number.
131 */
132 static void *scull_seq_start(struct seq_file *s, loff_t *pos)
133 {
134 if (*pos >= scull_nr_devs)
135 return NULL; /* No more to read */
136 return scull_devices + *pos;
137 }
138
139 static void *scull_seq_next(struct seq_file *s, void *v, loff_t *pos)
140 {
141 (*pos)++;
142 if (*pos >= scull_nr_devs)
143 return NULL;
144 return scull_devices + *pos;
145 }
146
147 static void scull_seq_stop(struct seq_file *s, void *v)
148 {
149 /* Actually, there's nothing to do here */
150 }
151
152 static int scull_seq_show(struct seq_file *s, void *v)
153 {
154 struct scull_dev *dev = (struct scull_dev *) v;
155 struct scull_qset *d;
156 int i;
157
158 if (down_interruptible(&dev->sem))
159 return -ERESTARTSYS;
160 seq_printf(s, "\nDevice %i: qset %i, q %i, sz %li\n",
161 (int) (dev - scull_devices), dev->qset,
162 dev->quantum, dev->size);
163 for (d = dev->data; d; d = d->next) { /* scan the list */
164 seq_printf(s, " item at %p, qset at %p\n", d, d->data);
165 if (d->data && !d->next) /* dump only the last item */
166 for (i = 0; i < dev->qset; i++) {
167 if (d->data[i])
168 seq_printf(s, " % 4i: %8p\n",
169 i, d->data[i]);
170 }
171 }
172 up(&dev->sem);
173 return 0;
174 }
175
176 /*
177 * Tie the sequence operators up.
178 */
179 static struct seq_operations scull_seq_ops = {
180 .start = scull_seq_start,
181 .next = scull_seq_next,
182 .stop = scull_seq_stop,
183 .show = scull_seq_show
184 };
185
186 /*
187 * Now to implement the /proc file we need only make an open
188 * method which sets up the sequence operators.
189 */
190 static int scull_proc_open(struct inode *inode, struct file *file)
191 {
192 return seq_open(file, &scull_seq_ops);
193 }
194
195 /*
196 * Create a set of file operations for our proc file.
197 */
198 static struct file_operations scull_proc_ops = {
199 .owner = THIS_MODULE,
200 .open = scull_proc_open,
201 .read = seq_read,
202 .llseek = seq_lseek,
203 .release = seq_release
204 };
205
206
207 /*
208 * Actually create (and remove) the /proc file(s).
209 */
210
211 static void scull_create_proc(void)
212 {
213 struct proc_dir_entry *entry;
214 create_proc_read_entry("scullmem", 0 /* default mode */,
215 NULL /* parent dir */, scull_read_procmem,
216 NULL /* client data */);
217 entry = create_proc_entry("scullseq", 0, NULL);
218 if (entry)
219 entry->proc_fops = &scull_proc_ops;
220 }
221
222 static void scull_remove_proc(void)
223 {
224 /* no problem if it was not registered */
225 remove_proc_entry("scullmem", NULL /* parent dir */);
226 remove_proc_entry("scullseq", NULL);
227 }
228
229
230 #endif /* SCULL_DEBUG */
231
232
233
234
235
236 /*
237 * Open and close
238 */
239
240 int scull_open(struct inode *inode, struct file *filp)
241 {
242 struct scull_dev *dev; /* device information */
243
244 dev = container_of(inode->i_cdev, struct scull_dev, cdev);
245 filp->private_data = dev; /* for other methods */
246
247 /* now trim to 0 the length of the device if open was write-only */
248 if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
249 if (down_interruptible(&dev->sem))
250 return -ERESTARTSYS;
251 scull_trim(dev); /* ignore errors */
252 up(&dev->sem);
253 }
254 return 0; /* success */
255 }
256
257 int scull_release(struct inode *inode, struct file *filp)
258 {
259 return 0;
260 }
261 /*
262 * Follow the list
263 */
264 struct scull_qset *scull_follow(struct scull_dev *dev, int n)
265 {
266 struct scull_qset *qs = dev->data;
267
268 /* Allocate first qset explicitly if need be */
269 if (! qs) {
270 qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
271 if (qs == NULL)
272 return NULL; /* Never mind */
273 memset(qs, 0, sizeof(struct scull_qset));
274 }
275
276 /* Then follow the list */
277 while (n--) {
278 if (!qs->next) {
279 qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
280 if (qs->next == NULL)
281 return NULL; /* Never mind */
282 memset(qs->next, 0, sizeof(struct scull_qset));
283 }
284 qs = qs->next;
285 continue;
286 }
287 return qs;
288 }
289
290 /*
291 * Data management: read and write
292 */
293
294 ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
295 loff_t *f_pos)
296 {
297 struct scull_dev *dev = filp->private_data;
298 struct scull_qset *dptr; /* the first listitem */
299 int quantum = dev->quantum, qset = dev->qset;
300 int itemsize = quantum * qset; /* how many bytes in the listitem */
301 int item, s_pos, q_pos, rest;
302 ssize_t retval = 0;
303
304 if (down_interruptible(&dev->sem))
305 return -ERESTARTSYS;
306 if (*f_pos >= dev->size)
307 goto out;
308 if (*f_pos + count > dev->size)
309 count = dev->size - *f_pos;
310
311 /* find listitem, qset index, and offset in the quantum */
312 item = (long)*f_pos / itemsize;
313 rest = (long)*f_pos % itemsize;
314 s_pos = rest / quantum; q_pos = rest % quantum;
315
316 /* follow the list up to the right position (defined elsewhere) */
317 dptr = scull_follow(dev, item);
318
319 if (dptr == NULL || !dptr->data || ! dptr->data[s_pos])
320 goto out; /* don't fill holes */
321
322 /* read only up to the end of this quantum */
323 if (count > quantum - q_pos)
324 count = quantum - q_pos;
325
326 if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count)) {
327 retval = -EFAULT;
328 goto out;
329 }
330 *f_pos += count;
331 retval = count;
332
333 out:
334 up(&dev->sem);
335 return retval;
336 }
337
338 ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -