diff options
Diffstat (limited to 'drivers/net/wireless/orinoco/hermes.c')
-rw-r--r-- | drivers/net/wireless/orinoco/hermes.c | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/drivers/net/wireless/orinoco/hermes.c b/drivers/net/wireless/orinoco/hermes.c new file mode 100644 index 000000000000..bfa375369df3 --- /dev/null +++ b/drivers/net/wireless/orinoco/hermes.c | |||
@@ -0,0 +1,544 @@ | |||
1 | /* hermes.c | ||
2 | * | ||
3 | * Driver core for the "Hermes" wireless MAC controller, as used in | ||
4 | * the Lucent Orinoco and Cabletron RoamAbout cards. It should also | ||
5 | * work on the hfa3841 and hfa3842 MAC controller chips used in the | ||
6 | * Prism II chipsets. | ||
7 | * | ||
8 | * This is not a complete driver, just low-level access routines for | ||
9 | * the MAC controller itself. | ||
10 | * | ||
11 | * Based on the prism2 driver from Absolute Value Systems' linux-wlan | ||
12 | * project, the Linux wvlan_cs driver, Lucent's HCF-Light | ||
13 | * (wvlan_hcf.c) library, and the NetBSD wireless driver (in no | ||
14 | * particular order). | ||
15 | * | ||
16 | * Copyright (C) 2000, David Gibson, Linuxcare Australia. | ||
17 | * (C) Copyright David Gibson, IBM Corp. 2001-2003. | ||
18 | * | ||
19 | * The contents of this file are subject to the Mozilla Public License | ||
20 | * Version 1.1 (the "License"); you may not use this file except in | ||
21 | * compliance with the License. You may obtain a copy of the License | ||
22 | * at http://www.mozilla.org/MPL/ | ||
23 | * | ||
24 | * Software distributed under the License is distributed on an "AS IS" | ||
25 | * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See | ||
26 | * the License for the specific language governing rights and | ||
27 | * limitations under the License. | ||
28 | * | ||
29 | * Alternatively, the contents of this file may be used under the | ||
30 | * terms of the GNU General Public License version 2 (the "GPL"), in | ||
31 | * which case the provisions of the GPL are applicable instead of the | ||
32 | * above. If you wish to allow the use of your version of this file | ||
33 | * only under the terms of the GPL and not to allow others to use your | ||
34 | * version of this file under the MPL, indicate your decision by | ||
35 | * deleting the provisions above and replace them with the notice and | ||
36 | * other provisions required by the GPL. If you do not delete the | ||
37 | * provisions above, a recipient may use your version of this file | ||
38 | * under either the MPL or the GPL. | ||
39 | */ | ||
40 | |||
41 | #include <linux/module.h> | ||
42 | #include <linux/kernel.h> | ||
43 | #include <linux/init.h> | ||
44 | #include <linux/delay.h> | ||
45 | |||
46 | #include "hermes.h" | ||
47 | |||
48 | MODULE_DESCRIPTION("Low-level driver helper for Lucent Hermes chipset and Prism II HFA384x wireless MAC controller"); | ||
49 | MODULE_AUTHOR("Pavel Roskin <proski@gnu.org>" | ||
50 | " & David Gibson <hermes@gibson.dropbear.id.au>"); | ||
51 | MODULE_LICENSE("Dual MPL/GPL"); | ||
52 | |||
53 | /* These are maximum timeouts. Most often, card wil react much faster */ | ||
54 | #define CMD_BUSY_TIMEOUT (100) /* In iterations of ~1us */ | ||
55 | #define CMD_INIT_TIMEOUT (50000) /* in iterations of ~10us */ | ||
56 | #define CMD_COMPL_TIMEOUT (20000) /* in iterations of ~10us */ | ||
57 | #define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */ | ||
58 | |||
59 | /* | ||
60 | * Debugging helpers | ||
61 | */ | ||
62 | |||
63 | #define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ %p: " , hw->iobase); \ | ||
64 | printk(stuff);} while (0) | ||
65 | |||
66 | #undef HERMES_DEBUG | ||
67 | #ifdef HERMES_DEBUG | ||
68 | #include <stdarg.h> | ||
69 | |||
70 | #define DEBUG(lvl, stuff...) if ( (lvl) <= HERMES_DEBUG) DMSG(stuff) | ||
71 | |||
72 | #else /* ! HERMES_DEBUG */ | ||
73 | |||
74 | #define DEBUG(lvl, stuff...) do { } while (0) | ||
75 | |||
76 | #endif /* ! HERMES_DEBUG */ | ||
77 | |||
78 | |||
79 | /* | ||
80 | * Internal functions | ||
81 | */ | ||
82 | |||
83 | /* Issue a command to the chip. Waiting for it to complete is the caller's | ||
84 | problem. | ||
85 | |||
86 | Returns -EBUSY if the command register is busy, 0 on success. | ||
87 | |||
88 | Callable from any context. | ||
89 | */ | ||
90 | static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0, | ||
91 | u16 param1, u16 param2) | ||
92 | { | ||
93 | int k = CMD_BUSY_TIMEOUT; | ||
94 | u16 reg; | ||
95 | |||
96 | /* First wait for the command register to unbusy */ | ||
97 | reg = hermes_read_regn(hw, CMD); | ||
98 | while ( (reg & HERMES_CMD_BUSY) && k ) { | ||
99 | k--; | ||
100 | udelay(1); | ||
101 | reg = hermes_read_regn(hw, CMD); | ||
102 | } | ||
103 | if (reg & HERMES_CMD_BUSY) { | ||
104 | return -EBUSY; | ||
105 | } | ||
106 | |||
107 | hermes_write_regn(hw, PARAM2, param2); | ||
108 | hermes_write_regn(hw, PARAM1, param1); | ||
109 | hermes_write_regn(hw, PARAM0, param0); | ||
110 | hermes_write_regn(hw, CMD, cmd); | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * Function definitions | ||
117 | */ | ||
118 | |||
119 | /* For doing cmds that wipe the magic constant in SWSUPPORT0 */ | ||
120 | int hermes_doicmd_wait(hermes_t *hw, u16 cmd, | ||
121 | u16 parm0, u16 parm1, u16 parm2, | ||
122 | struct hermes_response *resp) | ||
123 | { | ||
124 | int err = 0; | ||
125 | int k; | ||
126 | u16 status, reg; | ||
127 | |||
128 | err = hermes_issue_cmd(hw, cmd, parm0, parm1, parm2); | ||
129 | if (err) | ||
130 | return err; | ||
131 | |||
132 | reg = hermes_read_regn(hw, EVSTAT); | ||
133 | k = CMD_INIT_TIMEOUT; | ||
134 | while ((!(reg & HERMES_EV_CMD)) && k) { | ||
135 | k--; | ||
136 | udelay(10); | ||
137 | reg = hermes_read_regn(hw, EVSTAT); | ||
138 | } | ||
139 | |||
140 | hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC); | ||
141 | |||
142 | if (!hermes_present(hw)) { | ||
143 | DEBUG(0, "hermes @ 0x%x: Card removed during reset.\n", | ||
144 | hw->iobase); | ||
145 | err = -ENODEV; | ||
146 | goto out; | ||
147 | } | ||
148 | |||
149 | if (!(reg & HERMES_EV_CMD)) { | ||
150 | printk(KERN_ERR "hermes @ %p: " | ||
151 | "Timeout waiting for card to reset (reg=0x%04x)!\n", | ||
152 | hw->iobase, reg); | ||
153 | err = -ETIMEDOUT; | ||
154 | goto out; | ||
155 | } | ||
156 | |||
157 | status = hermes_read_regn(hw, STATUS); | ||
158 | if (resp) { | ||
159 | resp->status = status; | ||
160 | resp->resp0 = hermes_read_regn(hw, RESP0); | ||
161 | resp->resp1 = hermes_read_regn(hw, RESP1); | ||
162 | resp->resp2 = hermes_read_regn(hw, RESP2); | ||
163 | } | ||
164 | |||
165 | hermes_write_regn(hw, EVACK, HERMES_EV_CMD); | ||
166 | |||
167 | if (status & HERMES_STATUS_RESULT) | ||
168 | err = -EIO; | ||
169 | out: | ||
170 | return err; | ||
171 | } | ||
172 | EXPORT_SYMBOL(hermes_doicmd_wait); | ||
173 | |||
174 | void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing) | ||
175 | { | ||
176 | hw->iobase = address; | ||
177 | hw->reg_spacing = reg_spacing; | ||
178 | hw->inten = 0x0; | ||
179 | } | ||
180 | EXPORT_SYMBOL(hermes_struct_init); | ||
181 | |||
182 | int hermes_init(hermes_t *hw) | ||
183 | { | ||
184 | u16 reg; | ||
185 | int err = 0; | ||
186 | int k; | ||
187 | |||
188 | /* We don't want to be interrupted while resetting the chipset */ | ||
189 | hw->inten = 0x0; | ||
190 | hermes_write_regn(hw, INTEN, 0); | ||
191 | hermes_write_regn(hw, EVACK, 0xffff); | ||
192 | |||
193 | /* Normally it's a "can't happen" for the command register to | ||
194 | be busy when we go to issue a command because we are | ||
195 | serializing all commands. However we want to have some | ||
196 | chance of resetting the card even if it gets into a stupid | ||
197 | state, so we actually wait to see if the command register | ||
198 | will unbusy itself here. */ | ||
199 | k = CMD_BUSY_TIMEOUT; | ||
200 | reg = hermes_read_regn(hw, CMD); | ||
201 | while (k && (reg & HERMES_CMD_BUSY)) { | ||
202 | if (reg == 0xffff) /* Special case - the card has probably been removed, | ||
203 | so don't wait for the timeout */ | ||
204 | return -ENODEV; | ||
205 | |||
206 | k--; | ||
207 | udelay(1); | ||
208 | reg = hermes_read_regn(hw, CMD); | ||
209 | } | ||
210 | |||
211 | /* No need to explicitly handle the timeout - if we've timed | ||
212 | out hermes_issue_cmd() will probably return -EBUSY below */ | ||
213 | |||
214 | /* According to the documentation, EVSTAT may contain | ||
215 | obsolete event occurrence information. We have to acknowledge | ||
216 | it by writing EVACK. */ | ||
217 | reg = hermes_read_regn(hw, EVSTAT); | ||
218 | hermes_write_regn(hw, EVACK, reg); | ||
219 | |||
220 | /* We don't use hermes_docmd_wait here, because the reset wipes | ||
221 | the magic constant in SWSUPPORT0 away, and it gets confused */ | ||
222 | err = hermes_doicmd_wait(hw, HERMES_CMD_INIT, 0, 0, 0, NULL); | ||
223 | |||
224 | return err; | ||
225 | } | ||
226 | EXPORT_SYMBOL(hermes_init); | ||
227 | |||
228 | /* Issue a command to the chip, and (busy!) wait for it to | ||
229 | * complete. | ||
230 | * | ||
231 | * Returns: < 0 on internal error, 0 on success, > 0 on error returned by the firmware | ||
232 | * | ||
233 | * Callable from any context, but locking is your problem. */ | ||
234 | int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, | ||
235 | struct hermes_response *resp) | ||
236 | { | ||
237 | int err; | ||
238 | int k; | ||
239 | u16 reg; | ||
240 | u16 status; | ||
241 | |||
242 | err = hermes_issue_cmd(hw, cmd, parm0, 0, 0); | ||
243 | if (err) { | ||
244 | if (! hermes_present(hw)) { | ||
245 | if (net_ratelimit()) | ||
246 | printk(KERN_WARNING "hermes @ %p: " | ||
247 | "Card removed while issuing command " | ||
248 | "0x%04x.\n", hw->iobase, cmd); | ||
249 | err = -ENODEV; | ||
250 | } else | ||
251 | if (net_ratelimit()) | ||
252 | printk(KERN_ERR "hermes @ %p: " | ||
253 | "Error %d issuing command 0x%04x.\n", | ||
254 | hw->iobase, err, cmd); | ||
255 | goto out; | ||
256 | } | ||
257 | |||
258 | reg = hermes_read_regn(hw, EVSTAT); | ||
259 | k = CMD_COMPL_TIMEOUT; | ||
260 | while ( (! (reg & HERMES_EV_CMD)) && k) { | ||
261 | k--; | ||
262 | udelay(10); | ||
263 | reg = hermes_read_regn(hw, EVSTAT); | ||
264 | } | ||
265 | |||
266 | if (! hermes_present(hw)) { | ||
267 | printk(KERN_WARNING "hermes @ %p: Card removed " | ||
268 | "while waiting for command 0x%04x completion.\n", | ||
269 | hw->iobase, cmd); | ||
270 | err = -ENODEV; | ||
271 | goto out; | ||
272 | } | ||
273 | |||
274 | if (! (reg & HERMES_EV_CMD)) { | ||
275 | printk(KERN_ERR "hermes @ %p: Timeout waiting for " | ||
276 | "command 0x%04x completion.\n", hw->iobase, cmd); | ||
277 | err = -ETIMEDOUT; | ||
278 | goto out; | ||
279 | } | ||
280 | |||
281 | status = hermes_read_regn(hw, STATUS); | ||
282 | if (resp) { | ||
283 | resp->status = status; | ||
284 | resp->resp0 = hermes_read_regn(hw, RESP0); | ||
285 | resp->resp1 = hermes_read_regn(hw, RESP1); | ||
286 | resp->resp2 = hermes_read_regn(hw, RESP2); | ||
287 | } | ||
288 | |||
289 | hermes_write_regn(hw, EVACK, HERMES_EV_CMD); | ||
290 | |||
291 | if (status & HERMES_STATUS_RESULT) | ||
292 | err = -EIO; | ||
293 | |||
294 | out: | ||
295 | return err; | ||
296 | } | ||
297 | EXPORT_SYMBOL(hermes_docmd_wait); | ||
298 | |||
299 | int hermes_allocate(hermes_t *hw, u16 size, u16 *fid) | ||
300 | { | ||
301 | int err = 0; | ||
302 | int k; | ||
303 | u16 reg; | ||
304 | |||
305 | if ( (size < HERMES_ALLOC_LEN_MIN) || (size > HERMES_ALLOC_LEN_MAX) ) | ||
306 | return -EINVAL; | ||
307 | |||
308 | err = hermes_docmd_wait(hw, HERMES_CMD_ALLOC, size, NULL); | ||
309 | if (err) { | ||
310 | return err; | ||
311 | } | ||
312 | |||
313 | reg = hermes_read_regn(hw, EVSTAT); | ||
314 | k = ALLOC_COMPL_TIMEOUT; | ||
315 | while ( (! (reg & HERMES_EV_ALLOC)) && k) { | ||
316 | k--; | ||
317 | udelay(10); | ||
318 | reg = hermes_read_regn(hw, EVSTAT); | ||
319 | } | ||
320 | |||
321 | if (! hermes_present(hw)) { | ||
322 | printk(KERN_WARNING "hermes @ %p: " | ||
323 | "Card removed waiting for frame allocation.\n", | ||
324 | hw->iobase); | ||
325 | return -ENODEV; | ||
326 | } | ||
327 | |||
328 | if (! (reg & HERMES_EV_ALLOC)) { | ||
329 | printk(KERN_ERR "hermes @ %p: " | ||
330 | "Timeout waiting for frame allocation\n", | ||
331 | hw->iobase); | ||
332 | return -ETIMEDOUT; | ||
333 | } | ||
334 | |||
335 | *fid = hermes_read_regn(hw, ALLOCFID); | ||
336 | hermes_write_regn(hw, EVACK, HERMES_EV_ALLOC); | ||
337 | |||
338 | return 0; | ||
339 | } | ||
340 | EXPORT_SYMBOL(hermes_allocate); | ||
341 | |||
342 | /* Set up a BAP to read a particular chunk of data from card's internal buffer. | ||
343 | * | ||
344 | * Returns: < 0 on internal failure (errno), 0 on success, >0 on error | ||
345 | * from firmware | ||
346 | * | ||
347 | * Callable from any context */ | ||
348 | static int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset) | ||
349 | { | ||
350 | int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0; | ||
351 | int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0; | ||
352 | int k; | ||
353 | u16 reg; | ||
354 | |||
355 | /* Paranoia.. */ | ||
356 | if ( (offset > HERMES_BAP_OFFSET_MAX) || (offset % 2) ) | ||
357 | return -EINVAL; | ||
358 | |||
359 | k = HERMES_BAP_BUSY_TIMEOUT; | ||
360 | reg = hermes_read_reg(hw, oreg); | ||
361 | while ((reg & HERMES_OFFSET_BUSY) && k) { | ||
362 | k--; | ||
363 | udelay(1); | ||
364 | reg = hermes_read_reg(hw, oreg); | ||
365 | } | ||
366 | |||
367 | if (reg & HERMES_OFFSET_BUSY) | ||
368 | return -ETIMEDOUT; | ||
369 | |||
370 | /* Now we actually set up the transfer */ | ||
371 | hermes_write_reg(hw, sreg, id); | ||
372 | hermes_write_reg(hw, oreg, offset); | ||
373 | |||
374 | /* Wait for the BAP to be ready */ | ||
375 | k = HERMES_BAP_BUSY_TIMEOUT; | ||
376 | reg = hermes_read_reg(hw, oreg); | ||
377 | while ( (reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) { | ||
378 | k--; | ||
379 | udelay(1); | ||
380 | reg = hermes_read_reg(hw, oreg); | ||
381 | } | ||
382 | |||
383 | if (reg != offset) { | ||
384 | printk(KERN_ERR "hermes @ %p: BAP%d offset %s: " | ||
385 | "reg=0x%x id=0x%x offset=0x%x\n", hw->iobase, bap, | ||
386 | (reg & HERMES_OFFSET_BUSY) ? "timeout" : "error", | ||
387 | reg, id, offset); | ||
388 | |||
389 | if (reg & HERMES_OFFSET_BUSY) { | ||
390 | return -ETIMEDOUT; | ||
391 | } | ||
392 | |||
393 | return -EIO; /* error or wrong offset */ | ||
394 | } | ||
395 | |||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | /* Read a block of data from the chip's buffer, via the | ||
400 | * BAP. Synchronization/serialization is the caller's problem. len | ||
401 | * must be even. | ||
402 | * | ||
403 | * Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware | ||
404 | */ | ||
405 | int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, | ||
406 | u16 id, u16 offset) | ||
407 | { | ||
408 | int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; | ||
409 | int err = 0; | ||
410 | |||
411 | if ( (len < 0) || (len % 2) ) | ||
412 | return -EINVAL; | ||
413 | |||
414 | err = hermes_bap_seek(hw, bap, id, offset); | ||
415 | if (err) | ||
416 | goto out; | ||
417 | |||
418 | /* Actually do the transfer */ | ||
419 | hermes_read_words(hw, dreg, buf, len/2); | ||
420 | |||
421 | out: | ||
422 | return err; | ||
423 | } | ||
424 | EXPORT_SYMBOL(hermes_bap_pread); | ||
425 | |||
426 | /* Write a block of data to the chip's buffer, via the | ||
427 | * BAP. Synchronization/serialization is the caller's problem. | ||
428 | * | ||
429 | * Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware | ||
430 | */ | ||
431 | int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len, | ||
432 | u16 id, u16 offset) | ||
433 | { | ||
434 | int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; | ||
435 | int err = 0; | ||
436 | |||
437 | if (len < 0) | ||
438 | return -EINVAL; | ||
439 | |||
440 | err = hermes_bap_seek(hw, bap, id, offset); | ||
441 | if (err) | ||
442 | goto out; | ||
443 | |||
444 | /* Actually do the transfer */ | ||
445 | hermes_write_bytes(hw, dreg, buf, len); | ||
446 | |||
447 | out: | ||
448 | return err; | ||
449 | } | ||
450 | EXPORT_SYMBOL(hermes_bap_pwrite); | ||
451 | |||
452 | /* Read a Length-Type-Value record from the card. | ||
453 | * | ||
454 | * If length is NULL, we ignore the length read from the card, and | ||
455 | * read the entire buffer regardless. This is useful because some of | ||
456 | * the configuration records appear to have incorrect lengths in | ||
457 | * practice. | ||
458 | * | ||
459 | * Callable from user or bh context. */ | ||
460 | int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize, | ||
461 | u16 *length, void *buf) | ||
462 | { | ||
463 | int err = 0; | ||
464 | int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; | ||
465 | u16 rlength, rtype; | ||
466 | unsigned nwords; | ||
467 | |||
468 | if ( (bufsize < 0) || (bufsize % 2) ) | ||
469 | return -EINVAL; | ||
470 | |||
471 | err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, NULL); | ||
472 | if (err) | ||
473 | return err; | ||
474 | |||
475 | err = hermes_bap_seek(hw, bap, rid, 0); | ||
476 | if (err) | ||
477 | return err; | ||
478 | |||
479 | rlength = hermes_read_reg(hw, dreg); | ||
480 | |||
481 | if (! rlength) | ||
482 | return -ENODATA; | ||
483 | |||
484 | rtype = hermes_read_reg(hw, dreg); | ||
485 | |||
486 | if (length) | ||
487 | *length = rlength; | ||
488 | |||
489 | if (rtype != rid) | ||
490 | printk(KERN_WARNING "hermes @ %p: %s(): " | ||
491 | "rid (0x%04x) does not match type (0x%04x)\n", | ||
492 | hw->iobase, __func__, rid, rtype); | ||
493 | if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize) | ||
494 | printk(KERN_WARNING "hermes @ %p: " | ||
495 | "Truncating LTV record from %d to %d bytes. " | ||
496 | "(rid=0x%04x, len=0x%04x)\n", hw->iobase, | ||
497 | HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength); | ||
498 | |||
499 | nwords = min((unsigned)rlength - 1, bufsize / 2); | ||
500 | hermes_read_words(hw, dreg, buf, nwords); | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | EXPORT_SYMBOL(hermes_read_ltv); | ||
505 | |||
506 | int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, | ||
507 | u16 length, const void *value) | ||
508 | { | ||
509 | int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; | ||
510 | int err = 0; | ||
511 | unsigned count; | ||
512 | |||
513 | if (length == 0) | ||
514 | return -EINVAL; | ||
515 | |||
516 | err = hermes_bap_seek(hw, bap, rid, 0); | ||
517 | if (err) | ||
518 | return err; | ||
519 | |||
520 | hermes_write_reg(hw, dreg, length); | ||
521 | hermes_write_reg(hw, dreg, rid); | ||
522 | |||
523 | count = length - 1; | ||
524 | |||
525 | hermes_write_bytes(hw, dreg, value, count << 1); | ||
526 | |||
527 | err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE, | ||
528 | rid, NULL); | ||
529 | |||
530 | return err; | ||
531 | } | ||
532 | EXPORT_SYMBOL(hermes_write_ltv); | ||
533 | |||
534 | static int __init init_hermes(void) | ||
535 | { | ||
536 | return 0; | ||
537 | } | ||
538 | |||
539 | static void __exit exit_hermes(void) | ||
540 | { | ||
541 | } | ||
542 | |||
543 | module_init(init_hermes); | ||
544 | module_exit(exit_hermes); | ||