/*
* Sony Programmable I/O Control Device driver for VAIO
*
+ * Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
+ *
* Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net>
*
* Copyright (C) 2005 Narayanan R S <nars@kadamba.org>
*
- * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
+ * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
*
* Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>
*
*
*/
-#include <linux/config.h>
#include <linux/module.h>
+#include <linux/sched.h>
#include <linux/input.h>
#include <linux/pci.h>
-#include <linux/sched.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/err.h>
#include <linux/kfifo.h>
#include <linux/platform_device.h>
+#include <linux/gfp.h>
#include <asm/uaccess.h>
#include <asm/io.h>
MODULE_PARM_DESC(useinput,
"set this if you would like sonypi to feed events to the input subsystem");
+static int check_ioport = 1;
+module_param(check_ioport, int, 0444);
+MODULE_PARM_DESC(check_ioport,
+ "set this to 0 if you think the automatic ioport check for sony-laptop is wrong");
+
#define SONYPI_DEVICE_MODEL_TYPE1 1
#define SONYPI_DEVICE_MODEL_TYPE2 2
#define SONYPI_DEVICE_MODEL_TYPE3 3
u16 evtype_offset;
int camera_power;
int bluetooth_power;
- struct semaphore lock;
- struct kfifo *fifo;
+ struct mutex lock;
+ struct kfifo fifo;
spinlock_t fifo_lock;
wait_queue_head_t fifo_proc_list;
struct fasync_struct *fifo_async;
struct input_dev *input_jog_dev;
struct input_dev *input_key_dev;
struct work_struct input_work;
- struct kfifo *input_fifo;
+ struct kfifo input_fifo;
spinlock_t input_fifo_lock;
} sonypi_device;
while (--n && (command)) \
udelay(1); \
if (!n && (verbose || !quiet)) \
- printk(KERN_WARNING "sonypi command failed at %s : %s (line %d)\n", __FILE__, __FUNCTION__, __LINE__); \
+ printk(KERN_WARNING "sonypi command failed at %s : %s (line %d)\n", __FILE__, __func__, __LINE__); \
}
#ifdef CONFIG_ACPI
#ifdef CONFIG_ACPI
static struct acpi_device *sonypi_acpi_device;
-static int acpi_enabled;
+static int acpi_driver_registered;
#endif
static int sonypi_ec_write(u8 addr, u8 value)
{
-#ifdef CONFIG_ACPI_EC
+#ifdef CONFIG_ACPI
if (SONYPI_ACPI_ACTIVE)
return ec_write(addr, value);
#endif
static int sonypi_ec_read(u8 addr, u8 *value)
{
-#ifdef CONFIG_ACPI_EC
+#ifdef CONFIG_ACPI
if (SONYPI_ACPI_ACTIVE)
return ec_read(addr, value);
#endif
sonypi_device.bluetooth_power = state;
}
-static void input_keyrelease(void *data)
+static void input_keyrelease(struct work_struct *work)
{
struct sonypi_keypress kp;
- while (kfifo_get(sonypi_device.input_fifo, (unsigned char *)&kp,
- sizeof(kp)) == sizeof(kp)) {
+ while (kfifo_out_locked(&sonypi_device.input_fifo, (unsigned char *)&kp,
+ sizeof(kp), &sonypi_device.input_fifo_lock)
+ == sizeof(kp)) {
msleep(10);
input_report_key(kp.dev, kp.key, 0);
input_sync(kp.dev);
if (kp.dev) {
input_report_key(kp.dev, kp.key, 1);
input_sync(kp.dev);
- kfifo_put(sonypi_device.input_fifo,
- (unsigned char *)&kp, sizeof(kp));
+ kfifo_in_locked(&sonypi_device.input_fifo,
+ (unsigned char *)&kp, sizeof(kp),
+ &sonypi_device.input_fifo_lock);
schedule_work(&sonypi_device.input_work);
}
}
/* Interrupt handler: some event is available */
-static irqreturn_t sonypi_irq(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t sonypi_irq(int irq, void *dev_id)
{
u8 v1, v2, event = 0;
int i, j;
sonypi_report_input_event(event);
#ifdef CONFIG_ACPI
- if (acpi_enabled)
- acpi_bus_generate_event(sonypi_acpi_device, 1, event);
+ if (sonypi_acpi_device)
+ acpi_bus_generate_proc_event(sonypi_acpi_device, 1, event);
#endif
- kfifo_put(sonypi_device.fifo, (unsigned char *)&event, sizeof(event));
+ kfifo_in_locked(&sonypi_device.fifo, (unsigned char *)&event,
+ sizeof(event), &sonypi_device.fifo_lock);
kill_fasync(&sonypi_device.fifo_async, SIGIO, POLL_IN);
wake_up_interruptible(&sonypi_device.fifo_proc_list);
return IRQ_HANDLED;
}
-/* External camera command (exported to the motion eye v4l driver) */
-int sonypi_camera_command(int command, u8 value)
-{
- if (!camera)
- return -EIO;
-
- down(&sonypi_device.lock);
-
- switch (command) {
- case SONYPI_COMMAND_SETCAMERA:
- if (value)
- sonypi_camera_on();
- else
- sonypi_camera_off();
- break;
- case SONYPI_COMMAND_SETCAMERABRIGHTNESS:
- sonypi_set(SONYPI_CAMERA_BRIGHTNESS, value);
- break;
- case SONYPI_COMMAND_SETCAMERACONTRAST:
- sonypi_set(SONYPI_CAMERA_CONTRAST, value);
- break;
- case SONYPI_COMMAND_SETCAMERAHUE:
- sonypi_set(SONYPI_CAMERA_HUE, value);
- break;
- case SONYPI_COMMAND_SETCAMERACOLOR:
- sonypi_set(SONYPI_CAMERA_COLOR, value);
- break;
- case SONYPI_COMMAND_SETCAMERASHARPNESS:
- sonypi_set(SONYPI_CAMERA_SHARPNESS, value);
- break;
- case SONYPI_COMMAND_SETCAMERAPICTURE:
- sonypi_set(SONYPI_CAMERA_PICTURE, value);
- break;
- case SONYPI_COMMAND_SETCAMERAAGC:
- sonypi_set(SONYPI_CAMERA_AGC, value);
- break;
- default:
- printk(KERN_ERR "sonypi: sonypi_camera_command invalid: %d\n",
- command);
- break;
- }
- up(&sonypi_device.lock);
- return 0;
-}
-
-EXPORT_SYMBOL(sonypi_camera_command);
-
static int sonypi_misc_fasync(int fd, struct file *filp, int on)
{
- int retval;
-
- retval = fasync_helper(fd, filp, on, &sonypi_device.fifo_async);
- if (retval < 0)
- return retval;
- return 0;
+ return fasync_helper(fd, filp, on, &sonypi_device.fifo_async);
}
static int sonypi_misc_release(struct inode *inode, struct file *file)
{
- sonypi_misc_fasync(-1, file, 0);
- down(&sonypi_device.lock);
+ mutex_lock(&sonypi_device.lock);
sonypi_device.open_count--;
- up(&sonypi_device.lock);
+ mutex_unlock(&sonypi_device.lock);
return 0;
}
static int sonypi_misc_open(struct inode *inode, struct file *file)
{
- down(&sonypi_device.lock);
+ mutex_lock(&sonypi_device.lock);
/* Flush input queue on first open */
if (!sonypi_device.open_count)
- kfifo_reset(sonypi_device.fifo);
+ kfifo_reset(&sonypi_device.fifo);
sonypi_device.open_count++;
- up(&sonypi_device.lock);
+ mutex_unlock(&sonypi_device.lock);
+
return 0;
}
ssize_t ret;
unsigned char c;
- if ((kfifo_len(sonypi_device.fifo) == 0) &&
+ if ((kfifo_len(&sonypi_device.fifo) == 0) &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
ret = wait_event_interruptible(sonypi_device.fifo_proc_list,
- kfifo_len(sonypi_device.fifo) != 0);
+ kfifo_len(&sonypi_device.fifo) != 0);
if (ret)
return ret;
while (ret < count &&
- (kfifo_get(sonypi_device.fifo, &c, sizeof(c)) == sizeof(c))) {
+ (kfifo_out_locked(&sonypi_device.fifo, &c, sizeof(c),
+ &sonypi_device.fifo_lock) == sizeof(c))) {
if (put_user(c, buf++))
return -EFAULT;
ret++;
}
if (ret > 0) {
- struct inode *inode = file->f_dentry->d_inode;
+ struct inode *inode = file->f_path.dentry->d_inode;
inode->i_atime = current_fs_time(inode->i_sb);
}
static unsigned int sonypi_misc_poll(struct file *file, poll_table *wait)
{
poll_wait(file, &sonypi_device.fifo_proc_list, wait);
- if (kfifo_len(sonypi_device.fifo))
+ if (kfifo_len(&sonypi_device.fifo))
return POLLIN | POLLRDNORM;
return 0;
}
-static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
+static long sonypi_misc_ioctl(struct file *fp,
unsigned int cmd, unsigned long arg)
{
- int ret = 0;
+ long ret = 0;
void __user *argp = (void __user *)arg;
u8 val8;
u16 val16;
- down(&sonypi_device.lock);
+ mutex_lock(&sonypi_device.lock);
switch (cmd) {
case SONYPI_IOCGBRT:
if (sonypi_ec_read(SONYPI_LCD_LIGHT, &val8)) {
default:
ret = -EINVAL;
}
- up(&sonypi_device.lock);
+ mutex_unlock(&sonypi_device.lock);
return ret;
}
-static struct file_operations sonypi_misc_fops = {
+static const struct file_operations sonypi_misc_fops = {
.owner = THIS_MODULE,
.read = sonypi_misc_read,
.poll = sonypi_misc_poll,
.open = sonypi_misc_open,
.release = sonypi_misc_release,
.fasync = sonypi_misc_fasync,
- .ioctl = sonypi_misc_ioctl,
+ .unlocked_ioctl = sonypi_misc_ioctl,
+ .llseek = no_llseek,
};
static struct miscdevice sonypi_misc_device = {
return 0;
}
+static const struct acpi_device_id sonypi_device_ids[] = {
+ {"SNY6001", 0},
+ {"", 0},
+};
+
static struct acpi_driver sonypi_acpi_driver = {
.name = "sonypi",
.class = "hkey",
- .ids = "SNY6001",
+ .ids = sonypi_device_ids,
.ops = {
.add = sonypi_acpi_add,
.remove = sonypi_acpi_remove,
};
#endif
-static int __devinit sonypi_create_input_devices(void)
+static int __devinit sonypi_create_input_devices(struct platform_device *pdev)
{
struct input_dev *jog_dev;
struct input_dev *key_dev;
jog_dev->name = "Sony Vaio Jogdial";
jog_dev->id.bustype = BUS_ISA;
jog_dev->id.vendor = PCI_VENDOR_ID_SONY;
+ jog_dev->dev.parent = &pdev->dev;
- jog_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
- jog_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_MIDDLE);
- jog_dev->relbit[0] = BIT(REL_WHEEL);
+ jog_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ jog_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_MIDDLE);
+ jog_dev->relbit[0] = BIT_MASK(REL_WHEEL);
sonypi_device.input_key_dev = key_dev = input_allocate_device();
if (!key_dev) {
key_dev->name = "Sony Vaio Keys";
key_dev->id.bustype = BUS_ISA;
key_dev->id.vendor = PCI_VENDOR_ID_SONY;
+ key_dev->dev.parent = &pdev->dev;
/* Initialize the Input Drivers: special keys */
- key_dev->evbit[0] = BIT(EV_KEY);
+ key_dev->evbit[0] = BIT_MASK(EV_KEY);
for (i = 0; sonypi_inputkeys[i].sonypiev; i++)
if (sonypi_inputkeys[i].inputev)
set_bit(sonypi_inputkeys[i].inputev, key_dev->keybit);
static int __devinit sonypi_setup_ioports(struct sonypi_device *dev,
const struct sonypi_ioport_list *ioport_list)
{
+ /* try to detect if sony-laptop is being used and thus
+ * has already requested one of the known ioports.
+ * As in the deprecated check_region this is racy has we have
+ * multiple ioports available and one of them can be requested
+ * between this check and the subsequent request. Anyway, as an
+ * attempt to be some more user-friendly as we currently are,
+ * this is enough.
+ */
+ const struct sonypi_ioport_list *check = ioport_list;
+ while (check_ioport && check->port1) {
+ if (!request_region(check->port1,
+ sonypi_device.region_size,
+ "Sony Programable I/O Device Check")) {
+ printk(KERN_ERR "sonypi: ioport 0x%.4x busy, using sony-laptop? "
+ "if not use check_ioport=0\n",
+ check->port1);
+ return -EBUSY;
+ }
+ release_region(check->port1, sonypi_device.region_size);
+ check++;
+ }
+
while (ioport_list->port1) {
if (request_region(ioport_list->port1,
while (irq_list->irq) {
if (!request_irq(irq_list->irq, sonypi_irq,
- SA_SHIRQ, "sonypi", sonypi_irq)) {
+ IRQF_SHARED, "sonypi", sonypi_irq)) {
dev->irq = irq_list->irq;
dev->bits = irq_list->bits;
return 0;
struct pci_dev *pcidev;
int error;
+ printk(KERN_WARNING "sonypi: please try the sony-laptop module instead "
+ "and report failures, see also "
+ "http://www.linux.it/~malattia/wiki/index.php/Sony_drivers\n");
+
spin_lock_init(&sonypi_device.fifo_lock);
- sonypi_device.fifo = kfifo_alloc(SONYPI_BUF_SIZE, GFP_KERNEL,
- &sonypi_device.fifo_lock);
- if (IS_ERR(sonypi_device.fifo)) {
+ error = kfifo_alloc(&sonypi_device.fifo, SONYPI_BUF_SIZE, GFP_KERNEL);
+ if (error) {
printk(KERN_ERR "sonypi: kfifo_alloc failed\n");
- return PTR_ERR(sonypi_device.fifo);
+ return error;
}
init_waitqueue_head(&sonypi_device.fifo_proc_list);
- init_MUTEX(&sonypi_device.lock);
+ mutex_init(&sonypi_device.lock);
sonypi_device.bluetooth_power = -1;
if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
if (useinput) {
- error = sonypi_create_input_devices();
+ error = sonypi_create_input_devices(dev);
if (error) {
printk(KERN_ERR
"sonypi: failed to create input devices\n");
}
spin_lock_init(&sonypi_device.input_fifo_lock);
- sonypi_device.input_fifo =
- kfifo_alloc(SONYPI_BUF_SIZE, GFP_KERNEL,
- &sonypi_device.input_fifo_lock);
- if (IS_ERR(sonypi_device.input_fifo)) {
+ error = kfifo_alloc(&sonypi_device.input_fifo, SONYPI_BUF_SIZE,
+ GFP_KERNEL);
+ if (error) {
printk(KERN_ERR "sonypi: kfifo_alloc failed\n");
- error = PTR_ERR(sonypi_device.input_fifo);
goto err_inpdev_unregister;
}
- INIT_WORK(&sonypi_device.input_work, input_keyrelease, NULL);
+ INIT_WORK(&sonypi_device.input_work, input_keyrelease);
}
sonypi_enable(0);
pci_disable_device(pcidev);
err_put_pcidev:
pci_dev_put(pcidev);
- kfifo_free(sonypi_device.fifo);
+ kfifo_free(&sonypi_device.fifo);
return error;
}
{
sonypi_disable();
- synchronize_sched(); /* Allow sonypi interrupt to complete. */
+ synchronize_irq(sonypi_device.irq);
flush_scheduled_work();
if (useinput) {
input_unregister_device(sonypi_device.input_key_dev);
input_unregister_device(sonypi_device.input_jog_dev);
- kfifo_free(sonypi_device.input_fifo);
+ kfifo_free(&sonypi_device.input_fifo);
}
misc_deregister(&sonypi_misc_device);
pci_dev_put(sonypi_device.dev);
}
- kfifo_free(sonypi_device.fifo);
+ kfifo_free(&sonypi_device.fifo);
return 0;
}
goto err_free_device;
#ifdef CONFIG_ACPI
- if (acpi_bus_register_driver(&sonypi_acpi_driver) > 0)
- acpi_enabled = 1;
+ if (acpi_bus_register_driver(&sonypi_acpi_driver) >= 0)
+ acpi_driver_registered = 1;
#endif
return 0;
static void __exit sonypi_exit(void)
{
#ifdef CONFIG_ACPI
- if (acpi_enabled)
+ if (acpi_driver_registered)
acpi_bus_unregister_driver(&sonypi_acpi_driver);
#endif
platform_device_unregister(sonypi_platform_device);