Blame view

drivers/media/radio/radio-cadet.c 16.6 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
  /* radio-cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card
   *
   * by Fred Gleason <fredg@wava.com>
   * Version 0.3.3
   *
   * (Loosely) based on code for the Aztech radio card by
   *
   * Russell Kroll    (rkroll@exploits.org)
   * Quay Ly
   * Donald Song
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
11
   * Jason Lewis      (jlewis@twilight.vtc.vsc.edu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
16
17
18
19
20
21
22
23
24
25
   * Scott McGrath    (smcgrath@twilight.vtc.vsc.edu)
   * William McGrath  (wmcgrath@twilight.vtc.vsc.edu)
   *
   * History:
   * 2000-04-29	Russell Kroll <rkroll@exploits.org>
   *		Added ISAPnP detection for Linux 2.3/2.4
   *
   * 2001-01-10	Russell Kroll <rkroll@exploits.org>
   *		Removed dead CONFIG_RADIO_CADET_PORT code
   *		PnP detection on load is now default (no args necessary)
   *
   * 2002-01-17	Adam Belay <ambx1@neo.rr.com>
   *		Updated to latest pnp code
   *
d9b01449e   Alan Cox   V4L/DVB (9491): r...
26
   * 2003-01-31	Alan Cox <alan@lxorguk.ukuu.org.uk>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
   *		Cleaned up locking, delay code, general odds and ends
c0c7fa096   Hans J. Koch   V4L/DVB (4406): C...
28
29
30
   *
   * 2006-07-30	Hans J. Koch <koch@hjk-az.de>
   *		Changed API to V4L2
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
33
34
   */
  
  #include <linux/module.h>	/* Modules 			*/
  #include <linux/init.h>		/* Initdata			*/
fb911ee84   Peter Osterlund   [PATCH] Remove un...
35
  #include <linux/ioport.h>	/* request_region		*/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
  #include <linux/delay.h>	/* udelay			*/
c0c7fa096   Hans J. Koch   V4L/DVB (4406): C...
37
  #include <linux/videodev2.h>	/* V4L2 API defs		*/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
  #include <linux/param.h>
  #include <linux/pnp.h>
a99bbaf5e   Alexey Dobriyan   headers: remove s...
40
  #include <linux/sched.h>
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
41
  #include <linux/io.h>		/* outb, outb_p			*/
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
42
43
44
45
46
47
  #include <media/v4l2-device.h>
  #include <media/v4l2-ioctl.h>
  
  MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
  MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
  MODULE_LICENSE("GPL");
29834c1ac   Mauro Carvalho Chehab   [media] radio: Us...
48
  MODULE_VERSION("0.3.4");
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
49
50
51
52
53
54
55
  
  static int io = -1;		/* default to isapnp activation */
  static int radio_nr = -1;
  
  module_param(io, int, 0);
  MODULE_PARM_DESC(io, "I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e)");
  module_param(radio_nr, int, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
  #define RDS_BUFFER 256
c0c7fa096   Hans J. Koch   V4L/DVB (4406): C...
57
58
  #define RDS_RX_FLAG 1
  #define MBS_RX_FLAG 2
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  struct cadet {
  	struct v4l2_device v4l2_dev;
  	struct video_device vdev;
  	int io;
  	int users;
  	int curtuner;
  	int tunestat;
  	int sigstrength;
  	wait_queue_head_t read_queue;
  	struct timer_list readtimer;
  	__u8 rdsin, rdsout, rdsstat;
  	unsigned char rdsbuf[RDS_BUFFER];
  	struct mutex lock;
  	int reading;
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
73
  };
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
74
  static struct cadet cadet_card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
  
  /*
   * Signal Strength Threshold Values
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
78
   * The V4L API spec does not define any particular unit for the signal
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
80
   * strength value.  These values are in microvolts of RF at the tuner's input.
   */
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
81
82
83
84
  static __u16 sigtable[2][4] = {
  	{  5, 10, 30,  150 },
  	{ 28, 40, 63, 1000 }
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
87
  static int cadet_getstereo(struct cadet *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
  {
c0c7fa096   Hans J. Koch   V4L/DVB (4406): C...
89
  	int ret = V4L2_TUNER_SUB_MONO;
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
90
91
  
  	if (dev->curtuner != 0)	/* Only FM has stereo capability! */
c0c7fa096   Hans J. Koch   V4L/DVB (4406): C...
92
  		return V4L2_TUNER_SUB_MONO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
94
95
96
  	mutex_lock(&dev->lock);
  	outb(7, dev->io);          /* Select tuner control */
  	if ((inb(dev->io + 1) & 0x40) == 0)
c0c7fa096   Hans J. Koch   V4L/DVB (4406): C...
97
  		ret = V4L2_TUNER_SUB_STEREO;
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
98
  	mutex_unlock(&dev->lock);
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
99
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
  }
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
101
  static unsigned cadet_gettune(struct cadet *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
103
104
  	int curvol, i;
  	unsigned fifo = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105

4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
106
107
108
  	/*
  	 * Prepare for read
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
110
  	mutex_lock(&dev->lock);
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
111

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
112
113
114
115
  	outb(7, dev->io);       /* Select tuner control */
  	curvol = inb(dev->io + 1); /* Save current volume/mute setting */
  	outb(0x00, dev->io + 1);  /* Ensure WRITE-ENABLE is LOW */
  	dev->tunestat = 0xffff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116

4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
117
118
119
  	/*
  	 * Read the shift register
  	 */
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
120
121
122
123
124
125
  	for (i = 0; i < 25; i++) {
  		fifo = (fifo << 1) | ((inb(dev->io + 1) >> 7) & 0x01);
  		if (i < 24) {
  			outb(0x01, dev->io + 1);
  			dev->tunestat &= inb(dev->io + 1);
  			outb(0x00, dev->io + 1);
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
126
127
128
129
130
131
  		}
  	}
  
  	/*
  	 * Restore volume/mute setting
  	 */
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
132
133
  	outb(curvol, dev->io + 1);
  	mutex_unlock(&dev->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
135
136
  
  	return fifo;
  }
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
137
  static unsigned cadet_getfreq(struct cadet *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  {
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
139
  	int i;
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
140
  	unsigned freq = 0, test, fifo = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
144
  
  	/*
  	 * Read current tuning
  	 */
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
145
  	fifo = cadet_gettune(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146

4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
147
148
149
  	/*
  	 * Convert to actual frequency
  	 */
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
150
151
152
153
154
155
156
  	if (dev->curtuner == 0) {    /* FM */
  		test = 12500;
  		for (i = 0; i < 14; i++) {
  			if ((fifo & 0x01) != 0)
  				freq += test;
  			test = test << 1;
  			fifo = fifo >> 1;
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
157
  		}
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
158
159
  		freq -= 10700000;           /* IF frequency is 10.7 MHz */
  		freq = (freq * 16) / 1000000;   /* Make it 1/16 MHz */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
  	}
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
161
162
  	if (dev->curtuner == 1)    /* AM */
  		freq = ((fifo & 0x7fff) - 2010) * 16;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163

4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
164
  	return freq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
  }
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
166
  static void cadet_settune(struct cadet *dev, unsigned fifo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
  {
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
168
169
  	int i;
  	unsigned test;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
171
  	mutex_lock(&dev->lock);
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
172

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
173
  	outb(7, dev->io);                /* Select tuner control */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
176
  	/*
  	 * Write the shift register
  	 */
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
177
178
179
180
181
182
183
184
185
186
187
188
189
  	test = 0;
  	test = (fifo >> 23) & 0x02;      /* Align data for SDO */
  	test |= 0x1c;                /* SDM=1, SWE=1, SEN=1, SCK=0 */
  	outb(7, dev->io);                /* Select tuner control */
  	outb(test, dev->io + 1);           /* Initialize for write */
  	for (i = 0; i < 25; i++) {
  		test |= 0x01;              /* Toggle SCK High */
  		outb(test, dev->io + 1);
  		test &= 0xfe;              /* Toggle SCK Low */
  		outb(test, dev->io + 1);
  		fifo = fifo << 1;            /* Prepare the next bit */
  		test = 0x1c | ((fifo >> 23) & 0x02);
  		outb(test, dev->io + 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
  	}
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
191
  	mutex_unlock(&dev->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
  }
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
193
  static void cadet_setfreq(struct cadet *dev, unsigned freq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
  {
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
195
  	unsigned fifo;
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
196
  	int i, j, test;
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
197
  	int curvol;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198

4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
199
200
201
  	/*
  	 * Formulate a fifo command
  	 */
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
202
203
204
205
206
207
208
209
210
211
  	fifo = 0;
  	if (dev->curtuner == 0) {    /* FM */
  		test = 102400;
  		freq = (freq * 1000) / 16;       /* Make it kHz */
  		freq += 10700;               /* IF is 10700 kHz */
  		for (i = 0; i < 14; i++) {
  			fifo = fifo << 1;
  			if (freq >= test) {
  				fifo |= 0x01;
  				freq -= test;
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
212
  			}
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
213
  			test = test >> 1;
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
214
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
  	}
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
216
217
218
  	if (dev->curtuner == 1) {    /* AM */
  		fifo = (freq / 16) + 2010;            /* Make it kHz */
  		fifo |= 0x100000;            /* Select AM Band */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
  	}
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
220
221
222
  	/*
  	 * Save current volume/mute setting
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
224
225
226
227
  	mutex_lock(&dev->lock);
  	outb(7, dev->io);                /* Select tuner control */
  	curvol = inb(dev->io + 1);
  	mutex_unlock(&dev->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
229
230
231
  
  	/*
  	 * Tune the card
  	 */
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
232
233
  	for (j = 3; j > -1; j--) {
  		cadet_settune(dev, fifo | (j << 16));
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
234

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
235
236
237
238
  		mutex_lock(&dev->lock);
  		outb(7, dev->io);         /* Select tuner control */
  		outb(curvol, dev->io + 1);
  		mutex_unlock(&dev->lock);
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
239

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
  		msleep(100);
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
241
242
243
  		cadet_gettune(dev);
  		if ((dev->tunestat & 0x40) == 0) {   /* Tuned */
  			dev->sigstrength = sigtable[dev->curtuner][j];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
246
  			return;
  		}
  	}
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
247
  	dev->sigstrength = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
  }
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
249
  static int cadet_getvol(struct cadet *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
  {
  	int ret = 0;
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
252

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
253
  	mutex_lock(&dev->lock);
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
254

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
255
256
  	outb(7, dev->io);                /* Select tuner control */
  	if ((inb(dev->io + 1) & 0x20) != 0)
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
257
  		ret = 0xffff;
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
258
  	mutex_unlock(&dev->lock);
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
259
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
  }
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
261
  static void cadet_setvol(struct cadet *dev, int vol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
263
264
265
266
  	mutex_lock(&dev->lock);
  	outb(7, dev->io);                /* Select tuner control */
  	if (vol > 0)
  		outb(0x20, dev->io + 1);
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
267
  	else
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
268
269
  		outb(0x00, dev->io + 1);
  	mutex_unlock(&dev->lock);
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
270
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
272
  static void cadet_handler(unsigned long data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
274
  	struct cadet *dev = (void *)data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
276
277
278
279
  	/* Service the RDS fifo */
  	if (mutex_trylock(&dev->lock)) {
  		outb(0x3, dev->io);       /* Select RDS Decoder Control */
  		if ((inb(dev->io + 1) & 0x20) != 0)
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
280
281
  			printk(KERN_CRIT "cadet: RDS fifo overflow
  ");
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
282
283
284
285
  		outb(0x80, dev->io);      /* Select RDS fifo */
  		while ((inb(dev->io) & 0x80) != 0) {
  			dev->rdsbuf[dev->rdsin] = inb(dev->io + 1);
  			if (dev->rdsin == dev->rdsout)
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
286
287
  				printk(KERN_WARNING "cadet: RDS buffer overflow
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
  			else
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
289
  				dev->rdsin++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
  		}
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
291
  		mutex_unlock(&dev->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
295
296
  	}
  
  	/*
  	 * Service pending read
  	 */
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
297
298
  	if (dev->rdsin != dev->rdsout)
  		wake_up_interruptible(&dev->read_queue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299

4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
300
  	/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
302
  	 * Clean up and exit
  	 */
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
303
304
305
306
307
  	init_timer(&dev->readtimer);
  	dev->readtimer.function = cadet_handler;
  	dev->readtimer.data = (unsigned long)0;
  	dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
  	add_timer(&dev->readtimer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
  }
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
309
  static ssize_t cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
311
  	struct cadet *dev = video_drvdata(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
  	unsigned char readbuf[RDS_BUFFER];
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
313
  	int i = 0;
1cccee0b8   Hans Verkuil   [media] cadet: us...
314
  	mutex_lock(&dev->lock);
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
315
  	if (dev->rdsstat == 0) {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
316
317
  		dev->rdsstat = 1;
  		outb(0x80, dev->io);        /* Select RDS fifo */
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
318
319
320
321
322
  		init_timer(&dev->readtimer);
  		dev->readtimer.function = cadet_handler;
  		dev->readtimer.data = (unsigned long)dev;
  		dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
  		add_timer(&dev->readtimer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
  	}
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
324
  	if (dev->rdsin == dev->rdsout) {
1cccee0b8   Hans Verkuil   [media] cadet: us...
325
  		mutex_unlock(&dev->lock);
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
326
327
  		if (file->f_flags & O_NONBLOCK)
  			return -EWOULDBLOCK;
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
328
  		interruptible_sleep_on(&dev->read_queue);
1cccee0b8   Hans Verkuil   [media] cadet: us...
329
  		mutex_lock(&dev->lock);
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
330
  	}
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
331
332
  	while (i < count && dev->rdsin != dev->rdsout)
  		readbuf[i++] = dev->rdsbuf[dev->rdsout++];
1cccee0b8   Hans Verkuil   [media] cadet: us...
333
  	mutex_unlock(&dev->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
335
  	if (copy_to_user(data, readbuf, i))
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
336
  		return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
338
  	return i;
  }
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
339
340
341
  static int vidioc_querycap(struct file *file, void *priv,
  				struct v4l2_capability *v)
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
342
343
344
  	strlcpy(v->driver, "ADS Cadet", sizeof(v->driver));
  	strlcpy(v->card, "ADS Cadet", sizeof(v->card));
  	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
8e280f24d   Hans Verkuil   V4L/DVB (12217): ...
345
346
  	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
  			  V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE;
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
347
348
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349

c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
350
351
  static int vidioc_g_tuner(struct file *file, void *priv,
  				struct v4l2_tuner *v)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
353
  	struct cadet *dev = video_drvdata(file);
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
354
355
356
  	v->type = V4L2_TUNER_RADIO;
  	switch (v->index) {
  	case 0:
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
357
  		strlcpy(v->name, "FM", sizeof(v->name));
cb0ed2227   Matti Aaltonen   [media] [RFC,1/1]...
358
359
  		v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
  			V4L2_TUNER_CAP_RDS_BLOCK_IO;
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
360
361
  		v->rangelow = 1400;     /* 87.5 MHz */
  		v->rangehigh = 1728;    /* 108.0 MHz */
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
362
363
  		v->rxsubchans = cadet_getstereo(dev);
  		switch (v->rxsubchans) {
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
364
365
366
367
368
369
  		case V4L2_TUNER_SUB_MONO:
  			v->audmode = V4L2_TUNER_MODE_MONO;
  			break;
  		case V4L2_TUNER_SUB_STEREO:
  			v->audmode = V4L2_TUNER_MODE_STEREO;
  			break;
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
370
371
  		default:
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
  		}
8e280f24d   Hans Verkuil   V4L/DVB (12217): ...
373
  		v->rxsubchans |= V4L2_TUNER_SUB_RDS;
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
374
375
  		break;
  	case 1:
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
376
  		strlcpy(v->name, "AM", sizeof(v->name));
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
377
378
379
380
381
382
383
384
385
  		v->capability = V4L2_TUNER_CAP_LOW;
  		v->rangelow = 8320;      /* 520 kHz */
  		v->rangehigh = 26400;    /* 1650 kHz */
  		v->rxsubchans = V4L2_TUNER_SUB_MONO;
  		v->audmode = V4L2_TUNER_MODE_MONO;
  		break;
  	default:
  		return -EINVAL;
  	}
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
386
  	v->signal = dev->sigstrength; /* We might need to modify scaling of this */
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
387
388
  	return 0;
  }
c0c7fa096   Hans J. Koch   V4L/DVB (4406): C...
389

c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
390
391
392
  static int vidioc_s_tuner(struct file *file, void *priv,
  				struct v4l2_tuner *v)
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
393
394
395
  	struct cadet *dev = video_drvdata(file);
  
  	if (v->index != 0 && v->index != 1)
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
396
  		return -EINVAL;
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
397
  	dev->curtuner = v->index;
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
398
399
  	return 0;
  }
c0c7fa096   Hans J. Koch   V4L/DVB (4406): C...
400

c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
401
402
403
  static int vidioc_g_frequency(struct file *file, void *priv,
  				struct v4l2_frequency *f)
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
404
405
406
  	struct cadet *dev = video_drvdata(file);
  
  	f->tuner = dev->curtuner;
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
407
  	f->type = V4L2_TUNER_RADIO;
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
408
  	f->frequency = cadet_getfreq(dev);
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
409
410
411
412
413
414
415
  	return 0;
  }
  
  
  static int vidioc_s_frequency(struct file *file, void *priv,
  				struct v4l2_frequency *f)
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
416
  	struct cadet *dev = video_drvdata(file);
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
417
418
  	if (f->type != V4L2_TUNER_RADIO)
  		return -EINVAL;
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
419
  	if (dev->curtuner == 0 && (f->frequency < 1400 || f->frequency > 1728))
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
420
  		return -EINVAL;
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
421
  	if (dev->curtuner == 1 && (f->frequency < 8320 || f->frequency > 26400))
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
422
  		return -EINVAL;
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
423
  	cadet_setfreq(dev, f->frequency);
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
424
425
426
427
428
429
  	return 0;
  }
  
  static int vidioc_queryctrl(struct file *file, void *priv,
  				struct v4l2_queryctrl *qc)
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
430
431
432
433
434
  	switch (qc->id) {
  	case V4L2_CID_AUDIO_MUTE:
  		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
  	case V4L2_CID_AUDIO_VOLUME:
  		return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
435
436
437
  	}
  	return -EINVAL;
  }
c0c7fa096   Hans J. Koch   V4L/DVB (4406): C...
438

c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
439
440
441
  static int vidioc_g_ctrl(struct file *file, void *priv,
  				struct v4l2_control *ctrl)
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
442
443
444
  	struct cadet *dev = video_drvdata(file);
  
  	switch (ctrl->id) {
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
445
  	case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
446
  		ctrl->value = (cadet_getvol(dev) == 0);
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
447
448
  		break;
  	case V4L2_CID_AUDIO_VOLUME:
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
449
  		ctrl->value = cadet_getvol(dev);
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
450
451
452
  		break;
  	default:
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
  	}
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
454
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
  }
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
456
457
  static int vidioc_s_ctrl(struct file *file, void *priv,
  				struct v4l2_control *ctrl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
459
  	struct cadet *dev = video_drvdata(file);
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
460
461
462
  	switch (ctrl->id){
  	case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
  		if (ctrl->value)
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
463
  			cadet_setvol(dev, 0);
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
464
  		else
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
465
  			cadet_setvol(dev, 0xffff);
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
466
467
  		break;
  	case V4L2_CID_AUDIO_VOLUME:
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
468
  		cadet_setvol(dev, ctrl->value);
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
469
470
471
472
473
474
  		break;
  	default:
  		return -EINVAL;
  	}
  	return 0;
  }
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
475
476
477
478
479
480
481
482
  static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
  {
  	*i = 0;
  	return 0;
  }
  
  static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
483
484
485
486
487
488
489
490
491
  	return i ? -EINVAL : 0;
  }
  
  static int vidioc_g_audio(struct file *file, void *priv,
  				struct v4l2_audio *a)
  {
  	a->index = 0;
  	strlcpy(a->name, "Radio", sizeof(a->name));
  	a->capability = V4L2_AUDCAP_STEREO;
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
492
493
494
495
496
497
  	return 0;
  }
  
  static int vidioc_s_audio(struct file *file, void *priv,
  				struct v4l2_audio *a)
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
498
  	return a->index ? -EINVAL : 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
  }
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
500
  static int cadet_open(struct file *file)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
502
  	struct cadet *dev = video_drvdata(file);
1cccee0b8   Hans Verkuil   [media] cadet: us...
503
  	mutex_lock(&dev->lock);
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
504
505
506
  	dev->users++;
  	if (1 == dev->users)
  		init_waitqueue_head(&dev->read_queue);
1cccee0b8   Hans Verkuil   [media] cadet: us...
507
  	mutex_unlock(&dev->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
509
  	return 0;
  }
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
510
  static int cadet_release(struct file *file)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
512
  	struct cadet *dev = video_drvdata(file);
1cccee0b8   Hans Verkuil   [media] cadet: us...
513
  	mutex_lock(&dev->lock);
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
514
515
516
517
  	dev->users--;
  	if (0 == dev->users) {
  		del_timer_sync(&dev->readtimer);
  		dev->rdsstat = 0;
c0c7fa096   Hans J. Koch   V4L/DVB (4406): C...
518
  	}
1cccee0b8   Hans Verkuil   [media] cadet: us...
519
  	mutex_unlock(&dev->lock);
c0c7fa096   Hans J. Koch   V4L/DVB (4406): C...
520
521
  	return 0;
  }
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
522
  static unsigned int cadet_poll(struct file *file, struct poll_table_struct *wait)
c0c7fa096   Hans J. Koch   V4L/DVB (4406): C...
523
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
524
525
526
527
  	struct cadet *dev = video_drvdata(file);
  
  	poll_wait(file, &dev->read_queue, wait);
  	if (dev->rdsin != dev->rdsout)
c0c7fa096   Hans J. Koch   V4L/DVB (4406): C...
528
  		return POLLIN | POLLRDNORM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
530
  	return 0;
  }
bec43661b   Hans Verkuil   V4L/DVB (10135): ...
531
  static const struct v4l2_file_operations cadet_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532
533
534
535
  	.owner		= THIS_MODULE,
  	.open		= cadet_open,
  	.release       	= cadet_release,
  	.read		= cadet_read,
1cccee0b8   Hans Verkuil   [media] cadet: us...
536
  	.unlocked_ioctl	= video_ioctl2,
c0c7fa096   Hans J. Koch   V4L/DVB (4406): C...
537
  	.poll		= cadet_poll,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538
  };
a399810ca   Hans Verkuil   V4L/DVB (8482): v...
539
  static const struct v4l2_ioctl_ops cadet_ioctl_ops = {
c1c4fd3ee   Douglas Landgraf   V4L/DVB (5621): R...
540
541
542
543
544
545
546
547
548
549
550
551
  	.vidioc_querycap    = vidioc_querycap,
  	.vidioc_g_tuner     = vidioc_g_tuner,
  	.vidioc_s_tuner     = vidioc_s_tuner,
  	.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,
  	.vidioc_g_audio     = vidioc_g_audio,
  	.vidioc_s_audio     = vidioc_s_audio,
  	.vidioc_g_input     = vidioc_g_input,
  	.vidioc_s_input     = vidioc_s_input,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
  };
044dfc99f   Bjorn Helgaas   V4L/DVB (7486): r...
553
  #ifdef CONFIG_PNP
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
554
555
556
557
558
559
560
  static struct pnp_device_id cadet_pnp_devices[] = {
  	/* ADS Cadet AM/FM Radio Card */
  	{.id = "MSM0c24", .driver_data = 0},
  	{.id = ""}
  };
  
  MODULE_DEVICE_TABLE(pnp, cadet_pnp_devices);
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
561
  static int cadet_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
563
564
565
566
567
  {
  	if (!dev)
  		return -ENODEV;
  	/* only support one device */
  	if (io > 0)
  		return -EBUSY;
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
568
  	if (!pnp_port_valid(dev, 0))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
571
  
  	io = pnp_port_start(dev, 0);
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
572
573
  	printk(KERN_INFO "radio-cadet: PnP reports device at %#x
  ", io);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574
575
576
577
578
579
580
581
582
583
  
  	return io;
  }
  
  static struct pnp_driver cadet_pnp_driver = {
  	.name		= "radio-cadet",
  	.id_table	= cadet_pnp_devices,
  	.probe		= cadet_pnp_probe,
  	.remove		= NULL,
  };
044dfc99f   Bjorn Helgaas   V4L/DVB (7486): r...
584
585
586
  #else
  static struct pnp_driver cadet_pnp_driver;
  #endif
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
587
  static void cadet_probe(struct cadet *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
589
  	static int iovals[8] = { 0x330, 0x332, 0x334, 0x336, 0x338, 0x33a, 0x33c, 0x33e };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
  	int i;
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
591
592
593
594
595
596
597
  	for (i = 0; i < 8; i++) {
  		dev->io = iovals[i];
  		if (request_region(dev->io, 2, "cadet-probe")) {
  			cadet_setfreq(dev, 1410);
  			if (cadet_getfreq(dev) == 1410) {
  				release_region(dev->io, 2);
  				return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
  			}
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
599
  			release_region(dev->io, 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
600
601
  		}
  	}
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
602
  	dev->io = -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603
  }
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
604
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
605
606
607
608
609
610
   * io should only be set if the user has used something like
   * isapnp (the userspace program) to initialize this card for us
   */
  
  static int __init cadet_init(void)
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
611
612
613
  	struct cadet *dev = &cadet_card;
  	struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
  	int res;
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
614

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
615
616
617
618
  	strlcpy(v4l2_dev->name, "cadet", sizeof(v4l2_dev->name));
  	mutex_init(&dev->lock);
  
  	/* If a probe was requested then probe ISAPnP first (safest) */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
620
  	if (io < 0)
  		pnp_register_driver(&cadet_pnp_driver);
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
621
  	dev->io = io;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
623
624
625
  	/* If that fails then probe unsafely if probe is requested */
  	if (dev->io < 0)
  		cadet_probe(dev);
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
626

bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
627
628
  	/* Else we bail out */
  	if (dev->io < 0) {
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
629
  #ifdef MODULE
b24c20cc5   Hans Verkuil   V4L/DVB (10894): ...
630
631
632
633
  		v4l2_err(v4l2_dev, "you must set an I/O address with io=0x330, 0x332, 0x334,
  ");
  		v4l2_err(v4l2_dev, "0x336, 0x338, 0x33a, 0x33c or 0x33e
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
  #endif
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
635
  		goto fail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
  	}
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
637
638
639
640
641
642
643
644
  	if (!request_region(dev->io, 2, "cadet"))
  		goto fail;
  
  	res = v4l2_device_register(NULL, v4l2_dev);
  	if (res < 0) {
  		release_region(dev->io, 2);
  		v4l2_err(v4l2_dev, "could not register v4l2_device
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
645
  		goto fail;
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
646
647
648
649
650
651
652
653
654
655
656
657
  	}
  
  	strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
  	dev->vdev.v4l2_dev = v4l2_dev;
  	dev->vdev.fops = &cadet_fops;
  	dev->vdev.ioctl_ops = &cadet_ioctl_ops;
  	dev->vdev.release = video_device_release_empty;
  	video_set_drvdata(&dev->vdev, dev);
  
  	if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
  		v4l2_device_unregister(v4l2_dev);
  		release_region(dev->io, 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
659
  		goto fail;
  	}
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
660
661
  	v4l2_info(v4l2_dev, "ADS Cadet Radio Card at 0x%x
  ", dev->io);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
662
663
664
  	return 0;
  fail:
  	pnp_unregister_driver(&cadet_pnp_driver);
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
665
  	return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
666
  }
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
667
  static void __exit cadet_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668
  {
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
669
670
671
672
673
  	struct cadet *dev = &cadet_card;
  
  	video_unregister_device(&dev->vdev);
  	v4l2_device_unregister(&dev->v4l2_dev);
  	release_region(dev->io, 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674
675
676
677
  	pnp_unregister_driver(&cadet_pnp_driver);
  }
  
  module_init(cadet_init);
bec2aec56   Hans Verkuil   V4L/DVB (10882): ...
678
  module_exit(cadet_exit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
679