/drivers/staging/comedi/comedi_compat32.c

https://bitbucket.org/wisechild/galaxy-nexus · C · 463 lines · 351 code · 53 blank · 59 comment · 26 complexity · 8ec0abf1fe24e07306fe2fce3d9bc665 MD5 · raw file

  1. /*
  2. comedi/comedi_compat32.c
  3. 32-bit ioctl compatibility for 64-bit comedi kernel module.
  4. Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
  5. Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
  6. COMEDI - Linux Control and Measurement Device Interface
  7. Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
  8. This program is free software; you can redistribute it and/or modify
  9. it under the terms of the GNU General Public License as published by
  10. the Free Software Foundation; either version 2 of the License, or
  11. (at your option) any later version.
  12. This program is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. GNU General Public License for more details.
  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. #define __NO_VERSION__
  21. #include <linux/uaccess.h>
  22. #include "comedi.h"
  23. #include "comedi_compat32.h"
  24. #ifdef CONFIG_COMPAT
  25. #define COMEDI32_CHANINFO _IOR(CIO, 3, struct comedi32_chaninfo_struct)
  26. #define COMEDI32_RANGEINFO _IOR(CIO, 8, struct comedi32_rangeinfo_struct)
  27. /* N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR.
  28. * It's too late to change it now, but it only affects the command number. */
  29. #define COMEDI32_CMD _IOR(CIO, 9, struct comedi32_cmd_struct)
  30. /* N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR.
  31. * It's too late to change it now, but it only affects the command number. */
  32. #define COMEDI32_CMDTEST _IOR(CIO, 10, struct comedi32_cmd_struct)
  33. #define COMEDI32_INSNLIST _IOR(CIO, 11, struct comedi32_insnlist_struct)
  34. #define COMEDI32_INSN _IOR(CIO, 12, struct comedi32_insn_struct)
  35. struct comedi32_chaninfo_struct {
  36. unsigned int subdev;
  37. compat_uptr_t maxdata_list; /* 32-bit 'unsigned int *' */
  38. compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */
  39. compat_uptr_t rangelist; /* 32-bit 'unsigned int *' */
  40. unsigned int unused[4];
  41. };
  42. struct comedi32_rangeinfo_struct {
  43. unsigned int range_type;
  44. compat_uptr_t range_ptr; /* 32-bit 'void *' */
  45. };
  46. struct comedi32_cmd_struct {
  47. unsigned int subdev;
  48. unsigned int flags;
  49. unsigned int start_src;
  50. unsigned int start_arg;
  51. unsigned int scan_begin_src;
  52. unsigned int scan_begin_arg;
  53. unsigned int convert_src;
  54. unsigned int convert_arg;
  55. unsigned int scan_end_src;
  56. unsigned int scan_end_arg;
  57. unsigned int stop_src;
  58. unsigned int stop_arg;
  59. compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */
  60. unsigned int chanlist_len;
  61. compat_uptr_t data; /* 32-bit 'short *' */
  62. unsigned int data_len;
  63. };
  64. struct comedi32_insn_struct {
  65. unsigned int insn;
  66. unsigned int n;
  67. compat_uptr_t data; /* 32-bit 'unsigned int *' */
  68. unsigned int subdev;
  69. unsigned int chanspec;
  70. unsigned int unused[3];
  71. };
  72. struct comedi32_insnlist_struct {
  73. unsigned int n_insns;
  74. compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */
  75. };
  76. /* Handle translated ioctl. */
  77. static int translated_ioctl(struct file *file, unsigned int cmd,
  78. unsigned long arg)
  79. {
  80. if (!file->f_op)
  81. return -ENOTTY;
  82. if (file->f_op->unlocked_ioctl)
  83. return file->f_op->unlocked_ioctl(file, cmd, arg);
  84. return -ENOTTY;
  85. }
  86. /* Handle 32-bit COMEDI_CHANINFO ioctl. */
  87. static int compat_chaninfo(struct file *file, unsigned long arg)
  88. {
  89. struct comedi_chaninfo __user *chaninfo;
  90. struct comedi32_chaninfo_struct __user *chaninfo32;
  91. int err;
  92. union {
  93. unsigned int uint;
  94. compat_uptr_t uptr;
  95. } temp;
  96. chaninfo32 = compat_ptr(arg);
  97. chaninfo = compat_alloc_user_space(sizeof(*chaninfo));
  98. /* Copy chaninfo structure. Ignore unused members. */
  99. if (!access_ok(VERIFY_READ, chaninfo32, sizeof(*chaninfo32))
  100. || !access_ok(VERIFY_WRITE, chaninfo, sizeof(*chaninfo))) {
  101. return -EFAULT;
  102. }
  103. err = 0;
  104. err |= __get_user(temp.uint, &chaninfo32->subdev);
  105. err |= __put_user(temp.uint, &chaninfo->subdev);
  106. err |= __get_user(temp.uptr, &chaninfo32->maxdata_list);
  107. err |= __put_user(compat_ptr(temp.uptr), &chaninfo->maxdata_list);
  108. err |= __get_user(temp.uptr, &chaninfo32->flaglist);
  109. err |= __put_user(compat_ptr(temp.uptr), &chaninfo->flaglist);
  110. err |= __get_user(temp.uptr, &chaninfo32->rangelist);
  111. err |= __put_user(compat_ptr(temp.uptr), &chaninfo->rangelist);
  112. if (err)
  113. return -EFAULT;
  114. return translated_ioctl(file, COMEDI_CHANINFO, (unsigned long)chaninfo);
  115. }
  116. /* Handle 32-bit COMEDI_RANGEINFO ioctl. */
  117. static int compat_rangeinfo(struct file *file, unsigned long arg)
  118. {
  119. struct comedi_rangeinfo __user *rangeinfo;
  120. struct comedi32_rangeinfo_struct __user *rangeinfo32;
  121. int err;
  122. union {
  123. unsigned int uint;
  124. compat_uptr_t uptr;
  125. } temp;
  126. rangeinfo32 = compat_ptr(arg);
  127. rangeinfo = compat_alloc_user_space(sizeof(*rangeinfo));
  128. /* Copy rangeinfo structure. */
  129. if (!access_ok(VERIFY_READ, rangeinfo32, sizeof(*rangeinfo32))
  130. || !access_ok(VERIFY_WRITE, rangeinfo, sizeof(*rangeinfo))) {
  131. return -EFAULT;
  132. }
  133. err = 0;
  134. err |= __get_user(temp.uint, &rangeinfo32->range_type);
  135. err |= __put_user(temp.uint, &rangeinfo->range_type);
  136. err |= __get_user(temp.uptr, &rangeinfo32->range_ptr);
  137. err |= __put_user(compat_ptr(temp.uptr), &rangeinfo->range_ptr);
  138. if (err)
  139. return -EFAULT;
  140. return translated_ioctl(file, COMEDI_RANGEINFO,
  141. (unsigned long)rangeinfo);
  142. }
  143. /* Copy 32-bit cmd structure to native cmd structure. */
  144. static int get_compat_cmd(struct comedi_cmd __user *cmd,
  145. struct comedi32_cmd_struct __user *cmd32)
  146. {
  147. int err;
  148. union {
  149. unsigned int uint;
  150. compat_uptr_t uptr;
  151. } temp;
  152. /* Copy cmd structure. */
  153. if (!access_ok(VERIFY_READ, cmd32, sizeof(*cmd32))
  154. || !access_ok(VERIFY_WRITE, cmd, sizeof(*cmd))) {
  155. return -EFAULT;
  156. }
  157. err = 0;
  158. err |= __get_user(temp.uint, &cmd32->subdev);
  159. err |= __put_user(temp.uint, &cmd->subdev);
  160. err |= __get_user(temp.uint, &cmd32->flags);
  161. err |= __put_user(temp.uint, &cmd->flags);
  162. err |= __get_user(temp.uint, &cmd32->start_src);
  163. err |= __put_user(temp.uint, &cmd->start_src);
  164. err |= __get_user(temp.uint, &cmd32->start_arg);
  165. err |= __put_user(temp.uint, &cmd->start_arg);
  166. err |= __get_user(temp.uint, &cmd32->scan_begin_src);
  167. err |= __put_user(temp.uint, &cmd->scan_begin_src);
  168. err |= __get_user(temp.uint, &cmd32->scan_begin_arg);
  169. err |= __put_user(temp.uint, &cmd->scan_begin_arg);
  170. err |= __get_user(temp.uint, &cmd32->convert_src);
  171. err |= __put_user(temp.uint, &cmd->convert_src);
  172. err |= __get_user(temp.uint, &cmd32->convert_arg);
  173. err |= __put_user(temp.uint, &cmd->convert_arg);
  174. err |= __get_user(temp.uint, &cmd32->scan_end_src);
  175. err |= __put_user(temp.uint, &cmd->scan_end_src);
  176. err |= __get_user(temp.uint, &cmd32->scan_end_arg);
  177. err |= __put_user(temp.uint, &cmd->scan_end_arg);
  178. err |= __get_user(temp.uint, &cmd32->stop_src);
  179. err |= __put_user(temp.uint, &cmd->stop_src);
  180. err |= __get_user(temp.uint, &cmd32->stop_arg);
  181. err |= __put_user(temp.uint, &cmd->stop_arg);
  182. err |= __get_user(temp.uptr, &cmd32->chanlist);
  183. err |= __put_user(compat_ptr(temp.uptr), &cmd->chanlist);
  184. err |= __get_user(temp.uint, &cmd32->chanlist_len);
  185. err |= __put_user(temp.uint, &cmd->chanlist_len);
  186. err |= __get_user(temp.uptr, &cmd32->data);
  187. err |= __put_user(compat_ptr(temp.uptr), &cmd->data);
  188. err |= __get_user(temp.uint, &cmd32->data_len);
  189. err |= __put_user(temp.uint, &cmd->data_len);
  190. return err ? -EFAULT : 0;
  191. }
  192. /* Copy native cmd structure to 32-bit cmd structure. */
  193. static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32,
  194. struct comedi_cmd __user *cmd)
  195. {
  196. int err;
  197. unsigned int temp;
  198. /* Copy back most of cmd structure. */
  199. /* Assume the pointer values are already valid. */
  200. /* (Could use ptr_to_compat() to set them, but that wasn't implemented
  201. * until kernel version 2.6.11.) */
  202. if (!access_ok(VERIFY_READ, cmd, sizeof(*cmd))
  203. || !access_ok(VERIFY_WRITE, cmd32, sizeof(*cmd32))) {
  204. return -EFAULT;
  205. }
  206. err = 0;
  207. err |= __get_user(temp, &cmd->subdev);
  208. err |= __put_user(temp, &cmd32->subdev);
  209. err |= __get_user(temp, &cmd->flags);
  210. err |= __put_user(temp, &cmd32->flags);
  211. err |= __get_user(temp, &cmd->start_src);
  212. err |= __put_user(temp, &cmd32->start_src);
  213. err |= __get_user(temp, &cmd->start_arg);
  214. err |= __put_user(temp, &cmd32->start_arg);
  215. err |= __get_user(temp, &cmd->scan_begin_src);
  216. err |= __put_user(temp, &cmd32->scan_begin_src);
  217. err |= __get_user(temp, &cmd->scan_begin_arg);
  218. err |= __put_user(temp, &cmd32->scan_begin_arg);
  219. err |= __get_user(temp, &cmd->convert_src);
  220. err |= __put_user(temp, &cmd32->convert_src);
  221. err |= __get_user(temp, &cmd->convert_arg);
  222. err |= __put_user(temp, &cmd32->convert_arg);
  223. err |= __get_user(temp, &cmd->scan_end_src);
  224. err |= __put_user(temp, &cmd32->scan_end_src);
  225. err |= __get_user(temp, &cmd->scan_end_arg);
  226. err |= __put_user(temp, &cmd32->scan_end_arg);
  227. err |= __get_user(temp, &cmd->stop_src);
  228. err |= __put_user(temp, &cmd32->stop_src);
  229. err |= __get_user(temp, &cmd->stop_arg);
  230. err |= __put_user(temp, &cmd32->stop_arg);
  231. /* Assume chanlist pointer is unchanged. */
  232. err |= __get_user(temp, &cmd->chanlist_len);
  233. err |= __put_user(temp, &cmd32->chanlist_len);
  234. /* Assume data pointer is unchanged. */
  235. err |= __get_user(temp, &cmd->data_len);
  236. err |= __put_user(temp, &cmd32->data_len);
  237. return err ? -EFAULT : 0;
  238. }
  239. /* Handle 32-bit COMEDI_CMD ioctl. */
  240. static int compat_cmd(struct file *file, unsigned long arg)
  241. {
  242. struct comedi_cmd __user *cmd;
  243. struct comedi32_cmd_struct __user *cmd32;
  244. int rc;
  245. cmd32 = compat_ptr(arg);
  246. cmd = compat_alloc_user_space(sizeof(*cmd));
  247. rc = get_compat_cmd(cmd, cmd32);
  248. if (rc)
  249. return rc;
  250. return translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd);
  251. }
  252. /* Handle 32-bit COMEDI_CMDTEST ioctl. */
  253. static int compat_cmdtest(struct file *file, unsigned long arg)
  254. {
  255. struct comedi_cmd __user *cmd;
  256. struct comedi32_cmd_struct __user *cmd32;
  257. int rc, err;
  258. cmd32 = compat_ptr(arg);
  259. cmd = compat_alloc_user_space(sizeof(*cmd));
  260. rc = get_compat_cmd(cmd, cmd32);
  261. if (rc)
  262. return rc;
  263. rc = translated_ioctl(file, COMEDI_CMDTEST, (unsigned long)cmd);
  264. if (rc < 0)
  265. return rc;
  266. err = put_compat_cmd(cmd32, cmd);
  267. if (err)
  268. rc = err;
  269. return rc;
  270. }
  271. /* Copy 32-bit insn structure to native insn structure. */
  272. static int get_compat_insn(struct comedi_insn __user *insn,
  273. struct comedi32_insn_struct __user *insn32)
  274. {
  275. int err;
  276. union {
  277. unsigned int uint;
  278. compat_uptr_t uptr;
  279. } temp;
  280. /* Copy insn structure. Ignore the unused members. */
  281. err = 0;
  282. if (!access_ok(VERIFY_READ, insn32, sizeof(*insn32))
  283. || !access_ok(VERIFY_WRITE, insn, sizeof(*insn)))
  284. return -EFAULT;
  285. err |= __get_user(temp.uint, &insn32->insn);
  286. err |= __put_user(temp.uint, &insn->insn);
  287. err |= __get_user(temp.uint, &insn32->n);
  288. err |= __put_user(temp.uint, &insn->n);
  289. err |= __get_user(temp.uptr, &insn32->data);
  290. err |= __put_user(compat_ptr(temp.uptr), &insn->data);
  291. err |= __get_user(temp.uint, &insn32->subdev);
  292. err |= __put_user(temp.uint, &insn->subdev);
  293. err |= __get_user(temp.uint, &insn32->chanspec);
  294. err |= __put_user(temp.uint, &insn->chanspec);
  295. return err ? -EFAULT : 0;
  296. }
  297. /* Handle 32-bit COMEDI_INSNLIST ioctl. */
  298. static int compat_insnlist(struct file *file, unsigned long arg)
  299. {
  300. struct combined_insnlist {
  301. struct comedi_insnlist insnlist;
  302. struct comedi_insn insn[1];
  303. } __user *s;
  304. struct comedi32_insnlist_struct __user *insnlist32;
  305. struct comedi32_insn_struct __user *insn32;
  306. compat_uptr_t uptr;
  307. unsigned int n_insns, n;
  308. int err, rc;
  309. insnlist32 = compat_ptr(arg);
  310. /* Get 32-bit insnlist structure. */
  311. if (!access_ok(VERIFY_READ, insnlist32, sizeof(*insnlist32)))
  312. return -EFAULT;
  313. err = 0;
  314. err |= __get_user(n_insns, &insnlist32->n_insns);
  315. err |= __get_user(uptr, &insnlist32->insns);
  316. insn32 = compat_ptr(uptr);
  317. if (err)
  318. return -EFAULT;
  319. /* Allocate user memory to copy insnlist and insns into. */
  320. s = compat_alloc_user_space(offsetof(struct combined_insnlist,
  321. insn[n_insns]));
  322. /* Set native insnlist structure. */
  323. if (!access_ok(VERIFY_WRITE, &s->insnlist, sizeof(s->insnlist)))
  324. return -EFAULT;
  325. err |= __put_user(n_insns, &s->insnlist.n_insns);
  326. err |= __put_user(&s->insn[0], &s->insnlist.insns);
  327. if (err)
  328. return -EFAULT;
  329. /* Copy insn structures. */
  330. for (n = 0; n < n_insns; n++) {
  331. rc = get_compat_insn(&s->insn[n], &insn32[n]);
  332. if (rc)
  333. return rc;
  334. }
  335. return translated_ioctl(file, COMEDI_INSNLIST,
  336. (unsigned long)&s->insnlist);
  337. }
  338. /* Handle 32-bit COMEDI_INSN ioctl. */
  339. static int compat_insn(struct file *file, unsigned long arg)
  340. {
  341. struct comedi_insn __user *insn;
  342. struct comedi32_insn_struct __user *insn32;
  343. int rc;
  344. insn32 = compat_ptr(arg);
  345. insn = compat_alloc_user_space(sizeof(*insn));
  346. rc = get_compat_insn(insn, insn32);
  347. if (rc)
  348. return rc;
  349. return translated_ioctl(file, COMEDI_INSN, (unsigned long)insn);
  350. }
  351. /* Process untranslated ioctl. */
  352. /* Returns -ENOIOCTLCMD for unrecognised ioctl codes. */
  353. static inline int raw_ioctl(struct file *file, unsigned int cmd,
  354. unsigned long arg)
  355. {
  356. int rc;
  357. switch (cmd) {
  358. case COMEDI_DEVCONFIG:
  359. case COMEDI_DEVINFO:
  360. case COMEDI_SUBDINFO:
  361. case COMEDI_BUFCONFIG:
  362. case COMEDI_BUFINFO:
  363. /* Just need to translate the pointer argument. */
  364. arg = (unsigned long)compat_ptr(arg);
  365. rc = translated_ioctl(file, cmd, arg);
  366. break;
  367. case COMEDI_LOCK:
  368. case COMEDI_UNLOCK:
  369. case COMEDI_CANCEL:
  370. case COMEDI_POLL:
  371. /* No translation needed. */
  372. rc = translated_ioctl(file, cmd, arg);
  373. break;
  374. case COMEDI32_CHANINFO:
  375. rc = compat_chaninfo(file, arg);
  376. break;
  377. case COMEDI32_RANGEINFO:
  378. rc = compat_rangeinfo(file, arg);
  379. break;
  380. case COMEDI32_CMD:
  381. rc = compat_cmd(file, arg);
  382. break;
  383. case COMEDI32_CMDTEST:
  384. rc = compat_cmdtest(file, arg);
  385. break;
  386. case COMEDI32_INSNLIST:
  387. rc = compat_insnlist(file, arg);
  388. break;
  389. case COMEDI32_INSN:
  390. rc = compat_insn(file, arg);
  391. break;
  392. default:
  393. rc = -ENOIOCTLCMD;
  394. break;
  395. }
  396. return rc;
  397. }
  398. /* compat_ioctl file operation. */
  399. /* Returns -ENOIOCTLCMD for unrecognised ioctl codes. */
  400. long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  401. {
  402. return raw_ioctl(file, cmd, arg);
  403. }
  404. #endif /* CONFIG_COMPAT */