Rev 846 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
846 | giacomo | 1 | /*****************************************************************************/ |
2 | |||
3 | /* |
||
4 | * inode.c -- Inode/Dentry functions for the USB device file system. |
||
5 | * |
||
6 | * Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch) |
||
7 | * Copyright (C) 2001,2002 Greg Kroah-Hartman (greg@kroah.com) |
||
8 | * |
||
9 | * This program is free software; you can redistribute it and/or modify |
||
10 | * it under the terms of the GNU General Public License as published by |
||
11 | * the Free Software Foundation; either version 2 of the License, or |
||
12 | * (at your option) any later version. |
||
13 | * |
||
14 | * This program is distributed in the hope that it will be useful, |
||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
17 | * GNU General Public License for more details. |
||
18 | * |
||
19 | * You should have received a copy of the GNU General Public License |
||
20 | * along with this program; if not, write to the Free Software |
||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
22 | * |
||
23 | * History: |
||
24 | * 0.1 04.01.2000 Created |
||
25 | * 0.2 10.12.2001 converted to use the vfs layer better |
||
26 | */ |
||
27 | |||
28 | /*****************************************************************************/ |
||
29 | |||
30 | #include <linux/config.h> |
||
31 | #include <linux/module.h> |
||
32 | #include <linux/fs.h> |
||
33 | #include <linux/mount.h> |
||
34 | #include <linux/pagemap.h> |
||
35 | #include <linux/init.h> |
||
36 | #include <linux/proc_fs.h> |
||
37 | #include <linux/usb.h> |
||
38 | #include <linux/namei.h> |
||
39 | #include <linux/usbdevice_fs.h> |
||
40 | #include <linux/smp_lock.h> |
||
41 | #include <linux/parser.h> |
||
42 | #include <asm/byteorder.h> |
||
43 | |||
44 | static struct super_operations usbfs_ops; |
||
45 | static struct file_operations default_file_operations; |
||
46 | static struct inode_operations usbfs_dir_inode_operations; |
||
47 | static struct vfsmount *usbdevfs_mount; |
||
48 | static struct vfsmount *usbfs_mount; |
||
49 | static int usbdevfs_mount_count; /* = 0 */ |
||
50 | static int usbfs_mount_count; /* = 0 */ |
||
51 | |||
52 | static struct dentry *devices_usbdevfs_dentry; |
||
53 | static struct dentry *devices_usbfs_dentry; |
||
54 | static int num_buses; /* = 0 */ |
||
55 | |||
56 | static uid_t devuid; /* = 0 */ |
||
57 | static uid_t busuid; /* = 0 */ |
||
58 | static uid_t listuid; /* = 0 */ |
||
59 | static gid_t devgid; /* = 0 */ |
||
60 | static gid_t busgid; /* = 0 */ |
||
61 | static gid_t listgid; /* = 0 */ |
||
62 | static umode_t devmode = S_IWUSR | S_IRUGO; |
||
63 | static umode_t busmode = S_IXUGO | S_IRUGO; |
||
64 | static umode_t listmode = S_IRUGO; |
||
65 | |||
66 | enum { |
||
67 | Opt_devuid, Opt_devgid, Opt_devmode, |
||
68 | Opt_busuid, Opt_busgid, Opt_busmode, |
||
69 | Opt_listuid, Opt_listgid, Opt_listmode, |
||
70 | Opt_err, |
||
71 | }; |
||
72 | |||
73 | static match_table_t tokens = { |
||
74 | {Opt_devuid, "devuid=%u"}, |
||
75 | {Opt_devgid, "devgid=%u"}, |
||
76 | {Opt_devmode, "devmode=%o"}, |
||
77 | {Opt_busuid, "busuid=%u"}, |
||
78 | {Opt_busgid, "busgid=%u"}, |
||
79 | {Opt_busmode, "busmode=%o"}, |
||
80 | {Opt_listuid, "listuid=%u"}, |
||
81 | {Opt_listgid, "listgid=%u"}, |
||
82 | {Opt_listmode, "listmode=%o"}, |
||
83 | {Opt_err, NULL} |
||
84 | }; |
||
85 | |||
86 | static int parse_options(struct super_block *s, char *data) |
||
87 | { |
||
88 | char *p; |
||
89 | int option; |
||
90 | |||
91 | while ((p = strsep(&data, ",")) != NULL) { |
||
92 | substring_t args[MAX_OPT_ARGS]; |
||
93 | int token; |
||
94 | if (!*p) |
||
95 | continue; |
||
96 | |||
97 | token = match_token(p, tokens, args); |
||
98 | switch (token) { |
||
99 | case Opt_devuid: |
||
100 | if (match_int(&args[0], &option)) |
||
101 | return -EINVAL; |
||
102 | devuid = option; |
||
103 | break; |
||
104 | case Opt_devgid: |
||
105 | if (match_int(&args[0], &option)) |
||
106 | return -EINVAL; |
||
107 | devgid = option; |
||
108 | break; |
||
109 | case Opt_devmode: |
||
110 | if (match_octal(&args[0], &option)) |
||
111 | return -EINVAL; |
||
112 | devmode = option & S_IRWXUGO; |
||
113 | break; |
||
114 | case Opt_busuid: |
||
115 | if (match_int(&args[0], &option)) |
||
116 | return -EINVAL; |
||
117 | busuid = option; |
||
118 | break; |
||
119 | case Opt_busgid: |
||
120 | if (match_int(&args[0], &option)) |
||
121 | return -EINVAL; |
||
122 | busgid = option; |
||
123 | break; |
||
124 | case Opt_busmode: |
||
125 | if (match_octal(&args[0], &option)) |
||
126 | return -EINVAL; |
||
127 | busmode = option & S_IRWXUGO; |
||
128 | break; |
||
129 | case Opt_listuid: |
||
130 | if (match_int(&args[0], &option)) |
||
131 | return -EINVAL; |
||
132 | listuid = option; |
||
133 | break; |
||
134 | case Opt_listgid: |
||
135 | if (match_int(&args[0], &option)) |
||
136 | return -EINVAL; |
||
137 | listgid = option; |
||
138 | break; |
||
139 | case Opt_listmode: |
||
140 | if (match_octal(&args[0], &option)) |
||
141 | return -EINVAL; |
||
142 | listmode = option & S_IRWXUGO; |
||
143 | break; |
||
144 | default: |
||
145 | err("usbfs: unrecognised mount option \"%s\" " |
||
146 | "or missing value\n", p); |
||
147 | return -EINVAL; |
||
148 | } |
||
149 | } |
||
150 | |||
151 | return 0; |
||
152 | } |
||
153 | |||
154 | static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t dev) |
||
155 | { |
||
156 | struct inode *inode = new_inode(sb); |
||
157 | |||
158 | if (inode) { |
||
159 | inode->i_mode = mode; |
||
160 | inode->i_uid = current->fsuid; |
||
161 | inode->i_gid = current->fsgid; |
||
162 | inode->i_blksize = PAGE_CACHE_SIZE; |
||
163 | inode->i_blocks = 0; |
||
164 | inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; |
||
165 | switch (mode & S_IFMT) { |
||
166 | default: |
||
167 | init_special_inode(inode, mode, dev); |
||
168 | break; |
||
169 | case S_IFREG: |
||
170 | inode->i_fop = &default_file_operations; |
||
171 | break; |
||
172 | case S_IFDIR: |
||
173 | inode->i_op = &usbfs_dir_inode_operations; |
||
174 | inode->i_fop = &simple_dir_operations; |
||
175 | |||
176 | /* directory inodes start off with i_nlink == 2 (for "." entry) */ |
||
177 | inode->i_nlink++; |
||
178 | break; |
||
179 | } |
||
180 | } |
||
181 | return inode; |
||
182 | } |
||
183 | |||
184 | /* SMP-safe */ |
||
185 | static int usbfs_mknod (struct inode *dir, struct dentry *dentry, int mode, |
||
186 | dev_t dev) |
||
187 | { |
||
188 | struct inode *inode = usbfs_get_inode(dir->i_sb, mode, dev); |
||
189 | int error = -EPERM; |
||
190 | |||
191 | if (dentry->d_inode) |
||
192 | return -EEXIST; |
||
193 | |||
194 | if (inode) { |
||
195 | d_instantiate(dentry, inode); |
||
196 | dget(dentry); |
||
197 | error = 0; |
||
198 | } |
||
199 | return error; |
||
200 | } |
||
201 | |||
202 | static int usbfs_mkdir (struct inode *dir, struct dentry *dentry, int mode) |
||
203 | { |
||
204 | int res; |
||
205 | |||
206 | mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR; |
||
207 | res = usbfs_mknod (dir, dentry, mode, 0); |
||
208 | if (!res) |
||
209 | dir->i_nlink++; |
||
210 | return res; |
||
211 | } |
||
212 | |||
213 | static int usbfs_create (struct inode *dir, struct dentry *dentry, int mode) |
||
214 | { |
||
215 | mode = (mode & S_IALLUGO) | S_IFREG; |
||
216 | return usbfs_mknod (dir, dentry, mode, 0); |
||
217 | } |
||
218 | |||
219 | static inline int usbfs_positive (struct dentry *dentry) |
||
220 | { |
||
221 | return dentry->d_inode && !d_unhashed(dentry); |
||
222 | } |
||
223 | |||
224 | static int usbfs_empty (struct dentry *dentry) |
||
225 | { |
||
226 | struct list_head *list; |
||
227 | |||
228 | spin_lock(&dcache_lock); |
||
229 | |||
230 | list_for_each(list, &dentry->d_subdirs) { |
||
231 | struct dentry *de = list_entry(list, struct dentry, d_child); |
||
232 | if (usbfs_positive(de)) { |
||
233 | spin_unlock(&dcache_lock); |
||
234 | return 0; |
||
235 | } |
||
236 | } |
||
237 | |||
238 | spin_unlock(&dcache_lock); |
||
239 | return 1; |
||
240 | } |
||
241 | |||
242 | static int usbfs_unlink (struct inode *dir, struct dentry *dentry) |
||
243 | { |
||
244 | struct inode *inode = dentry->d_inode; |
||
245 | down(&inode->i_sem); |
||
246 | dentry->d_inode->i_nlink--; |
||
247 | dput(dentry); |
||
248 | up(&inode->i_sem); |
||
249 | d_delete(dentry); |
||
250 | return 0; |
||
251 | } |
||
252 | |||
253 | static void d_unhash(struct dentry *dentry) |
||
254 | { |
||
255 | dget(dentry); |
||
256 | spin_lock(&dcache_lock); |
||
257 | switch (atomic_read(&dentry->d_count)) { |
||
258 | default: |
||
259 | spin_unlock(&dcache_lock); |
||
260 | shrink_dcache_parent(dentry); |
||
261 | spin_lock(&dcache_lock); |
||
262 | if (atomic_read(&dentry->d_count) != 2) |
||
263 | break; |
||
264 | case 2: |
||
265 | __d_drop(dentry); |
||
266 | } |
||
267 | spin_unlock(&dcache_lock); |
||
268 | } |
||
269 | |||
270 | static int usbfs_rmdir(struct inode *dir, struct dentry *dentry) |
||
271 | { |
||
272 | int error = -ENOTEMPTY; |
||
273 | struct inode * inode = dentry->d_inode; |
||
274 | |||
275 | down(&inode->i_sem); |
||
276 | d_unhash(dentry); |
||
277 | if (usbfs_empty(dentry)) { |
||
278 | dentry->d_inode->i_nlink -= 2; |
||
279 | dput(dentry); |
||
280 | inode->i_flags |= S_DEAD; |
||
281 | dir->i_nlink--; |
||
282 | error = 0; |
||
283 | } |
||
284 | up(&inode->i_sem); |
||
285 | if (!error) |
||
286 | d_delete(dentry); |
||
287 | dput(dentry); |
||
288 | return error; |
||
289 | } |
||
290 | |||
291 | |||
292 | /* default file operations */ |
||
293 | static ssize_t default_read_file (struct file *file, char __user *buf, |
||
294 | size_t count, loff_t *ppos) |
||
295 | { |
||
296 | return 0; |
||
297 | } |
||
298 | |||
299 | static ssize_t default_write_file (struct file *file, const char __user *buf, |
||
300 | size_t count, loff_t *ppos) |
||
301 | { |
||
302 | return count; |
||
303 | } |
||
304 | |||
305 | static loff_t default_file_lseek (struct file *file, loff_t offset, int orig) |
||
306 | { |
||
307 | loff_t retval = -EINVAL; |
||
308 | |||
309 | down(&file->f_dentry->d_inode->i_sem); |
||
310 | switch(orig) { |
||
311 | case 0: |
||
312 | if (offset > 0) { |
||
313 | file->f_pos = offset; |
||
314 | retval = file->f_pos; |
||
315 | } |
||
316 | break; |
||
317 | case 1: |
||
318 | if ((offset + file->f_pos) > 0) { |
||
319 | file->f_pos += offset; |
||
320 | retval = file->f_pos; |
||
321 | } |
||
322 | break; |
||
323 | default: |
||
324 | break; |
||
325 | } |
||
326 | up(&file->f_dentry->d_inode->i_sem); |
||
327 | return retval; |
||
328 | } |
||
329 | |||
330 | static int default_open (struct inode *inode, struct file *file) |
||
331 | { |
||
332 | if (inode->u.generic_ip) |
||
333 | file->private_data = inode->u.generic_ip; |
||
334 | |||
335 | return 0; |
||
336 | } |
||
337 | |||
338 | static struct file_operations default_file_operations = { |
||
339 | .read = default_read_file, |
||
340 | .write = default_write_file, |
||
341 | .open = default_open, |
||
342 | .llseek = default_file_lseek, |
||
343 | }; |
||
344 | |||
345 | static struct inode_operations usbfs_dir_inode_operations = { |
||
346 | .lookup = simple_lookup, |
||
347 | }; |
||
348 | |||
349 | static struct super_operations usbfs_ops = { |
||
350 | .statfs = simple_statfs, |
||
351 | .drop_inode = generic_delete_inode, |
||
352 | }; |
||
353 | |||
354 | static int usbfs_fill_super(struct super_block *sb, void *data, int silent) |
||
355 | { |
||
356 | struct inode *inode; |
||
357 | struct dentry *root; |
||
358 | |||
359 | if (parse_options(sb, data)) { |
||
360 | warn("usbfs: mount parameter error:"); |
||
361 | return -EINVAL; |
||
362 | } |
||
363 | |||
364 | sb->s_blocksize = PAGE_CACHE_SIZE; |
||
365 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; |
||
366 | sb->s_magic = USBDEVICE_SUPER_MAGIC; |
||
367 | sb->s_op = &usbfs_ops; |
||
368 | inode = usbfs_get_inode(sb, S_IFDIR | 0755, 0); |
||
369 | |||
370 | if (!inode) { |
||
371 | dbg("%s: could not get inode!",__FUNCTION__); |
||
372 | return -ENOMEM; |
||
373 | } |
||
374 | |||
375 | root = d_alloc_root(inode); |
||
376 | if (!root) { |
||
377 | dbg("%s: could not get root dentry!",__FUNCTION__); |
||
378 | iput(inode); |
||
379 | return -ENOMEM; |
||
380 | } |
||
381 | sb->s_root = root; |
||
382 | return 0; |
||
383 | } |
||
384 | |||
385 | static struct dentry * get_dentry(struct dentry *parent, const char *name) |
||
386 | { |
||
387 | struct qstr qstr; |
||
388 | |||
389 | qstr.name = name; |
||
390 | qstr.len = strlen(name); |
||
391 | qstr.hash = full_name_hash(name,qstr.len); |
||
392 | return lookup_hash(&qstr,parent); |
||
393 | } |
||
394 | |||
395 | |||
396 | /* |
||
397 | * fs_create_by_name - create a file, given a name |
||
398 | * @name: name of file |
||
399 | * @mode: type of file |
||
400 | * @parent: dentry of directory to create it in |
||
401 | * @dentry: resulting dentry of file |
||
402 | * |
||
403 | * This function handles both regular files and directories. |
||
404 | */ |
||
405 | static int fs_create_by_name (const char *name, mode_t mode, |
||
406 | struct dentry *parent, struct dentry **dentry) |
||
407 | { |
||
408 | int error = 0; |
||
409 | |||
410 | /* If the parent is not specified, we create it in the root. |
||
411 | * We need the root dentry to do this, which is in the super |
||
412 | * block. A pointer to that is in the struct vfsmount that we |
||
413 | * have around. |
||
414 | */ |
||
415 | if (!parent ) { |
||
416 | if (usbfs_mount && usbfs_mount->mnt_sb) { |
||
417 | parent = usbfs_mount->mnt_sb->s_root; |
||
418 | } |
||
419 | } |
||
420 | |||
421 | if (!parent) { |
||
422 | dbg("Ah! can not find a parent!"); |
||
423 | return -EFAULT; |
||
424 | } |
||
425 | |||
426 | *dentry = NULL; |
||
427 | down(&parent->d_inode->i_sem); |
||
428 | *dentry = get_dentry (parent, name); |
||
429 | if (!IS_ERR(dentry)) { |
||
430 | if ((mode & S_IFMT) == S_IFDIR) |
||
431 | error = usbfs_mkdir (parent->d_inode, *dentry, mode); |
||
432 | else |
||
433 | error = usbfs_create (parent->d_inode, *dentry, mode); |
||
434 | } else |
||
435 | error = PTR_ERR(dentry); |
||
436 | up(&parent->d_inode->i_sem); |
||
437 | |||
438 | return error; |
||
439 | } |
||
440 | |||
441 | static struct dentry *fs_create_file (const char *name, mode_t mode, |
||
442 | struct dentry *parent, void *data, |
||
443 | struct file_operations *fops, |
||
444 | uid_t uid, gid_t gid) |
||
445 | { |
||
446 | struct dentry *dentry; |
||
447 | int error; |
||
448 | |||
449 | dbg("creating file '%s'",name); |
||
450 | |||
451 | error = fs_create_by_name (name, mode, parent, &dentry); |
||
452 | if (error) { |
||
453 | dentry = NULL; |
||
454 | } else { |
||
455 | if (dentry->d_inode) { |
||
456 | if (data) |
||
457 | dentry->d_inode->u.generic_ip = data; |
||
458 | if (fops) |
||
459 | dentry->d_inode->i_fop = fops; |
||
460 | dentry->d_inode->i_uid = uid; |
||
461 | dentry->d_inode->i_gid = gid; |
||
462 | } |
||
463 | } |
||
464 | |||
465 | return dentry; |
||
466 | } |
||
467 | |||
468 | static void fs_remove_file (struct dentry *dentry) |
||
469 | { |
||
470 | struct dentry *parent = dentry->d_parent; |
||
471 | |||
472 | if (!parent || !parent->d_inode) |
||
473 | return; |
||
474 | |||
475 | down(&parent->d_inode->i_sem); |
||
476 | if (usbfs_positive(dentry)) { |
||
477 | if (dentry->d_inode) { |
||
478 | if (S_ISDIR(dentry->d_inode->i_mode)) |
||
479 | usbfs_rmdir(parent->d_inode, dentry); |
||
480 | else |
||
481 | usbfs_unlink(parent->d_inode, dentry); |
||
482 | dput(dentry); |
||
483 | } |
||
484 | } |
||
485 | up(&parent->d_inode->i_sem); |
||
486 | } |
||
487 | |||
488 | /* --------------------------------------------------------------------- */ |
||
489 | |||
490 | |||
491 | |||
492 | /* |
||
493 | * The usbdevfs name is now deprecated (as of 2.5.1). |
||
494 | * It will be removed when the 2.7.x development cycle is started. |
||
495 | * You have been warned :) |
||
496 | */ |
||
497 | static struct file_system_type usbdevice_fs_type; |
||
498 | |||
499 | static struct super_block *usb_get_sb(struct file_system_type *fs_type, |
||
500 | int flags, const char *dev_name, void *data) |
||
501 | { |
||
502 | return get_sb_single(fs_type, flags, data, usbfs_fill_super); |
||
503 | } |
||
504 | |||
505 | static struct file_system_type usbdevice_fs_type = { |
||
506 | .owner = THIS_MODULE, |
||
507 | .name = "usbdevfs", |
||
508 | .get_sb = usb_get_sb, |
||
509 | .kill_sb = kill_litter_super, |
||
510 | }; |
||
511 | |||
512 | static struct file_system_type usb_fs_type = { |
||
513 | .owner = THIS_MODULE, |
||
514 | .name = "usbfs", |
||
515 | .get_sb = usb_get_sb, |
||
516 | .kill_sb = kill_litter_super, |
||
517 | }; |
||
518 | |||
519 | /* --------------------------------------------------------------------- */ |
||
520 | |||
521 | static int create_special_files (void) |
||
522 | { |
||
523 | struct dentry *parent; |
||
524 | int retval; |
||
525 | |||
526 | /* create the devices special file */ |
||
527 | retval = simple_pin_fs("usbdevfs", &usbdevfs_mount, &usbdevfs_mount_count); |
||
528 | if (retval) { |
||
529 | err ("Unable to get usbdevfs mount"); |
||
530 | goto exit; |
||
531 | } |
||
532 | |||
533 | retval = simple_pin_fs("usbfs", &usbfs_mount, &usbfs_mount_count); |
||
534 | if (retval) { |
||
535 | err ("Unable to get usbfs mount"); |
||
536 | goto error_clean_usbdevfs_mount; |
||
537 | } |
||
538 | |||
539 | parent = usbfs_mount->mnt_sb->s_root; |
||
540 | devices_usbfs_dentry = fs_create_file ("devices", |
||
541 | listmode | S_IFREG, parent, |
||
542 | NULL, &usbdevfs_devices_fops, |
||
543 | listuid, listgid); |
||
544 | if (devices_usbfs_dentry == NULL) { |
||
545 | err ("Unable to create devices usbfs file"); |
||
546 | retval = -ENODEV; |
||
547 | goto error_clean_mounts; |
||
548 | } |
||
549 | |||
550 | parent = usbdevfs_mount->mnt_sb->s_root; |
||
551 | devices_usbdevfs_dentry = fs_create_file ("devices", |
||
552 | listmode | S_IFREG, parent, |
||
553 | NULL, &usbdevfs_devices_fops, |
||
554 | listuid, listgid); |
||
555 | if (devices_usbdevfs_dentry == NULL) { |
||
556 | err ("Unable to create devices usbfs file"); |
||
557 | retval = -ENODEV; |
||
558 | goto error_remove_file; |
||
559 | } |
||
560 | |||
561 | goto exit; |
||
562 | |||
563 | error_remove_file: |
||
564 | fs_remove_file (devices_usbfs_dentry); |
||
565 | devices_usbfs_dentry = NULL; |
||
566 | |||
567 | error_clean_mounts: |
||
568 | simple_release_fs(&usbfs_mount, &usbfs_mount_count); |
||
569 | |||
570 | error_clean_usbdevfs_mount: |
||
571 | simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count); |
||
572 | |||
573 | exit: |
||
574 | return retval; |
||
575 | } |
||
576 | |||
577 | static void remove_special_files (void) |
||
578 | { |
||
579 | if (devices_usbdevfs_dentry) |
||
580 | fs_remove_file (devices_usbdevfs_dentry); |
||
581 | if (devices_usbfs_dentry) |
||
582 | fs_remove_file (devices_usbfs_dentry); |
||
583 | devices_usbdevfs_dentry = NULL; |
||
584 | devices_usbfs_dentry = NULL; |
||
585 | simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count); |
||
586 | simple_release_fs(&usbfs_mount, &usbfs_mount_count); |
||
587 | } |
||
588 | |||
589 | void usbfs_update_special (void) |
||
590 | { |
||
591 | struct inode *inode; |
||
592 | |||
593 | if (devices_usbdevfs_dentry) { |
||
594 | inode = devices_usbdevfs_dentry->d_inode; |
||
595 | if (inode) |
||
596 | inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; |
||
597 | } |
||
598 | if (devices_usbfs_dentry) { |
||
599 | inode = devices_usbfs_dentry->d_inode; |
||
600 | if (inode) |
||
601 | inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; |
||
602 | } |
||
603 | } |
||
604 | |||
605 | void usbfs_add_bus(struct usb_bus *bus) |
||
606 | { |
||
607 | struct dentry *parent; |
||
608 | char name[8]; |
||
609 | int retval; |
||
610 | |||
611 | /* create the special files if this is the first bus added */ |
||
612 | if (num_buses == 0) { |
||
613 | retval = create_special_files(); |
||
614 | if (retval) |
||
615 | return; |
||
616 | } |
||
617 | ++num_buses; |
||
618 | |||
619 | sprintf (name, "%03d", bus->busnum); |
||
620 | |||
621 | parent = usbfs_mount->mnt_sb->s_root; |
||
622 | bus->usbfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent, |
||
623 | bus, NULL, busuid, busgid); |
||
624 | if (bus->usbfs_dentry == NULL) { |
||
625 | err ("error creating usbfs bus entry"); |
||
626 | return; |
||
627 | } |
||
628 | |||
629 | parent = usbdevfs_mount->mnt_sb->s_root; |
||
630 | bus->usbdevfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent, |
||
631 | bus, NULL, busuid, busgid); |
||
632 | if (bus->usbdevfs_dentry == NULL) { |
||
633 | err ("error creating usbdevfs bus entry"); |
||
634 | return; |
||
635 | } |
||
636 | |||
637 | usbfs_update_special(); |
||
638 | usbdevfs_conn_disc_event(); |
||
639 | } |
||
640 | |||
641 | |||
642 | void usbfs_remove_bus(struct usb_bus *bus) |
||
643 | { |
||
644 | if (bus->usbfs_dentry) { |
||
645 | fs_remove_file (bus->usbfs_dentry); |
||
646 | bus->usbfs_dentry = NULL; |
||
647 | } |
||
648 | if (bus->usbdevfs_dentry) { |
||
649 | fs_remove_file (bus->usbdevfs_dentry); |
||
650 | bus->usbdevfs_dentry = NULL; |
||
651 | } |
||
652 | |||
653 | --num_buses; |
||
654 | if (num_buses <= 0) { |
||
655 | remove_special_files(); |
||
656 | num_buses = 0; |
||
657 | } |
||
658 | |||
659 | usbfs_update_special(); |
||
660 | usbdevfs_conn_disc_event(); |
||
661 | } |
||
662 | |||
663 | void usbfs_add_device(struct usb_device *dev) |
||
664 | { |
||
665 | char name[8]; |
||
666 | int i; |
||
667 | int i_size; |
||
668 | |||
669 | sprintf (name, "%03d", dev->devnum); |
||
670 | dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG, |
||
671 | dev->bus->usbfs_dentry, dev, |
||
672 | &usbdevfs_device_file_operations, |
||
673 | devuid, devgid); |
||
674 | if (dev->usbfs_dentry == NULL) { |
||
675 | err ("error creating usbfs device entry"); |
||
676 | return; |
||
677 | } |
||
678 | dev->usbdevfs_dentry = fs_create_file (name, devmode | S_IFREG, |
||
679 | dev->bus->usbdevfs_dentry, dev, |
||
680 | &usbdevfs_device_file_operations, |
||
681 | devuid, devgid); |
||
682 | if (dev->usbdevfs_dentry == NULL) { |
||
683 | err ("error creating usbdevfs device entry"); |
||
684 | return; |
||
685 | } |
||
686 | |||
687 | /* Set the size of the device's file to be |
||
688 | * equal to the size of the device descriptors. */ |
||
689 | i_size = sizeof (struct usb_device_descriptor); |
||
690 | for (i = 0; i < dev->descriptor.bNumConfigurations; ++i) { |
||
691 | struct usb_config_descriptor *config = |
||
692 | (struct usb_config_descriptor *)dev->rawdescriptors[i]; |
||
693 | i_size += le16_to_cpu (config->wTotalLength); |
||
694 | } |
||
695 | if (dev->usbfs_dentry->d_inode) |
||
696 | dev->usbfs_dentry->d_inode->i_size = i_size; |
||
697 | if (dev->usbdevfs_dentry->d_inode) |
||
698 | dev->usbdevfs_dentry->d_inode->i_size = i_size; |
||
699 | |||
700 | usbfs_update_special(); |
||
701 | usbdevfs_conn_disc_event(); |
||
702 | } |
||
703 | |||
704 | void usbfs_remove_device(struct usb_device *dev) |
||
705 | { |
||
706 | struct dev_state *ds; |
||
707 | struct siginfo sinfo; |
||
708 | |||
709 | if (dev->usbfs_dentry) { |
||
710 | fs_remove_file (dev->usbfs_dentry); |
||
711 | dev->usbfs_dentry = NULL; |
||
712 | } |
||
713 | if (dev->usbdevfs_dentry) { |
||
714 | fs_remove_file (dev->usbdevfs_dentry); |
||
715 | dev->usbdevfs_dentry = NULL; |
||
716 | } |
||
717 | while (!list_empty(&dev->filelist)) { |
||
718 | ds = list_entry(dev->filelist.next, struct dev_state, list); |
||
719 | list_del_init(&ds->list); |
||
720 | down_write(&ds->devsem); |
||
721 | ds->dev = NULL; |
||
722 | up_write(&ds->devsem); |
||
723 | if (ds->discsignr) { |
||
724 | sinfo.si_signo = SIGPIPE; |
||
725 | sinfo.si_errno = EPIPE; |
||
726 | sinfo.si_code = SI_ASYNCIO; |
||
727 | sinfo.si_addr = ds->disccontext; |
||
728 | send_sig_info(ds->discsignr, &sinfo, ds->disctask); |
||
729 | } |
||
730 | } |
||
731 | usbfs_update_special(); |
||
732 | usbdevfs_conn_disc_event(); |
||
733 | } |
||
734 | |||
735 | /* --------------------------------------------------------------------- */ |
||
736 | |||
737 | #ifdef CONFIG_PROC_FS |
||
738 | static struct proc_dir_entry *usbdir = NULL; |
||
739 | #endif |
||
740 | |||
741 | int __init usbfs_init(void) |
||
742 | { |
||
743 | int retval; |
||
744 | |||
745 | retval = usb_register(&usbdevfs_driver); |
||
746 | if (retval) |
||
747 | return retval; |
||
748 | |||
749 | retval = register_filesystem(&usb_fs_type); |
||
750 | if (retval) { |
||
751 | usb_deregister(&usbdevfs_driver); |
||
752 | return retval; |
||
753 | } |
||
754 | retval = register_filesystem(&usbdevice_fs_type); |
||
755 | if (retval) { |
||
756 | unregister_filesystem(&usb_fs_type); |
||
757 | usb_deregister(&usbdevfs_driver); |
||
758 | return retval; |
||
759 | } |
||
760 | |||
761 | #ifdef CONFIG_PROC_FS |
||
762 | /* create mount point for usbdevfs */ |
||
763 | usbdir = proc_mkdir("usb", proc_bus); |
||
764 | #endif |
||
765 | |||
766 | return 0; |
||
767 | } |
||
768 | |||
769 | void __exit usbfs_cleanup(void) |
||
770 | { |
||
771 | usb_deregister(&usbdevfs_driver); |
||
772 | unregister_filesystem(&usb_fs_type); |
||
773 | unregister_filesystem(&usbdevice_fs_type); |
||
774 | #ifdef CONFIG_PROC_FS |
||
775 | if (usbdir) |
||
776 | remove_proc_entry("usb", proc_bus); |
||
777 | #endif |
||
778 | } |
||
779 |