Blame view
drivers/media/radio/radio-sf16fmi.c
9.63 KB
4b8303747 V4L/DVB (13608): ... |
1 |
/* SF16-FMI and SF16-FMP radio driver for Linux radio support |
1da177e4c Linux-2.6.12-rc2 |
2 3 4 5 |
* heavily based on rtrack driver... * (c) 1997 M. Kirkwood * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz * |
d9b01449e V4L/DVB (9491): r... |
6 |
* Fitted to new interface by Alan Cox <alan@lxorguk.ukuu.org.uk> |
1da177e4c Linux-2.6.12-rc2 |
7 8 9 10 11 12 13 |
* Made working and cleaned up functions <mikael.hedin@irf.se> * Support for ISAPnP by Ladislav Michl <ladis@psi.cz> * * Notes on the hardware * * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); * No volume control - only mute/unmute - you have to use line volume |
4b8303747 V4L/DVB (13608): ... |
14 |
* control on SB-part of SF16-FMI/SF16-FMP |
4286c6f65 V4L/DVB (3753): W... |
15 |
* |
a2ef73af4 V4L/DVB (4353): V... |
16 |
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> |
1da177e4c Linux-2.6.12-rc2 |
17 18 19 20 21 |
*/ #include <linux/kernel.h> /* __setup */ #include <linux/module.h> /* Modules */ #include <linux/init.h> /* Initdata */ |
fb911ee84 [PATCH] Remove un... |
22 |
#include <linux/ioport.h> /* request_region */ |
1da177e4c Linux-2.6.12-rc2 |
23 |
#include <linux/delay.h> /* udelay */ |
1da177e4c Linux-2.6.12-rc2 |
24 |
#include <linux/isapnp.h> |
3593cab5d V4L/DVB (3318b): ... |
25 |
#include <linux/mutex.h> |
c41269fd9 V4L/DVB (10888): ... |
26 27 |
#include <linux/videodev2.h> /* kernel radio structs */ #include <linux/io.h> /* outb, outb_p */ |
c41269fd9 V4L/DVB (10888): ... |
28 29 |
#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> |
1da177e4c Linux-2.6.12-rc2 |
30 |
|
c41269fd9 V4L/DVB (10888): ... |
31 |
MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood"); |
4b8303747 V4L/DVB (13608): ... |
32 |
MODULE_DESCRIPTION("A driver for the SF16-FMI and SF16-FMP radio."); |
c41269fd9 V4L/DVB (10888): ... |
33 |
MODULE_LICENSE("GPL"); |
29834c1ac [media] radio: Us... |
34 |
MODULE_VERSION("0.0.3"); |
a2ef73af4 V4L/DVB (4353): V... |
35 |
|
c41269fd9 V4L/DVB (10888): ... |
36 37 38 39 |
static int io = -1; static int radio_nr = -1; module_param(io, int, 0); |
4b8303747 V4L/DVB (13608): ... |
40 |
MODULE_PARM_DESC(io, "I/O address of the SF16-FMI or SF16-FMP card (0x284 or 0x384)"); |
c41269fd9 V4L/DVB (10888): ... |
41 |
module_param(radio_nr, int, 0); |
c41269fd9 V4L/DVB (10888): ... |
42 |
struct fmi |
1da177e4c Linux-2.6.12-rc2 |
43 |
{ |
c41269fd9 V4L/DVB (10888): ... |
44 45 46 |
struct v4l2_device v4l2_dev; struct video_device vdev; int io; |
4b8303747 V4L/DVB (13608): ... |
47 |
bool mute; |
4286c6f65 V4L/DVB (3753): W... |
48 |
unsigned long curfreq; /* freq in kHz */ |
c41269fd9 V4L/DVB (10888): ... |
49 |
struct mutex lock; |
1da177e4c Linux-2.6.12-rc2 |
50 |
}; |
c41269fd9 V4L/DVB (10888): ... |
51 52 |
static struct fmi fmi_card; static struct pnp_dev *dev; |
67cabf503 V4L/DVB (13609): ... |
53 |
bool pnp_attached; |
1da177e4c Linux-2.6.12-rc2 |
54 55 |
/* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */ |
1d80db562 V4L/DVB (11676): ... |
56 |
/* It is only useful to give freq in interval of 800 (=0.05Mhz), |
4286c6f65 V4L/DVB (3753): W... |
57 |
* other bits will be truncated, e.g 92.7400016 -> 92.7, but |
1da177e4c Linux-2.6.12-rc2 |
58 59 |
* 92.7400017 -> 92.75 */ |
c41269fd9 V4L/DVB (10888): ... |
60 61 62 |
#define RSF16_ENCODE(x) ((x) / 800 + 214) #define RSF16_MINFREQ (87 * 16000) #define RSF16_MAXFREQ (108 * 16000) |
1da177e4c Linux-2.6.12-rc2 |
63 |
|
c41269fd9 V4L/DVB (10888): ... |
64 |
static void outbits(int bits, unsigned int data, int io) |
1da177e4c Linux-2.6.12-rc2 |
65 |
{ |
c41269fd9 V4L/DVB (10888): ... |
66 67 68 |
while (bits--) { if (data & 1) { outb(5, io); |
1da177e4c Linux-2.6.12-rc2 |
69 |
udelay(6); |
c41269fd9 V4L/DVB (10888): ... |
70 |
outb(7, io); |
1da177e4c Linux-2.6.12-rc2 |
71 72 |
udelay(6); } else { |
c41269fd9 V4L/DVB (10888): ... |
73 |
outb(1, io); |
1da177e4c Linux-2.6.12-rc2 |
74 |
udelay(6); |
c41269fd9 V4L/DVB (10888): ... |
75 |
outb(3, io); |
1da177e4c Linux-2.6.12-rc2 |
76 77 |
udelay(6); } |
c41269fd9 V4L/DVB (10888): ... |
78 |
data >>= 1; |
1da177e4c Linux-2.6.12-rc2 |
79 80 |
} } |
c41269fd9 V4L/DVB (10888): ... |
81 |
static inline void fmi_mute(struct fmi *fmi) |
1da177e4c Linux-2.6.12-rc2 |
82 |
{ |
c41269fd9 V4L/DVB (10888): ... |
83 84 85 |
mutex_lock(&fmi->lock); outb(0x00, fmi->io); mutex_unlock(&fmi->lock); |
1da177e4c Linux-2.6.12-rc2 |
86 |
} |
c41269fd9 V4L/DVB (10888): ... |
87 |
static inline void fmi_unmute(struct fmi *fmi) |
1da177e4c Linux-2.6.12-rc2 |
88 |
{ |
c41269fd9 V4L/DVB (10888): ... |
89 90 91 |
mutex_lock(&fmi->lock); outb(0x08, fmi->io); mutex_unlock(&fmi->lock); |
1da177e4c Linux-2.6.12-rc2 |
92 |
} |
c41269fd9 V4L/DVB (10888): ... |
93 |
static inline int fmi_setfreq(struct fmi *fmi, unsigned long freq) |
1da177e4c Linux-2.6.12-rc2 |
94 |
{ |
c41269fd9 V4L/DVB (10888): ... |
95 96 |
mutex_lock(&fmi->lock); fmi->curfreq = freq; |
1da177e4c Linux-2.6.12-rc2 |
97 |
|
c41269fd9 V4L/DVB (10888): ... |
98 99 |
outbits(16, RSF16_ENCODE(freq), fmi->io); outbits(8, 0xC0, fmi->io); |
1da177e4c Linux-2.6.12-rc2 |
100 |
msleep(143); /* was schedule_timeout(HZ/7) */ |
c41269fd9 V4L/DVB (10888): ... |
101 |
mutex_unlock(&fmi->lock); |
4b8303747 V4L/DVB (13608): ... |
102 |
if (!fmi->mute) |
c41269fd9 V4L/DVB (10888): ... |
103 |
fmi_unmute(fmi); |
1da177e4c Linux-2.6.12-rc2 |
104 105 |
return 0; } |
c41269fd9 V4L/DVB (10888): ... |
106 |
static inline int fmi_getsigstr(struct fmi *fmi) |
1da177e4c Linux-2.6.12-rc2 |
107 108 109 |
{ int val; int res; |
4286c6f65 V4L/DVB (3753): W... |
110 |
|
c41269fd9 V4L/DVB (10888): ... |
111 |
mutex_lock(&fmi->lock); |
4b8303747 V4L/DVB (13608): ... |
112 |
val = fmi->mute ? 0x00 : 0x08; /* mute/unmute */ |
c41269fd9 V4L/DVB (10888): ... |
113 114 |
outb(val, fmi->io); outb(val | 0x10, fmi->io); |
1da177e4c Linux-2.6.12-rc2 |
115 |
msleep(143); /* was schedule_timeout(HZ/7) */ |
c41269fd9 V4L/DVB (10888): ... |
116 117 |
res = (int)inb(fmi->io + 1); outb(val, fmi->io); |
4286c6f65 V4L/DVB (3753): W... |
118 |
|
c41269fd9 V4L/DVB (10888): ... |
119 |
mutex_unlock(&fmi->lock); |
1da177e4c Linux-2.6.12-rc2 |
120 121 |
return (res & 2) ? 0 : 0xFFFF; } |
c123b8677 V4L/DVB (5550): R... |
122 123 |
static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *v) |
1da177e4c Linux-2.6.12-rc2 |
124 |
{ |
c123b8677 V4L/DVB (5550): R... |
125 126 |
strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver)); strlcpy(v->card, "SF16-FMx radio", sizeof(v->card)); |
c41269fd9 V4L/DVB (10888): ... |
127 |
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); |
c41269fd9 V4L/DVB (10888): ... |
128 |
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; |
c123b8677 V4L/DVB (5550): R... |
129 130 131 132 133 134 |
return 0; } static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { |
c41269fd9 V4L/DVB (10888): ... |
135 |
struct fmi *fmi = video_drvdata(file); |
c123b8677 V4L/DVB (5550): R... |
136 137 138 |
if (v->index > 0) return -EINVAL; |
c41269fd9 V4L/DVB (10888): ... |
139 |
strlcpy(v->name, "FM", sizeof(v->name)); |
c123b8677 V4L/DVB (5550): R... |
140 |
v->type = V4L2_TUNER_RADIO; |
1d80db562 V4L/DVB (11676): ... |
141 142 |
v->rangelow = RSF16_MINFREQ; v->rangehigh = RSF16_MAXFREQ; |
dc47b7789 V4L/DVB (11675): ... |
143 |
v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; |
38092a440 V4L/DVB (11677): ... |
144 |
v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW; |
c123b8677 V4L/DVB (5550): R... |
145 146 147 148 |
v->audmode = V4L2_TUNER_MODE_STEREO; v->signal = fmi_getsigstr(fmi); return 0; } |
4286c6f65 V4L/DVB (3753): W... |
149 |
|
c123b8677 V4L/DVB (5550): R... |
150 151 152 |
static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { |
c41269fd9 V4L/DVB (10888): ... |
153 |
return v->index ? -EINVAL : 0; |
c123b8677 V4L/DVB (5550): R... |
154 |
} |
a2ef73af4 V4L/DVB (4353): V... |
155 |
|
c123b8677 V4L/DVB (5550): R... |
156 157 158 |
static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { |
c41269fd9 V4L/DVB (10888): ... |
159 |
struct fmi *fmi = video_drvdata(file); |
c123b8677 V4L/DVB (5550): R... |
160 |
|
a3a9e287d V4L/DVB (13547): ... |
161 162 |
if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) return -EINVAL; |
c123b8677 V4L/DVB (5550): R... |
163 |
if (f->frequency < RSF16_MINFREQ || |
c41269fd9 V4L/DVB (10888): ... |
164 |
f->frequency > RSF16_MAXFREQ) |
c123b8677 V4L/DVB (5550): R... |
165 |
return -EINVAL; |
c41269fd9 V4L/DVB (10888): ... |
166 167 168 |
/* rounding in steps of 800 to match the freq that will be used */ fmi_setfreq(fmi, (f->frequency / 800) * 800); |
c123b8677 V4L/DVB (5550): R... |
169 170 |
return 0; } |
a2ef73af4 V4L/DVB (4353): V... |
171 |
|
c123b8677 V4L/DVB (5550): R... |
172 173 174 |
static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { |
c41269fd9 V4L/DVB (10888): ... |
175 |
struct fmi *fmi = video_drvdata(file); |
a2ef73af4 V4L/DVB (4353): V... |
176 |
|
a3a9e287d V4L/DVB (13547): ... |
177 178 |
if (f->tuner != 0) return -EINVAL; |
c123b8677 V4L/DVB (5550): R... |
179 180 |
f->type = V4L2_TUNER_RADIO; f->frequency = fmi->curfreq; |
c123b8677 V4L/DVB (5550): R... |
181 182 |
return 0; } |
a2ef73af4 V4L/DVB (4353): V... |
183 |
|
c123b8677 V4L/DVB (5550): R... |
184 185 186 |
static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qc) { |
c41269fd9 V4L/DVB (10888): ... |
187 188 189 |
switch (qc->id) { case V4L2_CID_AUDIO_MUTE: return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); |
c123b8677 V4L/DVB (5550): R... |
190 191 192 |
} return -EINVAL; } |
a2ef73af4 V4L/DVB (4353): V... |
193 |
|
c123b8677 V4L/DVB (5550): R... |
194 195 196 |
static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { |
c41269fd9 V4L/DVB (10888): ... |
197 |
struct fmi *fmi = video_drvdata(file); |
a2ef73af4 V4L/DVB (4353): V... |
198 |
|
c123b8677 V4L/DVB (5550): R... |
199 200 |
switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: |
4b8303747 V4L/DVB (13608): ... |
201 |
ctrl->value = fmi->mute; |
c123b8677 V4L/DVB (5550): R... |
202 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
203 |
} |
c123b8677 V4L/DVB (5550): R... |
204 205 206 207 208 209 |
return -EINVAL; } static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { |
c41269fd9 V4L/DVB (10888): ... |
210 |
struct fmi *fmi = video_drvdata(file); |
c123b8677 V4L/DVB (5550): R... |
211 212 213 214 |
switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: if (ctrl->value) |
c41269fd9 V4L/DVB (10888): ... |
215 |
fmi_mute(fmi); |
c123b8677 V4L/DVB (5550): R... |
216 |
else |
c41269fd9 V4L/DVB (10888): ... |
217 |
fmi_unmute(fmi); |
4b8303747 V4L/DVB (13608): ... |
218 |
fmi->mute = ctrl->value; |
c123b8677 V4L/DVB (5550): R... |
219 220 221 222 |
return 0; } return -EINVAL; } |
c123b8677 V4L/DVB (5550): R... |
223 |
static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) |
1da177e4c Linux-2.6.12-rc2 |
224 |
{ |
c123b8677 V4L/DVB (5550): R... |
225 226 227 228 229 230 |
*i = 0; return 0; } static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) { |
c41269fd9 V4L/DVB (10888): ... |
231 |
return i ? -EINVAL : 0; |
c123b8677 V4L/DVB (5550): R... |
232 |
} |
c41269fd9 V4L/DVB (10888): ... |
233 |
static int vidioc_g_audio(struct file *file, void *priv, |
c123b8677 V4L/DVB (5550): R... |
234 235 |
struct v4l2_audio *a) { |
c41269fd9 V4L/DVB (10888): ... |
236 237 238 |
a->index = 0; strlcpy(a->name, "Radio", sizeof(a->name)); a->capability = V4L2_AUDCAP_STEREO; |
c123b8677 V4L/DVB (5550): R... |
239 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
240 |
} |
c41269fd9 V4L/DVB (10888): ... |
241 242 243 244 245 |
static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) { return a->index ? -EINVAL : 0; } |
1da177e4c Linux-2.6.12-rc2 |
246 |
|
bec43661b V4L/DVB (10135): ... |
247 |
static const struct v4l2_file_operations fmi_fops = { |
1da177e4c Linux-2.6.12-rc2 |
248 |
.owner = THIS_MODULE, |
32958fdd1 [media] BKL: triv... |
249 |
.unlocked_ioctl = video_ioctl2, |
1da177e4c Linux-2.6.12-rc2 |
250 |
}; |
a399810ca V4L/DVB (8482): v... |
251 |
static const struct v4l2_ioctl_ops fmi_ioctl_ops = { |
c123b8677 V4L/DVB (5550): R... |
252 253 254 255 256 257 258 259 260 261 262 263 |
.vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, |
1da177e4c Linux-2.6.12-rc2 |
264 265 266 267 268 269 270 271 272 273 |
}; /* ladis: this is my card. does any other types exist? */ static struct isapnp_device_id id_table[] __devinitdata = { { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0}, { ISAPNP_CARD_END, }, }; MODULE_DEVICE_TABLE(isapnp, id_table); |
a999337b4 V4L/DVB (7078): r... |
274 |
static int __init isapnp_fmi_probe(void) |
1da177e4c Linux-2.6.12-rc2 |
275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
{ int i = 0; while (id_table[i].card_vendor != 0 && dev == NULL) { dev = pnp_find_dev(NULL, id_table[i].vendor, id_table[i].function, NULL); i++; } if (!dev) return -ENODEV; if (pnp_device_attach(dev) < 0) return -EAGAIN; if (pnp_activate_dev(dev) < 0) { |
c41269fd9 V4L/DVB (10888): ... |
289 290 |
printk(KERN_ERR "radio-sf16fmi: PnP configure failed (out of resources?) "); |
1da177e4c Linux-2.6.12-rc2 |
291 292 293 294 295 296 297 298 299 |
pnp_device_detach(dev); return -ENOMEM; } if (!pnp_port_valid(dev, 0)) { pnp_device_detach(dev); return -ENODEV; } i = pnp_port_start(dev, 0); |
c41269fd9 V4L/DVB (10888): ... |
300 301 |
printk(KERN_INFO "radio-sf16fmi: PnP reports card at %#x ", i); |
1da177e4c Linux-2.6.12-rc2 |
302 303 304 305 306 307 |
return i; } static int __init fmi_init(void) { |
c41269fd9 V4L/DVB (10888): ... |
308 309 |
struct fmi *fmi = &fmi_card; struct v4l2_device *v4l2_dev = &fmi->v4l2_dev; |
67cabf503 V4L/DVB (13609): ... |
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 |
int res, i; int probe_ports[] = { 0, 0x284, 0x384 }; if (io < 0) { for (i = 0; i < ARRAY_SIZE(probe_ports); i++) { io = probe_ports[i]; if (io == 0) { io = isapnp_fmi_probe(); if (io < 0) continue; pnp_attached = 1; } if (!request_region(io, 2, "radio-sf16fmi")) { if (pnp_attached) pnp_device_detach(dev); io = -1; continue; } if (pnp_attached || ((inb(io) & 0xf9) == 0xf9 && (inb(io) & 0x4) == 0)) break; release_region(io, 2); io = -1; } } else { if (!request_region(io, 2, "radio-sf16fmi")) { printk(KERN_ERR "radio-sf16fmi: port %#x already in use ", io); return -EBUSY; } if (inb(io) == 0xff) { printk(KERN_ERR "radio-sf16fmi: card not present at %#x ", io); release_region(io, 2); return -ENODEV; } } if (io < 0) { printk(KERN_ERR "radio-sf16fmi: no cards found "); return -ENODEV; } |
c41269fd9 V4L/DVB (10888): ... |
352 |
|
c41269fd9 V4L/DVB (10888): ... |
353 354 |
strlcpy(v4l2_dev->name, "sf16fmi", sizeof(v4l2_dev->name)); fmi->io = io; |
1da177e4c Linux-2.6.12-rc2 |
355 |
|
c41269fd9 V4L/DVB (10888): ... |
356 357 358 |
res = v4l2_device_register(NULL, v4l2_dev); if (res < 0) { release_region(fmi->io, 2); |
67cabf503 V4L/DVB (13609): ... |
359 360 |
if (pnp_attached) pnp_device_detach(dev); |
c41269fd9 V4L/DVB (10888): ... |
361 362 363 364 |
v4l2_err(v4l2_dev, "Could not register v4l2_device "); return res; } |
c41269fd9 V4L/DVB (10888): ... |
365 366 367 368 369 370 |
strlcpy(fmi->vdev.name, v4l2_dev->name, sizeof(fmi->vdev.name)); fmi->vdev.v4l2_dev = v4l2_dev; fmi->vdev.fops = &fmi_fops; fmi->vdev.ioctl_ops = &fmi_ioctl_ops; fmi->vdev.release = video_device_release_empty; video_set_drvdata(&fmi->vdev, fmi); |
4286c6f65 V4L/DVB (3753): W... |
371 |
|
c41269fd9 V4L/DVB (10888): ... |
372 |
mutex_init(&fmi->lock); |
4286c6f65 V4L/DVB (3753): W... |
373 |
|
32958fdd1 [media] BKL: triv... |
374 375 |
/* mute card - prevents noisy bootups */ fmi_mute(fmi); |
c41269fd9 V4L/DVB (10888): ... |
376 377 378 |
if (video_register_device(&fmi->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { v4l2_device_unregister(v4l2_dev); release_region(fmi->io, 2); |
67cabf503 V4L/DVB (13609): ... |
379 380 |
if (pnp_attached) pnp_device_detach(dev); |
1da177e4c Linux-2.6.12-rc2 |
381 382 |
return -EINVAL; } |
4286c6f65 V4L/DVB (3753): W... |
383 |
|
c41269fd9 V4L/DVB (10888): ... |
384 385 |
v4l2_info(v4l2_dev, "card driver at 0x%x ", fmi->io); |
1da177e4c Linux-2.6.12-rc2 |
386 387 |
return 0; } |
c41269fd9 V4L/DVB (10888): ... |
388 |
static void __exit fmi_exit(void) |
1da177e4c Linux-2.6.12-rc2 |
389 |
{ |
c41269fd9 V4L/DVB (10888): ... |
390 391 392 393 394 |
struct fmi *fmi = &fmi_card; video_unregister_device(&fmi->vdev); v4l2_device_unregister(&fmi->v4l2_dev); release_region(fmi->io, 2); |
67cabf503 V4L/DVB (13609): ... |
395 |
if (dev && pnp_attached) |
1da177e4c Linux-2.6.12-rc2 |
396 397 398 399 |
pnp_device_detach(dev); } module_init(fmi_init); |
c41269fd9 V4L/DVB (10888): ... |
400 |
module_exit(fmi_exit); |