Ideapad S10-3 rfkill issues with fix

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



[linux-acpi cc'd because I don't know how to write to an ACPI field,
and the fix to this bug probably needs that ability.]

Hi platform people-

My nephew's Ideapad S10-3 decided that it didn't want to support
wireless anymore.  I think it happened when he fiddled with his rfkill
switch, but I haven't been able to reproduce the problem yet.  This
seems to be a common bug, and the only known fixes (so far) are to
boot into Windows and fiddle with the settings or to remove the CMOS
battery.

This seems to be related to:
http://ubuntuforums.org/showthread.php?t=1744402
https://bugs.meego.com/show_bug.cgi?id=4086
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/730972

Of course, he doesn't have Windows installed, and I didn't want to
take apart his laptop.  So I figured out the problem.

The relevant ACPI fields and functions are (as far as I can tell):

\FL07: The non-volatile copy of the RF enabled flag
\_SB.PCI0.LPCB.EC0.WRFS: RF enabled flag
\GO26: The GPIO line that controls the PHY (not really tested at all)
\GO28: The RF light, inverted.  This doesn't work without manual
override on my S10-3 because I don't have bluetooth or 3G, AFAICT.

\_SB.PCI0.LPCB.EC0.DSLD: Load WRFS from FL07 (on startup)
\_SB.PCI0.LPCB.EC0.DSSV: Save WRFS to FL07, called from some hotkey
handler or other
\_SB.PCI0.LPCB.EC0.DSGO: Commit WRFS to hardware (i.e. \GO26).

Oddly enough, nothing in the DSDT seems to write WRFS or FL07 except
to copy FL07 to WRFS.  So triggering the problem might involve
toggling the switch from inside BIOS or something similar.

The fix would be (I think) to change ideapad_sync_rfk_state to do this
(in pseudocode):
if (!hw_blocked != WRFS) {
  WRFS = !hw_blocked;
  DSSV()
  DSGO()
}

I don't plan to send a patch for two reasons:

1. I don't know how to write to an ACPI field.  acpi_ns_evaluate can
read fields, but it can't write them AFAICT.  The ability to write to
a field would be really nice.  I did it by frobbing the ports by hand.

2. My nephew's taking his laptop home tomorrow, so I won't be able to
test a patch.

In the mean time, if you are affected by this problem and you really
don't care how badly you damage your laptop, you could run the
attached program, passing 1 as a parameter.  Then you should probably
reboot.  Before you even consider trying that, you should check
whether the magic hardcoded numbers match your DSDT.  If you don't
know how to do that, then you probably shouldn't run the program.

--Andy

P.S.  Some people with this bug find that their system freezes hard
when they try and fail to fix it.  I think this is a bug in brcm80211
in 2.6.38 that's been fixed in brcmsmac in 2.6.39.  But I don't really
feel like testing that carefully, given that I've already fixed the
root cause.
#!/usr/bin/env python
# Copyright (c) 2011 Andy Lutomirski
# Licensed under the GPL v2
#
# This program will probably eat your wireless card, set your laptop on
# fire, and otherwise cause great mayhem.  If you run it on anything other
# than the one particular Lenovo Ideapad S10-3 I tested it on, it is even
# more likely to destroy things.  So DO NOT RUN THIS PROGRAM.
#
# In the event you do run this program, you might want to either reboot
# or invoke \_SB.PCI0.LPCB.EC0.DSGO() and \_SB.PCI0.LPCB.EC0.DSSV()
# afterwards.

import portio
import struct
import sys
import os

print 'Do not run this program.  If you really want to run it,'
print 'type "I am a fool".  Otherwise press Ctrl-C.'
print 'If you type "I am a fool" then you agree not to hold the author of'
print 'this program responsible for anything that goes wrong.'

if raw_input() != "I am a fool":
    sys.exit(1)

if portio.ioperm(0x72, 0x2, 1):
    print >>sys.stderr, 'You must be root to use this.'
    sys.exit(1)

os.system('modprobe ec_sys')

ec_io = os.open('/sys/kernel/debug/ec/ec0/io', os.O_RDWR)

def read_ec(idx):
    os.lseek(ec_io, idx, os.SEEK_SET)
    return struct.unpack('B', os.read(ec_io, 1))[0]

def write_ec(idx, data):
    os.lseek(ec_io, idx, os.SEEK_SET)
    os.write(ec_io, struct.pack('B', data))

def read_exco(idx):
    portio.outb(idx, 0x72)
    return portio.inb(0x73)

def write_exco(idx, data):
    portio.outb(idx, 0x72)
    portio.outb(data, 0x73)

FL07_IDX = 0xA0
FL07_MASK = 0x80

WRFS_IDX = 0xBF
WRFS_MASK = 0x4

FL07_reg = read_exco(FL07_IDX)
WRFS_reg = read_ec(WRFS_IDX)

print 'FL07 = %d  WRFS = %d' % ((FL07_reg & FL07_MASK) != 0,
                                (WRFS_reg & WRFS_MASK) != 0)

if len(sys.argv) == 1:
    print 'Nothing changed'
elif len(sys.argv) == 2:
    if sys.argv[1] == '0':
        val = 0
    elif sys.argv[1] == '1':
        val = 1
    else:
        print >>sys.stderr, 'You can only write 0 or 1'

    write_ec(WRFS_IDX, WRFS_reg & (~WRFS_MASK) | (val * WRFS_MASK))
    write_exco(FL07_IDX, FL07_reg & (~FL07_MASK) | (val * FL07_MASK))

    print 'Done.'
else:
    print >>sys.stderr, 'Too many arguments'
    sys.exit(1)

sys.exit(0)

[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux