diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2010-09-29 15:03:03 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-10-20 23:18:24 -0400 |
commit | 52c58ad6f95ff60343bf0c517182d5f649ca0403 (patch) | |
tree | ba5e136941f806d340bee2d0fe2b76deed4cb01c /drivers/media/video/uvc | |
parent | 071c8bb827c80a68510a1cdb7e8bebbda1a494d6 (diff) |
[media] uvcvideo: Delay initialization of XU controls
XU controls initialization requires querying the device for control
information. As some buggy UVC devices will crash when queried
repeatedly in a tight loop, delay XU controls initialization until first
use.
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_ctrl.c | 194 |
1 files changed, 107 insertions, 87 deletions
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 97a2395671b5..a0c9d580ca9d 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c | |||
@@ -1164,6 +1164,90 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, | |||
1164 | * Dynamic controls | 1164 | * Dynamic controls |
1165 | */ | 1165 | */ |
1166 | 1166 | ||
1167 | /* | ||
1168 | * Query control information (size and flags) for XU controls. | ||
1169 | */ | ||
1170 | static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, | ||
1171 | const struct uvc_control *ctrl, struct uvc_control_info *info) | ||
1172 | { | ||
1173 | u8 *data; | ||
1174 | int ret; | ||
1175 | |||
1176 | data = kmalloc(2, GFP_KERNEL); | ||
1177 | if (data == NULL) | ||
1178 | return -ENOMEM; | ||
1179 | |||
1180 | memcpy(info->entity, ctrl->entity->extension.guidExtensionCode, | ||
1181 | sizeof(info->entity)); | ||
1182 | info->index = ctrl->index; | ||
1183 | info->selector = ctrl->index + 1; | ||
1184 | |||
1185 | /* Query and verify the control length (GET_LEN) */ | ||
1186 | ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum, | ||
1187 | info->selector, data, 2); | ||
1188 | if (ret < 0) { | ||
1189 | uvc_trace(UVC_TRACE_CONTROL, | ||
1190 | "GET_LEN failed on control %pUl/%u (%d).\n", | ||
1191 | info->entity, info->selector, ret); | ||
1192 | goto done; | ||
1193 | } | ||
1194 | |||
1195 | info->size = le16_to_cpup((__le16 *)data); | ||
1196 | |||
1197 | /* Query the control information (GET_INFO) */ | ||
1198 | ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum, | ||
1199 | info->selector, data, 1); | ||
1200 | if (ret < 0) { | ||
1201 | uvc_trace(UVC_TRACE_CONTROL, | ||
1202 | "GET_INFO failed on control %pUl/%u (%d).\n", | ||
1203 | info->entity, info->selector, ret); | ||
1204 | goto done; | ||
1205 | } | ||
1206 | |||
1207 | info->flags = UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX | ||
1208 | | UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF | ||
1209 | | (data[0] & UVC_CONTROL_CAP_GET ? UVC_CONTROL_GET_CUR : 0) | ||
1210 | | (data[0] & UVC_CONTROL_CAP_SET ? UVC_CONTROL_SET_CUR : 0) | ||
1211 | | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ? | ||
1212 | UVC_CONTROL_AUTO_UPDATE : 0); | ||
1213 | |||
1214 | uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, " | ||
1215 | "flags { get %u set %u auto %u }.\n", | ||
1216 | info->entity, info->selector, info->size, | ||
1217 | (info->flags & UVC_CONTROL_GET_CUR) ? 1 : 0, | ||
1218 | (info->flags & UVC_CONTROL_SET_CUR) ? 1 : 0, | ||
1219 | (info->flags & UVC_CONTROL_AUTO_UPDATE) ? 1 : 0); | ||
1220 | |||
1221 | done: | ||
1222 | kfree(data); | ||
1223 | return ret; | ||
1224 | } | ||
1225 | |||
1226 | static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, | ||
1227 | const struct uvc_control_info *info); | ||
1228 | |||
1229 | static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev, | ||
1230 | struct uvc_control *ctrl) | ||
1231 | { | ||
1232 | struct uvc_control_info info; | ||
1233 | int ret; | ||
1234 | |||
1235 | if (ctrl->initialized) | ||
1236 | return 0; | ||
1237 | |||
1238 | ret = uvc_ctrl_fill_xu_info(dev, ctrl, &info); | ||
1239 | if (ret < 0) | ||
1240 | return ret; | ||
1241 | |||
1242 | ret = uvc_ctrl_add_info(dev, ctrl, &info); | ||
1243 | if (ret < 0) | ||
1244 | uvc_trace(UVC_TRACE_CONTROL, "Failed to initialize control " | ||
1245 | "%pUl/%u on device %s entity %u\n", info.entity, | ||
1246 | info.selector, dev->udev->devpath, ctrl->entity->id); | ||
1247 | |||
1248 | return ret; | ||
1249 | } | ||
1250 | |||
1167 | int uvc_xu_ctrl_query(struct uvc_video_chain *chain, | 1251 | int uvc_xu_ctrl_query(struct uvc_video_chain *chain, |
1168 | struct uvc_xu_control *xctrl, int set) | 1252 | struct uvc_xu_control *xctrl, int set) |
1169 | { | 1253 | { |
@@ -1186,13 +1270,10 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, | |||
1186 | return -EINVAL; | 1270 | return -EINVAL; |
1187 | } | 1271 | } |
1188 | 1272 | ||
1189 | /* Find the control. */ | 1273 | /* Find the control and perform delayed initialization if needed. */ |
1190 | for (i = 0; i < entity->ncontrols; ++i) { | 1274 | for (i = 0; i < entity->ncontrols; ++i) { |
1191 | ctrl = &entity->controls[i]; | 1275 | ctrl = &entity->controls[i]; |
1192 | if (!ctrl->initialized) | 1276 | if (ctrl->index == xctrl->selector - 1) { |
1193 | continue; | ||
1194 | |||
1195 | if (ctrl->info.selector == xctrl->selector) { | ||
1196 | found = 1; | 1277 | found = 1; |
1197 | break; | 1278 | break; |
1198 | } | 1279 | } |
@@ -1204,6 +1285,10 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, | |||
1204 | return -EINVAL; | 1285 | return -EINVAL; |
1205 | } | 1286 | } |
1206 | 1287 | ||
1288 | ret = uvc_ctrl_init_xu_ctrl(chain->dev, ctrl); | ||
1289 | if (ret < 0) | ||
1290 | return -ENOENT; | ||
1291 | |||
1207 | /* Validate control data size. */ | 1292 | /* Validate control data size. */ |
1208 | if (ctrl->info.size != xctrl->size) | 1293 | if (ctrl->info.size != xctrl->size) |
1209 | return -EINVAL; | 1294 | return -EINVAL; |
@@ -1295,65 +1380,6 @@ int uvc_ctrl_resume_device(struct uvc_device *dev) | |||
1295 | */ | 1380 | */ |
1296 | 1381 | ||
1297 | /* | 1382 | /* |
1298 | * Query control information (size and flags) for XU controls. | ||
1299 | */ | ||
1300 | static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, | ||
1301 | const struct uvc_control *ctrl, struct uvc_control_info *info) | ||
1302 | { | ||
1303 | u8 *data; | ||
1304 | int ret; | ||
1305 | |||
1306 | data = kmalloc(2, GFP_KERNEL); | ||
1307 | if (data == NULL) | ||
1308 | return -ENOMEM; | ||
1309 | |||
1310 | memcpy(info->entity, ctrl->entity->extension.guidExtensionCode, | ||
1311 | sizeof(info->entity)); | ||
1312 | info->index = ctrl->index; | ||
1313 | info->selector = ctrl->index + 1; | ||
1314 | |||
1315 | /* Query and verify the control length (GET_LEN) */ | ||
1316 | ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum, | ||
1317 | info->selector, data, 2); | ||
1318 | if (ret < 0) { | ||
1319 | uvc_trace(UVC_TRACE_CONTROL, | ||
1320 | "GET_LEN failed on control %pUl/%u (%d).\n", | ||
1321 | info->entity, info->selector, ret); | ||
1322 | goto done; | ||
1323 | } | ||
1324 | |||
1325 | info->size = le16_to_cpup((__le16 *)data); | ||
1326 | |||
1327 | /* Query the control information (GET_INFO) */ | ||
1328 | ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum, | ||
1329 | info->selector, data, 1); | ||
1330 | if (ret < 0) { | ||
1331 | uvc_trace(UVC_TRACE_CONTROL, | ||
1332 | "GET_INFO failed on control %pUl/%u (%d).\n", | ||
1333 | info->entity, info->selector, ret); | ||
1334 | goto done; | ||
1335 | } | ||
1336 | |||
1337 | info->flags = UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX | ||
1338 | | UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF | ||
1339 | | (data[0] & UVC_CONTROL_CAP_GET ? UVC_CONTROL_GET_CUR : 0) | ||
1340 | | (data[0] & UVC_CONTROL_CAP_SET ? UVC_CONTROL_SET_CUR : 0) | ||
1341 | | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ? | ||
1342 | UVC_CONTROL_AUTO_UPDATE : 0); | ||
1343 | |||
1344 | uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, " | ||
1345 | "flags { get %u set %u auto %u }.\n", | ||
1346 | info->entity, info->selector, info->size, | ||
1347 | (info->flags & UVC_CONTROL_GET_CUR) ? 1 : 0, | ||
1348 | (info->flags & UVC_CONTROL_SET_CUR) ? 1 : 0, | ||
1349 | (info->flags & UVC_CONTROL_AUTO_UPDATE) ? 1 : 0); | ||
1350 | |||
1351 | done: | ||
1352 | kfree(data); | ||
1353 | return ret; | ||
1354 | } | ||
1355 | |||
1356 | /* | ||
1357 | * Add control information to a given control. | 1383 | * Add control information to a given control. |
1358 | */ | 1384 | */ |
1359 | static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, | 1385 | static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, |
@@ -1434,7 +1460,7 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, | |||
1434 | 1460 | ||
1435 | if (mapping->id & ~V4L2_CTRL_ID_MASK) { | 1461 | if (mapping->id & ~V4L2_CTRL_ID_MASK) { |
1436 | uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', control " | 1462 | uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', control " |
1437 | "control id 0x%08x is invalid.\n", mapping->name, | 1463 | "id 0x%08x is invalid.\n", mapping->name, |
1438 | mapping->id); | 1464 | mapping->id); |
1439 | return -EINVAL; | 1465 | return -EINVAL; |
1440 | } | 1466 | } |
@@ -1443,13 +1469,13 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, | |||
1443 | list_for_each_entry(entity, &dev->entities, list) { | 1469 | list_for_each_entry(entity, &dev->entities, list) { |
1444 | unsigned int i; | 1470 | unsigned int i; |
1445 | 1471 | ||
1446 | if (!uvc_entity_match_guid(entity, mapping->entity)) | 1472 | if (UVC_ENTITY_TYPE(entity) != UVC_VC_EXTENSION_UNIT || |
1473 | !uvc_entity_match_guid(entity, mapping->entity)) | ||
1447 | continue; | 1474 | continue; |
1448 | 1475 | ||
1449 | for (i = 0; i < entity->ncontrols; ++i) { | 1476 | for (i = 0; i < entity->ncontrols; ++i) { |
1450 | ctrl = &entity->controls[i]; | 1477 | ctrl = &entity->controls[i]; |
1451 | if (ctrl->initialized && | 1478 | if (ctrl->index == mapping->selector - 1) { |
1452 | ctrl->info.selector == mapping->selector) { | ||
1453 | found = 1; | 1479 | found = 1; |
1454 | break; | 1480 | break; |
1455 | } | 1481 | } |
@@ -1464,6 +1490,13 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, | |||
1464 | if (mutex_lock_interruptible(&chain->ctrl_mutex)) | 1490 | if (mutex_lock_interruptible(&chain->ctrl_mutex)) |
1465 | return -ERESTARTSYS; | 1491 | return -ERESTARTSYS; |
1466 | 1492 | ||
1493 | /* Perform delayed initialization of XU controls */ | ||
1494 | ret = uvc_ctrl_init_xu_ctrl(dev, ctrl); | ||
1495 | if (ret < 0) { | ||
1496 | ret = -ENOENT; | ||
1497 | goto done; | ||
1498 | } | ||
1499 | |||
1467 | list_for_each_entry(map, &ctrl->info.mappings, list) { | 1500 | list_for_each_entry(map, &ctrl->info.mappings, list) { |
1468 | if (mapping->id == map->id) { | 1501 | if (mapping->id == map->id) { |
1469 | uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', " | 1502 | uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', " |
@@ -1567,26 +1600,13 @@ static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl) | |||
1567 | const struct uvc_control_mapping *mend = | 1600 | const struct uvc_control_mapping *mend = |
1568 | mapping + ARRAY_SIZE(uvc_ctrl_mappings); | 1601 | mapping + ARRAY_SIZE(uvc_ctrl_mappings); |
1569 | 1602 | ||
1570 | /* Query XU controls for control information */ | 1603 | /* XU controls initialization requires querying the device for control |
1571 | if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT) { | 1604 | * information. As some buggy UVC devices will crash when queried |
1572 | struct uvc_control_info info; | 1605 | * repeatedly in a tight loop, delay XU controls initialization until |
1573 | int ret; | 1606 | * first use. |
1574 | 1607 | */ | |
1575 | ret = uvc_ctrl_fill_xu_info(dev, ctrl, &info); | 1608 | if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT) |
1576 | if (ret < 0) | ||
1577 | return; | ||
1578 | |||
1579 | ret = uvc_ctrl_add_info(dev, ctrl, &info); | ||
1580 | if (ret < 0) { | ||
1581 | /* Skip the control */ | ||
1582 | uvc_trace(UVC_TRACE_CONTROL, "Failed to initialize " | ||
1583 | "control %pUl/%u on device %s entity %u\n", | ||
1584 | info.entity, info.selector, dev->udev->devpath, | ||
1585 | ctrl->entity->id); | ||
1586 | memset(ctrl, 0, sizeof(*ctrl)); | ||
1587 | } | ||
1588 | return; | 1609 | return; |
1589 | } | ||
1590 | 1610 | ||
1591 | for (; info < iend; ++info) { | 1611 | for (; info < iend; ++info) { |
1592 | if (uvc_entity_match_guid(ctrl->entity, info->entity) && | 1612 | if (uvc_entity_match_guid(ctrl->entity, info->entity) && |