[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)