diff options
author | Chris Pascoe <c.pascoe@itee.uq.edu.au> | 2007-11-19 04:06:08 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2008-01-25 16:02:26 -0500 |
commit | 06fd82dc7bdc6045bf8d6c1438d2a33ffb1cf337 (patch) | |
tree | f48a2b413ab38ca51cd1db2dc72b93f2f4e093a5 /drivers/media/video/tuner-xc2028.c | |
parent | b32f9fb962d593e3c5a61092a79434714f405ef5 (diff) |
V4L/DVB (6638): xc2028: firmware loading cleanup
Hold the private lock over set_config and set priv->firm_size to 0 after a
failed firmware load to prevent firmware accidentally being freed on us.
Clean up the firmware load/error messages somewhat and rename priv->version
to priv->firm_version to make it clear which "version" it is.
Signed-off-by: Chris Pascoe <c.pascoe@itee.uq.edu.au>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/tuner-xc2028.c')
-rw-r--r-- | drivers/media/video/tuner-xc2028.c | 82 |
1 files changed, 38 insertions, 44 deletions
diff --git a/drivers/media/video/tuner-xc2028.c b/drivers/media/video/tuner-xc2028.c index 7e558decad84..7c86971a8d64 100644 --- a/drivers/media/video/tuner-xc2028.c +++ b/drivers/media/video/tuner-xc2028.c | |||
@@ -65,8 +65,7 @@ struct xc2028_data { | |||
65 | 65 | ||
66 | struct firmware_description *firm; | 66 | struct firmware_description *firm; |
67 | int firm_size; | 67 | int firm_size; |
68 | 68 | __u16 firm_version; | |
69 | __u16 version; | ||
70 | 69 | ||
71 | struct xc2028_ctrl ctrl; | 70 | struct xc2028_ctrl ctrl; |
72 | 71 | ||
@@ -237,6 +236,7 @@ static void free_firmware(struct xc2028_data *priv) | |||
237 | kfree(priv->firm); | 236 | kfree(priv->firm); |
238 | 237 | ||
239 | priv->firm = NULL; | 238 | priv->firm = NULL; |
239 | priv->firm_size = 0; | ||
240 | priv->need_load_generic = 1; | 240 | priv->need_load_generic = 1; |
241 | } | 241 | } |
242 | 242 | ||
@@ -251,7 +251,7 @@ static int load_all_firmwares(struct dvb_frontend *fe) | |||
251 | 251 | ||
252 | tuner_dbg("%s called\n", __FUNCTION__); | 252 | tuner_dbg("%s called\n", __FUNCTION__); |
253 | 253 | ||
254 | tuner_info("Reading firmware %s\n", priv->ctrl.fname); | 254 | tuner_dbg("Reading firmware %s\n", priv->ctrl.fname); |
255 | rc = request_firmware(&fw, priv->ctrl.fname, | 255 | rc = request_firmware(&fw, priv->ctrl.fname, |
256 | &priv->i2c_props.adap->dev); | 256 | &priv->i2c_props.adap->dev); |
257 | if (rc < 0) { | 257 | if (rc < 0) { |
@@ -267,40 +267,34 @@ static int load_all_firmwares(struct dvb_frontend *fe) | |||
267 | p = fw->data; | 267 | p = fw->data; |
268 | endp = p + fw->size; | 268 | endp = p + fw->size; |
269 | 269 | ||
270 | if (fw->size < sizeof(name) - 1 + 2) { | 270 | if (fw->size < sizeof(name) - 1 + 2 + 2) { |
271 | tuner_err("Error: firmware size is zero!\n"); | 271 | tuner_err("Error: firmware file %s has invalid size!\n", |
272 | rc = -EINVAL; | 272 | priv->ctrl.fname); |
273 | goto done; | 273 | goto corrupt; |
274 | } | 274 | } |
275 | 275 | ||
276 | memcpy(name, p, sizeof(name) - 1); | 276 | memcpy(name, p, sizeof(name) - 1); |
277 | name[sizeof(name) - 1] = 0; | 277 | name[sizeof(name) - 1] = 0; |
278 | p += sizeof(name) - 1; | 278 | p += sizeof(name) - 1; |
279 | 279 | ||
280 | priv->version = le16_to_cpu(*(__u16 *) p); | 280 | priv->firm_version = le16_to_cpu(*(__u16 *) p); |
281 | p += 2; | 281 | p += 2; |
282 | 282 | ||
283 | tuner_info("Firmware: %s, ver %d.%d\n", name, | ||
284 | priv->version >> 8, priv->version & 0xff); | ||
285 | |||
286 | if (p + 2 > endp) | ||
287 | goto corrupt; | ||
288 | |||
289 | n_array = le16_to_cpu(*(__u16 *) p); | 283 | n_array = le16_to_cpu(*(__u16 *) p); |
290 | p += 2; | 284 | p += 2; |
291 | 285 | ||
292 | tuner_info("There are %d firmwares at %s\n", | 286 | tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n", |
293 | n_array, priv->ctrl.fname); | 287 | n_array, priv->ctrl.fname, name, |
288 | priv->firm_version >> 8, priv->firm_version & 0xff); | ||
294 | 289 | ||
295 | priv->firm = kzalloc(sizeof(*priv->firm) * n_array, GFP_KERNEL); | 290 | priv->firm = kzalloc(sizeof(*priv->firm) * n_array, GFP_KERNEL); |
296 | 291 | if (priv->firm == NULL) { | |
297 | if (!fw) { | 292 | tuner_err("Not enough memory to load firmware file.\n"); |
298 | tuner_err("Not enough memory for reading firmware.\n"); | ||
299 | rc = -ENOMEM; | 293 | rc = -ENOMEM; |
300 | goto done; | 294 | goto err; |
301 | } | 295 | } |
302 | |||
303 | priv->firm_size = n_array; | 296 | priv->firm_size = n_array; |
297 | |||
304 | n = -1; | 298 | n = -1; |
305 | while (p < endp) { | 299 | while (p < endp) { |
306 | __u32 type, size; | 300 | __u32 type, size; |
@@ -308,7 +302,8 @@ static int load_all_firmwares(struct dvb_frontend *fe) | |||
308 | 302 | ||
309 | n++; | 303 | n++; |
310 | if (n >= n_array) { | 304 | if (n >= n_array) { |
311 | tuner_err("Too much firmwares at the file\n"); | 305 | tuner_err("More firmware images in file than " |
306 | "were expected!\n"); | ||
312 | goto corrupt; | 307 | goto corrupt; |
313 | } | 308 | } |
314 | 309 | ||
@@ -338,15 +333,17 @@ static int load_all_firmwares(struct dvb_frontend *fe) | |||
338 | } | 333 | } |
339 | 334 | ||
340 | priv->firm[n].ptr = kzalloc(size, GFP_KERNEL); | 335 | priv->firm[n].ptr = kzalloc(size, GFP_KERNEL); |
341 | if (!priv->firm[n].ptr) { | 336 | if (priv->firm[n].ptr == NULL) { |
342 | tuner_err("Not enough memory.\n"); | 337 | tuner_err("Not enough memory to load firmware file.\n"); |
343 | rc = -ENOMEM; | 338 | rc = -ENOMEM; |
344 | goto err; | 339 | goto err; |
345 | } | 340 | } |
346 | tuner_info("Reading firmware type "); | 341 | tuner_dbg("Reading firmware type "); |
347 | dump_firm_type(type); | 342 | if (debug) { |
348 | printk("(%x), id %llx, size=%d.\n", | 343 | dump_firm_type(type); |
349 | type, (unsigned long long)id, size); | 344 | printk("(%x), id %llx, size=%d.\n", |
345 | type, (unsigned long long)id, size); | ||
346 | } | ||
350 | 347 | ||
351 | memcpy(priv->firm[n].ptr, p, size); | 348 | memcpy(priv->firm[n].ptr, p, size); |
352 | priv->firm[n].type = type; | 349 | priv->firm[n].type = type; |
@@ -368,13 +365,13 @@ corrupt: | |||
368 | tuner_err("Error: firmware file is corrupted!\n"); | 365 | tuner_err("Error: firmware file is corrupted!\n"); |
369 | 366 | ||
370 | err: | 367 | err: |
371 | tuner_info("Releasing loaded firmware file.\n"); | 368 | tuner_info("Releasing partially loaded firmware file.\n"); |
372 | |||
373 | free_firmware(priv); | 369 | free_firmware(priv); |
374 | 370 | ||
375 | done: | 371 | done: |
376 | release_firmware(fw); | 372 | release_firmware(fw); |
377 | tuner_dbg("Firmware files loaded.\n"); | 373 | if (rc == 0) |
374 | tuner_dbg("Firmware files loaded.\n"); | ||
378 | 375 | ||
379 | return rc; | 376 | return rc; |
380 | } | 377 | } |
@@ -442,11 +439,6 @@ static int load_firmware(struct dvb_frontend *fe, unsigned int type, | |||
442 | printk("(%x), id %016llx.\n", type, (unsigned long long)*id); | 439 | printk("(%x), id %016llx.\n", type, (unsigned long long)*id); |
443 | 440 | ||
444 | p = priv->firm[pos].ptr; | 441 | p = priv->firm[pos].ptr; |
445 | |||
446 | if (!p) { | ||
447 | tuner_err("Firmware pointer were freed!"); | ||
448 | return -EINVAL; | ||
449 | } | ||
450 | endp = p + priv->firm[pos].size; | 442 | endp = p + priv->firm[pos].size; |
451 | 443 | ||
452 | while (p < endp) { | 444 | while (p < endp) { |
@@ -546,15 +538,10 @@ static int load_scode(struct dvb_frontend *fe, unsigned int type, | |||
546 | 538 | ||
547 | p = priv->firm[pos].ptr; | 539 | p = priv->firm[pos].ptr; |
548 | 540 | ||
549 | if (!p) { | ||
550 | tuner_err("Firmware pointer were freed!"); | ||
551 | return -EINVAL; | ||
552 | } | ||
553 | |||
554 | if ((priv->firm[pos].size != 12 * 16) || (scode >= 16)) | 541 | if ((priv->firm[pos].size != 12 * 16) || (scode >= 16)) |
555 | return -EINVAL; | 542 | return -EINVAL; |
556 | 543 | ||
557 | if (priv->version < 0x0202) | 544 | if (priv->firm_version < 0x0202) |
558 | rc = send_seq(priv, {0x20, 0x00, 0x00, 0x00}); | 545 | rc = send_seq(priv, {0x20, 0x00, 0x00, 0x00}); |
559 | else | 546 | else |
560 | rc = send_seq(priv, {0xa0, 0x00, 0x00, 0x00}); | 547 | rc = send_seq(priv, {0xa0, 0x00, 0x00, 0x00}); |
@@ -783,7 +770,7 @@ static int generic_set_tv_freq(struct dvb_frontend *fe, u32 freq /* in Hz */ , | |||
783 | 770 | ||
784 | /* CMD= Set frequency */ | 771 | /* CMD= Set frequency */ |
785 | 772 | ||
786 | if (priv->version < 0x0202) | 773 | if (priv->firm_version < 0x0202) |
787 | rc = send_seq(priv, {0x00, 0x02, 0x00, 0x00}); | 774 | rc = send_seq(priv, {0x00, 0x02, 0x00, 0x00}); |
788 | else | 775 | else |
789 | rc = send_seq(priv, {0x80, 0x02, 0x00, 0x00}); | 776 | rc = send_seq(priv, {0x80, 0x02, 0x00, 0x00}); |
@@ -868,6 +855,7 @@ static int xc2028_dvb_release(struct dvb_frontend *fe) | |||
868 | 855 | ||
869 | free_firmware(priv); | 856 | free_firmware(priv); |
870 | kfree(priv); | 857 | kfree(priv); |
858 | fe->tuner_priv = NULL; | ||
871 | } | 859 | } |
872 | 860 | ||
873 | mutex_unlock(&xc2028_list_mutex); | 861 | mutex_unlock(&xc2028_list_mutex); |
@@ -893,14 +881,18 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) | |||
893 | 881 | ||
894 | tuner_dbg("%s called\n", __FUNCTION__); | 882 | tuner_dbg("%s called\n", __FUNCTION__); |
895 | 883 | ||
884 | mutex_lock(&priv->lock); | ||
885 | |||
896 | priv->ctrl.type = p->type; | 886 | priv->ctrl.type = p->type; |
897 | 887 | ||
898 | if (p->fname) { | 888 | if (p->fname) { |
899 | kfree(priv->ctrl.fname); | 889 | kfree(priv->ctrl.fname); |
900 | 890 | ||
901 | priv->ctrl.fname = kmalloc(strlen(p->fname) + 1, GFP_KERNEL); | 891 | priv->ctrl.fname = kmalloc(strlen(p->fname) + 1, GFP_KERNEL); |
902 | if (!priv->ctrl.fname) | 892 | if (priv->ctrl.fname == NULL) { |
893 | mutex_unlock(&priv->lock); | ||
903 | return -ENOMEM; | 894 | return -ENOMEM; |
895 | } | ||
904 | 896 | ||
905 | free_firmware(priv); | 897 | free_firmware(priv); |
906 | strcpy(priv->ctrl.fname, p->fname); | 898 | strcpy(priv->ctrl.fname, p->fname); |
@@ -909,6 +901,8 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) | |||
909 | if (p->max_len > 0) | 901 | if (p->max_len > 0) |
910 | priv->max_len = p->max_len; | 902 | priv->max_len = p->max_len; |
911 | 903 | ||
904 | mutex_unlock(&priv->lock); | ||
905 | |||
912 | return 0; | 906 | return 0; |
913 | } | 907 | } |
914 | 908 | ||