diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2011-12-18 18:45:39 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-03-19 13:07:01 -0400 |
commit | 1a5e4c867c611ed17db837f8f900b44c72990cc4 (patch) | |
tree | 736d1bc0975bcbaaba809fc9e2e73a87b48190d0 /drivers/media/video/uvc | |
parent | a3ec69b707718cb95317d1b783560210487c55a8 (diff) |
[media] uvcvideo: Implement compat_ioctl32 for custom ioctls
Support 32-bit/64-bit compatibility for the the UVCIOC_ ioctls.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/uvc')
-rw-r--r-- | drivers/media/video/uvc/uvc_v4l2.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index f09ee8b2ea5..ff2cdddf9bc 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c | |||
@@ -11,6 +11,7 @@ | |||
11 | * | 11 | * |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/compat.h> | ||
14 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
15 | #include <linux/version.h> | 16 | #include <linux/version.h> |
16 | #include <linux/list.h> | 17 | #include <linux/list.h> |
@@ -1030,6 +1031,207 @@ static long uvc_v4l2_ioctl(struct file *file, | |||
1030 | return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); | 1031 | return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); |
1031 | } | 1032 | } |
1032 | 1033 | ||
1034 | #ifdef CONFIG_COMPAT | ||
1035 | struct uvc_xu_control_mapping32 { | ||
1036 | __u32 id; | ||
1037 | __u8 name[32]; | ||
1038 | __u8 entity[16]; | ||
1039 | __u8 selector; | ||
1040 | |||
1041 | __u8 size; | ||
1042 | __u8 offset; | ||
1043 | __u32 v4l2_type; | ||
1044 | __u32 data_type; | ||
1045 | |||
1046 | compat_caddr_t menu_info; | ||
1047 | __u32 menu_count; | ||
1048 | |||
1049 | __u32 reserved[4]; | ||
1050 | }; | ||
1051 | |||
1052 | static int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp, | ||
1053 | const struct uvc_xu_control_mapping32 __user *up) | ||
1054 | { | ||
1055 | struct uvc_menu_info __user *umenus; | ||
1056 | struct uvc_menu_info __user *kmenus; | ||
1057 | compat_caddr_t p; | ||
1058 | |||
1059 | if (!access_ok(VERIFY_READ, up, sizeof(*up)) || | ||
1060 | __copy_from_user(kp, up, offsetof(typeof(*up), menu_info)) || | ||
1061 | __get_user(kp->menu_count, &up->menu_count)) | ||
1062 | return -EFAULT; | ||
1063 | |||
1064 | memset(kp->reserved, 0, sizeof(kp->reserved)); | ||
1065 | |||
1066 | if (kp->menu_count == 0) { | ||
1067 | kp->menu_info = NULL; | ||
1068 | return 0; | ||
1069 | } | ||
1070 | |||
1071 | if (__get_user(p, &up->menu_info)) | ||
1072 | return -EFAULT; | ||
1073 | umenus = compat_ptr(p); | ||
1074 | if (!access_ok(VERIFY_READ, umenus, kp->menu_count * sizeof(*umenus))) | ||
1075 | return -EFAULT; | ||
1076 | |||
1077 | kmenus = compat_alloc_user_space(kp->menu_count * sizeof(*kmenus)); | ||
1078 | if (kmenus == NULL) | ||
1079 | return -EFAULT; | ||
1080 | kp->menu_info = kmenus; | ||
1081 | |||
1082 | if (copy_in_user(kmenus, umenus, kp->menu_count * sizeof(*umenus))) | ||
1083 | return -EFAULT; | ||
1084 | |||
1085 | return 0; | ||
1086 | } | ||
1087 | |||
1088 | static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp, | ||
1089 | struct uvc_xu_control_mapping32 __user *up) | ||
1090 | { | ||
1091 | struct uvc_menu_info __user *umenus; | ||
1092 | struct uvc_menu_info __user *kmenus = kp->menu_info; | ||
1093 | compat_caddr_t p; | ||
1094 | |||
1095 | if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || | ||
1096 | __copy_to_user(up, kp, offsetof(typeof(*up), menu_info)) || | ||
1097 | __put_user(kp->menu_count, &up->menu_count)) | ||
1098 | return -EFAULT; | ||
1099 | |||
1100 | __clear_user(up->reserved, sizeof(up->reserved)); | ||
1101 | |||
1102 | if (kp->menu_count == 0) | ||
1103 | return 0; | ||
1104 | |||
1105 | if (get_user(p, &up->menu_info)) | ||
1106 | return -EFAULT; | ||
1107 | umenus = compat_ptr(p); | ||
1108 | if (!access_ok(VERIFY_WRITE, umenus, kp->menu_count * sizeof(*umenus))) | ||
1109 | return -EFAULT; | ||
1110 | |||
1111 | if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus))) | ||
1112 | return -EFAULT; | ||
1113 | |||
1114 | return 0; | ||
1115 | } | ||
1116 | |||
1117 | struct uvc_xu_control_query32 { | ||
1118 | __u8 unit; | ||
1119 | __u8 selector; | ||
1120 | __u8 query; | ||
1121 | __u16 size; | ||
1122 | compat_caddr_t data; | ||
1123 | }; | ||
1124 | |||
1125 | static int uvc_v4l2_get_xu_query(struct uvc_xu_control_query *kp, | ||
1126 | const struct uvc_xu_control_query32 __user *up) | ||
1127 | { | ||
1128 | u8 __user *udata; | ||
1129 | u8 __user *kdata; | ||
1130 | compat_caddr_t p; | ||
1131 | |||
1132 | if (!access_ok(VERIFY_READ, up, sizeof(*up)) || | ||
1133 | __copy_from_user(kp, up, offsetof(typeof(*up), data))) | ||
1134 | return -EFAULT; | ||
1135 | |||
1136 | if (kp->size == 0) { | ||
1137 | kp->data = NULL; | ||
1138 | return 0; | ||
1139 | } | ||
1140 | |||
1141 | if (__get_user(p, &up->data)) | ||
1142 | return -EFAULT; | ||
1143 | udata = compat_ptr(p); | ||
1144 | if (!access_ok(VERIFY_READ, udata, kp->size)) | ||
1145 | return -EFAULT; | ||
1146 | |||
1147 | kdata = compat_alloc_user_space(kp->size); | ||
1148 | if (kdata == NULL) | ||
1149 | return -EFAULT; | ||
1150 | kp->data = kdata; | ||
1151 | |||
1152 | if (copy_in_user(kdata, udata, kp->size)) | ||
1153 | return -EFAULT; | ||
1154 | |||
1155 | return 0; | ||
1156 | } | ||
1157 | |||
1158 | static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp, | ||
1159 | struct uvc_xu_control_query32 __user *up) | ||
1160 | { | ||
1161 | u8 __user *udata; | ||
1162 | u8 __user *kdata = kp->data; | ||
1163 | compat_caddr_t p; | ||
1164 | |||
1165 | if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || | ||
1166 | __copy_to_user(up, kp, offsetof(typeof(*up), data))) | ||
1167 | return -EFAULT; | ||
1168 | |||
1169 | if (kp->size == 0) | ||
1170 | return 0; | ||
1171 | |||
1172 | if (get_user(p, &up->data)) | ||
1173 | return -EFAULT; | ||
1174 | udata = compat_ptr(p); | ||
1175 | if (!access_ok(VERIFY_READ, udata, kp->size)) | ||
1176 | return -EFAULT; | ||
1177 | |||
1178 | if (copy_in_user(udata, kdata, kp->size)) | ||
1179 | return -EFAULT; | ||
1180 | |||
1181 | return 0; | ||
1182 | } | ||
1183 | |||
1184 | #define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32) | ||
1185 | #define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32) | ||
1186 | |||
1187 | static long uvc_v4l2_compat_ioctl32(struct file *file, | ||
1188 | unsigned int cmd, unsigned long arg) | ||
1189 | { | ||
1190 | union { | ||
1191 | struct uvc_xu_control_mapping xmap; | ||
1192 | struct uvc_xu_control_query xqry; | ||
1193 | } karg; | ||
1194 | void __user *up = compat_ptr(arg); | ||
1195 | mm_segment_t old_fs; | ||
1196 | long ret; | ||
1197 | |||
1198 | switch (cmd) { | ||
1199 | case UVCIOC_CTRL_MAP32: | ||
1200 | cmd = UVCIOC_CTRL_MAP; | ||
1201 | ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up); | ||
1202 | break; | ||
1203 | |||
1204 | case UVCIOC_CTRL_QUERY32: | ||
1205 | cmd = UVCIOC_CTRL_QUERY; | ||
1206 | ret = uvc_v4l2_get_xu_query(&karg.xqry, up); | ||
1207 | break; | ||
1208 | |||
1209 | default: | ||
1210 | return -ENOIOCTLCMD; | ||
1211 | } | ||
1212 | |||
1213 | old_fs = get_fs(); | ||
1214 | set_fs(KERNEL_DS); | ||
1215 | ret = uvc_v4l2_ioctl(file, cmd, (unsigned long)&karg); | ||
1216 | set_fs(old_fs); | ||
1217 | |||
1218 | if (ret < 0) | ||
1219 | return ret; | ||
1220 | |||
1221 | switch (cmd) { | ||
1222 | case UVCIOC_CTRL_MAP: | ||
1223 | ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up); | ||
1224 | break; | ||
1225 | |||
1226 | case UVCIOC_CTRL_QUERY: | ||
1227 | ret = uvc_v4l2_put_xu_query(&karg.xqry, up); | ||
1228 | break; | ||
1229 | } | ||
1230 | |||
1231 | return ret; | ||
1232 | } | ||
1233 | #endif | ||
1234 | |||
1033 | static ssize_t uvc_v4l2_read(struct file *file, char __user *data, | 1235 | static ssize_t uvc_v4l2_read(struct file *file, char __user *data, |
1034 | size_t count, loff_t *ppos) | 1236 | size_t count, loff_t *ppos) |
1035 | { | 1237 | { |
@@ -1076,6 +1278,9 @@ const struct v4l2_file_operations uvc_fops = { | |||
1076 | .open = uvc_v4l2_open, | 1278 | .open = uvc_v4l2_open, |
1077 | .release = uvc_v4l2_release, | 1279 | .release = uvc_v4l2_release, |
1078 | .unlocked_ioctl = uvc_v4l2_ioctl, | 1280 | .unlocked_ioctl = uvc_v4l2_ioctl, |
1281 | #ifdef CONFIG_COMPAT | ||
1282 | .compat_ioctl32 = uvc_v4l2_compat_ioctl32, | ||
1283 | #endif | ||
1079 | .read = uvc_v4l2_read, | 1284 | .read = uvc_v4l2_read, |
1080 | .mmap = uvc_v4l2_mmap, | 1285 | .mmap = uvc_v4l2_mmap, |
1081 | .poll = uvc_v4l2_poll, | 1286 | .poll = uvc_v4l2_poll, |