1
/*******************************************************************************
3
This program is free software; you can redistribute it and/or modify it
4
under the terms of the GNU General Public License as published by the Free
5
Software Foundation; either version 2 of the License, or (at your option)
8
This program is distributed in the hope that it will be useful, but WITHOUT
9
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13
You should have received a copy of the GNU General Public License along with
14
this program; if not, write to the Free Software Foundation, Inc., 59
15
Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
The full GNU General Public License is included in this distribution in the
21
Marcel Naziri <fsam7400@zwobbl.de>
24
pbe5.c by Pedro Ramalhais <pmr09313@students.fct.unl.pt>
27
Pedro Ramalhais for spending several nights with me on IRC disassembling
28
the structure of the windows driver files... :)
30
*******************************************************************************/
32
#include <linux/compiler.h>
33
#include <linux/kernel.h>
34
#include <linux/module.h>
35
#include <linux/types.h>
36
#include <linux/netdevice.h>
37
#include <linux/proc_fs.h>
38
#include <linux/ctype.h>
39
#include <linux/string.h>
40
#include <linux/kmod.h>
43
#define DRV_NAME "fsam7400"
44
#define DRV_VERSION "0.4.0"
45
#define DRV_DESCRIPTION "SW RF kill switch for Fujitsu Siemens Amilo M 7400"
46
#define DRV_COPYRIGHT "Copyright(c) 2004 zwobbl ;)"
47
#define DRV_AUTHOR "Marcel Naziri"
48
#define DRV_LICENSE "GPL"
50
MODULE_DESCRIPTION(DRV_DESCRIPTION);
51
MODULE_AUTHOR(DRV_AUTHOR);
52
MODULE_LICENSE(DRV_LICENSE);
54
#define RADIO_NONE 0xFFFFFFFF
55
#define RADIO_OFF 0x00000000
56
#define RADIO_ON 0x00000010
58
static int radio = RADIO_NONE;
59
module_param(radio, uint, 0400);
60
MODULE_PARM_DESC(radio, "desired radio state when loading module");
62
static int autooff = 1;
63
module_param(autooff, uint, 0400);
64
MODULE_PARM_DESC(autooff, "turns radio off when unloading module "
68
module_param(uid, uint, 0400);
69
MODULE_PARM_DESC(uid, "user ID for proc entry");
72
module_param(gid, uint, 0400);
73
MODULE_PARM_DESC(gid, "group ID for proc entry");
75
/* some more or less useful macros */
76
#ifdef CONFIG_IPW2100_DEBUG
77
#define DEBUG_OUT0(a) printk(KERN_INFO DRV_NAME ": " a)
78
#define DEBUG_OUT1(a,b) printk(KERN_INFO DRV_NAME ": " a,b)
79
#define DEBUG_OUT2(a,b,c) printk(KERN_INFO DRV_NAME ": " a,b,c)
80
#define DEBUG_OUT3(a,b,c,d) printk(KERN_INFO DRV_NAME ": " a,b,c,d)
83
#define DEBUG_OUT1(a,b)
84
#define DEBUG_OUT2(a,b,c)
85
#define DEBUG_OUT3(a,b,c,d)
88
#define ONOFF(x) (x) ? "ON" : "OFF"
89
#define RADIO_ONOFF(x) (x) == RADIO_ON ? "ON" : "OFF"
90
#define TOUL(x) (unsigned long) (x)
93
* NOTE: These values were obtained from disassembling the wbutton.sys driver
94
* installed in the Fujitsu Siemens Amilo M 7400 laptop. The names were guessed,
95
* so don't rely on them.
98
/*** hardware dependant stuff ***/
100
#define BIOS_CODE_ADDR 0x000F0000
101
#define BIOS_CODE_ALT_MASK 0xFFFFC000
103
#define BIOS_CODE_MAPSIZE 0x010000
104
#define BIOS_CODE_ALT_MAPSIZE 0x004000
106
#define BIOS_MAGIC_COMMAND 0x9610
107
#define BIOS_MAGIC_OFF 0x0035
108
#define BIOS_MAGIC_ON 0x0135
109
#define BIOS_MAGIC_CHECK 0x0235
111
#define PTR_POSITION 5
112
#define ALLIGNED_STEP 0x10
114
#define BIOS_SIGN_SIZE 4
115
static const char bios_sign[] = {
116
0x42, 0x21, 0x55, 0x30
119
#define WLAN_DISABLED_IN_BIOS 0x01
120
#define WLAN_ENABLED_IN_BIOS 0x03
122
static unsigned long bios_code = 0;
124
static int fsam_bios_routine(int eax, int ebx)
126
__asm__ __volatile__(
129
: "a"(eax), "b"(ebx), "c"(bios_code)
134
static int fsam_call_bios(int value)
137
int command = BIOS_MAGIC_COMMAND;
139
DEBUG_OUT2("bios routine gets parameter eax=%X and ebx=%X\n",
142
value = fsam_bios_routine(command, value);
144
DEBUG_OUT1("bios routine results %X\n", value);
150
/* pointer to mapped memory*/
151
static void *mem_code = NULL;
153
static inline void fsam_unmap_memory(void)
161
static inline int fsam_map_memory(void)
163
const unsigned long max_offset = BIOS_CODE_MAPSIZE - BIOS_SIGN_SIZE - PTR_POSITION;
164
unsigned long offset;
167
mem_code = ioremap(BIOS_CODE_ADDR, BIOS_CODE_MAPSIZE);
171
DEBUG_OUT3("physical memory %x-%x mapped to virtual address %p\n",
172
BIOS_CODE_ADDR, BIOS_CODE_ADDR+BIOS_CODE_MAPSIZE, mem_code);
174
for ( offset = 0; offset < max_offset; offset += ALLIGNED_STEP )
175
if (check_signature(mem_code + offset, bios_sign, BIOS_SIGN_SIZE))
178
if (offset >= max_offset)
181
DEBUG_OUT1("bios signature found at offset %lx\n", offset);
183
addr = readl(mem_code + offset + PTR_POSITION);
185
if (addr < BIOS_CODE_ADDR)
187
DEBUG_OUT0("bios routine out of memory range, "
188
"doing some new memory mapping...\n");
192
addr &= BIOS_CODE_ALT_MASK;
194
mem_code = ioremap(addr, BIOS_CODE_ALT_MAPSIZE);
198
DEBUG_OUT3("physical memory %x-%x mapped to virtual address %p\n",
199
addr, addr+BIOS_CODE_ALT_MAPSIZE, mem_code);
206
bios_code = addr + TOUL(mem_code);
207
DEBUG_OUT1("supposed address of bios routine is %lx\n", bios_code);
216
/*** interface stuff ***/
218
static void rfkill_set_radio(int value)
220
radio = value == RADIO_ON ? fsam_call_bios(BIOS_MAGIC_ON) :
221
fsam_call_bios(BIOS_MAGIC_OFF);
224
static inline int rfkill_get_radio(void)
229
static inline int rfkill_supported(void)
231
return bios_code != 0;
234
static inline void rfkill_initialize(void) {
237
if (rfkill_supported()) {
238
radio = radio != RADIO_NONE
239
? ( radio ? RADIO_ON : RADIO_OFF ) /*module parameter*/
240
: ( fsam_call_bios(BIOS_MAGIC_CHECK) == WLAN_ENABLED_IN_BIOS
241
? RADIO_ON : RADIO_OFF );
245
static inline void rfkill_uninitialize(void) {
251
static inline int common_proc_set_radio(struct file *file, const char *buffer,
252
unsigned long count, void *data)
254
unsigned long len = 4;
257
len = count < len ? count : len;
259
if ( copy_from_user(newstate, buffer, len) != 0 )
262
if ( (*newstate == '1' || *newstate == '0') &&
263
(count == 1 || isspace(newstate[1])) )
264
rfkill_set_radio(*newstate == '1' ? RADIO_ON : RADIO_OFF);
266
if ( !strncmp(newstate, "on", 2) &&
267
(count == 2 || isspace(newstate[2])) )
268
rfkill_set_radio(RADIO_ON);
270
if ( !strncmp(newstate, "off", 3) &&
271
(count == 3 || isspace(newstate[3])) )
272
rfkill_set_radio(RADIO_OFF);
277
static inline int common_proc_get_radio(char *page, char **start, off_t offset,
278
int count, int *eof, void *data)
280
int len = snprintf(page, count, DRV_DESCRIPTION ", v" DRV_VERSION "\n"
283
len += snprintf(page+len, count-len, " radio state is %s\n",
284
RADIO_ONOFF(rfkill_get_radio()));
290
#define PROC_DIR "driver/wireless"
291
#define PROC_RADIO "radio"
293
static struct proc_dir_entry *dir_base = NULL;
295
static inline void common_proc_cleanup(void)
298
remove_proc_entry(PROC_RADIO, dir_base);
299
remove_proc_entry(PROC_DIR, NULL);
304
static inline int common_proc_init(void)
306
struct proc_dir_entry *ent;
309
dir_base = proc_mkdir(PROC_DIR, NULL);
310
if (dir_base == NULL) {
311
printk(KERN_ERR DRV_NAME ": Unable to initialize /proc/" PROC_DIR "\n");
316
ent = create_proc_entry(PROC_RADIO,
317
S_IFREG | S_IRUGO | S_IWUSR | S_IWGRP,
322
ent->read_proc = common_proc_get_radio;
323
ent->write_proc = common_proc_set_radio;
325
printk(KERN_ERR DRV_NAME ": Unable to initialize /proc/"
326
PROC_DIR "/" PROC_RADIO "\n");
333
common_proc_cleanup();
337
/*** module stuff ***/
339
static int __init common_init(void)
341
printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", v" DRV_VERSION "\n");
342
printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
346
if (rfkill_supported()) {
348
if (radio != RADIO_NONE)
349
rfkill_set_radio(radio);
351
printk(KERN_INFO DRV_NAME ": no supported wireless hardware found\n");
356
static void __exit common_exit(void)
358
if (rfkill_supported() && autooff)
359
rfkill_set_radio(RADIO_OFF);
361
common_proc_cleanup();
362
rfkill_uninitialize();
364
printk(KERN_INFO DRV_NAME ": module removed successfully\n");
367
module_init(common_init);
368
module_exit(common_exit);
372
12345678901234567890123456789012345678901234567890123456789012345678901234567890