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