Blame view

drivers/media/radio/radio-sf16fmi.c 9.63 KB
4b8303747   Ondrej Zary   V4L/DVB (13608): ...
1
  /* SF16-FMI and SF16-FMP radio driver for Linux radio support
1da177e4c   Linus Torvalds   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   Alan Cox   V4L/DVB (9491): r...
6
   * Fitted to new interface by Alan Cox <alan@lxorguk.ukuu.org.uk>
1da177e4c   Linus Torvalds   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   Ondrej Zary   V4L/DVB (13608): ...
14
   *  control on SB-part of SF16-FMI/SF16-FMP
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
15
   *
a2ef73af4   Mauro Carvalho Chehab   V4L/DVB (4353): V...
16
   * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
1da177e4c   Linus Torvalds   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   Peter Osterlund   [PATCH] Remove un...
22
  #include <linux/ioport.h>	/* request_region		*/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
  #include <linux/delay.h>	/* udelay			*/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
  #include <linux/isapnp.h>
3593cab5d   Ingo Molnar   V4L/DVB (3318b): ...
25
  #include <linux/mutex.h>
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
26
27
  #include <linux/videodev2.h>	/* kernel radio structs		*/
  #include <linux/io.h>		/* outb, outb_p			*/
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
28
29
  #include <media/v4l2-device.h>
  #include <media/v4l2-ioctl.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30

c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
31
  MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
4b8303747   Ondrej Zary   V4L/DVB (13608): ...
32
  MODULE_DESCRIPTION("A driver for the SF16-FMI and SF16-FMP radio.");
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
33
  MODULE_LICENSE("GPL");
29834c1ac   Mauro Carvalho Chehab   [media] radio: Us...
34
  MODULE_VERSION("0.0.3");
a2ef73af4   Mauro Carvalho Chehab   V4L/DVB (4353): V...
35

c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
36
37
38
39
  static int io = -1;
  static int radio_nr = -1;
  
  module_param(io, int, 0);
4b8303747   Ondrej Zary   V4L/DVB (13608): ...
40
  MODULE_PARM_DESC(io, "I/O address of the SF16-FMI or SF16-FMP card (0x284 or 0x384)");
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
41
  module_param(radio_nr, int, 0);
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
42
  struct fmi
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
  {
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
44
45
46
  	struct v4l2_device v4l2_dev;
  	struct video_device vdev;
  	int io;
4b8303747   Ondrej Zary   V4L/DVB (13608): ...
47
  	bool mute;
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
48
  	unsigned long curfreq; /* freq in kHz */
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
49
  	struct mutex lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
  };
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
51
52
  static struct fmi fmi_card;
  static struct pnp_dev *dev;
67cabf503   Ondrej Zary   V4L/DVB (13609): ...
53
  bool pnp_attached;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
55
  
  /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
1d80db562   Hans Verkuil   V4L/DVB (11676): ...
56
  /* It is only useful to give freq in interval of 800 (=0.05Mhz),
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
57
   * other bits will be truncated, e.g 92.7400016 -> 92.7, but
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
59
   * 92.7400017 -> 92.75
   */
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
60
61
62
  #define RSF16_ENCODE(x)	((x) / 800 + 214)
  #define RSF16_MINFREQ (87 * 16000)
  #define RSF16_MAXFREQ (108 * 16000)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63

c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
64
  static void outbits(int bits, unsigned int data, int io)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
  {
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
66
67
68
  	while (bits--) {
  		if (data & 1) {
  			outb(5, io);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
  			udelay(6);
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
70
  			outb(7, io);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
  			udelay(6);
  		} else {
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
73
  			outb(1, io);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
  			udelay(6);
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
75
  			outb(3, io);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
77
  			udelay(6);
  		}
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
78
  		data >>= 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
80
  	}
  }
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
81
  static inline void fmi_mute(struct fmi *fmi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
  {
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
83
84
85
  	mutex_lock(&fmi->lock);
  	outb(0x00, fmi->io);
  	mutex_unlock(&fmi->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
  }
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
87
  static inline void fmi_unmute(struct fmi *fmi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
  {
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
89
90
91
  	mutex_lock(&fmi->lock);
  	outb(0x08, fmi->io);
  	mutex_unlock(&fmi->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
  }
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
93
  static inline int fmi_setfreq(struct fmi *fmi, unsigned long freq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
  {
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
95
96
  	mutex_lock(&fmi->lock);
  	fmi->curfreq = freq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97

c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
98
99
  	outbits(16, RSF16_ENCODE(freq), fmi->io);
  	outbits(8, 0xC0, fmi->io);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
  	msleep(143);		/* was schedule_timeout(HZ/7) */
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
101
  	mutex_unlock(&fmi->lock);
4b8303747   Ondrej Zary   V4L/DVB (13608): ...
102
  	if (!fmi->mute)
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
103
  		fmi_unmute(fmi);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
  	return 0;
  }
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
106
  static inline int fmi_getsigstr(struct fmi *fmi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
  {
  	int val;
  	int res;
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
110

c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
111
  	mutex_lock(&fmi->lock);
4b8303747   Ondrej Zary   V4L/DVB (13608): ...
112
  	val = fmi->mute ? 0x00 : 0x08;	/* mute/unmute */
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
113
114
  	outb(val, fmi->io);
  	outb(val | 0x10, fmi->io);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
  	msleep(143); 		/* was schedule_timeout(HZ/7) */
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
116
117
  	res = (int)inb(fmi->io + 1);
  	outb(val, fmi->io);
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
118

c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
119
  	mutex_unlock(&fmi->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
  	return (res & 2) ? 0 : 0xFFFF;
  }
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
122
123
  static int vidioc_querycap(struct file *file, void  *priv,
  					struct v4l2_capability *v)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
  {
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
125
126
  	strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver));
  	strlcpy(v->card, "SF16-FMx radio", sizeof(v->card));
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
127
  	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
128
  	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
c123b8677   Douglas Landgraf   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   Hans Verkuil   V4L/DVB (10888): ...
135
  	struct fmi *fmi = video_drvdata(file);
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
136
137
138
  
  	if (v->index > 0)
  		return -EINVAL;
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
139
  	strlcpy(v->name, "FM", sizeof(v->name));
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
140
  	v->type = V4L2_TUNER_RADIO;
1d80db562   Hans Verkuil   V4L/DVB (11676): ...
141
142
  	v->rangelow = RSF16_MINFREQ;
  	v->rangehigh = RSF16_MAXFREQ;
dc47b7789   Hans Verkuil   V4L/DVB (11675): ...
143
  	v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
38092a440   Hans Verkuil   V4L/DVB (11677): ...
144
  	v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
145
146
147
148
  	v->audmode = V4L2_TUNER_MODE_STEREO;
  	v->signal = fmi_getsigstr(fmi);
  	return 0;
  }
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
149

c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
150
151
152
  static int vidioc_s_tuner(struct file *file, void *priv,
  					struct v4l2_tuner *v)
  {
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
153
  	return v->index ? -EINVAL : 0;
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
154
  }
a2ef73af4   Mauro Carvalho Chehab   V4L/DVB (4353): V...
155

c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
156
157
158
  static int vidioc_s_frequency(struct file *file, void *priv,
  					struct v4l2_frequency *f)
  {
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
159
  	struct fmi *fmi = video_drvdata(file);
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
160

a3a9e287d   Hans Verkuil   V4L/DVB (13547): ...
161
162
  	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
  		return -EINVAL;
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
163
  	if (f->frequency < RSF16_MINFREQ ||
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
164
  			f->frequency > RSF16_MAXFREQ)
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
165
  		return -EINVAL;
c41269fd9   Hans Verkuil   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   Douglas Landgraf   V4L/DVB (5550): R...
169
170
  	return 0;
  }
a2ef73af4   Mauro Carvalho Chehab   V4L/DVB (4353): V...
171

c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
172
173
174
  static int vidioc_g_frequency(struct file *file, void *priv,
  					struct v4l2_frequency *f)
  {
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
175
  	struct fmi *fmi = video_drvdata(file);
a2ef73af4   Mauro Carvalho Chehab   V4L/DVB (4353): V...
176

a3a9e287d   Hans Verkuil   V4L/DVB (13547): ...
177
178
  	if (f->tuner != 0)
  		return -EINVAL;
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
179
180
  	f->type = V4L2_TUNER_RADIO;
  	f->frequency = fmi->curfreq;
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
181
182
  	return 0;
  }
a2ef73af4   Mauro Carvalho Chehab   V4L/DVB (4353): V...
183

c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
184
185
186
  static int vidioc_queryctrl(struct file *file, void *priv,
  					struct v4l2_queryctrl *qc)
  {
c41269fd9   Hans Verkuil   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   Douglas Landgraf   V4L/DVB (5550): R...
190
191
192
  	}
  	return -EINVAL;
  }
a2ef73af4   Mauro Carvalho Chehab   V4L/DVB (4353): V...
193

c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
194
195
196
  static int vidioc_g_ctrl(struct file *file, void *priv,
  					struct v4l2_control *ctrl)
  {
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
197
  	struct fmi *fmi = video_drvdata(file);
a2ef73af4   Mauro Carvalho Chehab   V4L/DVB (4353): V...
198

c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
199
200
  	switch (ctrl->id) {
  	case V4L2_CID_AUDIO_MUTE:
4b8303747   Ondrej Zary   V4L/DVB (13608): ...
201
  		ctrl->value = fmi->mute;
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
202
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
  	}
c123b8677   Douglas Landgraf   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   Hans Verkuil   V4L/DVB (10888): ...
210
  	struct fmi *fmi = video_drvdata(file);
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
211
212
213
214
  
  	switch (ctrl->id) {
  	case V4L2_CID_AUDIO_MUTE:
  		if (ctrl->value)
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
215
  			fmi_mute(fmi);
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
216
  		else
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
217
  			fmi_unmute(fmi);
4b8303747   Ondrej Zary   V4L/DVB (13608): ...
218
  		fmi->mute = ctrl->value;
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
219
220
221
222
  		return 0;
  	}
  	return -EINVAL;
  }
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
223
  static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
  {
c123b8677   Douglas Landgraf   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   Hans Verkuil   V4L/DVB (10888): ...
231
  	return i ? -EINVAL : 0;
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
232
  }
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
233
  static int vidioc_g_audio(struct file *file, void *priv,
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
234
235
  					struct v4l2_audio *a)
  {
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
236
237
238
  	a->index = 0;
  	strlcpy(a->name, "Radio", sizeof(a->name));
  	a->capability = V4L2_AUDCAP_STEREO;
c123b8677   Douglas Landgraf   V4L/DVB (5550): R...
239
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
  }
c41269fd9   Hans Verkuil   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   Linus Torvalds   Linux-2.6.12-rc2
246

bec43661b   Hans Verkuil   V4L/DVB (10135): ...
247
  static const struct v4l2_file_operations fmi_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
  	.owner		= THIS_MODULE,
32958fdd1   Hans Verkuil   [media] BKL: triv...
249
  	.unlocked_ioctl	= video_ioctl2,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
  };
a399810ca   Hans Verkuil   V4L/DVB (8482): v...
251
  static const struct v4l2_ioctl_ops fmi_ioctl_ops = {
c123b8677   Douglas Landgraf   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   Linus Torvalds   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   Randy Dunlap   V4L/DVB (7078): r...
274
  static int __init isapnp_fmi_probe(void)
1da177e4c   Linus Torvalds   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   Hans Verkuil   V4L/DVB (10888): ...
289
290
  		printk(KERN_ERR "radio-sf16fmi: PnP configure failed (out of resources?)
  ");
1da177e4c   Linus Torvalds   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   Hans Verkuil   V4L/DVB (10888): ...
300
301
  	printk(KERN_INFO "radio-sf16fmi: PnP reports card at %#x
  ", i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
304
305
306
307
  
  	return i;
  }
  
  static int __init fmi_init(void)
  {
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
308
309
  	struct fmi *fmi = &fmi_card;
  	struct v4l2_device *v4l2_dev = &fmi->v4l2_dev;
67cabf503   Ondrej Zary   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   Hans Verkuil   V4L/DVB (10888): ...
352

c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
353
354
  	strlcpy(v4l2_dev->name, "sf16fmi", sizeof(v4l2_dev->name));
  	fmi->io = io;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355

c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
356
357
358
  	res = v4l2_device_register(NULL, v4l2_dev);
  	if (res < 0) {
  		release_region(fmi->io, 2);
67cabf503   Ondrej Zary   V4L/DVB (13609): ...
359
360
  		if (pnp_attached)
  			pnp_device_detach(dev);
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
361
362
363
364
  		v4l2_err(v4l2_dev, "Could not register v4l2_device
  ");
  		return res;
  	}
c41269fd9   Hans Verkuil   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   Mauro Carvalho Chehab   V4L/DVB (3753): W...
371

c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
372
  	mutex_init(&fmi->lock);
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
373

32958fdd1   Hans Verkuil   [media] BKL: triv...
374
375
  	/* mute card - prevents noisy bootups */
  	fmi_mute(fmi);
c41269fd9   Hans Verkuil   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   Ondrej Zary   V4L/DVB (13609): ...
379
380
  		if (pnp_attached)
  			pnp_device_detach(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
382
  		return -EINVAL;
  	}
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
383

c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
384
385
  	v4l2_info(v4l2_dev, "card driver at 0x%x
  ", fmi->io);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
387
  	return 0;
  }
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
388
  static void __exit fmi_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
  {
c41269fd9   Hans Verkuil   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   Ondrej Zary   V4L/DVB (13609): ...
395
  	if (dev && pnp_attached)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
397
398
399
  		pnp_device_detach(dev);
  }
  
  module_init(fmi_init);
c41269fd9   Hans Verkuil   V4L/DVB (10888): ...
400
  module_exit(fmi_exit);