mtdchar-compat.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:12k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

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