aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/sony-laptop.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/sony-laptop.c')
-rw-r--r--drivers/misc/sony-laptop.c332
1 files changed, 329 insertions, 3 deletions
diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c
index 9042184d543a..8cc041182a11 100644
--- a/drivers/misc/sony-laptop.c
+++ b/drivers/misc/sony-laptop.c
@@ -62,6 +62,10 @@
62#include <acpi/acpi_bus.h> 62#include <acpi/acpi_bus.h>
63#include <asm/uaccess.h> 63#include <asm/uaccess.h>
64#include <linux/sonypi.h> 64#include <linux/sonypi.h>
65#ifdef CONFIG_SONY_LAPTOP_OLD
66#include <linux/poll.h>
67#include <linux/miscdevice.h>
68#endif
65 69
66#define DRV_PFX "sony-laptop: " 70#define DRV_PFX "sony-laptop: "
67#define dprintk(msg...) do { \ 71#define dprintk(msg...) do { \
@@ -96,13 +100,21 @@ MODULE_PARM_DESC(no_spic,
96static int compat; /* = 0 */ 100static int compat; /* = 0 */
97module_param(compat, int, 0444); 101module_param(compat, int, 0444);
98MODULE_PARM_DESC(compat, 102MODULE_PARM_DESC(compat,
99 "set this if you want to enable backward compatibility mode for SPIC"); 103 "set this if you want to enable backward compatibility mode");
100 104
101static unsigned long mask = 0xffffffff; 105static unsigned long mask = 0xffffffff;
102module_param(mask, ulong, 0644); 106module_param(mask, ulong, 0644);
103MODULE_PARM_DESC(mask, 107MODULE_PARM_DESC(mask,
104 "set this to the mask of event you want to enable (see doc)"); 108 "set this to the mask of event you want to enable (see doc)");
105 109
110#ifdef CONFIG_SONY_LAPTOP_OLD
111static int minor = -1;
112module_param(minor, int, 0);
113MODULE_PARM_DESC(minor,
114 "minor number of the misc device for the SPIC compatibility code, "
115 "default is -1 (automatic)");
116#endif
117
106/*********** Input Devices ***********/ 118/*********** Input Devices ***********/
107 119
108#define SONY_LAPTOP_BUF_SIZE 128 120#define SONY_LAPTOP_BUF_SIZE 128
@@ -1318,6 +1330,16 @@ static ssize_t sony_pic_bluetoothpower_show(struct device *dev,
1318/* fan speed */ 1330/* fan speed */
1319/* FAN0 information (reverse engineered from ACPI tables) */ 1331/* FAN0 information (reverse engineered from ACPI tables) */
1320#define SONY_PIC_FAN0_STATUS 0x93 1332#define SONY_PIC_FAN0_STATUS 0x93
1333static int sony_pic_set_fanspeed(unsigned long value)
1334{
1335 return ec_write(SONY_PIC_FAN0_STATUS, value);
1336}
1337
1338static int sony_pic_get_fanspeed(u8 *value)
1339{
1340 return ec_read(SONY_PIC_FAN0_STATUS, value);
1341}
1342
1321static ssize_t sony_pic_fanspeed_store(struct device *dev, 1343static ssize_t sony_pic_fanspeed_store(struct device *dev,
1322 struct device_attribute *attr, 1344 struct device_attribute *attr,
1323 const char *buffer, size_t count) 1345 const char *buffer, size_t count)
@@ -1327,7 +1349,7 @@ static ssize_t sony_pic_fanspeed_store(struct device *dev,
1327 return -EINVAL; 1349 return -EINVAL;
1328 1350
1329 value = simple_strtoul(buffer, NULL, 10); 1351 value = simple_strtoul(buffer, NULL, 10);
1330 if (ec_write(SONY_PIC_FAN0_STATUS, value)) 1352 if (sony_pic_set_fanspeed(value))
1331 return -EIO; 1353 return -EIO;
1332 1354
1333 return count; 1355 return count;
@@ -1337,7 +1359,7 @@ static ssize_t sony_pic_fanspeed_show(struct device *dev,
1337 struct device_attribute *attr, char *buffer) 1359 struct device_attribute *attr, char *buffer)
1338{ 1360{
1339 u8 value = 0; 1361 u8 value = 0;
1340 if (ec_read(SONY_PIC_FAN0_STATUS, &value)) 1362 if (sony_pic_get_fanspeed(&value))
1341 return -EIO; 1363 return -EIO;
1342 1364
1343 return snprintf(buffer, PAGE_SIZE, "%d\n", value); 1365 return snprintf(buffer, PAGE_SIZE, "%d\n", value);
@@ -1363,6 +1385,304 @@ static struct attribute_group spic_attribute_group = {
1363 .attrs = spic_attributes 1385 .attrs = spic_attributes
1364}; 1386};
1365 1387
1388/******** SONYPI compatibility **********/
1389#ifdef CONFIG_SONY_LAPTOP_OLD
1390
1391/* battery / brightness / temperature addresses */
1392#define SONYPI_BAT_FLAGS 0x81
1393#define SONYPI_LCD_LIGHT 0x96
1394#define SONYPI_BAT1_PCTRM 0xa0
1395#define SONYPI_BAT1_LEFT 0xa2
1396#define SONYPI_BAT1_MAXRT 0xa4
1397#define SONYPI_BAT2_PCTRM 0xa8
1398#define SONYPI_BAT2_LEFT 0xaa
1399#define SONYPI_BAT2_MAXRT 0xac
1400#define SONYPI_BAT1_MAXTK 0xb0
1401#define SONYPI_BAT1_FULL 0xb2
1402#define SONYPI_BAT2_MAXTK 0xb8
1403#define SONYPI_BAT2_FULL 0xba
1404#define SONYPI_TEMP_STATUS 0xC1
1405
1406struct sonypi_compat_s {
1407 struct fasync_struct *fifo_async;
1408 struct kfifo *fifo;
1409 spinlock_t fifo_lock;
1410 wait_queue_head_t fifo_proc_list;
1411 atomic_t open_count;
1412};
1413static struct sonypi_compat_s sonypi_compat = {
1414 .open_count = ATOMIC_INIT(0),
1415};
1416
1417static int sonypi_misc_fasync(int fd, struct file *filp, int on)
1418{
1419 int retval;
1420
1421 retval = fasync_helper(fd, filp, on, &sonypi_compat.fifo_async);
1422 if (retval < 0)
1423 return retval;
1424 return 0;
1425}
1426
1427static int sonypi_misc_release(struct inode *inode, struct file *file)
1428{
1429 sonypi_misc_fasync(-1, file, 0);
1430 atomic_dec(&sonypi_compat.open_count);
1431 return 0;
1432}
1433
1434static int sonypi_misc_open(struct inode *inode, struct file *file)
1435{
1436 /* Flush input queue on first open */
1437 if (atomic_inc_return(&sonypi_compat.open_count) == 1)
1438 kfifo_reset(sonypi_compat.fifo);
1439 return 0;
1440}
1441
1442static ssize_t sonypi_misc_read(struct file *file, char __user *buf,
1443 size_t count, loff_t *pos)
1444{
1445 ssize_t ret;
1446 unsigned char c;
1447
1448 if ((kfifo_len(sonypi_compat.fifo) == 0) &&
1449 (file->f_flags & O_NONBLOCK))
1450 return -EAGAIN;
1451
1452 ret = wait_event_interruptible(sonypi_compat.fifo_proc_list,
1453 kfifo_len(sonypi_compat.fifo) != 0);
1454 if (ret)
1455 return ret;
1456
1457 while (ret < count &&
1458 (kfifo_get(sonypi_compat.fifo, &c, sizeof(c)) == sizeof(c))) {
1459 if (put_user(c, buf++))
1460 return -EFAULT;
1461 ret++;
1462 }
1463
1464 if (ret > 0) {
1465 struct inode *inode = file->f_path.dentry->d_inode;
1466 inode->i_atime = current_fs_time(inode->i_sb);
1467 }
1468
1469 return ret;
1470}
1471
1472static unsigned int sonypi_misc_poll(struct file *file, poll_table *wait)
1473{
1474 poll_wait(file, &sonypi_compat.fifo_proc_list, wait);
1475 if (kfifo_len(sonypi_compat.fifo))
1476 return POLLIN | POLLRDNORM;
1477 return 0;
1478}
1479
1480static int ec_read16(u8 addr, u16 *value)
1481{
1482 u8 val_lb, val_hb;
1483 if (ec_read(addr, &val_lb))
1484 return -1;
1485 if (ec_read(addr + 1, &val_hb))
1486 return -1;
1487 *value = val_lb | (val_hb << 8);
1488 return 0;
1489}
1490
1491static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
1492 unsigned int cmd, unsigned long arg)
1493{
1494 int ret = 0;
1495 void __user *argp = (void __user *)arg;
1496 u8 val8;
1497 u16 val16;
1498 int value;
1499
1500 /*down(&sonypi_device.lock);*/
1501 switch (cmd) {
1502 case SONYPI_IOCGBRT:
1503 if (sony_backlight_device == NULL) {
1504 ret = -EIO;
1505 break;
1506 }
1507 if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) {
1508 ret = -EIO;
1509 break;
1510 }
1511 val8 = ((value & 0xff) - 1) << 5;
1512 if (copy_to_user(argp, &val8, sizeof(val8)))
1513 ret = -EFAULT;
1514 break;
1515 case SONYPI_IOCSBRT:
1516 if (sony_backlight_device == NULL) {
1517 ret = -EIO;
1518 break;
1519 }
1520 if (copy_from_user(&val8, argp, sizeof(val8))) {
1521 ret = -EFAULT;
1522 break;
1523 }
1524 if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
1525 (val8 >> 5) + 1, NULL)) {
1526 ret = -EIO;
1527 break;
1528 }
1529 /* sync the backlight device status */
1530 sony_backlight_device->props.brightness =
1531 sony_backlight_get_brightness(sony_backlight_device);
1532 break;
1533 case SONYPI_IOCGBAT1CAP:
1534 if (ec_read16(SONYPI_BAT1_FULL, &val16)) {
1535 ret = -EIO;
1536 break;
1537 }
1538 if (copy_to_user(argp, &val16, sizeof(val16)))
1539 ret = -EFAULT;
1540 break;
1541 case SONYPI_IOCGBAT1REM:
1542 if (ec_read16(SONYPI_BAT1_LEFT, &val16)) {
1543 ret = -EIO;
1544 break;
1545 }
1546 if (copy_to_user(argp, &val16, sizeof(val16)))
1547 ret = -EFAULT;
1548 break;
1549 case SONYPI_IOCGBAT2CAP:
1550 if (ec_read16(SONYPI_BAT2_FULL, &val16)) {
1551 ret = -EIO;
1552 break;
1553 }
1554 if (copy_to_user(argp, &val16, sizeof(val16)))
1555 ret = -EFAULT;
1556 break;
1557 case SONYPI_IOCGBAT2REM:
1558 if (ec_read16(SONYPI_BAT2_LEFT, &val16)) {
1559 ret = -EIO;
1560 break;
1561 }
1562 if (copy_to_user(argp, &val16, sizeof(val16)))
1563 ret = -EFAULT;
1564 break;
1565 case SONYPI_IOCGBATFLAGS:
1566 if (ec_read(SONYPI_BAT_FLAGS, &val8)) {
1567 ret = -EIO;
1568 break;
1569 }
1570 val8 &= 0x07;
1571 if (copy_to_user(argp, &val8, sizeof(val8)))
1572 ret = -EFAULT;
1573 break;
1574 case SONYPI_IOCGBLUE:
1575 val8 = spic_dev.bluetooth_power;
1576 if (copy_to_user(argp, &val8, sizeof(val8)))
1577 ret = -EFAULT;
1578 break;
1579 case SONYPI_IOCSBLUE:
1580 if (copy_from_user(&val8, argp, sizeof(val8))) {
1581 ret = -EFAULT;
1582 break;
1583 }
1584 sony_pic_set_bluetoothpower(val8);
1585 break;
1586 /* FAN Controls */
1587 case SONYPI_IOCGFAN:
1588 if (sony_pic_get_fanspeed(&val8)) {
1589 ret = -EIO;
1590 break;
1591 }
1592 if (copy_to_user(argp, &val8, sizeof(val8)))
1593 ret = -EFAULT;
1594 break;
1595 case SONYPI_IOCSFAN:
1596 if (copy_from_user(&val8, argp, sizeof(val8))) {
1597 ret = -EFAULT;
1598 break;
1599 }
1600 if (sony_pic_set_fanspeed(val8))
1601 ret = -EIO;
1602 break;
1603 /* GET Temperature (useful under APM) */
1604 case SONYPI_IOCGTEMP:
1605 if (ec_read(SONYPI_TEMP_STATUS, &val8)) {
1606 ret = -EIO;
1607 break;
1608 }
1609 if (copy_to_user(argp, &val8, sizeof(val8)))
1610 ret = -EFAULT;
1611 break;
1612 default:
1613 ret = -EINVAL;
1614 }
1615 /*up(&sonypi_device.lock);*/
1616 return ret;
1617}
1618
1619static const struct file_operations sonypi_misc_fops = {
1620 .owner = THIS_MODULE,
1621 .read = sonypi_misc_read,
1622 .poll = sonypi_misc_poll,
1623 .open = sonypi_misc_open,
1624 .release = sonypi_misc_release,
1625 .fasync = sonypi_misc_fasync,
1626 .ioctl = sonypi_misc_ioctl,
1627};
1628
1629static struct miscdevice sonypi_misc_device = {
1630 .minor = MISC_DYNAMIC_MINOR,
1631 .name = "sonypi",
1632 .fops = &sonypi_misc_fops,
1633};
1634
1635static void sonypi_compat_report_event(u8 event)
1636{
1637 kfifo_put(sonypi_compat.fifo, (unsigned char *)&event, sizeof(event));
1638 kill_fasync(&sonypi_compat.fifo_async, SIGIO, POLL_IN);
1639 wake_up_interruptible(&sonypi_compat.fifo_proc_list);
1640}
1641
1642static int sonypi_compat_init(void)
1643{
1644 int error;
1645
1646 spin_lock_init(&sonypi_compat.fifo_lock);
1647 sonypi_compat.fifo = kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL,
1648 &sonypi_compat.fifo_lock);
1649 if (IS_ERR(sonypi_compat.fifo)) {
1650 printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
1651 return PTR_ERR(sonypi_compat.fifo);
1652 }
1653
1654 init_waitqueue_head(&sonypi_compat.fifo_proc_list);
1655 /*init_MUTEX(&sonypi_device.lock);*/
1656
1657 if (minor != -1)
1658 sonypi_misc_device.minor = minor;
1659 error = misc_register(&sonypi_misc_device);
1660 if (error) {
1661 printk(KERN_ERR DRV_PFX "misc_register failed\n");
1662 goto err_free_kfifo;
1663 }
1664 if (minor == -1)
1665 printk(KERN_INFO "sonypi: device allocated minor is %d\n",
1666 sonypi_misc_device.minor);
1667
1668 return 0;
1669
1670err_free_kfifo:
1671 kfifo_free(sonypi_compat.fifo);
1672 return error;
1673}
1674
1675static void sonypi_compat_exit(void)
1676{
1677 misc_deregister(&sonypi_misc_device);
1678 kfifo_free(sonypi_compat.fifo);
1679}
1680#else
1681static int sonypi_compat_init(void) { return 0; }
1682static void sonypi_compat_exit(void) { }
1683static void sonypi_compat_report_event(u8 event) { }
1684#endif /* CONFIG_SONY_LAPTOP_OLD */
1685
1366/* 1686/*
1367 * ACPI callbacks 1687 * ACPI callbacks
1368 */ 1688 */
@@ -1604,6 +1924,7 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id)
1604found: 1924found:
1605 sony_laptop_report_input_event(device_event); 1925 sony_laptop_report_input_event(device_event);
1606 acpi_bus_generate_event(spic_dev.acpi_dev, 1, device_event); 1926 acpi_bus_generate_event(spic_dev.acpi_dev, 1, device_event);
1927 sonypi_compat_report_event(device_event);
1607 1928
1608 return IRQ_HANDLED; 1929 return IRQ_HANDLED;
1609} 1930}
@@ -1618,6 +1939,8 @@ static int sony_pic_remove(struct acpi_device *device, int type)
1618 struct sony_pic_ioport *io, *tmp_io; 1939 struct sony_pic_ioport *io, *tmp_io;
1619 struct sony_pic_irq *irq, *tmp_irq; 1940 struct sony_pic_irq *irq, *tmp_irq;
1620 1941
1942 sonypi_compat_exit();
1943
1621 if (sony_pic_disable(device)) { 1944 if (sony_pic_disable(device)) {
1622 printk(KERN_ERR DRV_PFX "Couldn't disable device.\n"); 1945 printk(KERN_ERR DRV_PFX "Couldn't disable device.\n");
1623 return -ENXIO; 1946 return -ENXIO;
@@ -1731,6 +2054,9 @@ static int sony_pic_add(struct acpi_device *device)
1731 if (result) 2054 if (result)
1732 goto err_remove_pf; 2055 goto err_remove_pf;
1733 2056
2057 if (sonypi_compat_init())
2058 goto err_remove_pf;
2059
1734 return 0; 2060 return 0;
1735 2061
1736err_remove_pf: 2062err_remove_pf: