1 /* 2 * $Id: mtdcore.c,v 1.47 2005/11/07 11:14:20 gleixner Exp $ 3 * 4 * Core registration and callback routines for MTD 5 * drivers and users. 6 * 7 */ 8 9 #include <linux/module.h> 10 #include <linux/kernel.h> 11 #include <linux/sched.h> 12 #include <linux/ptrace.h> 13 #include <linux/slab.h> 14 #include <linux/string.h> 15 #include <linux/timer.h> 16 #include <linux/major.h> 17 #include <linux/fs.h> 18 #include <linux/err.h> 19 #include <linux/ioctl.h> 20 #include <linux/init.h> 21 #include <linux/mtd/compatmac.h> 22 #include <linux/proc_fs.h> 23 24 #include <linux/mtd/mtd.h> 25 26 /* These are exported solely for the purpose of mtd_blkdevs.c. You 27 should not use them for _anything_ else */ 28 DEFINE_MUTEX(mtd_table_mutex); 29 struct mtd_info *mtd_table[MAX_MTD_DEVICES]; 30 31 EXPORT_SYMBOL_GPL(mtd_table_mutex); 32 EXPORT_SYMBOL_GPL(mtd_table); 33 34 static LIST_HEAD(mtd_notifiers); 35 36 /** 37 * add_mtd_device - register an MTD device 38 * @mtd: pointer to new MTD device info structure 39 * 40 * Add a device to the list of MTD devices present in the system, and 41 * notify each currently active MTD 'user' of its arrival. Returns 42 * zero on success or 1 on failure, which currently will only happen 43 * if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16) 44 */ 45 46 int add_mtd_device(struct mtd_info *mtd) 47 { 48 int i; 49 50 BUG_ON(mtd->writesize == 0); 51 mutex_lock(&mtd_table_mutex); 52 53 for (i=0; i < MAX_MTD_DEVICES; i++) 54 if (!mtd_table[i]) { 55 struct list_head *this; 56 57 mtd_table[i] = mtd; 58 mtd->index = i; 59 mtd->usecount = 0; 60 61 /* Some chips always power up locked. Unlock them now */ 62 if ((mtd->flags & MTD_WRITEABLE) 63 && (mtd->flags & MTD_STUPID_LOCK) && mtd->unlock) { 64 if (mtd->unlock(mtd, 0, mtd->size)) 65 printk(KERN_WARNING 66 "%s: unlock failed, " 67 "writes may not work\n", 68 mtd->name); 69 } 70 71 DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); 72 /* No need to get a refcount on the module containing 73 the notifier, since we hold the mtd_table_mutex */ 74 list_for_each(this, &mtd_notifiers) { 75 struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); 76 not->add(mtd); 77 } 78 79 mutex_unlock(&mtd_table_mutex); 80 /* We _know_ we aren't being removed, because 81 our caller is still holding us here. So none 82 of this try_ nonsense, and no bitching about it 83 either. :) */ 84 __module_get(THIS_MODULE); 85 return 0; 86 } 87 88 mutex_unlock(&mtd_table_mutex); 89 return 1; 90 } 91 92 /** 93 * del_mtd_device - unregister an MTD device 94 * @mtd: pointer to MTD device info structure 95 * 96 * Remove a device from the list of MTD devices present in the system, 97 * and notify each currently active MTD 'user' of its departure. 98 * Returns zero on success or 1 on failure, which currently will happen 99 * if the requested device does not appear to be present in the list. 100 */ 101 102 int del_mtd_device (struct mtd_info *mtd) 103 { 104 int ret; 105 106 mutex_lock(&mtd_table_mutex); 107 108 if (mtd_table[mtd->index] != mtd) { 109 ret = -ENODEV; 110 } else if (mtd->usecount) { 111 printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n", 112 mtd->index, mtd->name, mtd->usecount); 113 ret = -EBUSY; 114 } else { 115 struct list_head *this; 116 117 /* No need to get a refcount on the module containing 118 the notifier, since we hold the mtd_table_mutex */ 119 list_for_each(this, &mtd_notifiers) { 120 struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); 121 not->remove(mtd); 122 } 123 124 mtd_table[mtd->index] = NULL; 125 126 module_put(THIS_MODULE); 127 ret = 0; 128 } 129 130 mutex_unlock(&mtd_table_mutex); 131 return ret; 132 } 133 134 /** 135 * register_mtd_user - register a 'user' of MTD devices. 136 * @new: pointer to notifier info structure 137 * 138 * Registers a pair of callbacks function to be called upon addition 139 * or removal of MTD devices. Causes the 'add' callback to be immediately 140 * invoked for each MTD device currently present in the system. 141 */ 142 143 void register_mtd_user (struct mtd_notifier *new) 144 { 145 int i; 146 147 mutex_lock(&mtd_table_mutex); 148 149 list_add(&new->list, &mtd_notifiers); 150 151 __module_get(THIS_MODULE); 152 153 for (i=0; i< MAX_MTD_DEVICES; i++) 154 if (mtd_table[i]) 155 new->add(mtd_table[i]); 156 157 mutex_unlock(&mtd_table_mutex); 158 } 159 160 /** 161 * unregister_mtd_user - unregister a 'user' of MTD devices. 162 * @old: pointer to notifier info structure 163 * 164 * Removes a callback function pair from the list of 'users' to be 165 * notified upon addition or removal of MTD devices. Causes the 166 * 'remove' callback to be immediately invoked for each MTD device 167 * currently present in the system. 168 */ 169 170 int unregister_mtd_user (struct mtd_notifier *old) 171 { 172 int i; 173 174 mutex_lock(&mtd_table_mutex); 175 176 module_put(THIS_MODULE); 177 178 for (i=0; i< MAX_MTD_DEVICES; i++) 179 if (mtd_table[i]) 180 old->remove(mtd_table[i]); 181 182 list_del(&old->list); 183 mutex_unlock(&mtd_table_mutex); 184 return 0; 185 } 186 187 188 /** 189 * get_mtd_device - obtain a validated handle for an MTD device 190 * @mtd: last known address of the required MTD device 191 * @num: internal device number of the required MTD device 192 * 193 * Given a number and NULL address, return the num'th entry in the device 194 * table, if any. Given an address and num == -1, search the device table 195 * for a device with that address and return if it's still present. Given 196 * both, return the num'th driver only if its address matches. Return 197 * error code if not. 198 */ 199 200 struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) 201 { 202 struct mtd_info *ret = NULL; 203 int i, err = -ENODEV; 204 205 mutex_lock(&mtd_table_mutex); 206 207 if (num == -1) { 208 for (i=0; i< MAX_MTD_DEVICES; i++) 209 if (mtd_table[i] == mtd) 210 ret = mtd_table[i]; 211 } else if (num < MAX_MTD_DEVICES) { 212 ret = mtd_table[num]; 213 if (mtd && mtd != ret) 214 ret = NULL; 215 } 216 217 if (!ret) 218 goto out_unlock; 219 220 if (!try_module_get(ret->owner)) 221 goto out_unlock; 222 223 if (ret->get_device) { 224 err = ret->get_device(ret); 225 if (err) 226 goto out_put; 227 } 228 229 ret->usecount++; 230 mutex_unlock(&mtd_table_mutex); 231 return ret; 232 233 out_put: 234 module_put(ret->owner); 235 out_unlock: 236 mutex_unlock(&mtd_table_mutex); 237 return ERR_PTR(err); 238 } 239 240 /** 241 * get_mtd_device_nm - obtain a validated handle for an MTD device by 242 * device name 243 * @name: MTD device name to open 244 * 245 * This function returns MTD device description structure in case of 246 * success and an error code in case of failure. 247 */ 248 249 struct mtd_info *get_mtd_device_nm(const char *name) 250 { 251 int i, err = -ENODEV; 252 struct mtd_info *mtd = NULL; 253 254 mutex_lock(&mtd_table_mutex); 255 256 for (i = 0; i < MAX_MTD_DEVICES; i++) { 257 if (mtd_table[i] && !strcmp(name, mtd_table[i]->name)) { 258 mtd = mtd_table[i]; 259 break; 260 } 261 } 262 263 if (!mtd) 264 goto out_unlock; 265 266 if (!try_module_get(mtd->owner)) 267 goto out_unlock; 268 269 if (mtd->get_device) { 270 err = mtd->get_device(mtd); 271 if (err) 272 goto out_put; 273 } 274 275 mtd->usecount++; 276 mutex_unlock(&mtd_table_mutex); 277 return mtd; 278 279 out_put: 280 module_put(mtd->owner); 281 out_unlock: 282 mutex_unlock(&mtd_table_mutex); 283 return ERR_PTR(err); 284 } 285 286 void put_mtd_device(struct mtd_info *mtd) 287 { 288 int c; 289 290 mutex_lock(&mtd_table_mutex); 291 c = --mtd->usecount; 292 if (mtd->put_device) 293 mtd->put_device(mtd); 294 mutex_unlock(&mtd_table_mutex); 295 BUG_ON(c < 0); 296 297 module_put(mtd->owner); 298 } 299 300 /* default_mtd_writev - default mtd writev method for MTD devices that 301 * don't implement their own 302 */ 303 304 int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, 305 unsigned long count, loff_t to, size_t *retlen) 306 { 307 unsigned long i; 308 size_t totlen = 0, thislen; 309 int ret = 0; 310 311 if(!mtd->write) { 312 ret = -EROFS; 313 } else { 314 for (i=0; i<count; i++) { 315 if (!vecs[i].iov_len) 316 continue; 317 ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base); 318 totlen += thislen; 319 if (ret || thislen != vecs[i].iov_len) 320 break; 321 to += vecs[i].iov_len; 322 } 323 } 324 if (retlen) 325 *retlen = totlen; 326 return ret; 327 } 328 329 EXPORT_SYMBOL_GPL(add_mtd_device); 330 EXPORT_SYMBOL_GPL(del_mtd_device); 331 EXPORT_SYMBOL_GPL(get_mtd_device); 332 EXPORT_SYMBOL_GPL(get_mtd_device_nm); 333 EXPORT_SYMBOL_GPL(put_mtd_device); 334 EXPORT_SYMBOL_GPL(register_mtd_user); 335 EXPORT_SYMBOL_GPL(unregister_mtd_user); 336 EXPORT_SYMBOL_GPL(default_mtd_writev); 337 338 #ifdef CONFIG_PROC_FS 339 340 /*====================================================================*/ 341 /* Support for /proc/mtd */ 342 343 static struct proc_dir_entry *proc_mtd; 344 345 static inline int mtd_proc_info (char *buf, int i) 346 { 347 struct mtd_info *this = mtd_table[i]; 348 349 if (!this) 350 return 0; 351 352 return sprintf(buf, "mtd%d: %8.8x %8.8x \"%s\"\n", i, this->size, 353 this->erasesize, this->name); 354 } 355 356 static int mtd_read_proc (char *page, char **start, off_t off, int count, 357 int *eof, void *data_unused) 358 { 359 int len, l, i; 360 off_t begin = 0; 361 362 mutex_lock(&mtd_table_mutex); 363 364 len = sprintf(page, "dev: size erasesize name\n"); 365 for (i=0; i< MAX_MTD_DEVICES; i++) { 366 367 l = mtd_proc_info(page + len, i); 368 len += l; 369 if (len+begin > off+count) 370 goto done; 371 if (len+begin < off) { 372 begin += len; 373 len = 0; 374 } 375 } 376 377 *eof = 1; 378 379 done: 380 mutex_unlock(&mtd_table_mutex); 381 if (off >= len+begin) 382 return 0; 383 *start = page + (off-begin); 384 return ((count < begin+len-off) ? count : begin+len-off); 385 } 386 387 /*====================================================================*/ 388 /* Init code */ 389 390 static int __init init_mtd(void) 391 { 392 if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) 393 proc_mtd->read_proc = mtd_read_proc; 394 return 0; 395 } 396 397 static void __exit cleanup_mtd(void) 398 { 399 if (proc_mtd) 400 remove_proc_entry( "mtd", NULL); 401 } 402 403 module_init(init_mtd); 404 module_exit(cleanup_mtd); 405 406 #endif /* CONFIG_PROC_FS */ 407 408 409 MODULE_LICENSE("GPL"); 410 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 411 MODULE_DESCRIPTION("Core MTD registration and access routines"); 412