* Re: [PATCH] ideapad-laptop: add new driver
2010-08-13 9:34 ` David Woodhouse
@ 2010-08-14 2:56 ` Ike Panhc
2010-08-14 9:49 ` David Woodhouse
2010-08-15 2:33 ` Len Brown
2010-08-15 8:14 ` Florian Echtler
2010-08-15 8:20 ` Florian Echtler
2 siblings, 2 replies; 14+ messages in thread
From: Ike Panhc @ 2010-08-14 2:56 UTC (permalink / raw)
To: David Woodhouse
Cc: platform-driver-x86, linux-acpi, Matthew Garrett, Len Brown,
Randy Dunlap, Corentin Chary, Andrew Morton, Alan Cox,
Thomas Renninger, linux-kernel
On 08/13/2010 05:34 PM, David Woodhouse wrote:
> On Fri, 2010-08-13 at 17:27 +0800, Ike Panhc wrote:
>> Yes, IIRC EC will send i8042 keycode when you booting with QS button, not
>> with power button. atkbd.c reports unknown key after booting with QS button.
>
> That's interesting. I don't *ever* boot with the QS button; I get the
> keycodes when I boot normally. I tried the QS button precisely once, but
> only *after* I blew away the original contents of the hard drive with my
> MeeGo installation, so it didn't boot.
>
> Do you know how to restore it? Or do you have a copy of the 'lenovo-ec'
> module that it apparently contains?
>
> I did keep the Windows restore partition, just in case I needed to go
> back to it to get wireless working. But when I tried to recover, it told
> me it didn't like my partition table -- so I fixed that and next time I
> booted into the OS recovery, it just dumped me at a command prompt and
> didn't even manage to start the graphical recovery tool.
>
No, I received the ideapads without any OS preinstalled, and get the design
spec with NDA. I asked and got the approve to send the driver to mainline
kernel according to the spec.
Since your driver is pulled. I am happy to have another patch made against
your driver and post again.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] ideapad-laptop: add new driver
2010-08-14 2:56 ` Ike Panhc
@ 2010-08-14 9:49 ` David Woodhouse
2010-08-15 2:33 ` Len Brown
1 sibling, 0 replies; 14+ messages in thread
From: David Woodhouse @ 2010-08-14 9:49 UTC (permalink / raw)
To: Ike Panhc
Cc: platform-driver-x86, linux-acpi, Matthew Garrett, Len Brown,
Randy Dunlap, Corentin Chary, Andrew Morton, Alan Cox,
Thomas Renninger, linux-kernel
On Sat, 2010-08-14 at 10:56 +0800, Ike Panhc wrote:
> No, I received the ideapads without any OS preinstalled, and get the
> design spec with NDA. I asked and got the approve to send the driver
> to mainline kernel according to the spec.
Cool. I'd like to have a copy of that spec. Please could you tell me who
in Lenovo (presumably) I should contact?
--
David Woodhouse Open Source Technology Centre
David.Woodhouse@intel.com Intel Corporation
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] ideapad-laptop: add new driver
2010-08-14 2:56 ` Ike Panhc
2010-08-14 9:49 ` David Woodhouse
@ 2010-08-15 2:33 ` Len Brown
1 sibling, 0 replies; 14+ messages in thread
From: Len Brown @ 2010-08-15 2:33 UTC (permalink / raw)
To: Ike Panhc
Cc: David Woodhouse, platform-driver-x86, linux-acpi, Matthew Garrett,
Randy Dunlap, Corentin Chary, Andrew Morton, Alan Cox,
Thomas Renninger, Linux Kernel Mailing List
If you merge the drivers, I'd sure prefer the name "ideapad-laptop"
over "ideapad-acpi".
While the maintainerless "toshiba-acpi" driver was a pioneer,
and thinkpad-acpi feared installed-base confusion when considering
a name change, we tend to reserve the "acpi" in drivers for things
that supply ACPI, rather than simply use ACPI.
Also, you may find that the platform driver ends up doing
more for the laptop than just using what is behind its
platform-specific ACPI extension.
thanks,
Len Brown, Intel Open Source Technology Center
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH] ideapad-laptop: add new driver
2010-08-13 9:34 ` David Woodhouse
2010-08-14 2:56 ` Ike Panhc
@ 2010-08-15 8:14 ` Florian Echtler
2010-08-15 8:20 ` Florian Echtler
2 siblings, 0 replies; 14+ messages in thread
From: Florian Echtler @ 2010-08-15 8:14 UTC (permalink / raw)
To: David Woodhouse
Cc: Ike Panhc, platform-driver-x86, linux-acpi, Matthew Garrett,
Len Brown, Randy Dunlap, Corentin Chary, Andrew Morton, Alan Cox,
Thomas Renninger, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 441 bytes --]
On Fri, 2010-08-13 at 10:34 +0100, David Woodhouse wrote:
> Do you know how to restore it? Or do you have a copy of the 'lenovo-ec'
> module that it apparently contains?
Hello David,
I do have that module: I coaxed it out of Lenovo with the help of
gpl-violations.org as no source was available on my S10-3t. Since it is
flagged as Dual BSD/GPL license, there should be no harm in posting it
here. I hope it helps you in some way.
Florian
[-- Attachment #2: lenovo_ec.c --]
[-- Type: text/x-csrc, Size: 27251 bytes --]
/*
* lenovo_s11_ec.c Lenovo_S11 ACPI EC Extras
*/
#include <linux/init.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_from/to_user */
#include <asm/io.h> /* inb, outb */
#include <linux/sched.h> /* set_cpus_allowed */
#include <linux/acpi.h>
#include <linux/seq_file.h>
#include <acpi/acpi_drivers.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
typedef unsigned long long evaluate_size_t;
#else
typedef unsigned long evaluate_size_t;
#endif
static DECLARE_MUTEX(gecn_sem);
static DECLARE_MUTEX(secn_sem);
static struct proc_dir_entry *lenovo_ec_fs_dir;
static char *model = NULL;
struct acpi_lenovo_cs2_device {
acpi_handle handle;
char DECN;
char GECN;
char SECN;
};
struct acpi_lenovo_cs2_device lenovo_cs2_device;
static acpi_handle vpc0_handle;
MODULE_LICENSE("Dual BSD/GPL");
module_param(model, charp, S_IRUGO);
/*
* Proc handle for reading the status of ec device.
*/
static int acpi_s11_ec_read_camera(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0xb8,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",!(tmp & 0x8) == 0);
return 0;
}
static int acpi_s11_ec_read_wifi(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0xbb,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",!(tmp & 0x1) == 0);
return 0;
}
static int acpi_s11_ec_read_dvme(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0xb8,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",!(tmp & 0x80) == 0);
return 0;
}
static int acpi_s11_ec_read_tp(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_write(0x00,0x1b)){
printk("ec_write fail\n");
return -EFAULT;
}
mdelay(10);
if(ec_read(0x01,&tmp)){
printk("ec_read_fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",tmp);
return 0;
}
static int acpi_s11_ec_read_antenna(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0xbb,&tmp)){
printk("ec_read_fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",!(tmp & 0x10) == 0);
return 0;
}
static int acpi_s11_ec_read_w3g(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0xbb,&tmp)){
printk("ec_read_fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",((tmp >> 6) & 0x1) == 1);
return 0;
}
static int acpi_s11_ec_read_brightness(struct seq_file *seq, void *offset)
{
char tmp;
int i;
if(ec_read(0xb9,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
seq_printf(seq, "levels: ");
for(i=0;i<=100;i=i+10)
seq_printf(seq," %d",i);
seq_printf(seq, "\ncurrent: %d\n",tmp*10);
return 0;
}
static int acpi_s11_ec_write_camera(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
char tmp;
if(buffer[0]!='0' && buffer[0]!='1'){
return count;
}
if(ec_read(0xb8,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
ec_write(0xb8, buffer[0]== '1' ? (0x08 | tmp) : (~0x08 & tmp));
return count;
}
static int acpi_s11_ec_write_wifi(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
char tmp;
if(buffer[0]!='0' && buffer[0]!='1'){
return count;
}
if(ec_read(0xbb,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
ec_write(0xbb, buffer[0]== '1' ? (0x01 | tmp) : (~0x01 & tmp));
return count;
}
static int acpi_s11_ec_write_dvme(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
char tmp;
if(buffer[0] != '0' && buffer[0] != '1'){
return count;
}
if(ec_read(0xb8,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
ec_write(0xb8, buffer[0]== '1' ? (0x80 | tmp) : (~0x80 & tmp));
return count;
}
static int acpi_s11_ec_write_brightness(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
unsigned int level = 0;
char str[5] = { 0 };
if(copy_from_user(str,buffer,count))
return -EFAULT;
str[count] = 0;
level = simple_strtoul(str, NULL, 0);
if(level > 100)
return -EFAULT;
if(ec_write(0xb9,(level / 10))){
printk("ec_write fail\n");
return -EFAULT;
}
return count;
}
static int acpi_s11_ec_write_w3g(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
char tmp;
if(buffer[0]!='0' && buffer[0]!='1'){
return count;
}
if(ec_read(0xbb,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
ec_write(0xbb, buffer[0]== '1' ? (0x80 | tmp) : (~0x80 & tmp));
return count;
}
static int acpi_s11_ec_camera_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s11_ec_read_camera, PDE(inode)->data);
}
static int acpi_s11_ec_wifi_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s11_ec_read_wifi, PDE(inode)->data);
}
static int acpi_s11_ec_tp_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s11_ec_read_tp, PDE(inode)->data);
}
static int acpi_s11_ec_antenna_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s11_ec_read_antenna, PDE(inode)->data);
}
static int acpi_s11_ec_w3g_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s11_ec_read_w3g, PDE(inode)->data);
}
static int acpi_s11_ec_dvme_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s11_ec_read_dvme, PDE(inode)->data);
}
static int acpi_s11_ec_brightness_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s11_ec_read_brightness, PDE(inode)->data);
}
static struct file_operations acpi_s11_ec_camera_ops = {
.open = acpi_s11_ec_camera_open_fs,
.read = seq_read,
.write = acpi_s11_ec_write_camera,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s11_ec_wifi_ops = {
.open = acpi_s11_ec_wifi_open_fs,
.read = seq_read,
.write = acpi_s11_ec_write_wifi,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s11_ec_tp_ops = {
.open = acpi_s11_ec_tp_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s11_ec_antenna_ops = {
.open = acpi_s11_ec_antenna_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s11_ec_w3g_ops = {
.open = acpi_s11_ec_w3g_open_fs,
.read = seq_read,
.write = acpi_s11_ec_write_w3g,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s11_ec_dvme_ops = {
.open = acpi_s11_ec_dvme_open_fs,
.read = seq_read,
.write = acpi_s11_ec_write_dvme,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s11_ec_brightness_ops = {
.open = acpi_s11_ec_brightness_open_fs,
.read = seq_read,
.write = acpi_s11_ec_write_brightness,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int acpi_s20_ec_read_antenna(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0x52,&tmp)){
printk("ec_read_fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",!(tmp & 0x01) == 0);
return 0;
}
static int acpi_s20_ec_read_w3g(struct seq_file *seq, void *offset)
{
evaluate_size_t tmp;
acpi_evaluate_integer(vpc0_handle, "_CFG", NULL,
&tmp);
seq_printf(seq, "%d\n",((tmp >> 17) & 0x1) == 1);
return 0;
}
static int acpi_s20_ec_read_wifi(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0x71,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",!(tmp & 0x1) == 0);
return 0;
}
static int acpi_s20_ec_antenna_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s20_ec_read_antenna, PDE(inode)->data);
}
static int acpi_s20_ec_w3g_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s20_ec_read_w3g, PDE(inode)->data);
}
static int acpi_s20_ec_wifi_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s20_ec_read_wifi, PDE(inode)->data);
}
static struct file_operations acpi_s20_ec_antenna_ops = {
.open = acpi_s20_ec_antenna_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s20_ec_w3g_ops = {
.open = acpi_s20_ec_w3g_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s20_ec_wifi_ops = {
.open = acpi_s20_ec_wifi_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int acpi_cs2_ec_read_w3g(struct seq_file *seq, void *offset)
{
evaluate_size_t tmp;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
arg0.integer.value = 4;
if (lenovo_cs2_device.GECN != 1){
seq_printf(seq, "No device\n");
return 0;
}
else{
if (down_interruptible(&gecn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_integer(lenovo_cs2_device.handle, "GECN", &args, &tmp);
up(&gecn_sem);
seq_printf(seq, "%d\n",(int)tmp);
return 0;
}
}
static int acpi_cs2_ec_read_antenna(struct seq_file *seq, void *offset)
{
evaluate_size_t tmp;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
arg0.integer.value = 5;
if (lenovo_cs2_device.GECN != 1){
seq_printf(seq, "No device\n");
return 0;
}
else{
if (down_interruptible(&gecn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_integer(lenovo_cs2_device.handle, "GECN", &args, &tmp);
up(&gecn_sem);
seq_printf(seq, "%d\n",(int)tmp);
return 0;
}
}
static int acpi_cs2_ec_read_wifi(struct seq_file *seq, void *offset)
{
evaluate_size_t tmp;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
arg0.integer.value = 2;
if (lenovo_cs2_device.GECN != 1){
seq_printf(seq, "No device\n");
return 0;
}
else{
if (down_interruptible(&gecn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_integer(lenovo_cs2_device.handle, "GECN", &args, &tmp);
up(&gecn_sem);
seq_printf(seq, "%d\n",(int)tmp);
return 0;
}
}
static int acpi_cs2_ec_read_camera(struct seq_file *seq, void *offset)
{
evaluate_size_t tmp;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
arg0.integer.value = 1;
if (lenovo_cs2_device.GECN != 1){
seq_printf(seq, "No device\n");
return 0;
}
else{
if (down_interruptible(&gecn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_integer(lenovo_cs2_device.handle, "GECN", &args, &tmp);
up(&gecn_sem);
seq_printf(seq, "%d\n",(int)tmp);
return 0;
}
}
static int acpi_cs2_ec_read_bluetooth(struct seq_file *seq, void *offset)
{
evaluate_size_t tmp;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
arg0.integer.value = 3;
if (lenovo_cs2_device.GECN != 1){
seq_printf(seq, "No device\n");
return 0;
}
else{
if (down_interruptible(&gecn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_integer(lenovo_cs2_device.handle, "GECN", &args, &tmp);
up(&gecn_sem);
seq_printf(seq, "%d\n",(int)tmp);
return 0;
}
}
static int acpi_cs2_ec_write_camera(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
union acpi_object in_arg[2];
struct acpi_object_list args = { 2, in_arg };
if(buffer[0]!='0' && buffer[0]!='1'){
return count;
}
in_arg[0].type = ACPI_TYPE_INTEGER;
in_arg[0].integer.value = 1;
in_arg[1].type = ACPI_TYPE_INTEGER;
in_arg[1].integer.value = (buffer[0] == '1');
if (lenovo_cs2_device.SECN == 1){
if (down_interruptible(&secn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_object(lenovo_cs2_device.handle, "SECN", &args, NULL);
up(&secn_sem);
}
return count;
}
static int acpi_cs2_ec_write_wifi(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
union acpi_object in_arg[2];
struct acpi_object_list args = { 2, in_arg };
if(buffer[0]!='0' && buffer[0]!='1'){
return count;
}
in_arg[0].type = ACPI_TYPE_INTEGER;
in_arg[0].integer.value = 2;
in_arg[1].type = ACPI_TYPE_INTEGER;
in_arg[1].integer.value = (buffer[0] == '1');
if (lenovo_cs2_device.SECN == 1){
if (down_interruptible(&secn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_object(lenovo_cs2_device.handle, "SECN", &args, NULL);
up(&secn_sem);
}
return count;
}
static int acpi_cs2_ec_write_w3g(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
union acpi_object in_arg[2];
struct acpi_object_list args = { 2, in_arg };
if(buffer[0]!='0' && buffer[0]!='1'){
return count;
}
in_arg[0].type = ACPI_TYPE_INTEGER;
in_arg[0].integer.value = 4;
in_arg[1].type = ACPI_TYPE_INTEGER;
in_arg[1].integer.value = (buffer[0] == '1');
if (lenovo_cs2_device.SECN == 1){
if (down_interruptible(&secn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_object(lenovo_cs2_device.handle, "SECN", &args, NULL);
up(&secn_sem);
}
return count;
}
static int acpi_cs2_ec_write_bluetooth(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
union acpi_object in_arg[2];
struct acpi_object_list args = { 2, in_arg };
if(buffer[0]!='0' && buffer[0]!='1'){
return count;
}
in_arg[0].type = ACPI_TYPE_INTEGER;
in_arg[0].integer.value = 3;
in_arg[1].type = ACPI_TYPE_INTEGER;
in_arg[1].integer.value = (buffer[0] == '1');
if (lenovo_cs2_device.SECN == 1){
if (down_interruptible(&secn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_object(lenovo_cs2_device.handle, "SECN", &args, NULL);
up(&secn_sem);
}
return count;
}
static int acpi_cs2_ec_antenna_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_cs2_ec_read_antenna, PDE(inode)->data);
}
static int acpi_cs2_ec_w3g_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_cs2_ec_read_w3g, PDE(inode)->data);
}
static int acpi_cs2_ec_wifi_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_cs2_ec_read_wifi, PDE(inode)->data);
}
static int acpi_cs2_ec_camera_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_cs2_ec_read_camera, PDE(inode)->data);
}
static int acpi_cs2_ec_bluetooth_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_cs2_ec_read_bluetooth, PDE(inode)->data);
}
static struct file_operations acpi_cs2_ec_antenna_ops = {
.open = acpi_cs2_ec_antenna_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_cs2_ec_w3g_ops = {
.open = acpi_cs2_ec_w3g_open_fs,
.read = seq_read,
.write = acpi_cs2_ec_write_w3g,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_cs2_ec_wifi_ops = {
.open = acpi_cs2_ec_wifi_open_fs,
.read = seq_read,
.write = acpi_cs2_ec_write_wifi,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_cs2_ec_camera_ops = {
.open = acpi_cs2_ec_camera_open_fs,
.read = seq_read,
.write = acpi_cs2_ec_write_camera,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_cs2_ec_bluetooth_ops = {
.open = acpi_cs2_ec_bluetooth_open_fs,
.read = seq_read,
.write = acpi_cs2_ec_write_bluetooth,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int acpi_m3b_ec_read_antenna(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0x52,&tmp)){
printk("ec_read_fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",!(tmp & 0x01) == 0);
return 0;
}
static int acpi_m3b_ec_antenna_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_m3b_ec_read_antenna, PDE(inode)->data);
}
static struct file_operations acpi_m3b_ec_antenna_ops = {
.open = acpi_m3b_ec_antenna_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
/*
* This function is used to regiter the ec device's proc file and operations
*/
static int lenovo_ec_proc_add(char *name,struct file_operations *acpi_ec_ops,struct proc_dir_entry *dir)
{
struct proc_dir_entry * entry = NULL;
entry = create_proc_entry(name, S_IRUGO,
dir);
if (!entry)
return -ENODEV;
else {
entry->proc_fops = acpi_ec_ops;
entry->data = NULL;
entry->owner = THIS_MODULE;
}
return 0;
}
/*
* The lenovo S11 has six ec devices : camera, wifi, brightness controller, touch pad, dvm enalbe, antenna.
*/
static int lenovo_s11_ec_init(void)
{
int result;
printk("Lenovo s11 ec driver init\n");
result = lenovo_ec_proc_add("camera",&acpi_s11_ec_camera_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("wifi", &acpi_s11_ec_wifi_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("tp", &acpi_s11_ec_tp_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("dvme", &acpi_s11_ec_dvme_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("brightness", &acpi_s11_ec_brightness_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("antenna", &acpi_s11_ec_antenna_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("w3g", &acpi_s11_ec_w3g_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
return 0;
}
static void lenovo_s11_ec_exit(void)
{
printk("Lenovo_s11 ec driver remove\n");
remove_proc_entry("camera", lenovo_ec_fs_dir);
remove_proc_entry("wifi", lenovo_ec_fs_dir);
remove_proc_entry("tp", lenovo_ec_fs_dir);
remove_proc_entry("dvme", lenovo_ec_fs_dir);
remove_proc_entry("brightness", lenovo_ec_fs_dir);
remove_proc_entry("antenna", lenovo_ec_fs_dir);
remove_proc_entry("w3g", lenovo_ec_fs_dir);
return;
}
static acpi_status
find_vpc0(acpi_handle handle, u32 lvl, void *context, void **rv)
{
char prefix[80] = {'\0'};
int *vpc;
struct acpi_buffer buffer = {sizeof(prefix), prefix };
vpc = context;
acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
if (strcmp(prefix,"VPC0") == 0){
vpc0_handle = handle;
*vpc = 1;
}
return AE_OK;
}
static int lenovo_s20_vpn_init(void)
{
int vpc0 = 0;
acpi_handle h_dummy1;
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, find_vpc0, &vpc0, NULL);
if (vpc0 == 1){
if (ACPI_SUCCESS(acpi_get_handle(vpc0_handle, "_CFG", &h_dummy1)))
return 0;
}
return -1;
}
static int lenovo_s20_ec_init(void)
{
int result;
printk("Lenovo s20 ec driver init\n");
result = lenovo_s20_vpn_init();
if(result == -1)
return -ENODEV;
result = lenovo_ec_proc_add("antenna",&acpi_s20_ec_antenna_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("w3g",&acpi_s20_ec_w3g_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("wifi",&acpi_s20_ec_wifi_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
return 0;
}
static void lenovo_s20_ec_exit(void)
{
printk("Lenovo_s20 ec driver remove\n");
remove_proc_entry("antenna", lenovo_ec_fs_dir);
remove_proc_entry("w3g", lenovo_ec_fs_dir);
remove_proc_entry("wifi", lenovo_ec_fs_dir);
}
static acpi_status
find_sb(acpi_handle handle, u32 lvl, void *context, void **rv)
{
char prefix[80] = {'\0'};
int *sb;
struct acpi_buffer buffer = {sizeof(prefix), prefix };
sb = context;
acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
if (strcmp(prefix,"_SB_") == 0){
lenovo_cs2_device.handle = handle;
*sb = 1;
}
return AE_OK;
}
static int lenovo_cs2_rootpath_init(void)
{
int sb = 0;
acpi_handle h_dummy1;
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, find_sb, &sb, NULL);
if (sb == 1){
if (ACPI_SUCCESS(acpi_get_handle(lenovo_cs2_device.handle, "DECN", &h_dummy1)))
lenovo_cs2_device.DECN = 1;
else
lenovo_cs2_device.DECN = 0;
if (ACPI_SUCCESS(acpi_get_handle(lenovo_cs2_device.handle, "SECN", &h_dummy1)))
lenovo_cs2_device.SECN = 1;
else
lenovo_cs2_device.SECN = 0;
if (ACPI_SUCCESS(acpi_get_handle(lenovo_cs2_device.handle, "GECN", &h_dummy1)))
lenovo_cs2_device.GECN = 1;
else
lenovo_cs2_device.GECN = 0;
return 0;
}
else{
lenovo_cs2_device.GECN = 0;
lenovo_cs2_device.SECN = 0;
lenovo_cs2_device.DECN = 0;
return -1;
}
}
static int lenovo_cs2_ec_init(void)
{
int result;
printk("Lenovo cs2 ec driver init\n");
result = lenovo_cs2_rootpath_init();
if(result == -1)
return -ENODEV;
result = lenovo_ec_proc_add("antenna",&acpi_cs2_ec_antenna_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("w3g",&acpi_cs2_ec_w3g_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("wifi",&acpi_cs2_ec_wifi_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("camera",&acpi_cs2_ec_camera_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("bluetooth",&acpi_cs2_ec_bluetooth_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
return 0;
}
static void lenovo_cs2_ec_exit(void)
{
printk("Lenovo_cs2 ec driver remove\n");
remove_proc_entry("antenna", lenovo_ec_fs_dir);
remove_proc_entry("w3g", lenovo_ec_fs_dir);
remove_proc_entry("wifi", lenovo_ec_fs_dir);
remove_proc_entry("camera", lenovo_ec_fs_dir);
}
static int lenovo_m3b_ec_init(void)
{
/* Use the acpi method interface same as Lenovo CS2*/
int result;
printk("Lenovo mariana-3b ec driver init\n");
result = lenovo_cs2_rootpath_init();
if(result == -1)
return -ENODEV;
result = lenovo_ec_proc_add("antenna",&acpi_cs2_ec_antenna_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("w3g",&acpi_cs2_ec_w3g_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("wifi",&acpi_cs2_ec_wifi_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("camera",&acpi_cs2_ec_camera_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("bluetooth",&acpi_cs2_ec_bluetooth_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
return 0;
}
static void lenovo_m3b_ec_exit(void)
{
printk("Lenovo mariana-3b ec driver remove\n");
remove_proc_entry("antenna", lenovo_ec_fs_dir);
}
static int lenovo_ec_init(void)
{
lenovo_ec_fs_dir = proc_mkdir("lenovo",acpi_root_dir);
if (model != NULL){
if (strncmp(model, "S11", 3) == 0)
lenovo_s11_ec_init();
else if (strncmp(model, "S20", 3) == 0)
lenovo_s20_ec_init();
else if (strncmp(model, "CS2", 3) == 0)
lenovo_cs2_ec_init();
else if (strncmp(model, "M3B", 3) == 0)
lenovo_m3b_ec_init();
}
return 0;
}
static void lenovo_ec_exit(void)
{
if (model != NULL){
if (strncmp(model, "S11", 3) == 0)
lenovo_s11_ec_exit();
else if (strncmp(model, "S20", 3) == 0)
lenovo_s20_ec_exit();
else if (strncmp(model, "CS2", 3) == 0)
lenovo_cs2_ec_exit();
else if (strncmp(model, "M3B", 3) == 0)
lenovo_m3b_ec_exit();
}
remove_proc_entry("lenovo", acpi_root_dir);
}
module_init(lenovo_ec_init);
module_exit(lenovo_ec_exit);
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH] ideapad-laptop: add new driver
2010-08-13 9:34 ` David Woodhouse
2010-08-14 2:56 ` Ike Panhc
2010-08-15 8:14 ` Florian Echtler
@ 2010-08-15 8:20 ` Florian Echtler
2 siblings, 0 replies; 14+ messages in thread
From: Florian Echtler @ 2010-08-15 8:20 UTC (permalink / raw)
To: David Woodhouse
Cc: Ike Panhc, platform-driver-x86, linux-acpi, Matthew Garrett,
Len Brown, Randy Dunlap, Corentin Chary, Andrew Morton, Alan Cox,
Thomas Renninger, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 442 bytes --]
On Fri, 2010-08-13 at 10:34 +0100, David Woodhouse wrote:
> Do you know how to restore it? Or do you have a copy of the 'lenovo-ec'
> module that it apparently contains?
Hello David,
I do have that module: I coaxed it out of Lenovo with the help of
gpl-violations.org as no source was available on my S10-3t. Since it is
flagged as Dual BSD/GPL license, there should be no harm in posting it
here. I hope it helps you in some way.
Florian
[-- Attachment #2: lenovo_ec.c --]
[-- Type: text/x-csrc, Size: 27251 bytes --]
/*
* lenovo_s11_ec.c Lenovo_S11 ACPI EC Extras
*/
#include <linux/init.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_from/to_user */
#include <asm/io.h> /* inb, outb */
#include <linux/sched.h> /* set_cpus_allowed */
#include <linux/acpi.h>
#include <linux/seq_file.h>
#include <acpi/acpi_drivers.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
typedef unsigned long long evaluate_size_t;
#else
typedef unsigned long evaluate_size_t;
#endif
static DECLARE_MUTEX(gecn_sem);
static DECLARE_MUTEX(secn_sem);
static struct proc_dir_entry *lenovo_ec_fs_dir;
static char *model = NULL;
struct acpi_lenovo_cs2_device {
acpi_handle handle;
char DECN;
char GECN;
char SECN;
};
struct acpi_lenovo_cs2_device lenovo_cs2_device;
static acpi_handle vpc0_handle;
MODULE_LICENSE("Dual BSD/GPL");
module_param(model, charp, S_IRUGO);
/*
* Proc handle for reading the status of ec device.
*/
static int acpi_s11_ec_read_camera(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0xb8,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",!(tmp & 0x8) == 0);
return 0;
}
static int acpi_s11_ec_read_wifi(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0xbb,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",!(tmp & 0x1) == 0);
return 0;
}
static int acpi_s11_ec_read_dvme(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0xb8,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",!(tmp & 0x80) == 0);
return 0;
}
static int acpi_s11_ec_read_tp(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_write(0x00,0x1b)){
printk("ec_write fail\n");
return -EFAULT;
}
mdelay(10);
if(ec_read(0x01,&tmp)){
printk("ec_read_fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",tmp);
return 0;
}
static int acpi_s11_ec_read_antenna(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0xbb,&tmp)){
printk("ec_read_fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",!(tmp & 0x10) == 0);
return 0;
}
static int acpi_s11_ec_read_w3g(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0xbb,&tmp)){
printk("ec_read_fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",((tmp >> 6) & 0x1) == 1);
return 0;
}
static int acpi_s11_ec_read_brightness(struct seq_file *seq, void *offset)
{
char tmp;
int i;
if(ec_read(0xb9,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
seq_printf(seq, "levels: ");
for(i=0;i<=100;i=i+10)
seq_printf(seq," %d",i);
seq_printf(seq, "\ncurrent: %d\n",tmp*10);
return 0;
}
static int acpi_s11_ec_write_camera(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
char tmp;
if(buffer[0]!='0' && buffer[0]!='1'){
return count;
}
if(ec_read(0xb8,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
ec_write(0xb8, buffer[0]== '1' ? (0x08 | tmp) : (~0x08 & tmp));
return count;
}
static int acpi_s11_ec_write_wifi(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
char tmp;
if(buffer[0]!='0' && buffer[0]!='1'){
return count;
}
if(ec_read(0xbb,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
ec_write(0xbb, buffer[0]== '1' ? (0x01 | tmp) : (~0x01 & tmp));
return count;
}
static int acpi_s11_ec_write_dvme(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
char tmp;
if(buffer[0] != '0' && buffer[0] != '1'){
return count;
}
if(ec_read(0xb8,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
ec_write(0xb8, buffer[0]== '1' ? (0x80 | tmp) : (~0x80 & tmp));
return count;
}
static int acpi_s11_ec_write_brightness(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
unsigned int level = 0;
char str[5] = { 0 };
if(copy_from_user(str,buffer,count))
return -EFAULT;
str[count] = 0;
level = simple_strtoul(str, NULL, 0);
if(level > 100)
return -EFAULT;
if(ec_write(0xb9,(level / 10))){
printk("ec_write fail\n");
return -EFAULT;
}
return count;
}
static int acpi_s11_ec_write_w3g(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
char tmp;
if(buffer[0]!='0' && buffer[0]!='1'){
return count;
}
if(ec_read(0xbb,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
ec_write(0xbb, buffer[0]== '1' ? (0x80 | tmp) : (~0x80 & tmp));
return count;
}
static int acpi_s11_ec_camera_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s11_ec_read_camera, PDE(inode)->data);
}
static int acpi_s11_ec_wifi_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s11_ec_read_wifi, PDE(inode)->data);
}
static int acpi_s11_ec_tp_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s11_ec_read_tp, PDE(inode)->data);
}
static int acpi_s11_ec_antenna_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s11_ec_read_antenna, PDE(inode)->data);
}
static int acpi_s11_ec_w3g_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s11_ec_read_w3g, PDE(inode)->data);
}
static int acpi_s11_ec_dvme_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s11_ec_read_dvme, PDE(inode)->data);
}
static int acpi_s11_ec_brightness_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s11_ec_read_brightness, PDE(inode)->data);
}
static struct file_operations acpi_s11_ec_camera_ops = {
.open = acpi_s11_ec_camera_open_fs,
.read = seq_read,
.write = acpi_s11_ec_write_camera,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s11_ec_wifi_ops = {
.open = acpi_s11_ec_wifi_open_fs,
.read = seq_read,
.write = acpi_s11_ec_write_wifi,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s11_ec_tp_ops = {
.open = acpi_s11_ec_tp_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s11_ec_antenna_ops = {
.open = acpi_s11_ec_antenna_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s11_ec_w3g_ops = {
.open = acpi_s11_ec_w3g_open_fs,
.read = seq_read,
.write = acpi_s11_ec_write_w3g,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s11_ec_dvme_ops = {
.open = acpi_s11_ec_dvme_open_fs,
.read = seq_read,
.write = acpi_s11_ec_write_dvme,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s11_ec_brightness_ops = {
.open = acpi_s11_ec_brightness_open_fs,
.read = seq_read,
.write = acpi_s11_ec_write_brightness,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int acpi_s20_ec_read_antenna(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0x52,&tmp)){
printk("ec_read_fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",!(tmp & 0x01) == 0);
return 0;
}
static int acpi_s20_ec_read_w3g(struct seq_file *seq, void *offset)
{
evaluate_size_t tmp;
acpi_evaluate_integer(vpc0_handle, "_CFG", NULL,
&tmp);
seq_printf(seq, "%d\n",((tmp >> 17) & 0x1) == 1);
return 0;
}
static int acpi_s20_ec_read_wifi(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0x71,&tmp)){
printk("ec_read fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",!(tmp & 0x1) == 0);
return 0;
}
static int acpi_s20_ec_antenna_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s20_ec_read_antenna, PDE(inode)->data);
}
static int acpi_s20_ec_w3g_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s20_ec_read_w3g, PDE(inode)->data);
}
static int acpi_s20_ec_wifi_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_s20_ec_read_wifi, PDE(inode)->data);
}
static struct file_operations acpi_s20_ec_antenna_ops = {
.open = acpi_s20_ec_antenna_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s20_ec_w3g_ops = {
.open = acpi_s20_ec_w3g_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_s20_ec_wifi_ops = {
.open = acpi_s20_ec_wifi_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int acpi_cs2_ec_read_w3g(struct seq_file *seq, void *offset)
{
evaluate_size_t tmp;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
arg0.integer.value = 4;
if (lenovo_cs2_device.GECN != 1){
seq_printf(seq, "No device\n");
return 0;
}
else{
if (down_interruptible(&gecn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_integer(lenovo_cs2_device.handle, "GECN", &args, &tmp);
up(&gecn_sem);
seq_printf(seq, "%d\n",(int)tmp);
return 0;
}
}
static int acpi_cs2_ec_read_antenna(struct seq_file *seq, void *offset)
{
evaluate_size_t tmp;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
arg0.integer.value = 5;
if (lenovo_cs2_device.GECN != 1){
seq_printf(seq, "No device\n");
return 0;
}
else{
if (down_interruptible(&gecn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_integer(lenovo_cs2_device.handle, "GECN", &args, &tmp);
up(&gecn_sem);
seq_printf(seq, "%d\n",(int)tmp);
return 0;
}
}
static int acpi_cs2_ec_read_wifi(struct seq_file *seq, void *offset)
{
evaluate_size_t tmp;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
arg0.integer.value = 2;
if (lenovo_cs2_device.GECN != 1){
seq_printf(seq, "No device\n");
return 0;
}
else{
if (down_interruptible(&gecn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_integer(lenovo_cs2_device.handle, "GECN", &args, &tmp);
up(&gecn_sem);
seq_printf(seq, "%d\n",(int)tmp);
return 0;
}
}
static int acpi_cs2_ec_read_camera(struct seq_file *seq, void *offset)
{
evaluate_size_t tmp;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
arg0.integer.value = 1;
if (lenovo_cs2_device.GECN != 1){
seq_printf(seq, "No device\n");
return 0;
}
else{
if (down_interruptible(&gecn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_integer(lenovo_cs2_device.handle, "GECN", &args, &tmp);
up(&gecn_sem);
seq_printf(seq, "%d\n",(int)tmp);
return 0;
}
}
static int acpi_cs2_ec_read_bluetooth(struct seq_file *seq, void *offset)
{
evaluate_size_t tmp;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
arg0.integer.value = 3;
if (lenovo_cs2_device.GECN != 1){
seq_printf(seq, "No device\n");
return 0;
}
else{
if (down_interruptible(&gecn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_integer(lenovo_cs2_device.handle, "GECN", &args, &tmp);
up(&gecn_sem);
seq_printf(seq, "%d\n",(int)tmp);
return 0;
}
}
static int acpi_cs2_ec_write_camera(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
union acpi_object in_arg[2];
struct acpi_object_list args = { 2, in_arg };
if(buffer[0]!='0' && buffer[0]!='1'){
return count;
}
in_arg[0].type = ACPI_TYPE_INTEGER;
in_arg[0].integer.value = 1;
in_arg[1].type = ACPI_TYPE_INTEGER;
in_arg[1].integer.value = (buffer[0] == '1');
if (lenovo_cs2_device.SECN == 1){
if (down_interruptible(&secn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_object(lenovo_cs2_device.handle, "SECN", &args, NULL);
up(&secn_sem);
}
return count;
}
static int acpi_cs2_ec_write_wifi(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
union acpi_object in_arg[2];
struct acpi_object_list args = { 2, in_arg };
if(buffer[0]!='0' && buffer[0]!='1'){
return count;
}
in_arg[0].type = ACPI_TYPE_INTEGER;
in_arg[0].integer.value = 2;
in_arg[1].type = ACPI_TYPE_INTEGER;
in_arg[1].integer.value = (buffer[0] == '1');
if (lenovo_cs2_device.SECN == 1){
if (down_interruptible(&secn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_object(lenovo_cs2_device.handle, "SECN", &args, NULL);
up(&secn_sem);
}
return count;
}
static int acpi_cs2_ec_write_w3g(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
union acpi_object in_arg[2];
struct acpi_object_list args = { 2, in_arg };
if(buffer[0]!='0' && buffer[0]!='1'){
return count;
}
in_arg[0].type = ACPI_TYPE_INTEGER;
in_arg[0].integer.value = 4;
in_arg[1].type = ACPI_TYPE_INTEGER;
in_arg[1].integer.value = (buffer[0] == '1');
if (lenovo_cs2_device.SECN == 1){
if (down_interruptible(&secn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_object(lenovo_cs2_device.handle, "SECN", &args, NULL);
up(&secn_sem);
}
return count;
}
static int acpi_cs2_ec_write_bluetooth(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
union acpi_object in_arg[2];
struct acpi_object_list args = { 2, in_arg };
if(buffer[0]!='0' && buffer[0]!='1'){
return count;
}
in_arg[0].type = ACPI_TYPE_INTEGER;
in_arg[0].integer.value = 3;
in_arg[1].type = ACPI_TYPE_INTEGER;
in_arg[1].integer.value = (buffer[0] == '1');
if (lenovo_cs2_device.SECN == 1){
if (down_interruptible(&secn_sem)){
return -ERESTARTSYS;
}
acpi_evaluate_object(lenovo_cs2_device.handle, "SECN", &args, NULL);
up(&secn_sem);
}
return count;
}
static int acpi_cs2_ec_antenna_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_cs2_ec_read_antenna, PDE(inode)->data);
}
static int acpi_cs2_ec_w3g_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_cs2_ec_read_w3g, PDE(inode)->data);
}
static int acpi_cs2_ec_wifi_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_cs2_ec_read_wifi, PDE(inode)->data);
}
static int acpi_cs2_ec_camera_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_cs2_ec_read_camera, PDE(inode)->data);
}
static int acpi_cs2_ec_bluetooth_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_cs2_ec_read_bluetooth, PDE(inode)->data);
}
static struct file_operations acpi_cs2_ec_antenna_ops = {
.open = acpi_cs2_ec_antenna_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_cs2_ec_w3g_ops = {
.open = acpi_cs2_ec_w3g_open_fs,
.read = seq_read,
.write = acpi_cs2_ec_write_w3g,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_cs2_ec_wifi_ops = {
.open = acpi_cs2_ec_wifi_open_fs,
.read = seq_read,
.write = acpi_cs2_ec_write_wifi,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_cs2_ec_camera_ops = {
.open = acpi_cs2_ec_camera_open_fs,
.read = seq_read,
.write = acpi_cs2_ec_write_camera,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct file_operations acpi_cs2_ec_bluetooth_ops = {
.open = acpi_cs2_ec_bluetooth_open_fs,
.read = seq_read,
.write = acpi_cs2_ec_write_bluetooth,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int acpi_m3b_ec_read_antenna(struct seq_file *seq, void *offset)
{
char tmp;
if(ec_read(0x52,&tmp)){
printk("ec_read_fail\n");
return -EFAULT;
}
seq_printf(seq, "%d\n",!(tmp & 0x01) == 0);
return 0;
}
static int acpi_m3b_ec_antenna_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_m3b_ec_read_antenna, PDE(inode)->data);
}
static struct file_operations acpi_m3b_ec_antenna_ops = {
.open = acpi_m3b_ec_antenna_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
/*
* This function is used to regiter the ec device's proc file and operations
*/
static int lenovo_ec_proc_add(char *name,struct file_operations *acpi_ec_ops,struct proc_dir_entry *dir)
{
struct proc_dir_entry * entry = NULL;
entry = create_proc_entry(name, S_IRUGO,
dir);
if (!entry)
return -ENODEV;
else {
entry->proc_fops = acpi_ec_ops;
entry->data = NULL;
entry->owner = THIS_MODULE;
}
return 0;
}
/*
* The lenovo S11 has six ec devices : camera, wifi, brightness controller, touch pad, dvm enalbe, antenna.
*/
static int lenovo_s11_ec_init(void)
{
int result;
printk("Lenovo s11 ec driver init\n");
result = lenovo_ec_proc_add("camera",&acpi_s11_ec_camera_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("wifi", &acpi_s11_ec_wifi_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("tp", &acpi_s11_ec_tp_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("dvme", &acpi_s11_ec_dvme_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("brightness", &acpi_s11_ec_brightness_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("antenna", &acpi_s11_ec_antenna_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("w3g", &acpi_s11_ec_w3g_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
return 0;
}
static void lenovo_s11_ec_exit(void)
{
printk("Lenovo_s11 ec driver remove\n");
remove_proc_entry("camera", lenovo_ec_fs_dir);
remove_proc_entry("wifi", lenovo_ec_fs_dir);
remove_proc_entry("tp", lenovo_ec_fs_dir);
remove_proc_entry("dvme", lenovo_ec_fs_dir);
remove_proc_entry("brightness", lenovo_ec_fs_dir);
remove_proc_entry("antenna", lenovo_ec_fs_dir);
remove_proc_entry("w3g", lenovo_ec_fs_dir);
return;
}
static acpi_status
find_vpc0(acpi_handle handle, u32 lvl, void *context, void **rv)
{
char prefix[80] = {'\0'};
int *vpc;
struct acpi_buffer buffer = {sizeof(prefix), prefix };
vpc = context;
acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
if (strcmp(prefix,"VPC0") == 0){
vpc0_handle = handle;
*vpc = 1;
}
return AE_OK;
}
static int lenovo_s20_vpn_init(void)
{
int vpc0 = 0;
acpi_handle h_dummy1;
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, find_vpc0, &vpc0, NULL);
if (vpc0 == 1){
if (ACPI_SUCCESS(acpi_get_handle(vpc0_handle, "_CFG", &h_dummy1)))
return 0;
}
return -1;
}
static int lenovo_s20_ec_init(void)
{
int result;
printk("Lenovo s20 ec driver init\n");
result = lenovo_s20_vpn_init();
if(result == -1)
return -ENODEV;
result = lenovo_ec_proc_add("antenna",&acpi_s20_ec_antenna_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("w3g",&acpi_s20_ec_w3g_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("wifi",&acpi_s20_ec_wifi_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
return 0;
}
static void lenovo_s20_ec_exit(void)
{
printk("Lenovo_s20 ec driver remove\n");
remove_proc_entry("antenna", lenovo_ec_fs_dir);
remove_proc_entry("w3g", lenovo_ec_fs_dir);
remove_proc_entry("wifi", lenovo_ec_fs_dir);
}
static acpi_status
find_sb(acpi_handle handle, u32 lvl, void *context, void **rv)
{
char prefix[80] = {'\0'};
int *sb;
struct acpi_buffer buffer = {sizeof(prefix), prefix };
sb = context;
acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
if (strcmp(prefix,"_SB_") == 0){
lenovo_cs2_device.handle = handle;
*sb = 1;
}
return AE_OK;
}
static int lenovo_cs2_rootpath_init(void)
{
int sb = 0;
acpi_handle h_dummy1;
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, find_sb, &sb, NULL);
if (sb == 1){
if (ACPI_SUCCESS(acpi_get_handle(lenovo_cs2_device.handle, "DECN", &h_dummy1)))
lenovo_cs2_device.DECN = 1;
else
lenovo_cs2_device.DECN = 0;
if (ACPI_SUCCESS(acpi_get_handle(lenovo_cs2_device.handle, "SECN", &h_dummy1)))
lenovo_cs2_device.SECN = 1;
else
lenovo_cs2_device.SECN = 0;
if (ACPI_SUCCESS(acpi_get_handle(lenovo_cs2_device.handle, "GECN", &h_dummy1)))
lenovo_cs2_device.GECN = 1;
else
lenovo_cs2_device.GECN = 0;
return 0;
}
else{
lenovo_cs2_device.GECN = 0;
lenovo_cs2_device.SECN = 0;
lenovo_cs2_device.DECN = 0;
return -1;
}
}
static int lenovo_cs2_ec_init(void)
{
int result;
printk("Lenovo cs2 ec driver init\n");
result = lenovo_cs2_rootpath_init();
if(result == -1)
return -ENODEV;
result = lenovo_ec_proc_add("antenna",&acpi_cs2_ec_antenna_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("w3g",&acpi_cs2_ec_w3g_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("wifi",&acpi_cs2_ec_wifi_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("camera",&acpi_cs2_ec_camera_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("bluetooth",&acpi_cs2_ec_bluetooth_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
return 0;
}
static void lenovo_cs2_ec_exit(void)
{
printk("Lenovo_cs2 ec driver remove\n");
remove_proc_entry("antenna", lenovo_ec_fs_dir);
remove_proc_entry("w3g", lenovo_ec_fs_dir);
remove_proc_entry("wifi", lenovo_ec_fs_dir);
remove_proc_entry("camera", lenovo_ec_fs_dir);
}
static int lenovo_m3b_ec_init(void)
{
/* Use the acpi method interface same as Lenovo CS2*/
int result;
printk("Lenovo mariana-3b ec driver init\n");
result = lenovo_cs2_rootpath_init();
if(result == -1)
return -ENODEV;
result = lenovo_ec_proc_add("antenna",&acpi_cs2_ec_antenna_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("w3g",&acpi_cs2_ec_w3g_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("wifi",&acpi_cs2_ec_wifi_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("camera",&acpi_cs2_ec_camera_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
result = lenovo_ec_proc_add("bluetooth",&acpi_cs2_ec_bluetooth_ops,lenovo_ec_fs_dir);
if(result == -ENODEV)
return result;
return 0;
}
static void lenovo_m3b_ec_exit(void)
{
printk("Lenovo mariana-3b ec driver remove\n");
remove_proc_entry("antenna", lenovo_ec_fs_dir);
}
static int lenovo_ec_init(void)
{
lenovo_ec_fs_dir = proc_mkdir("lenovo",acpi_root_dir);
if (model != NULL){
if (strncmp(model, "S11", 3) == 0)
lenovo_s11_ec_init();
else if (strncmp(model, "S20", 3) == 0)
lenovo_s20_ec_init();
else if (strncmp(model, "CS2", 3) == 0)
lenovo_cs2_ec_init();
else if (strncmp(model, "M3B", 3) == 0)
lenovo_m3b_ec_init();
}
return 0;
}
static void lenovo_ec_exit(void)
{
if (model != NULL){
if (strncmp(model, "S11", 3) == 0)
lenovo_s11_ec_exit();
else if (strncmp(model, "S20", 3) == 0)
lenovo_s20_ec_exit();
else if (strncmp(model, "CS2", 3) == 0)
lenovo_cs2_ec_exit();
else if (strncmp(model, "M3B", 3) == 0)
lenovo_m3b_ec_exit();
}
remove_proc_entry("lenovo", acpi_root_dir);
}
module_init(lenovo_ec_init);
module_exit(lenovo_ec_exit);
^ permalink raw reply [flat|nested] 14+ messages in thread