1 /* 2 * ISA Plug & Play support 3 * Copyright (c) by Jaroslav Kysela <perex@suse.cz> 4 * 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 * 20 */ 21 22 #include <linux/config.h> 23 #include <linux/module.h> 24 #include <linux/isapnp.h> 25 #include <linux/proc_fs.h> 26 #include <linux/init.h> 27 #include <linux/smp_lock.h> 28 #include <asm/uaccess.h> 29 30 extern struct pnp_protocol isapnp_protocol; 31 32 static struct proc_dir_entry *isapnp_proc_bus_dir = NULL; 33 34 static loff_t isapnp_proc_bus_lseek(struct file *file, loff_t off, int whence) 35 { 36 loff_t new = -1; 37 38 lock_kernel(); 39 switch (whence) { 40 case 0: 41 new = off; 42 break; 43 case 1: 44 new = file->f_pos + off; 45 break; 46 case 2: 47 new = 256 + off; 48 break; 49 } 50 if (new < 0 || new > 256) { 51 unlock_kernel(); 52 return -EINVAL; 53 } 54 unlock_kernel(); 55 return (file->f_pos = new); 56 } 57 58 static ssize_t isapnp_proc_bus_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) 59 { 60 struct inode *ino = file->f_dentry->d_inode; 61 struct proc_dir_entry *dp = PDE(ino); 62 struct pnp_dev *dev = dp->data; 63 int pos = *ppos; 64 int cnt, size = 256; 65 66 if (pos >= size) 67 return 0; 68 if (nbytes >= size) 69 nbytes = size; 70 if (pos + nbytes > size) 71 nbytes = size - pos; 72 cnt = nbytes; 73 74 if (!access_ok(VERIFY_WRITE, buf, cnt)) 75 return -EINVAL; 76 77 isapnp_cfg_begin(dev->card->number, dev->number); 78 for ( ; pos < 256 && cnt > 0; pos++, buf++, cnt--) { 79 unsigned char val; 80 val = isapnp_read_byte(pos); 81 __put_user(val, buf); 82 } 83 isapnp_cfg_end(); 84 85 *ppos = pos; 86 return nbytes; 87 } 88 89 static struct file_operations isapnp_proc_bus_file_operations = 90 { 91 .llseek = isapnp_proc_bus_lseek, 92 .read = isapnp_proc_bus_read, 93 }; 94 95 static int isapnp_proc_attach_device(struct pnp_dev *dev) 96 { 97 struct pnp_card *bus = dev->card; 98 struct proc_dir_entry *de, *e; 99 char name[16]; 100 101 if (!(de = bus->procdir)) { 102 sprintf(name, "%02x", bus->number); 103 de = bus->procdir = proc_mkdir(name, isapnp_proc_bus_dir); 104 if (!de) 105 return -ENOMEM; 106 } 107 sprintf(name, "%02x", dev->number); 108 e = dev->procent = create_proc_entry(name, S_IFREG | S_IRUGO, de); 109 if (!e) 110 return -ENOMEM; 111 e->proc_fops = &isapnp_proc_bus_file_operations; 112 e->owner = THIS_MODULE; 113 e->data = dev; 114 e->size = 256; 115 return 0; 116 } 117 118 #ifdef MODULE 119 static int __exit isapnp_proc_detach_device(struct pnp_dev *dev) 120 { 121 struct pnp_card *bus = dev->card; 122 struct proc_dir_entry *de; 123 char name[16]; 124 125 if (!(de = bus->procdir)) 126 return -EINVAL; 127 sprintf(name, "%02x", dev->number); 128 remove_proc_entry(name, de); 129 return 0; 130 } 131 132 static int __exit isapnp_proc_detach_bus(struct pnp_card *bus) 133 { 134 struct proc_dir_entry *de; 135 char name[16]; 136 137 if (!(de = bus->procdir)) 138 return -EINVAL; 139 sprintf(name, "%02x", bus->number); 140 remove_proc_entry(name, isapnp_proc_bus_dir); 141 return 0; 142 } 143 #endif /* MODULE */ 144 145 int __init isapnp_proc_init(void) 146 { 147 struct pnp_dev *dev; 148 isapnp_proc_bus_dir = proc_mkdir("isapnp", proc_bus); 149 protocol_for_each_dev(&isapnp_protocol,dev) { 150 isapnp_proc_attach_device(dev); 151 } 152 return 0; 153 } 154 155 #ifdef MODULE 156 int __exit isapnp_proc_done(void) 157 { 158 struct pnp_dev *dev; 159 struct pnp_bus *card; 160 161 isapnp_for_each_dev(dev) { 162 isapnp_proc_detach_device(dev); 163 } 164 isapnp_for_each_card(card) { 165 isapnp_proc_detach_bus(card); 166 } 167 if (isapnp_proc_bus_dir) 168 remove_proc_entry("isapnp", proc_bus); 169 return 0; 170 } 171 #endif /* MODULE */ 172