📄 pci_hotplug_core.c
字号:
/* * PCI HotPlug Controller Core * * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) * Copyright (c) 2001 IBM Corp. * * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or * NON INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Send feedback to <greg@kroah.com> * */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/list.h>#include <linux/pagemap.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/init.h>#include <linux/pci.h>#include <asm/uaccess.h>#include "pci_hotplug.h"#if !defined(CONFIG_HOTPLUG_PCI_MODULE) #define MY_NAME "pci_hotplug"#else #define MY_NAME THIS_MODULE->name#endif#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: "__FUNCTION__": " fmt , MY_NAME , ## arg); } while (0)#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)/* local variables */static int debug;#define DRIVER_VERSION "0.3"#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"#define DRIVER_DESC "PCI Hot Plug PCI Core"///////////////////////////////////////////////////////////////////* Random magic number */#define PCIHPFS_MAGIC 0x52454541struct hotplug_slot_core { struct dentry *dir_dentry; struct dentry *power_dentry; struct dentry *attention_dentry; struct dentry *latch_dentry; struct dentry *adapter_dentry; struct dentry *test_dentry;};static struct super_operations pcihpfs_ops;static struct address_space_operations pcihpfs_aops;static struct file_operations pcihpfs_dir_operations;static struct file_operations default_file_operations;static struct inode_operations pcihpfs_dir_inode_operations;static struct vfsmount *pcihpfs_mount; /* one of the mounts of our fs for reference counting */static int pcihpfs_mount_count; /* times we have mounted our fs */static spinlock_t mount_lock; /* protects our mount_count */static spinlock_t list_lock;LIST_HEAD(pci_hotplug_slot_list);static int pcihpfs_statfs (struct super_block *sb, struct statfs *buf){ buf->f_type = PCIHPFS_MAGIC; buf->f_bsize = PAGE_CACHE_SIZE; buf->f_namelen = 255; return 0;}static struct dentry *pcihpfs_lookup (struct inode *dir, struct dentry *dentry){ d_add(dentry, NULL); return NULL;}static struct inode *pcihpfs_get_inode (struct super_block *sb, int mode, int dev){ struct inode *inode = new_inode(sb); if (inode) { inode->i_mode = mode; inode->i_uid = current->fsuid; inode->i_gid = current->fsgid; inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blocks = 0; inode->i_rdev = NODEV; inode->i_mapping->a_ops = &pcihpfs_aops; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; switch (mode & S_IFMT) { default: init_special_inode(inode, mode, dev); break; case S_IFREG: inode->i_fop = &default_file_operations; break; case S_IFDIR: inode->i_op = &pcihpfs_dir_inode_operations; inode->i_fop = &pcihpfs_dir_operations; break; } } return inode; }static int pcihpfs_mknod (struct inode *dir, struct dentry *dentry, int mode, int dev){ struct inode *inode = pcihpfs_get_inode(dir->i_sb, mode, dev); int error = -ENOSPC; if (inode) { d_instantiate(dentry, inode); dget(dentry); error = 0; } return error;}static int pcihpfs_mkdir (struct inode *dir, struct dentry *dentry, int mode){ return pcihpfs_mknod (dir, dentry, mode | S_IFDIR, 0);}static int pcihpfs_create (struct inode *dir, struct dentry *dentry, int mode){ return pcihpfs_mknod (dir, dentry, mode | S_IFREG, 0);}static int pcihpfs_link (struct dentry *old_dentry, struct inode *dir, struct dentry *dentry){ struct inode *inode = old_dentry->d_inode; if(S_ISDIR(inode->i_mode)) return -EPERM; inode->i_nlink++; atomic_inc(&inode->i_count); dget(dentry); d_instantiate(dentry, inode); return 0;}static inline int pcihpfs_positive (struct dentry *dentry){ return dentry->d_inode && !d_unhashed(dentry);}static int pcihpfs_empty (struct dentry *dentry){ struct list_head *list; spin_lock(&dcache_lock); list_for_each(list, &dentry->d_subdirs) { struct dentry *de = list_entry(list, struct dentry, d_child); if (pcihpfs_positive(de)) { spin_unlock(&dcache_lock); return 0; } } spin_unlock(&dcache_lock); return 1;}static int pcihpfs_unlink (struct inode *dir, struct dentry *dentry){ int error = -ENOTEMPTY; if (pcihpfs_empty(dentry)) { struct inode *inode = dentry->d_inode; inode->i_nlink--; dput(dentry); error = 0; } return error;}static int pcihpfs_rename (struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry){ int error = -ENOTEMPTY; if (pcihpfs_empty(new_dentry)) { struct inode *inode = new_dentry->d_inode; if (inode) { inode->i_nlink--; dput(new_dentry); } error = 0; } return error;}#define pcihpfs_rmdir pcihpfs_unlink/* default file operations */static ssize_t default_read_file (struct file *file, char *buf, size_t count, loff_t *ppos){ dbg ("\n"); return 0;}static ssize_t default_write_file (struct file *file, const char *buf, size_t count, loff_t *ppos){ dbg ("\n"); return count;}static loff_t default_file_lseek (struct file *file, loff_t offset, int orig){ loff_t retval = -EINVAL; switch(orig) { case 0: if (offset > 0) { file->f_pos = offset; retval = file->f_pos; } break; case 1: if ((offset + file->f_pos) > 0) { file->f_pos += offset; retval = file->f_pos; } break; default: break; } return retval;}static int default_open (struct inode *inode, struct file *filp){ if (inode->u.generic_ip) filp->private_data = inode->u.generic_ip; return 0;}static int default_sync_file (struct file *file, struct dentry *dentry, int datasync){ return 0;}static struct address_space_operations pcihpfs_aops = {};static struct file_operations pcihpfs_dir_operations = { read: generic_read_dir, readdir: dcache_readdir, fsync: default_sync_file,};static struct file_operations default_file_operations = { read: default_read_file, write: default_write_file, open: default_open, llseek: default_file_lseek, fsync: default_sync_file, mmap: generic_file_mmap,};/* file ops for the "power" files */static ssize_t power_read_file (struct file *file, char *buf, size_t count, loff_t *offset);static ssize_t power_write_file (struct file *file, const char *buf, size_t count, loff_t *ppos);static struct file_operations power_file_operations = { read: power_read_file, write: power_write_file, open: default_open, llseek: default_file_lseek, fsync: default_sync_file, mmap: generic_file_mmap,};/* file ops for the "attention" files */static ssize_t attention_read_file (struct file *file, char *buf, size_t count, loff_t *offset);static ssize_t attention_write_file (struct file *file, const char *buf, size_t count, loff_t *ppos);static struct file_operations attention_file_operations = { read: attention_read_file, write: attention_write_file, open: default_open, llseek: default_file_lseek, fsync: default_sync_file, mmap: generic_file_mmap,};/* file ops for the "latch" files */static ssize_t latch_read_file (struct file *file, char *buf, size_t count, loff_t *offset);static struct file_operations latch_file_operations = { read: latch_read_file, write: default_write_file, open: default_open, llseek: default_file_lseek, fsync: default_sync_file, mmap: generic_file_mmap,};/* file ops for the "presence" files */static ssize_t presence_read_file (struct file *file, char *buf, size_t count, loff_t *offset);static struct file_operations presence_file_operations = { read: presence_read_file, write: default_write_file, open: default_open, llseek: default_file_lseek, fsync: default_sync_file, mmap: generic_file_mmap,};/* file ops for the "test" files */static ssize_t test_write_file (struct file *file, const char *buf, size_t count, loff_t *ppos);static struct file_operations test_file_operations = { read: default_read_file, write: test_write_file, open: default_open, llseek: default_file_lseek, fsync: default_sync_file, mmap: generic_file_mmap,};static struct inode_operations pcihpfs_dir_inode_operations = { create: pcihpfs_create, lookup: pcihpfs_lookup, link: pcihpfs_link, unlink: pcihpfs_unlink, mkdir: pcihpfs_mkdir, rmdir: pcihpfs_rmdir, mknod: pcihpfs_mknod, rename: pcihpfs_rename,};static struct super_operations pcihpfs_ops = { statfs: pcihpfs_statfs, put_inode: force_delete,};static struct super_block *pcihpfs_read_super (struct super_block *sb, void *data, int silent){ struct inode *inode; struct dentry *root; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = PCIHPFS_MAGIC; sb->s_op = &pcihpfs_ops; inode = pcihpfs_get_inode(sb, S_IFDIR | 0755, 0); if (!inode) { dbg("%s: could not get inode!\n",__FUNCTION__); return NULL; } root = d_alloc_root(inode); if (!root) { dbg("%s: could not get root dentry!\n",__FUNCTION__); iput(inode); return NULL; } sb->s_root = root; return sb;}static DECLARE_FSTYPE(pcihpfs_fs_type, "pcihpfs", pcihpfs_read_super, FS_SINGLE | FS_LITTER);static int get_mount (void){ struct vfsmount *mnt; spin_lock (&mount_lock); if (pcihpfs_mount) { mntget(pcihpfs_mount); ++pcihpfs_mount_count; spin_unlock (&mount_lock); goto go_ahead; } spin_unlock (&mount_lock); mnt = kern_mount (&pcihpfs_fs_type); if (IS_ERR(mnt)) { err ("could not mount the fs...erroring out!\n"); return -ENODEV; } spin_lock (&mount_lock); if (!pcihpfs_mount) { pcihpfs_mount = mnt; ++pcihpfs_mount_count; spin_unlock (&mount_lock); goto go_ahead; } mntget(pcihpfs_mount); ++pcihpfs_mount_count; spin_unlock (&mount_lock); mntput(mnt);go_ahead: dbg("pcihpfs_mount_count = %d\n", pcihpfs_mount_count); return 0;}static void remove_mount (void){ struct vfsmount *mnt; spin_lock (&mount_lock); mnt = pcihpfs_mount; --pcihpfs_mount_count; if (!pcihpfs_mount_count) pcihpfs_mount = NULL; spin_unlock (&mount_lock); mntput(mnt); dbg("pcihpfs_mount_count = %d\n", pcihpfs_mount_count);}/** * pcihpfs_create_by_name - create a file, given a name * @name: name of file * @mode: type of file * @parent: dentry of directory to create it in * @dentry: resulting dentry of file * * There is a bit of overhead in creating a file - basically, we * have to hash the name of the file, then look it up. This will * prevent files of the same name. * We then call the proper vfs_ function to take care of all the * file creation details. * This function handles both regular files and directories. */static int pcihpfs_create_by_name (const char *name, mode_t mode, struct dentry *parent, struct dentry **dentry){ struct dentry *d = NULL; struct qstr qstr; int error; /* If the parent is not specified, we create it in the root. * We need the root dentry to do this, which is in the super * block. A pointer to that is in the struct vfsmount that we * have around. */ if (!parent ) { if (pcihpfs_mount && pcihpfs_mount->mnt_sb) { parent = pcihpfs_mount->mnt_sb->s_root; } } if (!parent) { dbg("Ah! can not find a parent!\n"); return -EFAULT; } *dentry = NULL; qstr.name = name; qstr.len = strlen(name); qstr.hash = full_name_hash(name,qstr.len); parent = dget(parent); down(&parent->d_inode->i_sem); d = lookup_hash(&qstr,parent); error = PTR_ERR(d); if (!IS_ERR(d)) { switch(mode & S_IFMT) { case 0: case S_IFREG: error = vfs_create(parent->d_inode,d,mode); break; case S_IFDIR: error = vfs_mkdir(parent->d_inode,d,mode); break; default: err("cannot create special files\n"); } *dentry = d; } up(&parent->d_inode->i_sem); dput(parent); return error;}static struct dentry *fs_create_file (const char *name, mode_t mode, struct dentry *parent, void *data, struct file_operations *fops){ struct dentry *dentry; int error; dbg("creating file '%s'\n",name); error = pcihpfs_create_by_name(name,mode,parent,&dentry); if (error) { dentry = NULL; } else { if (dentry->d_inode) { if (data) dentry->d_inode->u.generic_ip = data; if (fops) dentry->d_inode->i_fop = fops; } } return dentry;}static void fs_remove_file (struct dentry *dentry){ struct dentry *parent = dentry->d_parent; if (!parent || !parent->d_inode) return; down(&parent->d_inode->i_sem); if (pcihpfs_positive(dentry)) { if (dentry->d_inode) { if (S_ISDIR(dentry->d_inode->i_mode)) vfs_rmdir(parent->d_inode,dentry); else vfs_unlink(parent->d_inode,dentry); } dput(dentry); } up(&parent->d_inode->i_sem);}#define GET_STATUS(name) \static int get_##name##_status (struct hotplug_slot *slot, u8 *value) \{ \ struct hotplug_slot_ops *ops = slot->ops; \ int retval = 0; \ if (ops->owner) \ __MOD_INC_USE_COUNT(ops->owner); \ if (ops->get_##name##_status) \ retval = ops->get_##name##_status (slot, value); \ else \ *value = slot->info->name##_status; \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -