diff options
Diffstat (limited to 'drivers/media/radio')
-rw-r--r-- | drivers/media/radio/Kconfig | 8 | ||||
-rw-r--r-- | drivers/media/radio/radio-typhoon.c | 365 |
2 files changed, 73 insertions, 300 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index a81d15ccf067..2a5b17d837c8 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig | |||
@@ -400,14 +400,14 @@ config RADIO_TRUST_PORT | |||
400 | config RADIO_TYPHOON | 400 | config RADIO_TYPHOON |
401 | tristate "Typhoon Radio (a.k.a. EcoRadio)" | 401 | tristate "Typhoon Radio (a.k.a. EcoRadio)" |
402 | depends on ISA && VIDEO_V4L2 | 402 | depends on ISA && VIDEO_V4L2 |
403 | select RADIO_ISA | ||
403 | ---help--- | 404 | ---help--- |
404 | Choose Y here if you have one of these FM radio cards, and then fill | 405 | Choose Y here if you have one of these FM radio cards, and then fill |
405 | in the port address and the frequency used for muting below. | 406 | in the port address and the frequency used for muting below. |
406 | 407 | ||
407 | In order to control your radio card, you will need to use programs | 408 | Note: this driver hasn't been tested since a long time due to lack |
408 | that are compatible with the Video For Linux API. Information on | 409 | of hardware. If you have this hardware, then please contact the |
409 | this API and pointers to "v4l" programs may be found at | 410 | linux-media mailinglist. |
410 | <file:Documentation/video4linux/API.html>. | ||
411 | 411 | ||
412 | To compile this driver as a module, choose M here: the | 412 | To compile this driver as a module, choose M here: the |
413 | module will be called radio-typhoon. | 413 | module will be called radio-typhoon. |
diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c index 398726abc0c8..145d10c08556 100644 --- a/drivers/media/radio/radio-typhoon.c +++ b/drivers/media/radio/radio-typhoon.c | |||
@@ -35,61 +35,50 @@ | |||
35 | #include <linux/io.h> /* outb, outb_p */ | 35 | #include <linux/io.h> /* outb, outb_p */ |
36 | #include <media/v4l2-device.h> | 36 | #include <media/v4l2-device.h> |
37 | #include <media/v4l2-ioctl.h> | 37 | #include <media/v4l2-ioctl.h> |
38 | #include "radio-isa.h" | ||
38 | 39 | ||
39 | #define DRIVER_VERSION "0.1.2" | 40 | #define DRIVER_VERSION "0.1.2" |
40 | 41 | ||
41 | MODULE_AUTHOR("Dr. Henrik Seidel"); | 42 | MODULE_AUTHOR("Dr. Henrik Seidel"); |
42 | MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio)."); | 43 | MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio)."); |
43 | MODULE_LICENSE("GPL"); | 44 | MODULE_LICENSE("GPL"); |
44 | MODULE_VERSION(DRIVER_VERSION); | 45 | MODULE_VERSION("0.1.99"); |
45 | 46 | ||
46 | #ifndef CONFIG_RADIO_TYPHOON_PORT | 47 | #ifndef CONFIG_RADIO_TYPHOON_PORT |
47 | #define CONFIG_RADIO_TYPHOON_PORT -1 | 48 | #define CONFIG_RADIO_TYPHOON_PORT -1 |
48 | #endif | 49 | #endif |
49 | 50 | ||
50 | #ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ | 51 | #ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ |
51 | #define CONFIG_RADIO_TYPHOON_MUTEFREQ 0 | 52 | #define CONFIG_RADIO_TYPHOON_MUTEFREQ 87000 |
52 | #endif | 53 | #endif |
53 | 54 | ||
54 | static int io = CONFIG_RADIO_TYPHOON_PORT; | 55 | #define TYPHOON_MAX 2 |
55 | static int radio_nr = -1; | ||
56 | |||
57 | module_param(io, int, 0); | ||
58 | MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)"); | ||
59 | |||
60 | module_param(radio_nr, int, 0); | ||
61 | 56 | ||
57 | static int io[TYPHOON_MAX] = { [0] = CONFIG_RADIO_TYPHOON_PORT, | ||
58 | [1 ... (TYPHOON_MAX - 1)] = -1 }; | ||
59 | static int radio_nr[TYPHOON_MAX] = { [0 ... (TYPHOON_MAX - 1)] = -1 }; | ||
62 | static unsigned long mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ; | 60 | static unsigned long mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ; |
61 | |||
62 | module_param_array(io, int, NULL, 0444); | ||
63 | MODULE_PARM_DESC(io, "I/O addresses of the Typhoon card (0x316 or 0x336)"); | ||
64 | module_param_array(radio_nr, int, NULL, 0444); | ||
65 | MODULE_PARM_DESC(radio_nr, "Radio device numbers"); | ||
63 | module_param(mutefreq, ulong, 0); | 66 | module_param(mutefreq, ulong, 0); |
64 | MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)"); | 67 | MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)"); |
65 | 68 | ||
66 | #define BANNER "Typhoon Radio Card driver v" DRIVER_VERSION "\n" | ||
67 | |||
68 | struct typhoon { | 69 | struct typhoon { |
69 | struct v4l2_device v4l2_dev; | 70 | struct radio_isa_card isa; |
70 | struct video_device vdev; | ||
71 | int io; | ||
72 | int curvol; | ||
73 | int muted; | 71 | int muted; |
74 | unsigned long curfreq; | ||
75 | unsigned long mutefreq; | ||
76 | struct mutex lock; | ||
77 | }; | 72 | }; |
78 | 73 | ||
79 | static struct typhoon typhoon_card; | 74 | static struct radio_isa_card *typhoon_alloc(void) |
80 | |||
81 | static void typhoon_setvol_generic(struct typhoon *dev, int vol) | ||
82 | { | 75 | { |
83 | mutex_lock(&dev->lock); | 76 | struct typhoon *ty = kzalloc(sizeof(*ty), GFP_KERNEL); |
84 | vol >>= 14; /* Map 16 bit to 2 bit */ | 77 | |
85 | vol &= 3; | 78 | return ty ? &ty->isa : NULL; |
86 | outb_p(vol / 2, dev->io); /* Set the volume, high bit. */ | ||
87 | outb_p(vol % 2, dev->io + 2); /* Set the volume, low bit. */ | ||
88 | mutex_unlock(&dev->lock); | ||
89 | } | 79 | } |
90 | 80 | ||
91 | static int typhoon_setfreq_generic(struct typhoon *dev, | 81 | static int typhoon_s_frequency(struct radio_isa_card *isa, u32 freq) |
92 | unsigned long frequency) | ||
93 | { | 82 | { |
94 | unsigned long outval; | 83 | unsigned long outval; |
95 | unsigned long x; | 84 | unsigned long x; |
@@ -105,302 +94,86 @@ static int typhoon_setfreq_generic(struct typhoon *dev, | |||
105 | * | 94 | * |
106 | */ | 95 | */ |
107 | 96 | ||
108 | mutex_lock(&dev->lock); | 97 | x = freq / 160; |
109 | x = frequency / 160; | ||
110 | outval = (x * x + 2500) / 5000; | 98 | outval = (x * x + 2500) / 5000; |
111 | outval = (outval * x + 5000) / 10000; | 99 | outval = (outval * x + 5000) / 10000; |
112 | outval -= (10 * x * x + 10433) / 20866; | 100 | outval -= (10 * x * x + 10433) / 20866; |
113 | outval += 4 * x - 11505; | 101 | outval += 4 * x - 11505; |
114 | 102 | ||
115 | outb_p((outval >> 8) & 0x01, dev->io + 4); | 103 | outb_p((outval >> 8) & 0x01, isa->io + 4); |
116 | outb_p(outval >> 9, dev->io + 6); | 104 | outb_p(outval >> 9, isa->io + 6); |
117 | outb_p(outval & 0xff, dev->io + 8); | 105 | outb_p(outval & 0xff, isa->io + 8); |
118 | mutex_unlock(&dev->lock); | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static int typhoon_setfreq(struct typhoon *dev, unsigned long frequency) | ||
124 | { | ||
125 | typhoon_setfreq_generic(dev, frequency); | ||
126 | dev->curfreq = frequency; | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static void typhoon_mute(struct typhoon *dev) | ||
131 | { | ||
132 | if (dev->muted == 1) | ||
133 | return; | ||
134 | typhoon_setvol_generic(dev, 0); | ||
135 | typhoon_setfreq_generic(dev, dev->mutefreq); | ||
136 | dev->muted = 1; | ||
137 | } | ||
138 | |||
139 | static void typhoon_unmute(struct typhoon *dev) | ||
140 | { | ||
141 | if (dev->muted == 0) | ||
142 | return; | ||
143 | typhoon_setfreq_generic(dev, dev->curfreq); | ||
144 | typhoon_setvol_generic(dev, dev->curvol); | ||
145 | dev->muted = 0; | ||
146 | } | ||
147 | |||
148 | static int typhoon_setvol(struct typhoon *dev, int vol) | ||
149 | { | ||
150 | if (dev->muted && vol != 0) { /* user is unmuting the card */ | ||
151 | dev->curvol = vol; | ||
152 | typhoon_unmute(dev); | ||
153 | return 0; | ||
154 | } | ||
155 | if (vol == dev->curvol) /* requested volume == current */ | ||
156 | return 0; | ||
157 | |||
158 | if (vol == 0) { /* volume == 0 means mute the card */ | ||
159 | typhoon_mute(dev); | ||
160 | dev->curvol = vol; | ||
161 | return 0; | ||
162 | } | ||
163 | typhoon_setvol_generic(dev, vol); | ||
164 | dev->curvol = vol; | ||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static int vidioc_querycap(struct file *file, void *priv, | ||
169 | struct v4l2_capability *v) | ||
170 | { | ||
171 | strlcpy(v->driver, "radio-typhoon", sizeof(v->driver)); | ||
172 | strlcpy(v->card, "Typhoon Radio", sizeof(v->card)); | ||
173 | strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); | ||
174 | v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static int vidioc_g_tuner(struct file *file, void *priv, | ||
179 | struct v4l2_tuner *v) | ||
180 | { | ||
181 | if (v->index > 0) | ||
182 | return -EINVAL; | ||
183 | |||
184 | strlcpy(v->name, "FM", sizeof(v->name)); | ||
185 | v->type = V4L2_TUNER_RADIO; | ||
186 | v->rangelow = 87.5 * 16000; | ||
187 | v->rangehigh = 108 * 16000; | ||
188 | v->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
189 | v->capability = V4L2_TUNER_CAP_LOW; | ||
190 | v->audmode = V4L2_TUNER_MODE_MONO; | ||
191 | v->signal = 0xFFFF; /* We can't get the signal strength */ | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static int vidioc_s_tuner(struct file *file, void *priv, | ||
196 | struct v4l2_tuner *v) | ||
197 | { | ||
198 | return v->index ? -EINVAL : 0; | ||
199 | } | ||
200 | |||
201 | static int vidioc_g_frequency(struct file *file, void *priv, | ||
202 | struct v4l2_frequency *f) | ||
203 | { | ||
204 | struct typhoon *dev = video_drvdata(file); | ||
205 | |||
206 | if (f->tuner != 0) | ||
207 | return -EINVAL; | ||
208 | f->type = V4L2_TUNER_RADIO; | ||
209 | f->frequency = dev->curfreq; | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | static int vidioc_s_frequency(struct file *file, void *priv, | ||
214 | struct v4l2_frequency *f) | ||
215 | { | ||
216 | struct typhoon *dev = video_drvdata(file); | ||
217 | |||
218 | if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) | ||
219 | return -EINVAL; | ||
220 | dev->curfreq = f->frequency; | ||
221 | typhoon_setfreq(dev, dev->curfreq); | ||
222 | return 0; | 106 | return 0; |
223 | } | 107 | } |
224 | 108 | ||
225 | static int vidioc_queryctrl(struct file *file, void *priv, | 109 | static int typhoon_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) |
226 | struct v4l2_queryctrl *qc) | ||
227 | { | 110 | { |
228 | switch (qc->id) { | 111 | struct typhoon *ty = container_of(isa, struct typhoon, isa); |
229 | case V4L2_CID_AUDIO_MUTE: | ||
230 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); | ||
231 | case V4L2_CID_AUDIO_VOLUME: | ||
232 | return v4l2_ctrl_query_fill(qc, 0, 65535, 16384, 65535); | ||
233 | } | ||
234 | return -EINVAL; | ||
235 | } | ||
236 | 112 | ||
237 | static int vidioc_g_ctrl(struct file *file, void *priv, | 113 | if (mute) |
238 | struct v4l2_control *ctrl) | 114 | vol = 0; |
239 | { | 115 | vol >>= 14; /* Map 16 bit to 2 bit */ |
240 | struct typhoon *dev = video_drvdata(file); | 116 | vol &= 3; |
117 | outb_p(vol / 2, isa->io); /* Set the volume, high bit. */ | ||
118 | outb_p(vol % 2, isa->io + 2); /* Set the volume, low bit. */ | ||
241 | 119 | ||
242 | switch (ctrl->id) { | 120 | if (vol == 0 && !ty->muted) { |
243 | case V4L2_CID_AUDIO_MUTE: | 121 | ty->muted = true; |
244 | ctrl->value = dev->muted; | 122 | return typhoon_s_frequency(isa, mutefreq << 4); |
245 | return 0; | ||
246 | case V4L2_CID_AUDIO_VOLUME: | ||
247 | ctrl->value = dev->curvol; | ||
248 | return 0; | ||
249 | } | 123 | } |
250 | return -EINVAL; | 124 | if (vol && ty->muted) { |
251 | } | 125 | ty->muted = false; |
252 | 126 | return typhoon_s_frequency(isa, isa->freq); | |
253 | static int vidioc_s_ctrl (struct file *file, void *priv, | ||
254 | struct v4l2_control *ctrl) | ||
255 | { | ||
256 | struct typhoon *dev = video_drvdata(file); | ||
257 | |||
258 | switch (ctrl->id) { | ||
259 | case V4L2_CID_AUDIO_MUTE: | ||
260 | if (ctrl->value) | ||
261 | typhoon_mute(dev); | ||
262 | else | ||
263 | typhoon_unmute(dev); | ||
264 | return 0; | ||
265 | case V4L2_CID_AUDIO_VOLUME: | ||
266 | typhoon_setvol(dev, ctrl->value); | ||
267 | return 0; | ||
268 | } | 127 | } |
269 | return -EINVAL; | ||
270 | } | ||
271 | |||
272 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | ||
273 | { | ||
274 | *i = 0; | ||
275 | return 0; | 128 | return 0; |
276 | } | 129 | } |
277 | 130 | ||
278 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | 131 | static const struct radio_isa_ops typhoon_ops = { |
279 | { | 132 | .alloc = typhoon_alloc, |
280 | return i ? -EINVAL : 0; | 133 | .s_mute_volume = typhoon_s_mute_volume, |
281 | } | 134 | .s_frequency = typhoon_s_frequency, |
282 | |||
283 | static int vidioc_g_audio(struct file *file, void *priv, | ||
284 | struct v4l2_audio *a) | ||
285 | { | ||
286 | a->index = 0; | ||
287 | strlcpy(a->name, "Radio", sizeof(a->name)); | ||
288 | a->capability = V4L2_AUDCAP_STEREO; | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static int vidioc_s_audio(struct file *file, void *priv, | ||
293 | struct v4l2_audio *a) | ||
294 | { | ||
295 | return a->index ? -EINVAL : 0; | ||
296 | } | ||
297 | |||
298 | static int vidioc_log_status(struct file *file, void *priv) | ||
299 | { | ||
300 | struct typhoon *dev = video_drvdata(file); | ||
301 | struct v4l2_device *v4l2_dev = &dev->v4l2_dev; | ||
302 | |||
303 | v4l2_info(v4l2_dev, BANNER); | ||
304 | #ifdef MODULE | ||
305 | v4l2_info(v4l2_dev, "Load type: Driver loaded as a module\n\n"); | ||
306 | #else | ||
307 | v4l2_info(v4l2_dev, "Load type: Driver compiled into kernel\n\n"); | ||
308 | #endif | ||
309 | v4l2_info(v4l2_dev, "frequency = %lu kHz\n", dev->curfreq >> 4); | ||
310 | v4l2_info(v4l2_dev, "volume = %d\n", dev->curvol); | ||
311 | v4l2_info(v4l2_dev, "mute = %s\n", dev->muted ? "on" : "off"); | ||
312 | v4l2_info(v4l2_dev, "io = 0x%x\n", dev->io); | ||
313 | v4l2_info(v4l2_dev, "mute frequency = %lu kHz\n", dev->mutefreq >> 4); | ||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | static const struct v4l2_file_operations typhoon_fops = { | ||
318 | .owner = THIS_MODULE, | ||
319 | .unlocked_ioctl = video_ioctl2, | ||
320 | }; | 135 | }; |
321 | 136 | ||
322 | static const struct v4l2_ioctl_ops typhoon_ioctl_ops = { | 137 | static const int typhoon_ioports[] = { 0x316, 0x336 }; |
323 | .vidioc_log_status = vidioc_log_status, | 138 | |
324 | .vidioc_querycap = vidioc_querycap, | 139 | static struct radio_isa_driver typhoon_driver = { |
325 | .vidioc_g_tuner = vidioc_g_tuner, | 140 | .driver = { |
326 | .vidioc_s_tuner = vidioc_s_tuner, | 141 | .match = radio_isa_match, |
327 | .vidioc_g_audio = vidioc_g_audio, | 142 | .probe = radio_isa_probe, |
328 | .vidioc_s_audio = vidioc_s_audio, | 143 | .remove = radio_isa_remove, |
329 | .vidioc_g_input = vidioc_g_input, | 144 | .driver = { |
330 | .vidioc_s_input = vidioc_s_input, | 145 | .name = "radio-typhoon", |
331 | .vidioc_g_frequency = vidioc_g_frequency, | 146 | }, |
332 | .vidioc_s_frequency = vidioc_s_frequency, | 147 | }, |
333 | .vidioc_queryctrl = vidioc_queryctrl, | 148 | .io_params = io, |
334 | .vidioc_g_ctrl = vidioc_g_ctrl, | 149 | .radio_nr_params = radio_nr, |
335 | .vidioc_s_ctrl = vidioc_s_ctrl, | 150 | .io_ports = typhoon_ioports, |
151 | .num_of_io_ports = ARRAY_SIZE(typhoon_ioports), | ||
152 | .region_size = 8, | ||
153 | .card = "Typhoon Radio", | ||
154 | .ops = &typhoon_ops, | ||
155 | .has_stereo = true, | ||
156 | .max_volume = 3, | ||
336 | }; | 157 | }; |
337 | 158 | ||
338 | static int __init typhoon_init(void) | 159 | static int __init typhoon_init(void) |
339 | { | 160 | { |
340 | struct typhoon *dev = &typhoon_card; | 161 | if (mutefreq < 87000 || mutefreq > 108000) { |
341 | struct v4l2_device *v4l2_dev = &dev->v4l2_dev; | 162 | printk(KERN_ERR "%s: You must set a frequency (in kHz) used when muting the card,\n", |
342 | int res; | 163 | typhoon_driver.driver.driver.name); |
343 | 164 | printk(KERN_ERR "%s: e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108000)\n", | |
344 | strlcpy(v4l2_dev->name, "typhoon", sizeof(v4l2_dev->name)); | 165 | typhoon_driver.driver.driver.name); |
345 | dev->io = io; | 166 | return -ENODEV; |
346 | |||
347 | if (dev->io == -1) { | ||
348 | v4l2_err(v4l2_dev, "You must set an I/O address with io=0x316 or io=0x336\n"); | ||
349 | return -EINVAL; | ||
350 | } | ||
351 | |||
352 | if (mutefreq < 87000 || mutefreq > 108500) { | ||
353 | v4l2_err(v4l2_dev, "You must set a frequency (in kHz) used when muting the card,\n"); | ||
354 | v4l2_err(v4l2_dev, "e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n"); | ||
355 | return -EINVAL; | ||
356 | } | ||
357 | dev->curfreq = dev->mutefreq = mutefreq << 4; | ||
358 | |||
359 | mutex_init(&dev->lock); | ||
360 | if (!request_region(dev->io, 8, "typhoon")) { | ||
361 | v4l2_err(v4l2_dev, "port 0x%x already in use\n", | ||
362 | dev->io); | ||
363 | return -EBUSY; | ||
364 | } | ||
365 | |||
366 | res = v4l2_device_register(NULL, v4l2_dev); | ||
367 | if (res < 0) { | ||
368 | release_region(dev->io, 8); | ||
369 | v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); | ||
370 | return res; | ||
371 | } | 167 | } |
372 | v4l2_info(v4l2_dev, BANNER); | 168 | return isa_register_driver(&typhoon_driver.driver, TYPHOON_MAX); |
373 | |||
374 | strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); | ||
375 | dev->vdev.v4l2_dev = v4l2_dev; | ||
376 | dev->vdev.fops = &typhoon_fops; | ||
377 | dev->vdev.ioctl_ops = &typhoon_ioctl_ops; | ||
378 | dev->vdev.release = video_device_release_empty; | ||
379 | video_set_drvdata(&dev->vdev, dev); | ||
380 | |||
381 | /* mute card - prevents noisy bootups */ | ||
382 | typhoon_mute(dev); | ||
383 | |||
384 | if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { | ||
385 | v4l2_device_unregister(&dev->v4l2_dev); | ||
386 | release_region(dev->io, 8); | ||
387 | return -EINVAL; | ||
388 | } | ||
389 | v4l2_info(v4l2_dev, "port 0x%x.\n", dev->io); | ||
390 | v4l2_info(v4l2_dev, "mute frequency is %lu kHz.\n", mutefreq); | ||
391 | |||
392 | return 0; | ||
393 | } | 169 | } |
394 | 170 | ||
395 | static void __exit typhoon_exit(void) | 171 | static void __exit typhoon_exit(void) |
396 | { | 172 | { |
397 | struct typhoon *dev = &typhoon_card; | 173 | isa_unregister_driver(&typhoon_driver.driver); |
398 | |||
399 | video_unregister_device(&dev->vdev); | ||
400 | v4l2_device_unregister(&dev->v4l2_dev); | ||
401 | release_region(dev->io, 8); | ||
402 | } | 174 | } |
403 | 175 | ||
176 | |||
404 | module_init(typhoon_init); | 177 | module_init(typhoon_init); |
405 | module_exit(typhoon_exit); | 178 | module_exit(typhoon_exit); |
406 | 179 | ||