~ubuntu-branches/ubuntu/karmic/linux-mvl-dove/karmic-proposed

« back to all changes in this revision

Viewing changes to ubuntu/misc/fsam7400.c

  • Committer: Bazaar Package Importer
  • Author(s): Stefan Bader
  • Date: 2010-03-10 22:24:12 UTC
  • mto: (15.1.2 karmic-security)
  • mto: This revision was merged to the branch mainline in revision 18.
  • Revision ID: james.westby@ubuntu.com-20100310222412-k86m3r53jw0je7x1
Tags: upstream-2.6.31
ImportĀ upstreamĀ versionĀ 2.6.31

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*******************************************************************************
2
 
  
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) 
6
 
  any later version.
7
 
  
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 
11
 
  more details.
12
 
  
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.
16
 
  
17
 
  The full GNU General Public License is included in this distribution in the
18
 
  file called LICENSE.
19
 
  
20
 
  Author:
21
 
  Marcel Naziri <fsam7400@zwobbl.de>
22
 
    
23
 
  Based on:
24
 
  pbe5.c by Pedro Ramalhais <pmr09313@students.fct.unl.pt>
25
 
 
26
 
  Many thanks to:
27
 
  Pedro Ramalhais for spending several nights with me on IRC disassembling 
28
 
  the structure of the windows driver files... :)
29
 
  
30
 
*******************************************************************************/
31
 
 
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>
41
 
#include <linux/io.h>
42
 
 
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"
49
 
 
50
 
MODULE_DESCRIPTION(DRV_DESCRIPTION);
51
 
MODULE_AUTHOR(DRV_AUTHOR);
52
 
MODULE_LICENSE(DRV_LICENSE);
53
 
 
54
 
#define RADIO_NONE     0xFFFFFFFF
55
 
#define RADIO_OFF      0x00000000
56
 
#define RADIO_ON       0x00000010
57
 
 
58
 
static int radio = RADIO_NONE;
59
 
module_param(radio, uint, 0400);
60
 
MODULE_PARM_DESC(radio, "desired radio state when loading module");
61
 
 
62
 
static int autooff = 1;
63
 
module_param(autooff, uint, 0400);
64
 
MODULE_PARM_DESC(autooff, "turns radio off when unloading module "
65
 
                          "(default)");
66
 
 
67
 
static int uid = 0;
68
 
module_param(uid, uint, 0400);
69
 
MODULE_PARM_DESC(uid, "user ID for proc entry");                          
70
 
                           
71
 
static int gid = 0;
72
 
module_param(gid, uint, 0400);
73
 
MODULE_PARM_DESC(gid, "group ID for proc entry");                          
74
 
                                             
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) 
81
 
#else
82
 
#define DEBUG_OUT0(a)  
83
 
#define DEBUG_OUT1(a,b)  
84
 
#define DEBUG_OUT2(a,b,c)  
85
 
#define DEBUG_OUT3(a,b,c,d) 
86
 
#endif
87
 
 
88
 
#define ONOFF(x) (x) ? "ON" : "OFF"
89
 
#define RADIO_ONOFF(x) (x) == RADIO_ON ? "ON" : "OFF"
90
 
#define TOUL(x) (unsigned long) (x)
91
 
                       
92
 
/*
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.
96
 
 */
97
 
 
98
 
/*** hardware dependant stuff ***/
99
 
 
100
 
#define BIOS_CODE_ADDR       0x000F0000
101
 
#define BIOS_CODE_ALT_MASK   0xFFFFC000 
102
 
 
103
 
#define BIOS_CODE_MAPSIZE      0x010000
104
 
#define BIOS_CODE_ALT_MAPSIZE  0x004000
105
 
 
106
 
#define BIOS_MAGIC_COMMAND    0x9610 
107
 
#define BIOS_MAGIC_OFF        0x0035
108
 
#define BIOS_MAGIC_ON         0x0135
109
 
#define BIOS_MAGIC_CHECK      0x0235
110
 
 
111
 
#define PTR_POSITION  5
112
 
#define ALLIGNED_STEP 0x10
113
 
 
114
 
#define BIOS_SIGN_SIZE 4
115
 
static const char bios_sign[] = {
116
 
   0x42, 0x21, 0x55, 0x30
117
 
};
118
 
 
119
 
#define WLAN_DISABLED_IN_BIOS  0x01
120
 
#define WLAN_ENABLED_IN_BIOS   0x03
121
 
 
122
 
static unsigned long bios_code = 0;
123
 
 
124
 
static int fsam_bios_routine(int eax, int ebx)
125
 
{
126
 
   __asm__ __volatile__(
127
 
      "call *%3 \t\n"
128
 
      : "=a"(eax)
129
 
      : "a"(eax), "b"(ebx), "c"(bios_code)           
130
 
      ); 
131
 
   return (eax & 0xFF);
132
 
}
133
 
 
134
 
static int fsam_call_bios(int value)
135
 
{
136
 
   if (bios_code) {
137
 
      int command = BIOS_MAGIC_COMMAND;
138
 
      
139
 
      DEBUG_OUT2("bios routine gets parameter eax=%X and ebx=%X\n",
140
 
                  command, value);
141
 
                  
142
 
      value = fsam_bios_routine(command, value);                    
143
 
         
144
 
      DEBUG_OUT1("bios routine results %X\n", value);
145
 
      return value;   
146
 
   }
147
 
   return ~0;
148
 
}
149
 
 
150
 
/* pointer to mapped memory*/
151
 
static void *mem_code = NULL;
152
 
 
153
 
static inline void fsam_unmap_memory(void)
154
 
{
155
 
   bios_code = 0;   
156
 
   if (mem_code) {
157
 
      iounmap(mem_code);
158
 
   }      
159
 
}
160
 
 
161
 
static inline int fsam_map_memory(void)
162
 
{   
163
 
   const unsigned long max_offset = BIOS_CODE_MAPSIZE - BIOS_SIGN_SIZE - PTR_POSITION;
164
 
   unsigned long offset;   
165
 
   unsigned int addr;
166
 
   
167
 
   mem_code = ioremap(BIOS_CODE_ADDR, BIOS_CODE_MAPSIZE);
168
 
   if (!mem_code)
169
 
      goto fail;
170
 
 
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); 
173
 
   
174
 
   for ( offset = 0; offset < max_offset; offset += ALLIGNED_STEP ) 
175
 
      if (check_signature(mem_code + offset, bios_sign, BIOS_SIGN_SIZE))
176
 
         break;
177
 
   
178
 
   if (offset >= max_offset) 
179
 
     goto fail; 
180
 
   
181
 
   DEBUG_OUT1("bios signature found at offset %lx\n", offset);   
182
 
   
183
 
   addr = readl(mem_code + offset + PTR_POSITION);  
184
 
            
185
 
   if (addr < BIOS_CODE_ADDR)
186
 
   {      
187
 
      DEBUG_OUT0("bios routine out of memory range, "
188
 
                 "doing some new memory mapping...\n");
189
 
      iounmap(mem_code);
190
 
      mem_code = NULL;
191
 
      
192
 
      addr &= BIOS_CODE_ALT_MASK;
193
 
           
194
 
      mem_code = ioremap(addr, BIOS_CODE_ALT_MAPSIZE);
195
 
      if (!mem_code)
196
 
         goto fail;
197
 
      
198
 
      DEBUG_OUT3("physical memory %x-%x mapped to virtual address %p\n",
199
 
                 addr, addr+BIOS_CODE_ALT_MAPSIZE, mem_code);
200
 
            
201
 
      addr &= 0x3FFF;           
202
 
   } 
203
 
   else 
204
 
     addr &= 0xFFFF;
205
 
      
206
 
   bios_code = addr + TOUL(mem_code);
207
 
   DEBUG_OUT1("supposed address of bios routine is %lx\n", bios_code);
208
 
 
209
 
   return 1;        
210
 
 
211
 
 fail:
212
 
   fsam_unmap_memory();
213
 
   return 0;
214
 
}
215
 
 
216
 
/*** interface stuff ***/
217
 
 
218
 
static void rfkill_set_radio(int value) 
219
 
{
220
 
   radio = value == RADIO_ON ? fsam_call_bios(BIOS_MAGIC_ON) :
221
 
                               fsam_call_bios(BIOS_MAGIC_OFF);
222
 
}
223
 
 
224
 
static inline int rfkill_get_radio(void)
225
 
{
226
 
   return radio;
227
 
}
228
 
 
229
 
static inline int rfkill_supported(void)
230
 
{
231
 
   return bios_code != 0;
232
 
}
233
 
 
234
 
static inline void rfkill_initialize(void) {
235
 
   fsam_map_memory();
236
 
   
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 );  
242
 
   }
243
 
}
244
 
 
245
 
static inline void rfkill_uninitialize(void) {
246
 
   fsam_unmap_memory();
247
 
}
248
 
 
249
 
/*** proc stuff ***/
250
 
 
251
 
static inline int common_proc_set_radio(struct file *file, const char *buffer, 
252
 
                                        unsigned long count, void *data)
253
 
{
254
 
   unsigned long len = 4;
255
 
   char newstate[len];
256
 
   
257
 
   len = count < len ? count : len;
258
 
 
259
 
   if ( copy_from_user(newstate, buffer, len) != 0 )
260
 
     return -EFAULT;
261
 
       
262
 
   if ( (*newstate == '1' || *newstate == '0') &&
263
 
        (count == 1 || isspace(newstate[1])) )
264
 
     rfkill_set_radio(*newstate == '1' ? RADIO_ON : RADIO_OFF);
265
 
   else 
266
 
   if ( !strncmp(newstate, "on", 2)  &&
267
 
        (count == 2 || isspace(newstate[2])) )
268
 
     rfkill_set_radio(RADIO_ON);
269
 
   else 
270
 
   if ( !strncmp(newstate, "off", 3) &&
271
 
        (count == 3 || isspace(newstate[3])) ) 
272
 
     rfkill_set_radio(RADIO_OFF);  
273
 
   
274
 
   return count;
275
 
}
276
 
 
277
 
static inline int common_proc_get_radio(char *page, char **start, off_t offset,
278
 
                                        int count, int *eof, void *data)
279
 
{
280
 
   int len = snprintf(page, count, DRV_DESCRIPTION ", v" DRV_VERSION "\n"
281
 
                                   "  auto-off is %s\n", 
282
 
                                   ONOFF(autooff));
283
 
   len += snprintf(page+len, count-len, "  radio state is %s\n",
284
 
                                        RADIO_ONOFF(rfkill_get_radio()));   
285
 
   *eof = 1;
286
 
   
287
 
   return len;
288
 
}
289
 
 
290
 
#define PROC_DIR    "driver/wireless"
291
 
#define PROC_RADIO  "radio"
292
 
 
293
 
static struct proc_dir_entry *dir_base = NULL;
294
 
 
295
 
static inline void common_proc_cleanup(void)
296
 
{
297
 
   if (dir_base) {
298
 
      remove_proc_entry(PROC_RADIO, dir_base);
299
 
      remove_proc_entry(PROC_DIR, NULL);
300
 
      dir_base = NULL;
301
 
   }
302
 
}
303
 
 
304
 
static inline int common_proc_init(void)
305
 
{
306
 
   struct proc_dir_entry *ent;
307
 
   int err = 0;
308
 
 
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");
312
 
      err = -ENOMEM;
313
 
      goto fail;
314
 
   }
315
 
 
316
 
   ent = create_proc_entry(PROC_RADIO, 
317
 
                           S_IFREG | S_IRUGO | S_IWUSR | S_IWGRP, 
318
 
                           dir_base);
319
 
   ent->uid = uid;
320
 
   ent->gid = gid;
321
 
   if (ent) {
322
 
      ent->read_proc = common_proc_get_radio; 
323
 
      ent->write_proc = common_proc_set_radio; 
324
 
   } else {
325
 
      printk(KERN_ERR DRV_NAME ": Unable to initialize /proc/" 
326
 
                      PROC_DIR "/" PROC_RADIO "\n");
327
 
      err = -ENOMEM;
328
 
      goto fail;
329
 
   }
330
 
   return 0;
331
 
 
332
 
 fail:
333
 
   common_proc_cleanup();
334
 
   return err;
335
 
}
336
 
 
337
 
/*** module stuff ***/
338
 
 
339
 
static int __init common_init(void)
340
 
341
 
   printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", v" DRV_VERSION "\n");
342
 
   printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
343
 
   
344
 
   rfkill_initialize();
345
 
   
346
 
   if (rfkill_supported()) {
347
 
      common_proc_init();   
348
 
      if (radio != RADIO_NONE) 
349
 
         rfkill_set_radio(radio);
350
 
   } else
351
 
      printk(KERN_INFO DRV_NAME ": no supported wireless hardware found\n");   
352
 
   
353
 
   return 0;
354
 
}
355
 
 
356
 
static void __exit common_exit(void)
357
 
{
358
 
   if (rfkill_supported() && autooff) 
359
 
      rfkill_set_radio(RADIO_OFF);
360
 
   
361
 
   common_proc_cleanup();
362
 
   rfkill_uninitialize();   
363
 
   
364
 
   printk(KERN_INFO DRV_NAME ": module removed successfully\n");
365
 
}
366
 
 
367
 
module_init(common_init);
368
 
module_exit(common_exit);
369
 
 
370
 
/*
371
 
         1         2         3         4         5         6         7
372
 
12345678901234567890123456789012345678901234567890123456789012345678901234567890
373
 
*/