mtdchar.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:11k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * $Id: mtdchar.c,v 1.44 2001/10/02 15:05:11 dwmw2 Exp $
  3.  *
  4.  * Character-device access to raw MTD devices.
  5.  * Pure 2.4 version - compatibility cruft removed to mtdchar-compat.c
  6.  *
  7.  */
  8. #include <linux/config.h>
  9. #include <linux/kernel.h>
  10. #include <linux/module.h>
  11. #include <linux/mtd/mtd.h>
  12. #include <linux/slab.h>
  13. #ifdef CONFIG_DEVFS_FS
  14. #include <linux/devfs_fs_kernel.h>
  15. static void mtd_notify_add(struct mtd_info* mtd);
  16. static void mtd_notify_remove(struct mtd_info* mtd);
  17. static struct mtd_notifier notifier = {
  18. add: mtd_notify_add,
  19. remove: mtd_notify_remove,
  20. };
  21. static devfs_handle_t devfs_dir_handle;
  22. static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES];
  23. static devfs_handle_t devfs_ro_handle[MAX_MTD_DEVICES];
  24. #endif
  25. static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
  26. {
  27. struct mtd_info *mtd=(struct mtd_info *)file->private_data;
  28. switch (orig) {
  29. case 0:
  30. /* SEEK_SET */
  31. file->f_pos = offset;
  32. break;
  33. case 1:
  34. /* SEEK_CUR */
  35. file->f_pos += offset;
  36. break;
  37. case 2:
  38. /* SEEK_END */
  39. file->f_pos =mtd->size + offset;
  40. break;
  41. default:
  42. return -EINVAL;
  43. }
  44. if (file->f_pos < 0)
  45. file->f_pos = 0;
  46. else if (file->f_pos >= mtd->size)
  47. file->f_pos = mtd->size - 1;
  48. return file->f_pos;
  49. }
  50. static int mtd_open(struct inode *inode, struct file *file)
  51. {
  52. int minor = MINOR(inode->i_rdev);
  53. int devnum = minor >> 1;
  54. struct mtd_info *mtd;
  55. DEBUG(MTD_DEBUG_LEVEL0, "MTD_openn");
  56. if (devnum >= MAX_MTD_DEVICES)
  57. return -ENODEV;
  58. /* You can't open the RO devices RW */
  59. if ((file->f_mode & 2) && (minor & 1))
  60. return -EACCES;
  61. mtd = get_mtd_device(NULL, devnum);
  62. if (!mtd)
  63. return -ENODEV;
  64. if (MTD_ABSENT == mtd->type) {
  65. put_mtd_device(mtd);
  66. return -ENODEV;
  67. }
  68. file->private_data = mtd;
  69. /* You can't open it RW if it's not a writeable device */
  70. if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) {
  71. put_mtd_device(mtd);
  72. return -EACCES;
  73. }
  74. return 0;
  75. } /* mtd_open */
  76. /*====================================================================*/
  77. static int mtd_close(struct inode *inode, struct file *file)
  78. {
  79. struct mtd_info *mtd;
  80. DEBUG(MTD_DEBUG_LEVEL0, "MTD_closen");
  81. mtd = (struct mtd_info *)file->private_data;
  82. if (mtd->sync)
  83. mtd->sync(mtd);
  84. put_mtd_device(mtd);
  85. return 0;
  86. } /* mtd_close */
  87. /* FIXME: This _really_ needs to die. In 2.5, we should lock the
  88.    userspace buffer down and use it directly with readv/writev.
  89. */
  90. #define MAX_KMALLOC_SIZE 0x20000
  91. static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos)
  92. {
  93. struct mtd_info *mtd = (struct mtd_info *)file->private_data;
  94. size_t retlen=0;
  95. size_t total_retlen=0;
  96. int ret=0;
  97. int len;
  98. char *kbuf;
  99. DEBUG(MTD_DEBUG_LEVEL0,"MTD_readn");
  100. if (*ppos + count > mtd->size)
  101. count = mtd->size - *ppos;
  102. if (!count)
  103. return 0;
  104. /* FIXME: Use kiovec in 2.5 to lock down the user's buffers
  105.    and pass them directly to the MTD functions */
  106. while (count) {
  107. if (count > MAX_KMALLOC_SIZE) 
  108. len = MAX_KMALLOC_SIZE;
  109. else
  110. len = count;
  111. kbuf=kmalloc(len,GFP_KERNEL);
  112. if (!kbuf)
  113. return -ENOMEM;
  114. ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf);
  115. if (!ret) {
  116. *ppos += retlen;
  117. if (copy_to_user(buf, kbuf, retlen)) {
  118.         kfree(kbuf);
  119. return -EFAULT;
  120. }
  121. else
  122. total_retlen += retlen;
  123. count -= retlen;
  124. buf += retlen;
  125. }
  126. else {
  127. kfree(kbuf);
  128. return ret;
  129. }
  130. kfree(kbuf);
  131. }
  132. return total_retlen;
  133. } /* mtd_read */
  134. static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t *ppos)
  135. {
  136. struct mtd_info *mtd = (struct mtd_info *)file->private_data;
  137. char *kbuf;
  138. size_t retlen;
  139. size_t total_retlen=0;
  140. int ret=0;
  141. int len;
  142. DEBUG(MTD_DEBUG_LEVEL0,"MTD_writen");
  143. if (*ppos == mtd->size)
  144. return -ENOSPC;
  145. if (*ppos + count > mtd->size)
  146. count = mtd->size - *ppos;
  147. if (!count)
  148. return 0;
  149. while (count) {
  150. if (count > MAX_KMALLOC_SIZE) 
  151. len = MAX_KMALLOC_SIZE;
  152. else
  153. len = count;
  154. kbuf=kmalloc(len,GFP_KERNEL);
  155. if (!kbuf) {
  156. printk("kmalloc is nulln");
  157. return -ENOMEM;
  158. }
  159. if (copy_from_user(kbuf, buf, len)) {
  160. kfree(kbuf);
  161. return -EFAULT;
  162. }
  163.         ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
  164. if (!ret) {
  165. *ppos += retlen;
  166. total_retlen += retlen;
  167. count -= retlen;
  168. buf += retlen;
  169. }
  170. else {
  171. kfree(kbuf);
  172. return ret;
  173. }
  174. kfree(kbuf);
  175. }
  176. return total_retlen;
  177. } /* mtd_write */
  178. /*======================================================================
  179.     IOCTL calls for getting device parameters.
  180. ======================================================================*/
  181. static void mtd_erase_callback (struct erase_info *instr)
  182. {
  183. wake_up((wait_queue_head_t *)instr->priv);
  184. }
  185. static int mtd_ioctl(struct inode *inode, struct file *file,
  186.      u_int cmd, u_long arg)
  187. {
  188. struct mtd_info *mtd = (struct mtd_info *)file->private_data;
  189. int ret = 0;
  190. u_long size;
  191. DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctln");
  192. size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
  193. if (cmd & IOC_IN) {
  194. ret = verify_area(VERIFY_READ, (char *)arg, size);
  195. if (ret) return ret;
  196. }
  197. if (cmd & IOC_OUT) {
  198. ret = verify_area(VERIFY_WRITE, (char *)arg, size);
  199. if (ret) return ret;
  200. }
  201. switch (cmd) {
  202. case MEMGETREGIONCOUNT:
  203. if (copy_to_user((int *) arg, &(mtd->numeraseregions), sizeof(int)))
  204. return -EFAULT;
  205. break;
  206. case MEMGETREGIONINFO:
  207. {
  208. struct region_info_user ur;
  209. if (copy_from_user( &ur, 
  210. (struct region_info_user *)arg, 
  211. sizeof(struct region_info_user))) {
  212. return -EFAULT;
  213. }
  214. if (ur.regionindex >= mtd->numeraseregions)
  215. return -EINVAL;
  216. if (copy_to_user((struct mtd_erase_region_info *) arg, 
  217. &(mtd->eraseregions[ur.regionindex]),
  218. sizeof(struct mtd_erase_region_info)))
  219. return -EFAULT;
  220. break;
  221. }
  222. case MEMGETINFO:
  223. if (copy_to_user((struct mtd_info *)arg, mtd,
  224.  sizeof(struct mtd_info_user)))
  225. return -EFAULT;
  226. break;
  227. case MEMERASE:
  228. {
  229. struct erase_info *erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL);
  230. if (!erase)
  231. ret = -ENOMEM;
  232. else {
  233. wait_queue_head_t waitq;
  234. DECLARE_WAITQUEUE(wait, current);
  235. init_waitqueue_head(&waitq);
  236. memset (erase,0,sizeof(struct erase_info));
  237. if (copy_from_user(&erase->addr, (u_long *)arg,
  238.    2 * sizeof(u_long))) {
  239. kfree(erase);
  240. return -EFAULT;
  241. }
  242. erase->mtd = mtd;
  243. erase->callback = mtd_erase_callback;
  244. erase->priv = (unsigned long)&waitq;
  245. /*
  246.   FIXME: Allow INTERRUPTIBLE. Which means
  247.   not having the wait_queue head on the stack.
  248.   
  249.   If the wq_head is on the stack, and we
  250.   leave because we got interrupted, then the
  251.   wq_head is no longer there when the
  252.   callback routine tries to wake us up.
  253. */
  254. ret = mtd->erase(mtd, erase);
  255. if (!ret) {
  256. set_current_state(TASK_UNINTERRUPTIBLE);
  257. add_wait_queue(&waitq, &wait);
  258. if (erase->state != MTD_ERASE_DONE &&
  259.     erase->state != MTD_ERASE_FAILED)
  260. schedule();
  261. remove_wait_queue(&waitq, &wait);
  262. set_current_state(TASK_RUNNING);
  263. ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;
  264. }
  265. kfree(erase);
  266. }
  267. break;
  268. }
  269. case MEMWRITEOOB:
  270. {
  271. struct mtd_oob_buf buf;
  272. void *databuf;
  273. ssize_t retlen;
  274. if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
  275. return -EFAULT;
  276. if (buf.length > 0x4096)
  277. return -EINVAL;
  278. if (!mtd->write_oob)
  279. ret = -EOPNOTSUPP;
  280. else
  281. ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length);
  282. if (ret)
  283. return ret;
  284. databuf = kmalloc(buf.length, GFP_KERNEL);
  285. if (!databuf)
  286. return -ENOMEM;
  287. if (copy_from_user(databuf, buf.ptr, buf.length)) {
  288. kfree(databuf);
  289. return -EFAULT;
  290. }
  291. ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);
  292. if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
  293. ret = -EFAULT;
  294. kfree(databuf);
  295. break;
  296. }
  297. case MEMREADOOB:
  298. {
  299. struct mtd_oob_buf buf;
  300. void *databuf;
  301. ssize_t retlen;
  302. if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
  303. return -EFAULT;
  304. if (buf.length > 0x4096)
  305. return -EINVAL;
  306. if (!mtd->read_oob)
  307. ret = -EOPNOTSUPP;
  308. else
  309. ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);
  310. if (ret)
  311. return ret;
  312. databuf = kmalloc(buf.length, GFP_KERNEL);
  313. if (!databuf)
  314. return -ENOMEM;
  315. ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
  316. if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
  317. ret = -EFAULT;
  318. else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
  319. ret = -EFAULT;
  320. kfree(databuf);
  321. break;
  322. }
  323. case MEMLOCK:
  324. {
  325. unsigned long adrs[2];
  326. if (copy_from_user(adrs ,(void *)arg, 2* sizeof(unsigned long)))
  327. return -EFAULT;
  328. if (!mtd->lock)
  329. ret = -EOPNOTSUPP;
  330. else
  331. ret = mtd->lock(mtd, adrs[0], adrs[1]);
  332. break;
  333. }
  334. case MEMUNLOCK:
  335. {
  336. unsigned long adrs[2];
  337. if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long)))
  338. return -EFAULT;
  339. if (!mtd->unlock)
  340. ret = -EOPNOTSUPP;
  341. else
  342. ret = mtd->unlock(mtd, adrs[0], adrs[1]);
  343. break;
  344. }
  345. default:
  346. DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)n", cmd, MEMGETINFO);
  347. ret = -ENOTTY;
  348. }
  349. return ret;
  350. } /* memory_ioctl */
  351. static struct file_operations mtd_fops = {
  352. owner: THIS_MODULE,
  353. llseek: mtd_lseek,      /* lseek */
  354. read: mtd_read, /* read */
  355. write:  mtd_write,  /* write */
  356. ioctl: mtd_ioctl, /* ioctl */
  357. open: mtd_open, /* open */
  358. release: mtd_close, /* release */
  359. };
  360. #ifdef CONFIG_DEVFS_FS
  361. /* Notification that a new device has been added. Create the devfs entry for
  362.  * it. */
  363. static void mtd_notify_add(struct mtd_info* mtd)
  364. {
  365. char name[8];
  366. if (!mtd)
  367. return;
  368. sprintf(name, "%d", mtd->index);
  369. devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
  370. DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2,
  371. S_IFCHR | S_IRUGO | S_IWUGO,
  372. &mtd_fops, NULL);
  373. sprintf(name, "%dro", mtd->index);
  374. devfs_ro_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
  375. DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2+1,
  376. S_IFCHR | S_IRUGO | S_IWUGO,
  377. &mtd_fops, NULL);
  378. }
  379. static void mtd_notify_remove(struct mtd_info* mtd)
  380. {
  381. if (!mtd)
  382. return;
  383. devfs_unregister(devfs_rw_handle[mtd->index]);
  384. devfs_unregister(devfs_ro_handle[mtd->index]);
  385. }
  386. #endif
  387. static int __init init_mtdchar(void)
  388. {
  389. #ifdef CONFIG_DEVFS_FS
  390. if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
  391. {
  392. printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.n",
  393.        MTD_CHAR_MAJOR);
  394. return -EAGAIN;
  395. }
  396. devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL);
  397. register_mtd_user(&notifier);
  398. #else
  399. if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
  400. {
  401. printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.n",
  402.        MTD_CHAR_MAJOR);
  403. return -EAGAIN;
  404. }
  405. #endif
  406. return 0;
  407. }
  408. static void __exit cleanup_mtdchar(void)
  409. {
  410. #ifdef CONFIG_DEVFS_FS
  411. unregister_mtd_user(&notifier);
  412. devfs_unregister(devfs_dir_handle);
  413. devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
  414. #else
  415. unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
  416. #endif
  417. }
  418. module_init(init_mtdchar);
  419. module_exit(cleanup_mtdchar);
  420. MODULE_LICENSE("GPL");
  421. MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
  422. MODULE_DESCRIPTION("Direct character-device access to MTD devices");