diff options
author | Bruno Prémont <bonbons@linux-vserver.org> | 2010-03-30 16:38:09 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2010-03-31 05:32:31 -0400 |
commit | 9bbf2b98ba11d00bd73e3254e15cfe17ccaff6ba (patch) | |
tree | cffe1c088a62e943fde7a954177e530bc780d2c3 /drivers/hid/hid-picolcd.c | |
parent | 467d6523065187d4c081b078755da4103d7ffacb (diff) |
HID: add experimental access to PicoLCD device's EEPROM and FLASH
The PicoLCD device has a small amount of EEPROM and also provides access
to its FLASH where firmware and splash image are saved.
In flasher mode FLASH access is the only active feature.
Give read/write access to both via debugfs files.
NOTE: EEPROM and FLASH access should be switched to better suited API,
until then the will reside in debugfs
Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hid-picolcd.c')
-rw-r--r-- | drivers/hid/hid-picolcd.c | 439 |
1 files changed, 422 insertions, 17 deletions
diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c index 517677305ef9..66f9cfd41abe 100644 --- a/drivers/hid/hid-picolcd.c +++ b/drivers/hid/hid-picolcd.c | |||
@@ -169,6 +169,10 @@ struct picolcd_pending { | |||
169 | struct picolcd_data { | 169 | struct picolcd_data { |
170 | struct hid_device *hdev; | 170 | struct hid_device *hdev; |
171 | #ifdef CONFIG_DEBUG_FS | 171 | #ifdef CONFIG_DEBUG_FS |
172 | struct dentry *debug_reset; | ||
173 | struct dentry *debug_eeprom; | ||
174 | struct dentry *debug_flash; | ||
175 | struct mutex mutex_flash; | ||
172 | int addr_sz; | 176 | int addr_sz; |
173 | #endif | 177 | #endif |
174 | u8 version[2]; | 178 | u8 version[2]; |
@@ -1314,6 +1318,357 @@ static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show, | |||
1314 | 1318 | ||
1315 | #ifdef CONFIG_DEBUG_FS | 1319 | #ifdef CONFIG_DEBUG_FS |
1316 | /* | 1320 | /* |
1321 | * The "reset" file | ||
1322 | */ | ||
1323 | static int picolcd_debug_reset_show(struct seq_file *f, void *p) | ||
1324 | { | ||
1325 | if (picolcd_fbinfo((struct picolcd_data *)f->private)) | ||
1326 | seq_printf(f, "all fb\n"); | ||
1327 | else | ||
1328 | seq_printf(f, "all\n"); | ||
1329 | return 0; | ||
1330 | } | ||
1331 | |||
1332 | static int picolcd_debug_reset_open(struct inode *inode, struct file *f) | ||
1333 | { | ||
1334 | return single_open(f, picolcd_debug_reset_show, inode->i_private); | ||
1335 | } | ||
1336 | |||
1337 | static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf, | ||
1338 | size_t count, loff_t *ppos) | ||
1339 | { | ||
1340 | struct picolcd_data *data = ((struct seq_file *)f->private_data)->private; | ||
1341 | char buf[32]; | ||
1342 | size_t cnt = min(count, sizeof(buf)-1); | ||
1343 | if (copy_from_user(buf, user_buf, cnt)) | ||
1344 | return -EFAULT; | ||
1345 | |||
1346 | while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n')) | ||
1347 | cnt--; | ||
1348 | buf[cnt] = '\0'; | ||
1349 | if (strcmp(buf, "all") == 0) { | ||
1350 | picolcd_reset(data->hdev); | ||
1351 | picolcd_fb_reset(data, 1); | ||
1352 | } else if (strcmp(buf, "fb") == 0) { | ||
1353 | picolcd_fb_reset(data, 1); | ||
1354 | } else { | ||
1355 | return -EINVAL; | ||
1356 | } | ||
1357 | return count; | ||
1358 | } | ||
1359 | |||
1360 | static const struct file_operations picolcd_debug_reset_fops = { | ||
1361 | .owner = THIS_MODULE, | ||
1362 | .open = picolcd_debug_reset_open, | ||
1363 | .read = seq_read, | ||
1364 | .llseek = seq_lseek, | ||
1365 | .write = picolcd_debug_reset_write, | ||
1366 | .release = single_release, | ||
1367 | }; | ||
1368 | |||
1369 | /* | ||
1370 | * The "eeprom" file | ||
1371 | */ | ||
1372 | static int picolcd_debug_eeprom_open(struct inode *i, struct file *f) | ||
1373 | { | ||
1374 | f->private_data = i->i_private; | ||
1375 | return 0; | ||
1376 | } | ||
1377 | |||
1378 | static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u, | ||
1379 | size_t s, loff_t *off) | ||
1380 | { | ||
1381 | struct picolcd_data *data = f->private_data; | ||
1382 | struct picolcd_pending *resp; | ||
1383 | u8 raw_data[3]; | ||
1384 | ssize_t ret = -EIO; | ||
1385 | |||
1386 | if (s == 0) | ||
1387 | return -EINVAL; | ||
1388 | if (*off > 0x0ff) | ||
1389 | return 0; | ||
1390 | |||
1391 | /* prepare buffer with info about what we want to read (addr & len) */ | ||
1392 | raw_data[0] = *off & 0xff; | ||
1393 | raw_data[1] = (*off >> 8) && 0xff; | ||
1394 | raw_data[2] = s < 20 ? s : 20; | ||
1395 | if (*off + raw_data[2] > 0xff) | ||
1396 | raw_data[2] = 0x100 - *off; | ||
1397 | resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data, | ||
1398 | sizeof(raw_data)); | ||
1399 | if (!resp) | ||
1400 | return -EIO; | ||
1401 | |||
1402 | if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) { | ||
1403 | /* successful read :) */ | ||
1404 | ret = resp->raw_data[2]; | ||
1405 | if (ret > s) | ||
1406 | ret = s; | ||
1407 | if (copy_to_user(u, resp->raw_data+3, ret)) | ||
1408 | ret = -EFAULT; | ||
1409 | else | ||
1410 | *off += ret; | ||
1411 | } /* anything else is some kind of IO error */ | ||
1412 | |||
1413 | kfree(resp); | ||
1414 | return ret; | ||
1415 | } | ||
1416 | |||
1417 | static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u, | ||
1418 | size_t s, loff_t *off) | ||
1419 | { | ||
1420 | struct picolcd_data *data = f->private_data; | ||
1421 | struct picolcd_pending *resp; | ||
1422 | ssize_t ret = -EIO; | ||
1423 | u8 raw_data[23]; | ||
1424 | |||
1425 | if (s == 0) | ||
1426 | return -EINVAL; | ||
1427 | if (*off > 0x0ff) | ||
1428 | return -ENOSPC; | ||
1429 | |||
1430 | memset(raw_data, 0, sizeof(raw_data)); | ||
1431 | raw_data[0] = *off & 0xff; | ||
1432 | raw_data[1] = (*off >> 8) && 0xff; | ||
1433 | raw_data[2] = s < 20 ? s : 20; | ||
1434 | if (*off + raw_data[2] > 0xff) | ||
1435 | raw_data[2] = 0x100 - *off; | ||
1436 | |||
1437 | if (copy_from_user(raw_data+3, u, raw_data[2])) | ||
1438 | return -EFAULT; | ||
1439 | resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data, | ||
1440 | sizeof(raw_data)); | ||
1441 | |||
1442 | if (!resp) | ||
1443 | return -EIO; | ||
1444 | |||
1445 | if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) { | ||
1446 | /* check if written data matches */ | ||
1447 | if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) { | ||
1448 | *off += raw_data[2]; | ||
1449 | ret = raw_data[2]; | ||
1450 | } | ||
1451 | } | ||
1452 | kfree(resp); | ||
1453 | return ret; | ||
1454 | } | ||
1455 | |||
1456 | /* | ||
1457 | * Notes: | ||
1458 | * - read/write happens in chunks of at most 20 bytes, it's up to userspace | ||
1459 | * to loop in order to get more data. | ||
1460 | * - on write errors on otherwise correct write request the bytes | ||
1461 | * that should have been written are in undefined state. | ||
1462 | */ | ||
1463 | static const struct file_operations picolcd_debug_eeprom_fops = { | ||
1464 | .owner = THIS_MODULE, | ||
1465 | .open = picolcd_debug_eeprom_open, | ||
1466 | .read = picolcd_debug_eeprom_read, | ||
1467 | .write = picolcd_debug_eeprom_write, | ||
1468 | .llseek = generic_file_llseek, | ||
1469 | }; | ||
1470 | |||
1471 | /* | ||
1472 | * The "flash" file | ||
1473 | */ | ||
1474 | static int picolcd_debug_flash_open(struct inode *i, struct file *f) | ||
1475 | { | ||
1476 | f->private_data = i->i_private; | ||
1477 | return 0; | ||
1478 | } | ||
1479 | |||
1480 | /* record a flash address to buf (bounds check to be done by caller) */ | ||
1481 | static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off) | ||
1482 | { | ||
1483 | buf[0] = off & 0xff; | ||
1484 | buf[1] = (off >> 8) & 0xff; | ||
1485 | if (data->addr_sz == 3) | ||
1486 | buf[2] = (off >> 16) & 0xff; | ||
1487 | return data->addr_sz == 2 ? 2 : 3; | ||
1488 | } | ||
1489 | |||
1490 | /* read a given size of data (bounds check to be done by caller) */ | ||
1491 | static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id, | ||
1492 | char __user *u, size_t s, loff_t *off) | ||
1493 | { | ||
1494 | struct picolcd_pending *resp; | ||
1495 | u8 raw_data[4]; | ||
1496 | ssize_t ret = 0; | ||
1497 | int len_off, err = -EIO; | ||
1498 | |||
1499 | while (s > 0) { | ||
1500 | err = -EIO; | ||
1501 | len_off = _picolcd_flash_setaddr(data, raw_data, *off); | ||
1502 | raw_data[len_off] = s > 32 ? 32 : s; | ||
1503 | resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1); | ||
1504 | if (!resp || !resp->in_report) | ||
1505 | goto skip; | ||
1506 | if (resp->in_report->id == REPORT_MEMORY || | ||
1507 | resp->in_report->id == REPORT_BL_READ_MEMORY) { | ||
1508 | if (memcmp(raw_data, resp->raw_data, len_off+1) != 0) | ||
1509 | goto skip; | ||
1510 | if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) { | ||
1511 | err = -EFAULT; | ||
1512 | goto skip; | ||
1513 | } | ||
1514 | *off += raw_data[len_off]; | ||
1515 | s -= raw_data[len_off]; | ||
1516 | ret += raw_data[len_off]; | ||
1517 | err = 0; | ||
1518 | } | ||
1519 | skip: | ||
1520 | kfree(resp); | ||
1521 | if (err) | ||
1522 | return ret > 0 ? ret : err; | ||
1523 | } | ||
1524 | return ret; | ||
1525 | } | ||
1526 | |||
1527 | static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u, | ||
1528 | size_t s, loff_t *off) | ||
1529 | { | ||
1530 | struct picolcd_data *data = f->private_data; | ||
1531 | |||
1532 | if (s == 0) | ||
1533 | return -EINVAL; | ||
1534 | if (*off > 0x05fff) | ||
1535 | return 0; | ||
1536 | if (*off + s > 0x05fff) | ||
1537 | s = 0x06000 - *off; | ||
1538 | |||
1539 | if (data->status & PICOLCD_BOOTLOADER) | ||
1540 | return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off); | ||
1541 | else | ||
1542 | return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off); | ||
1543 | } | ||
1544 | |||
1545 | /* erase block aligned to 64bytes boundary */ | ||
1546 | static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id, | ||
1547 | loff_t *off) | ||
1548 | { | ||
1549 | struct picolcd_pending *resp; | ||
1550 | u8 raw_data[3]; | ||
1551 | int len_off; | ||
1552 | ssize_t ret = -EIO; | ||
1553 | |||
1554 | if (*off & 0x3f) | ||
1555 | return -EINVAL; | ||
1556 | |||
1557 | len_off = _picolcd_flash_setaddr(data, raw_data, *off); | ||
1558 | resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off); | ||
1559 | if (!resp || !resp->in_report) | ||
1560 | goto skip; | ||
1561 | if (resp->in_report->id == REPORT_MEMORY || | ||
1562 | resp->in_report->id == REPORT_BL_ERASE_MEMORY) { | ||
1563 | if (memcmp(raw_data, resp->raw_data, len_off) != 0) | ||
1564 | goto skip; | ||
1565 | ret = 0; | ||
1566 | } | ||
1567 | skip: | ||
1568 | kfree(resp); | ||
1569 | return ret; | ||
1570 | } | ||
1571 | |||
1572 | /* write a given size of data (bounds check to be done by caller) */ | ||
1573 | static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id, | ||
1574 | const char __user *u, size_t s, loff_t *off) | ||
1575 | { | ||
1576 | struct picolcd_pending *resp; | ||
1577 | u8 raw_data[36]; | ||
1578 | ssize_t ret = 0; | ||
1579 | int len_off, err = -EIO; | ||
1580 | |||
1581 | while (s > 0) { | ||
1582 | err = -EIO; | ||
1583 | len_off = _picolcd_flash_setaddr(data, raw_data, *off); | ||
1584 | raw_data[len_off] = s > 32 ? 32 : s; | ||
1585 | if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) { | ||
1586 | err = -EFAULT; | ||
1587 | goto skip; | ||
1588 | } | ||
1589 | resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, | ||
1590 | len_off+1+raw_data[len_off]); | ||
1591 | if (!resp || !resp->in_report) | ||
1592 | goto skip; | ||
1593 | if (resp->in_report->id == REPORT_MEMORY || | ||
1594 | resp->in_report->id == REPORT_BL_WRITE_MEMORY) { | ||
1595 | if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0) | ||
1596 | goto skip; | ||
1597 | *off += raw_data[len_off]; | ||
1598 | s -= raw_data[len_off]; | ||
1599 | ret += raw_data[len_off]; | ||
1600 | err = 0; | ||
1601 | } | ||
1602 | skip: | ||
1603 | kfree(resp); | ||
1604 | if (err) | ||
1605 | break; | ||
1606 | } | ||
1607 | return ret > 0 ? ret : err; | ||
1608 | } | ||
1609 | |||
1610 | static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u, | ||
1611 | size_t s, loff_t *off) | ||
1612 | { | ||
1613 | struct picolcd_data *data = f->private_data; | ||
1614 | ssize_t err, ret = 0; | ||
1615 | int report_erase, report_write; | ||
1616 | |||
1617 | if (s == 0) | ||
1618 | return -EINVAL; | ||
1619 | if (*off > 0x5fff) | ||
1620 | return -ENOSPC; | ||
1621 | if (s & 0x3f) | ||
1622 | return -EINVAL; | ||
1623 | if (*off & 0x3f) | ||
1624 | return -EINVAL; | ||
1625 | |||
1626 | if (data->status & PICOLCD_BOOTLOADER) { | ||
1627 | report_erase = REPORT_BL_ERASE_MEMORY; | ||
1628 | report_write = REPORT_BL_WRITE_MEMORY; | ||
1629 | } else { | ||
1630 | report_erase = REPORT_ERASE_MEMORY; | ||
1631 | report_write = REPORT_WRITE_MEMORY; | ||
1632 | } | ||
1633 | mutex_lock(&data->mutex_flash); | ||
1634 | while (s > 0) { | ||
1635 | err = _picolcd_flash_erase64(data, report_erase, off); | ||
1636 | if (err) | ||
1637 | break; | ||
1638 | err = _picolcd_flash_write(data, report_write, u, 64, off); | ||
1639 | if (err < 0) | ||
1640 | break; | ||
1641 | ret += err; | ||
1642 | *off += err; | ||
1643 | s -= err; | ||
1644 | if (err != 64) | ||
1645 | break; | ||
1646 | } | ||
1647 | mutex_unlock(&data->mutex_flash); | ||
1648 | return ret > 0 ? ret : err; | ||
1649 | } | ||
1650 | |||
1651 | /* | ||
1652 | * Notes: | ||
1653 | * - concurrent writing is prevented by mutex and all writes must be | ||
1654 | * n*64 bytes and 64-byte aligned, each write being preceeded by an | ||
1655 | * ERASE which erases a 64byte block. | ||
1656 | * If less than requested was written or an error is returned for an | ||
1657 | * otherwise correct write request the next 64-byte block which should | ||
1658 | * have been written is in undefined state (mostly: original, erased, | ||
1659 | * (half-)written with write error) | ||
1660 | * - reading can happend without special restriction | ||
1661 | */ | ||
1662 | static const struct file_operations picolcd_debug_flash_fops = { | ||
1663 | .owner = THIS_MODULE, | ||
1664 | .open = picolcd_debug_flash_open, | ||
1665 | .read = picolcd_debug_flash_read, | ||
1666 | .write = picolcd_debug_flash_write, | ||
1667 | .llseek = generic_file_llseek, | ||
1668 | }; | ||
1669 | |||
1670 | |||
1671 | /* | ||
1317 | * Helper code for HID report level dumping/debugging | 1672 | * Helper code for HID report level dumping/debugging |
1318 | */ | 1673 | */ |
1319 | static const char *error_codes[] = { | 1674 | static const char *error_codes[] = { |
@@ -1788,9 +2143,66 @@ static void picolcd_debug_raw_event(struct picolcd_data *data, | |||
1788 | wake_up_interruptible(&hdev->debug_wait); | 2143 | wake_up_interruptible(&hdev->debug_wait); |
1789 | kfree(buff); | 2144 | kfree(buff); |
1790 | } | 2145 | } |
2146 | |||
2147 | static void picolcd_init_devfs(struct picolcd_data *data, | ||
2148 | struct hid_report *eeprom_r, struct hid_report *eeprom_w, | ||
2149 | struct hid_report *flash_r, struct hid_report *flash_w, | ||
2150 | struct hid_report *reset) | ||
2151 | { | ||
2152 | struct hid_device *hdev = data->hdev; | ||
2153 | |||
2154 | mutex_init(&data->mutex_flash); | ||
2155 | |||
2156 | /* reset */ | ||
2157 | if (reset) | ||
2158 | data->debug_reset = debugfs_create_file("reset", 0600, | ||
2159 | hdev->debug_dir, data, &picolcd_debug_reset_fops); | ||
2160 | |||
2161 | /* eeprom */ | ||
2162 | if (eeprom_r || eeprom_w) | ||
2163 | data->debug_eeprom = debugfs_create_file("eeprom", | ||
2164 | (eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0), | ||
2165 | hdev->debug_dir, data, &picolcd_debug_eeprom_fops); | ||
2166 | |||
2167 | /* flash */ | ||
2168 | if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8) | ||
2169 | data->addr_sz = flash_r->field[0]->report_count - 1; | ||
2170 | else | ||
2171 | data->addr_sz = -1; | ||
2172 | if (data->addr_sz == 2 || data->addr_sz == 3) { | ||
2173 | data->debug_flash = debugfs_create_file("flash", | ||
2174 | (flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0), | ||
2175 | hdev->debug_dir, data, &picolcd_debug_flash_fops); | ||
2176 | } else if (flash_r || flash_w) | ||
2177 | dev_warn(&hdev->dev, "Unexpected FLASH access reports, " | ||
2178 | "please submit rdesc for review\n"); | ||
2179 | } | ||
2180 | |||
2181 | static void picolcd_exit_devfs(struct picolcd_data *data) | ||
2182 | { | ||
2183 | struct dentry *dent; | ||
2184 | |||
2185 | dent = data->debug_reset; | ||
2186 | data->debug_reset = NULL; | ||
2187 | if (dent) | ||
2188 | debugfs_remove(dent); | ||
2189 | dent = data->debug_eeprom; | ||
2190 | data->debug_eeprom = NULL; | ||
2191 | if (dent) | ||
2192 | debugfs_remove(dent); | ||
2193 | dent = data->debug_flash; | ||
2194 | data->debug_flash = NULL; | ||
2195 | if (dent) | ||
2196 | debugfs_remove(dent); | ||
2197 | mutex_destroy(&data->mutex_flash); | ||
2198 | } | ||
1791 | #else | 2199 | #else |
1792 | #define picolcd_debug_raw_event(data, hdev, report, raw_data, size) | 2200 | #define picolcd_debug_raw_event(data, hdev, report, raw_data, size) |
1793 | #endif | 2201 | #define picolcd_init_devfs(data, eeprom_r, eeprom_w, flash_r, flash_w, reset) |
2202 | static void picolcd_exit_devfs(struct picolcd_data *data) | ||
2203 | { | ||
2204 | } | ||
2205 | #endif /* CONFIG_DEBUG_FS */ | ||
1794 | 2206 | ||
1795 | /* | 2207 | /* |
1796 | * Handle raw report as sent by device | 2208 | * Handle raw report as sent by device |
@@ -1900,7 +2312,6 @@ static inline void picolcd_exit_cir(struct picolcd_data *data) | |||
1900 | 2312 | ||
1901 | static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data) | 2313 | static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data) |
1902 | { | 2314 | { |
1903 | struct hid_report *report; | ||
1904 | int error; | 2315 | int error; |
1905 | 2316 | ||
1906 | error = picolcd_check_version(hdev); | 2317 | error = picolcd_check_version(hdev); |
@@ -1942,13 +2353,11 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data) | |||
1942 | if (error) | 2353 | if (error) |
1943 | goto err; | 2354 | goto err; |
1944 | 2355 | ||
1945 | #ifdef CONFIG_DEBUG_FS | 2356 | picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev), |
1946 | report = picolcd_out_report(REPORT_READ_MEMORY, hdev); | 2357 | picolcd_out_report(REPORT_EE_WRITE, hdev), |
1947 | if (report && report->maxfield == 1 && report->field[0]->report_size == 8) | 2358 | picolcd_out_report(REPORT_READ_MEMORY, hdev), |
1948 | data->addr_sz = report->field[0]->report_count - 1; | 2359 | picolcd_out_report(REPORT_WRITE_MEMORY, hdev), |
1949 | else | 2360 | picolcd_out_report(REPORT_RESET, hdev)); |
1950 | data->addr_sz = -1; | ||
1951 | #endif | ||
1952 | return 0; | 2361 | return 0; |
1953 | err: | 2362 | err: |
1954 | picolcd_exit_leds(data); | 2363 | picolcd_exit_leds(data); |
@@ -1962,7 +2371,6 @@ err: | |||
1962 | 2371 | ||
1963 | static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data) | 2372 | static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data) |
1964 | { | 2373 | { |
1965 | struct hid_report *report; | ||
1966 | int error; | 2374 | int error; |
1967 | 2375 | ||
1968 | error = picolcd_check_version(hdev); | 2376 | error = picolcd_check_version(hdev); |
@@ -1974,13 +2382,9 @@ static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data | |||
1974 | "please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n", | 2382 | "please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n", |
1975 | dev_name(&hdev->dev)); | 2383 | dev_name(&hdev->dev)); |
1976 | 2384 | ||
1977 | #ifdef CONFIG_DEBUG_FS | 2385 | picolcd_init_devfs(data, NULL, NULL, |
1978 | report = picolcd_out_report(REPORT_BL_READ_MEMORY, hdev); | 2386 | picolcd_out_report(REPORT_BL_READ_MEMORY, hdev), |
1979 | if (report && report->maxfield == 1 && report->field[0]->report_size == 8) | 2387 | picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL); |
1980 | data->addr_sz = report->field[0]->report_count - 1; | ||
1981 | else | ||
1982 | data->addr_sz = -1; | ||
1983 | #endif | ||
1984 | return 0; | 2388 | return 0; |
1985 | } | 2389 | } |
1986 | 2390 | ||
@@ -2073,6 +2477,7 @@ static void picolcd_remove(struct hid_device *hdev) | |||
2073 | data->status |= PICOLCD_FAILED; | 2477 | data->status |= PICOLCD_FAILED; |
2074 | spin_unlock_irqrestore(&data->lock, flags); | 2478 | spin_unlock_irqrestore(&data->lock, flags); |
2075 | 2479 | ||
2480 | picolcd_exit_devfs(data); | ||
2076 | device_remove_file(&hdev->dev, &dev_attr_operation_mode); | 2481 | device_remove_file(&hdev->dev, &dev_attr_operation_mode); |
2077 | hdev->ll_driver->close(hdev); | 2482 | hdev->ll_driver->close(hdev); |
2078 | hid_hw_stop(hdev); | 2483 | hid_hw_stop(hdev); |