Commit | Line | Data |
---|---|---|
aa337ef1 SS |
1 | /* |
2 | ||
3 | Copyright 1996,2002,2005 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan, | |
4 | Jason Lapenta, Scott Smedley, Greg Sharp | |
5 | ||
6 | This file is part of the DT3155 Device Driver. | |
7 | ||
8 | The DT3155 Device Driver is free software; you can redistribute it | |
9 | and/or modify it under the terms of the GNU General Public License as | |
10 | published by the Free Software Foundation; either version 2 of the | |
11 | License, or (at your option) any later version. | |
12 | ||
13 | The DT3155 Device Driver is distributed in the hope that it will be | |
14 | useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with the DT3155 Device Driver; if not, write to the Free | |
20 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
21 | MA 02111-1307 USA | |
22 | ||
23 | -- Changes -- | |
24 | ||
25 | Date Programmer Description of changes made | |
26 | ------------------------------------------------------------------- | |
27 | 03-Jul-2000 JML n/a | |
28 | 10-Oct-2001 SS port to 2.4 kernel | |
29 | 02-Apr-2002 SS Mods to use allocator as a standalone module; | |
30 | Merged John Roll's changes (john@cfa.harvard.edu) | |
31 | to make work with multiple boards. | |
32 | 02-Jul-2002 SS Merged James Rose's chages (rosejr@purdue.edu) to: | |
33 | * fix successive interrupt-driven captures | |
34 | * add select/poll support. | |
35 | 10-Jul-2002 GCS Add error check when ndevices > MAXBOARDS. | |
36 | 02-Aug-2002 GCS Fix field mode so that odd (lower) field is stored | |
37 | in lower half of buffer. | |
38 | 05-Aug-2005 SS port to 2.6 kernel. | |
39 | 26-Oct-2009 SS port to 2.6.30 kernel. | |
40 | ||
41 | -- Notes -- | |
42 | ||
43 | ** appended "mem=124" in lilo.conf to allow for 4megs free on my 128meg system. | |
44 | * using allocator.c and allocator.h from o'reilly book (alessandro rubini) | |
45 | ftp://ftp.systemy.it/pub/develop (see README.allocator) | |
46 | ||
47 | + might want to get rid of MAXboards for allocating initial buffer. | |
48 | confusing and not necessary | |
49 | ||
50 | + in cleanup_module the MOD_IN_USE looks like it is check after it should | |
51 | ||
52 | * GFP_DMA should not be set with a PCI system (pg 291) | |
53 | ||
54 | - NJC why are only two buffers allowed? (see isr, approx line 358) | |
55 | ||
56 | */ | |
57 | ||
58 | extern void printques(int); | |
59 | ||
aa337ef1 | 60 | #include <linux/module.h> |
aa337ef1 | 61 | #include <linux/interrupt.h> |
aa337ef1 SS |
62 | #include <linux/pci.h> |
63 | #include <linux/types.h> | |
64 | #include <linux/poll.h> | |
ae7fd7b8 | 65 | #include <linux/sched.h> |
aa337ef1 SS |
66 | |
67 | #include <asm/io.h> | |
68 | #include <asm/uaccess.h> | |
69 | ||
70 | #include "dt3155.h" | |
71 | #include "dt3155_drv.h" | |
72 | #include "dt3155_isr.h" | |
73 | #include "dt3155_io.h" | |
74 | #include "allocator.h" | |
75 | ||
74a92013 RD |
76 | |
77 | MODULE_LICENSE("GPL"); | |
78 | ||
aa337ef1 SS |
79 | /* Error variable. Zero means no error. */ |
80 | int dt3155_errno = 0; | |
81 | ||
82 | #ifndef PCI_DEVICE_ID_INTEL_7116 | |
83 | #define PCI_DEVICE_ID_INTEL_7116 0x1223 | |
84 | #endif | |
85 | ||
86 | #define DT3155_VENDORID PCI_VENDOR_ID_INTEL | |
87 | #define DT3155_DEVICEID PCI_DEVICE_ID_INTEL_7116 | |
88 | #define MAXPCI 16 | |
89 | ||
90 | #ifdef DT_DEBUG | |
91 | #define DT_3155_DEBUG_MSG(x,y) printk(x,y) | |
92 | #else | |
93 | #define DT_3155_DEBUG_MSG(x,y) | |
94 | #endif | |
95 | ||
96 | /* wait queue for interrupts */ | |
aa337ef1 | 97 | wait_queue_head_t dt3155_read_wait_queue[ MAXBOARDS ]; |
aa337ef1 SS |
98 | |
99 | #define DT_3155_SUCCESS 0 | |
100 | #define DT_3155_FAILURE -EIO | |
101 | ||
102 | /* set to dynamicaly allocate, but it is tunable: */ | |
103 | /* insmod DT_3155 dt3155 dt3155_major=XX */ | |
104 | int dt3155_major = 0; | |
105 | ||
106 | /* The minor numbers are 0 and 1 ... they are not tunable. | |
107 | * They are used as the indices for the structure vectors, | |
108 | * and register address vectors | |
109 | */ | |
110 | ||
111 | /* Global structures and variables */ | |
112 | ||
113 | /* Status of each device */ | |
114 | struct dt3155_status_s dt3155_status[ MAXBOARDS ]; | |
115 | ||
116 | /* kernel logical address of the board */ | |
5d392111 | 117 | u8 *dt3155_lbase[ MAXBOARDS ] = { NULL |
aa337ef1 SS |
118 | #if MAXBOARDS == 2 |
119 | , NULL | |
120 | #endif | |
121 | }; | |
122 | /* DT3155 registers */ | |
5d392111 | 123 | u8 *dt3155_bbase = NULL; /* kernel logical address of the * |
aa337ef1 | 124 | * buffer region */ |
dcff74ce | 125 | u32 dt3155_dev_open[ MAXBOARDS ] = {0 |
aa337ef1 SS |
126 | #if MAXBOARDS == 2 |
127 | , 0 | |
128 | #endif | |
129 | }; | |
130 | ||
dcff74ce | 131 | u32 ndevices = 0; |
3a8954e8 | 132 | u32 unique_tag = 0;; |
aa337ef1 SS |
133 | |
134 | ||
135 | /* | |
136 | * Stops interrupt generation right away and resets the status | |
137 | * to idle. I don't know why this works and the other way doesn't. | |
138 | * (James Rose) | |
139 | */ | |
140 | static void quick_stop (int minor) | |
141 | { | |
142 | // TODO: scott was here | |
143 | #if 1 | |
144 | ReadMReg((dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg); | |
145 | /* disable interrupts */ | |
146 | int_csr_r.fld.FLD_END_EVE_EN = 0; | |
147 | int_csr_r.fld.FLD_END_ODD_EN = 0; | |
148 | WriteMReg((dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg ); | |
149 | ||
150 | dt3155_status[ minor ].state &= ~(DT3155_STATE_STOP|0xff); | |
151 | /* mark the system stopped: */ | |
152 | dt3155_status[ minor ].state |= DT3155_STATE_IDLE; | |
153 | dt3155_fbuffer[ minor ]->stop_acquire = 0; | |
154 | dt3155_fbuffer[ minor ]->even_stopped = 0; | |
155 | #else | |
156 | dt3155_status[minor].state |= DT3155_STATE_STOP; | |
157 | dt3155_status[minor].fbuffer.stop_acquire = 1; | |
158 | #endif | |
159 | ||
160 | } | |
161 | ||
162 | ||
163 | /***************************************************** | |
164 | * dt3155_isr() Interrupt service routien | |
165 | * | |
166 | * - looks like this isr supports IRQ sharing (or could) JML | |
167 | * - Assumes irq's are disabled, via SA_INTERRUPT flag | |
168 | * being set in request_irq() call from init_module() | |
169 | *****************************************************/ | |
170 | static inline void dt3155_isr( int irq, void *dev_id, struct pt_regs *regs ) | |
171 | { | |
172 | int minor = -1; | |
173 | int index; | |
aa337ef1 | 174 | unsigned long flags; |
3a8954e8 | 175 | u32 buffer_addr; |
aa337ef1 SS |
176 | |
177 | /* find out who issued the interrupt */ | |
178 | for ( index = 0; index < ndevices; index++ ) { | |
179 | if( dev_id == (void*) &dt3155_status[ index ]) | |
180 | { | |
181 | minor = index; | |
182 | break; | |
183 | } | |
184 | } | |
185 | ||
186 | /* hopefully we should not get here */ | |
187 | if ( minor < 0 || minor >= MAXBOARDS ) { | |
188 | printk(KERN_ERR "dt3155_isr called with invalid dev_id\n"); | |
189 | return; | |
190 | } | |
191 | ||
192 | /* Check for corruption and set a flag if so */ | |
193 | ReadMReg( (dt3155_lbase[ minor ] + CSR1), csr1_r.reg ); | |
194 | ||
195 | if ( (csr1_r.fld.FLD_CRPT_EVE) || (csr1_r.fld.FLD_CRPT_ODD) ) | |
196 | { | |
197 | /* TODO: this should probably stop acquisition */ | |
198 | /* and set some flags so that dt3155_read */ | |
199 | /* returns an error next time it is called */ | |
200 | dt3155_errno = DT_ERR_CORRUPT; | |
201 | printk("dt3155: corrupt field\n"); | |
202 | return; | |
203 | } | |
204 | ||
205 | ReadMReg((dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg); | |
206 | ||
207 | /* Handle the even field ... */ | |
208 | if (int_csr_r.fld.FLD_END_EVE) | |
209 | { | |
210 | if ( (dt3155_status[ minor ].state & DT3155_STATE_MODE) == | |
211 | DT3155_STATE_FLD ) | |
212 | { | |
213 | dt3155_fbuffer[ minor ]->frame_count++; | |
214 | } | |
215 | ||
216 | ReadI2C(dt3155_lbase[ minor ], EVEN_CSR, &i2c_even_csr.reg); | |
217 | ||
218 | /* Clear the interrupt? */ | |
219 | int_csr_r.fld.FLD_END_EVE = 1; | |
220 | ||
221 | /* disable the interrupt if last field */ | |
222 | if (dt3155_fbuffer[ minor ]->stop_acquire) | |
223 | { | |
224 | printk("dt3155: even stopped.\n"); | |
225 | dt3155_fbuffer[ minor ]->even_stopped = 1; | |
226 | if (i2c_even_csr.fld.SNGL_EVE) | |
227 | { | |
228 | int_csr_r.fld.FLD_END_EVE_EN = 0; | |
229 | } | |
230 | else | |
231 | { | |
232 | i2c_even_csr.fld.SNGL_EVE = 1; | |
233 | } | |
234 | } | |
235 | ||
236 | WriteMReg( (dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg ); | |
237 | ||
238 | /* Set up next DMA if we are doing FIELDS */ | |
239 | if ( (dt3155_status[ minor ].state & DT3155_STATE_MODE ) == | |
240 | DT3155_STATE_FLD) | |
241 | { | |
242 | /* GCS (Aug 2, 2002) -- In field mode, dma the odd field | |
243 | into the lower half of the buffer */ | |
3a8954e8 | 244 | const u32 stride = dt3155_status[ minor ].config.cols; |
aa337ef1 SS |
245 | buffer_addr = dt3155_fbuffer[ minor ]-> |
246 | frame_info[ dt3155_fbuffer[ minor ]->active_buf ].addr | |
247 | + (DT3155_MAX_ROWS / 2) * stride; | |
aa337ef1 SS |
248 | local_save_flags(flags); |
249 | local_irq_disable(); | |
aa337ef1 SS |
250 | wake_up_interruptible( &dt3155_read_wait_queue[ minor ] ); |
251 | ||
252 | /* Set up the DMA address for the next field */ | |
aa337ef1 | 253 | local_irq_restore(flags); |
aa337ef1 SS |
254 | WriteMReg((dt3155_lbase[ minor ] + ODD_DMA_START), buffer_addr); |
255 | } | |
256 | ||
257 | /* Check for errors. */ | |
258 | i2c_even_csr.fld.DONE_EVE = 1; | |
259 | if ( i2c_even_csr.fld.ERROR_EVE ) | |
260 | dt3155_errno = DT_ERR_OVERRUN; | |
261 | ||
262 | WriteI2C( dt3155_lbase[ minor ], EVEN_CSR, i2c_even_csr.reg ); | |
263 | ||
264 | /* Note that we actually saw an even field meaning */ | |
265 | /* that subsequent odd field complete the frame */ | |
266 | dt3155_fbuffer[ minor ]->even_happened = 1; | |
267 | ||
268 | /* recording the time that the even field finished, this should be */ | |
269 | /* about time in the middle of the frame */ | |
270 | do_gettimeofday( &(dt3155_fbuffer[ minor ]-> | |
271 | frame_info[ dt3155_fbuffer[ minor ]-> | |
272 | active_buf ].time) ); | |
273 | return; | |
274 | } | |
275 | ||
276 | /* ... now handle the odd field */ | |
277 | if ( int_csr_r.fld.FLD_END_ODD ) | |
278 | { | |
279 | ReadI2C( dt3155_lbase[ minor ], ODD_CSR, &i2c_odd_csr.reg ); | |
280 | ||
281 | /* Clear the interrupt? */ | |
282 | int_csr_r.fld.FLD_END_ODD = 1; | |
283 | ||
284 | if (dt3155_fbuffer[ minor ]->even_happened || | |
285 | (dt3155_status[ minor ].state & DT3155_STATE_MODE) == | |
286 | DT3155_STATE_FLD) | |
287 | { | |
288 | dt3155_fbuffer[ minor ]->frame_count++; | |
289 | } | |
290 | ||
291 | if ( dt3155_fbuffer[ minor ]->stop_acquire && | |
292 | dt3155_fbuffer[ minor ]->even_stopped ) | |
293 | { | |
294 | printk(KERN_DEBUG "dt3155: stopping odd..\n"); | |
295 | if ( i2c_odd_csr.fld.SNGL_ODD ) | |
296 | { | |
297 | /* disable interrupts */ | |
298 | int_csr_r.fld.FLD_END_ODD_EN = 0; | |
299 | dt3155_status[ minor ].state &= ~(DT3155_STATE_STOP|0xff); | |
300 | ||
301 | /* mark the system stopped: */ | |
302 | dt3155_status[ minor ].state |= DT3155_STATE_IDLE; | |
303 | dt3155_fbuffer[ minor ]->stop_acquire = 0; | |
304 | dt3155_fbuffer[ minor ]->even_stopped = 0; | |
305 | ||
dcff74ce | 306 | printk(KERN_DEBUG "dt3155: state is now %x\n", |
3a8954e8 | 307 | dt3155_status[minor].state); |
aa337ef1 SS |
308 | } |
309 | else | |
310 | { | |
311 | i2c_odd_csr.fld.SNGL_ODD = 1; | |
312 | } | |
313 | } | |
314 | ||
315 | WriteMReg( (dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg ); | |
316 | ||
317 | /* if the odd field has been acquired, then */ | |
318 | /* change the next dma location for both fields */ | |
319 | /* and wake up the process if sleeping */ | |
320 | if ( dt3155_fbuffer[ minor ]->even_happened || | |
321 | (dt3155_status[ minor ].state & DT3155_STATE_MODE) == | |
322 | DT3155_STATE_FLD ) | |
323 | { | |
324 | ||
aa337ef1 SS |
325 | local_save_flags(flags); |
326 | local_irq_disable(); | |
aa337ef1 SS |
327 | |
328 | #ifdef DEBUG_QUES_B | |
329 | printques( minor ); | |
330 | #endif | |
331 | if ( dt3155_fbuffer[ minor ]->nbuffers > 2 ) | |
332 | { | |
333 | if ( !are_empty_buffers( minor ) ) | |
334 | { | |
335 | /* The number of active + locked buffers is | |
336 | * at most 2, and since there are none empty, there | |
337 | * must be at least nbuffers-2 ready buffers. | |
338 | * This is where we 'drop frames', oldest first. */ | |
339 | push_empty( pop_ready( minor ), minor ); | |
340 | } | |
341 | ||
342 | /* The ready_que can't be full, since we know | |
343 | * there is one active buffer right now, so it's safe | |
344 | * to push the active buf on the ready_que. */ | |
345 | push_ready( minor, dt3155_fbuffer[ minor ]->active_buf ); | |
346 | /* There's at least 1 empty -- make it active */ | |
347 | dt3155_fbuffer[ minor ]->active_buf = pop_empty( minor ); | |
348 | dt3155_fbuffer[ minor ]-> | |
349 | frame_info[ dt3155_fbuffer[ minor ]-> | |
350 | active_buf ].tag = ++unique_tag; | |
351 | } | |
352 | else /* nbuffers == 2, special case */ | |
353 | { /* There is 1 active buffer. | |
354 | * If there is a locked buffer, keep the active buffer | |
355 | * the same -- that means we drop a frame. | |
356 | */ | |
357 | if ( dt3155_fbuffer[ minor ]->locked_buf < 0 ) | |
358 | { | |
359 | push_ready( minor, | |
360 | dt3155_fbuffer[ minor ]->active_buf ); | |
361 | if (are_empty_buffers( minor ) ) | |
362 | { | |
363 | dt3155_fbuffer[ minor ]->active_buf = | |
364 | pop_empty( minor ); | |
365 | } | |
366 | else | |
367 | { /* no empty or locked buffers, so use a readybuf */ | |
368 | dt3155_fbuffer[ minor ]->active_buf = | |
369 | pop_ready( minor ); | |
370 | } | |
371 | } | |
372 | } | |
373 | ||
374 | #ifdef DEBUG_QUES_B | |
375 | printques( minor ); | |
376 | #endif | |
377 | ||
378 | dt3155_fbuffer[ minor ]->even_happened = 0; | |
379 | ||
380 | wake_up_interruptible( &dt3155_read_wait_queue[ minor ] ); | |
381 | ||
aa337ef1 | 382 | local_irq_restore(flags); |
aa337ef1 SS |
383 | } |
384 | ||
385 | ||
386 | /* Set up the DMA address for the next frame/field */ | |
387 | buffer_addr = dt3155_fbuffer[ minor ]-> | |
388 | frame_info[ dt3155_fbuffer[ minor ]->active_buf ].addr; | |
389 | if ( (dt3155_status[ minor ].state & DT3155_STATE_MODE) == | |
390 | DT3155_STATE_FLD ) | |
391 | { | |
392 | WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_START), buffer_addr); | |
393 | } | |
394 | else | |
395 | { | |
396 | WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_START), buffer_addr); | |
397 | ||
398 | WriteMReg((dt3155_lbase[ minor ] + ODD_DMA_START), buffer_addr | |
399 | + dt3155_status[ minor ].config.cols); | |
400 | } | |
401 | ||
402 | /* Do error checking */ | |
403 | i2c_odd_csr.fld.DONE_ODD = 1; | |
404 | if ( i2c_odd_csr.fld.ERROR_ODD ) | |
405 | dt3155_errno = DT_ERR_OVERRUN; | |
406 | ||
407 | WriteI2C(dt3155_lbase[ minor ], ODD_CSR, i2c_odd_csr.reg ); | |
408 | ||
409 | return; | |
410 | } | |
411 | /* If we get here, the Odd Field wasn't it either... */ | |
412 | printk( "neither even nor odd. shared perhaps?\n"); | |
413 | } | |
414 | ||
415 | /***************************************************** | |
416 | * init_isr(int minor) | |
417 | * turns on interupt generation for the card | |
418 | * designated by "minor". | |
419 | * It is called *only* from inside ioctl(). | |
420 | *****************************************************/ | |
421 | static void dt3155_init_isr(int minor) | |
422 | { | |
3a8954e8 | 423 | const u32 stride = dt3155_status[ minor ].config.cols; |
aa337ef1 SS |
424 | |
425 | switch (dt3155_status[ minor ].state & DT3155_STATE_MODE) | |
426 | { | |
427 | case DT3155_STATE_FLD: | |
428 | { | |
429 | even_dma_start_r = dt3155_status[ minor ]. | |
430 | fbuffer.frame_info[ dt3155_status[ minor ].fbuffer.active_buf ].addr; | |
431 | even_dma_stride_r = 0; | |
432 | odd_dma_stride_r = 0; | |
433 | ||
434 | WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_START), | |
435 | even_dma_start_r); | |
436 | WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_STRIDE), | |
437 | even_dma_stride_r); | |
438 | WriteMReg((dt3155_lbase[ minor ] + ODD_DMA_STRIDE), | |
439 | odd_dma_stride_r); | |
440 | break; | |
441 | } | |
442 | ||
443 | case DT3155_STATE_FRAME: | |
444 | default: | |
445 | { | |
446 | even_dma_start_r = dt3155_status[ minor ]. | |
447 | fbuffer.frame_info[ dt3155_status[ minor ].fbuffer.active_buf ].addr; | |
448 | odd_dma_start_r = even_dma_start_r + stride; | |
449 | even_dma_stride_r = stride; | |
450 | odd_dma_stride_r = stride; | |
451 | ||
452 | WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_START), | |
453 | even_dma_start_r); | |
454 | WriteMReg((dt3155_lbase[ minor ] + ODD_DMA_START), | |
455 | odd_dma_start_r); | |
456 | WriteMReg((dt3155_lbase[ minor ] + EVEN_DMA_STRIDE), | |
457 | even_dma_stride_r); | |
458 | WriteMReg((dt3155_lbase[ minor ] + ODD_DMA_STRIDE), | |
459 | odd_dma_stride_r); | |
460 | break; | |
461 | } | |
462 | } | |
463 | ||
464 | /* 50/60 Hz should be set before this point but let's make sure it is */ | |
465 | /* right anyway */ | |
466 | ||
6536560c | 467 | ReadI2C(dt3155_lbase[ minor ], CSR2, &i2c_csr2.reg); |
aa337ef1 | 468 | i2c_csr2.fld.HZ50 = FORMAT50HZ; |
6536560c | 469 | WriteI2C(dt3155_lbase[ minor ], CSR2, i2c_csr2.reg); |
aa337ef1 SS |
470 | |
471 | /* enable busmaster chip, clear flags */ | |
472 | ||
473 | /* | |
474 | * TODO: | |
475 | * shouldn't we be concered with continuous values of | |
476 | * DT3155_SNAP & DT3155_ACQ here? (SS) | |
477 | */ | |
478 | ||
479 | csr1_r.reg = 0; | |
480 | csr1_r.fld.CAP_CONT_EVE = 1; /* use continuous capture bits to */ | |
481 | csr1_r.fld.CAP_CONT_ODD = 1; /* enable */ | |
482 | csr1_r.fld.FLD_DN_EVE = 1; /* writing a 1 clears flags */ | |
483 | csr1_r.fld.FLD_DN_ODD = 1; | |
484 | csr1_r.fld.SRST = 1; /* reset - must be 1 */ | |
485 | csr1_r.fld.FIFO_EN = 1; /* fifo control - must be 1 */ | |
486 | csr1_r.fld.FLD_CRPT_EVE = 1; /* writing a 1 clears flags */ | |
487 | csr1_r.fld.FLD_CRPT_ODD = 1; | |
488 | ||
489 | WriteMReg((dt3155_lbase[ minor ] + CSR1),csr1_r.reg); | |
490 | ||
491 | /* Enable interrupts at the end of each field */ | |
492 | ||
493 | int_csr_r.reg = 0; | |
494 | int_csr_r.fld.FLD_END_EVE_EN = 1; | |
495 | int_csr_r.fld.FLD_END_ODD_EN = 1; | |
496 | int_csr_r.fld.FLD_START_EN = 0; | |
497 | ||
498 | WriteMReg((dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg); | |
499 | ||
500 | /* start internal BUSY bits */ | |
501 | ||
502 | ReadI2C(dt3155_lbase[ minor ], CSR2, &i2c_csr2.reg); | |
503 | i2c_csr2.fld.BUSY_ODD = 1; | |
504 | i2c_csr2.fld.BUSY_EVE = 1; | |
505 | WriteI2C(dt3155_lbase[ minor ], CSR2, i2c_csr2.reg); | |
506 | ||
507 | /* Now its up to the interrupt routine!! */ | |
508 | ||
509 | return; | |
510 | } | |
511 | ||
512 | ||
513 | /***************************************************** | |
514 | * ioctl() | |
515 | * | |
516 | *****************************************************/ | |
dcff74ce GKH |
517 | static int dt3155_ioctl(struct inode *inode, |
518 | struct file *file, | |
519 | unsigned int cmd, | |
520 | unsigned long arg) | |
aa337ef1 SS |
521 | { |
522 | int minor = MINOR(inode->i_rdev); /* What device are we ioctl()'ing? */ | |
523 | ||
524 | if ( minor >= MAXBOARDS || minor < 0 ) | |
525 | return -ENODEV; | |
526 | ||
527 | /* make sure it is valid command */ | |
528 | if (_IOC_NR(cmd) > DT3155_IOC_MAXNR) | |
529 | { | |
530 | printk("DT3155: invalid IOCTL(0x%x)\n",cmd); | |
531 | printk("DT3155: Valid commands (0x%x), (0x%x), (0x%x), (0x%x), (0x%x)\n", | |
f721ad7a GKH |
532 | (unsigned int)DT3155_GET_CONFIG, |
533 | (unsigned int)DT3155_SET_CONFIG, | |
534 | (unsigned int)DT3155_START, | |
535 | (unsigned int)DT3155_STOP, | |
536 | (unsigned int)DT3155_FLUSH); | |
aa337ef1 SS |
537 | return -EINVAL; |
538 | } | |
539 | ||
540 | switch (cmd) | |
541 | { | |
542 | case DT3155_SET_CONFIG: | |
543 | { | |
544 | if (dt3155_status[minor].state != DT3155_STATE_IDLE) | |
545 | return -EBUSY; | |
546 | ||
547 | { | |
548 | struct dt3155_config_s tmp; | |
549 | if (copy_from_user((void *)&tmp, (void *) arg, sizeof(tmp))) | |
550 | return -EFAULT; | |
551 | /* check for valid settings */ | |
552 | if (tmp.rows > DT3155_MAX_ROWS || | |
553 | tmp.cols > DT3155_MAX_COLS || | |
554 | (tmp.acq_mode != DT3155_MODE_FRAME && | |
555 | tmp.acq_mode != DT3155_MODE_FIELD) || | |
556 | (tmp.continuous != DT3155_SNAP && | |
557 | tmp.continuous != DT3155_ACQ)) | |
558 | { | |
559 | return -EINVAL; | |
560 | } | |
561 | dt3155_status[minor].config = tmp; | |
562 | } | |
563 | return 0; | |
564 | } | |
565 | case DT3155_GET_CONFIG: | |
566 | { | |
567 | if (copy_to_user((void *) arg, (void *) &dt3155_status[minor], | |
568 | sizeof(dt3155_status_t) )) | |
569 | return -EFAULT; | |
570 | return 0; | |
571 | } | |
572 | case DT3155_FLUSH: /* Flushes the buffers -- ensures fresh data */ | |
573 | { | |
574 | if (dt3155_status[minor].state != DT3155_STATE_IDLE) | |
575 | return -EBUSY; | |
576 | return dt3155_flush(minor); | |
577 | } | |
578 | case DT3155_STOP: | |
579 | { | |
580 | if (dt3155_status[minor].state & DT3155_STATE_STOP || | |
581 | dt3155_status[minor].fbuffer.stop_acquire) | |
582 | return -EBUSY; | |
583 | ||
584 | if (dt3155_status[minor].state == DT3155_STATE_IDLE) | |
585 | return 0; | |
586 | ||
587 | quick_stop(minor); | |
588 | if (copy_to_user((void *) arg, (void *) &dt3155_status[minor], | |
589 | sizeof(dt3155_status_t))) | |
590 | return -EFAULT; | |
591 | return 0; | |
592 | } | |
593 | case DT3155_START: | |
594 | { | |
595 | if (dt3155_status[minor].state != DT3155_STATE_IDLE) | |
596 | return -EBUSY; | |
597 | ||
598 | dt3155_status[minor].fbuffer.stop_acquire = 0; | |
599 | dt3155_status[minor].fbuffer.frame_count = 0; | |
600 | ||
601 | /* Set the MODE in the status -- we default to FRAME */ | |
602 | if (dt3155_status[minor].config.acq_mode == DT3155_MODE_FIELD) | |
603 | { | |
604 | dt3155_status[minor].state = DT3155_STATE_FLD; | |
605 | } | |
606 | else | |
607 | { | |
608 | dt3155_status[minor].state = DT3155_STATE_FRAME; | |
609 | } | |
610 | ||
611 | dt3155_init_isr(minor); | |
612 | if (copy_to_user( (void *) arg, (void *) &dt3155_status[minor], | |
613 | sizeof(dt3155_status_t))) | |
614 | return -EFAULT; | |
615 | return 0; | |
616 | } | |
617 | default: | |
618 | { | |
619 | printk("DT3155: invalid IOCTL(0x%x)\n",cmd); | |
620 | printk("DT3155: Valid commands (0x%x), (0x%x), (0x%x), (0x%x), (0x%x)\n", | |
f721ad7a GKH |
621 | (unsigned int)DT3155_GET_CONFIG, |
622 | (unsigned int)DT3155_SET_CONFIG, | |
aa337ef1 SS |
623 | DT3155_START, DT3155_STOP, DT3155_FLUSH); |
624 | return -ENOSYS; | |
625 | } | |
626 | } | |
627 | return -ENOSYS; | |
628 | } | |
629 | ||
630 | /***************************************************** | |
631 | * mmap() | |
632 | * | |
633 | * only allow the user to mmap the registers and buffer | |
634 | * It is quite possible that this is broken, since the | |
635 | * addition of of the capacity for two cards!!!!!!!! | |
636 | * It *looks* like it should work but since I'm not | |
637 | * sure how to use it, I'm not actually sure. (NJC? ditto by SS) | |
638 | *****************************************************/ | |
639 | static int dt3155_mmap (struct file * file, struct vm_area_struct * vma) | |
640 | { | |
641 | /* which device are we mmapping? */ | |
642 | int minor = MINOR(file->f_dentry->d_inode->i_rdev); | |
643 | unsigned long offset; | |
aa337ef1 SS |
644 | offset = vma->vm_pgoff << PAGE_SHIFT; |
645 | ||
646 | if (offset >= __pa(high_memory) || (file->f_flags & O_SYNC)) | |
647 | vma->vm_flags |= VM_IO; | |
648 | ||
649 | /* Don't try to swap out physical pages.. */ | |
650 | vma->vm_flags |= VM_RESERVED; | |
651 | ||
aa337ef1 SS |
652 | /* they are mapping the registers or the buffer */ |
653 | if ((offset == dt3155_status[minor].reg_addr && | |
654 | vma->vm_end - vma->vm_start == PCI_PAGE_SIZE) || | |
655 | (offset == dt3155_status[minor].mem_addr && | |
656 | vma->vm_end - vma->vm_start == dt3155_status[minor].mem_size)) | |
657 | { | |
aa337ef1 SS |
658 | if (remap_pfn_range(vma, |
659 | vma->vm_start, | |
660 | offset >> PAGE_SHIFT, | |
661 | vma->vm_end - vma->vm_start, | |
2141ec62 | 662 | vma->vm_page_prot)) { |
aa337ef1 SS |
663 | printk("DT3155: remap_page_range() failed.\n"); |
664 | return -EAGAIN; | |
665 | } | |
666 | } | |
667 | else | |
668 | { | |
669 | printk("DT3155: dt3155_mmap() bad call.\n"); | |
670 | return -ENXIO; | |
671 | } | |
672 | ||
673 | return 0; | |
674 | } | |
675 | ||
676 | ||
677 | /***************************************************** | |
678 | * open() | |
679 | * | |
680 | * Our special open code. | |
681 | * MOD_INC_USE_COUNT make sure that the driver memory is not freed | |
682 | * while the device is in use. | |
683 | *****************************************************/ | |
684 | static int dt3155_open( struct inode* inode, struct file* filep) | |
685 | { | |
686 | int minor = MINOR(inode->i_rdev); /* what device are we opening? */ | |
687 | if (dt3155_dev_open[ minor ]) { | |
688 | printk ("DT3155: Already opened by another process.\n"); | |
689 | return -EBUSY; | |
690 | } | |
691 | ||
692 | if (dt3155_status[ minor ].device_installed==0) | |
693 | { | |
694 | printk("DT3155 Open Error: No such device dt3155 minor number %d\n", | |
695 | minor); | |
696 | return -EIO; | |
697 | } | |
698 | ||
699 | if (dt3155_status[ minor ].state != DT3155_STATE_IDLE) { | |
dcff74ce | 700 | printk ("DT3155: Not in idle state (state = %x)\n", |
3a8954e8 | 701 | dt3155_status[ minor ].state); |
aa337ef1 SS |
702 | return -EBUSY; |
703 | } | |
704 | ||
705 | printk("DT3155: Device opened.\n"); | |
706 | ||
707 | dt3155_dev_open[ minor ] = 1 ; | |
aa337ef1 SS |
708 | |
709 | dt3155_flush( minor ); | |
710 | ||
711 | /* Disable ALL interrupts */ | |
712 | int_csr_r.reg = 0; | |
713 | WriteMReg( (dt3155_lbase[ minor ] + INT_CSR), int_csr_r.reg ); | |
714 | ||
aa337ef1 | 715 | init_waitqueue_head(&(dt3155_read_wait_queue[minor])); |
aa337ef1 SS |
716 | |
717 | return 0; | |
718 | } | |
719 | ||
720 | ||
721 | /***************************************************** | |
722 | * close() | |
723 | * | |
724 | * Now decrement the use count. | |
725 | * | |
726 | *****************************************************/ | |
727 | static int dt3155_close( struct inode *inode, struct file *filep) | |
728 | { | |
729 | int minor; | |
730 | ||
731 | minor = MINOR(inode->i_rdev); /* which device are we closing */ | |
732 | if (!dt3155_dev_open[ minor ]) | |
733 | { | |
734 | printk("DT3155: attempt to CLOSE a not OPEN device\n"); | |
735 | } | |
736 | else | |
737 | { | |
aa337ef1 SS |
738 | dt3155_dev_open[ minor ] = 0; |
739 | ||
740 | if (dt3155_status[ minor ].state != DT3155_STATE_IDLE) | |
741 | { | |
742 | quick_stop(minor); | |
743 | } | |
744 | } | |
745 | return 0; | |
746 | } | |
747 | ||
748 | /***************************************************** | |
749 | * read() | |
750 | * | |
751 | *****************************************************/ | |
f721ad7a GKH |
752 | static ssize_t dt3155_read(struct file *filep, char __user *buf, |
753 | size_t count, loff_t *ppos) | |
aa337ef1 SS |
754 | { |
755 | /* which device are we reading from? */ | |
756 | int minor = MINOR(filep->f_dentry->d_inode->i_rdev); | |
3a8954e8 | 757 | u32 offset; |
aa337ef1 SS |
758 | int frame_index; |
759 | frame_info_t *frame_info_p; | |
760 | ||
761 | /* TODO: this should check the error flag and */ | |
762 | /* return an error on hardware failures */ | |
763 | if (count != sizeof(dt3155_read_t)) | |
764 | { | |
765 | printk("DT3155 ERROR (NJC): count is not right\n"); | |
766 | return -EINVAL; | |
767 | } | |
768 | ||
769 | ||
770 | /* Hack here -- I'm going to allow reading even when idle. | |
771 | * this is so that the frames can be read after STOP has | |
772 | * been called. Leaving it here, commented out, as a reminder | |
773 | * for a short while to make sure there are no problems. | |
774 | * Note that if the driver is not opened in non_blocking mode, | |
775 | * and the device is idle, then it could sit here forever! */ | |
776 | ||
777 | /* if (dt3155_status[minor].state == DT3155_STATE_IDLE)*/ | |
778 | /* return -EBUSY;*/ | |
779 | ||
aa337ef1 SS |
780 | /* non-blocking reads should return if no data */ |
781 | if (filep->f_flags & O_NDELAY) | |
782 | { | |
783 | if ((frame_index = dt3155_get_ready_buffer(minor)) < 0) { | |
784 | /*printk( "dt3155: no buffers available (?)\n");*/ | |
785 | /* printques(minor); */ | |
786 | return -EAGAIN; | |
787 | } | |
788 | } | |
789 | else | |
790 | { | |
791 | /* | |
792 | * sleep till data arrives , or we get interrupted. | |
793 | * Note that wait_event_interruptible() does not actually | |
794 | * sleep/wait if it's condition evaluates to true upon entry. | |
795 | */ | |
796 | wait_event_interruptible(dt3155_read_wait_queue[minor], | |
797 | (frame_index = dt3155_get_ready_buffer(minor)) | |
798 | >= 0); | |
799 | ||
800 | if (frame_index < 0) | |
801 | { | |
802 | printk ("DT3155: read: interrupted\n"); | |
803 | quick_stop (minor); | |
804 | printques(minor); | |
805 | return -EINTR; | |
806 | } | |
807 | } | |
808 | ||
aa337ef1 SS |
809 | frame_info_p = &dt3155_status[minor].fbuffer.frame_info[frame_index]; |
810 | ||
811 | /* make this an offset */ | |
812 | offset = frame_info_p->addr - dt3155_status[minor].mem_addr; | |
813 | ||
814 | put_user(offset, (unsigned int *) buf); | |
3a8954e8 | 815 | buf += sizeof(u32); |
aa337ef1 | 816 | put_user( dt3155_status[minor].fbuffer.frame_count, (unsigned int *) buf); |
3a8954e8 | 817 | buf += sizeof(u32); |
aa337ef1 | 818 | put_user(dt3155_status[minor].state, (unsigned int *) buf); |
3a8954e8 | 819 | buf += sizeof(u32); |
aa337ef1 SS |
820 | if (copy_to_user(buf, frame_info_p, sizeof(frame_info_t))) |
821 | return -EFAULT; | |
822 | ||
823 | return sizeof(dt3155_read_t); | |
824 | } | |
825 | ||
826 | static unsigned int dt3155_poll (struct file * filp, poll_table *wait) | |
827 | { | |
828 | int minor = MINOR(filp->f_dentry->d_inode->i_rdev); | |
829 | ||
830 | if (!is_ready_buf_empty(minor)) | |
831 | return POLLIN | POLLRDNORM; | |
832 | ||
833 | poll_wait (filp, &dt3155_read_wait_queue[minor], wait); | |
834 | ||
835 | return 0; | |
836 | } | |
837 | ||
838 | ||
839 | /***************************************************** | |
840 | * file operations supported by DT3155 driver | |
841 | * needed by init_module | |
842 | * register_chrdev | |
843 | *****************************************************/ | |
844 | static struct file_operations dt3155_fops = { | |
845 | read: dt3155_read, | |
846 | ioctl: dt3155_ioctl, | |
847 | mmap: dt3155_mmap, | |
848 | poll: dt3155_poll, | |
849 | open: dt3155_open, | |
850 | release: dt3155_close | |
851 | }; | |
852 | ||
853 | ||
854 | /***************************************************** | |
855 | * find_PCI(); | |
856 | * | |
857 | * PCI has been totally reworked in 2.1.. | |
858 | *****************************************************/ | |
859 | static int find_PCI (void) | |
860 | { | |
861 | struct pci_dev *pci_dev = NULL; | |
862 | int error, pci_index = 0; | |
863 | unsigned short rev_device; | |
864 | unsigned long base; | |
865 | unsigned char irq; | |
866 | ||
6910dadf | 867 | while ((pci_dev = pci_get_device |
aa337ef1 SS |
868 | (DT3155_VENDORID, DT3155_DEVICEID, pci_dev)) != NULL) |
869 | { | |
870 | pci_index ++; | |
871 | ||
872 | /* Is it really there? */ | |
873 | if ((error = | |
874 | pci_read_config_word(pci_dev, PCI_CLASS_DEVICE, &rev_device))) | |
875 | continue; | |
876 | ||
877 | /* Found a board */ | |
878 | DT_3155_DEBUG_MSG("DT3155: Device number %d \n", pci_index); | |
879 | ||
880 | /* Make sure the driver was compiled with enough buffers to handle | |
881 | this many boards */ | |
882 | if (pci_index > MAXBOARDS) { | |
883 | printk("DT3155: ERROR - found %d devices, but driver only configured " | |
884 | "for %d devices\n" | |
885 | "DT3155: Please change MAXBOARDS in dt3155.h\n", | |
886 | pci_index, MAXBOARDS); | |
6910dadf | 887 | goto err; |
aa337ef1 SS |
888 | } |
889 | ||
890 | /* Now, just go out and make sure that this/these device(s) is/are | |
891 | actually mapped into the kernel address space */ | |
892 | if ((error = pci_read_config_dword( pci_dev, PCI_BASE_ADDRESS_0, | |
dcff74ce | 893 | (u32 *) &base))) |
aa337ef1 SS |
894 | { |
895 | printk("DT3155: Was not able to find device \n"); | |
6910dadf | 896 | goto err; |
aa337ef1 SS |
897 | } |
898 | ||
899 | DT_3155_DEBUG_MSG("DT3155: Base address 0 for device is %lx \n", base); | |
900 | dt3155_status[pci_index-1].reg_addr = base; | |
901 | ||
902 | /* Remap the base address to a logical address through which we | |
903 | * can access it. */ | |
904 | dt3155_lbase[ pci_index - 1 ] = ioremap(base,PCI_PAGE_SIZE); | |
905 | dt3155_status[ pci_index - 1 ].reg_addr = base; | |
f721ad7a GKH |
906 | DT_3155_DEBUG_MSG("DT3155: New logical address is %p \n", |
907 | dt3155_lbase[pci_index-1]); | |
aa337ef1 SS |
908 | if ( !dt3155_lbase[pci_index-1] ) |
909 | { | |
910 | printk("DT3155: Unable to remap control registers\n"); | |
6910dadf | 911 | goto err; |
aa337ef1 SS |
912 | } |
913 | ||
914 | if ( (error = pci_read_config_byte( pci_dev, PCI_INTERRUPT_LINE, &irq)) ) | |
915 | { | |
916 | printk("DT3155: Was not able to find device \n"); | |
6910dadf | 917 | goto err; |
aa337ef1 SS |
918 | } |
919 | ||
920 | DT_3155_DEBUG_MSG("DT3155: IRQ is %d \n",irq); | |
921 | dt3155_status[ pci_index-1 ].irq = irq; | |
922 | /* Set flag: kth device found! */ | |
923 | dt3155_status[ pci_index-1 ].device_installed = 1; | |
f721ad7a | 924 | printk("DT3155: Installing device %d w/irq %d and address %p\n", |
aa337ef1 | 925 | pci_index, |
3a8954e8 | 926 | dt3155_status[pci_index-1].irq, |
f721ad7a | 927 | dt3155_lbase[pci_index-1]); |
aa337ef1 SS |
928 | |
929 | } | |
930 | ndevices = pci_index; | |
931 | ||
932 | return DT_3155_SUCCESS; | |
6910dadf SH |
933 | |
934 | err: | |
935 | pci_dev_put(pci_dev); | |
936 | return DT_3155_FAILURE; | |
aa337ef1 SS |
937 | } |
938 | ||
3a8954e8 | 939 | u32 allocatorAddr = 0; |
aa337ef1 SS |
940 | |
941 | /***************************************************** | |
942 | * init_module() | |
943 | *****************************************************/ | |
944 | int init_module(void) | |
945 | { | |
946 | int index; | |
947 | int rcode = 0; | |
948 | char *devname[ MAXBOARDS ]; | |
949 | ||
aa337ef1 SS |
950 | devname[ 0 ] = "dt3155a"; |
951 | #if MAXBOARDS == 2 | |
952 | devname[ 1 ] = "dt3155b"; | |
953 | #endif | |
954 | ||
955 | printk("DT3155: Loading module...\n"); | |
956 | ||
957 | /* Register the device driver */ | |
958 | rcode = register_chrdev( dt3155_major, "dt3155", &dt3155_fops ); | |
959 | if( rcode < 0 ) | |
960 | { | |
961 | printk( KERN_INFO "DT3155: register_chrdev failed \n"); | |
962 | return rcode; | |
963 | } | |
964 | ||
965 | if( dt3155_major == 0 ) | |
966 | dt3155_major = rcode; /* dynamic */ | |
967 | ||
968 | ||
969 | /* init the status variables. */ | |
970 | /* DMA memory is taken care of in setup_buffers() */ | |
971 | for ( index = 0; index < MAXBOARDS; index++ ) | |
972 | { | |
973 | dt3155_status[ index ].config.acq_mode = DT3155_MODE_FRAME; | |
974 | dt3155_status[ index ].config.continuous = DT3155_ACQ; | |
975 | dt3155_status[ index ].config.cols = DT3155_MAX_COLS; | |
976 | dt3155_status[ index ].config.rows = DT3155_MAX_ROWS; | |
977 | dt3155_status[ index ].state = DT3155_STATE_IDLE; | |
978 | ||
979 | /* find_PCI() will check if devices are installed; */ | |
980 | /* first assume they're not: */ | |
981 | dt3155_status[ index ].mem_addr = 0; | |
982 | dt3155_status[ index ].mem_size = 0; | |
983 | dt3155_status[ index ].state = DT3155_STATE_IDLE; | |
984 | dt3155_status[ index ].device_installed = 0; | |
985 | } | |
986 | ||
987 | /* Now let's find the hardware. find_PCI() will set ndevices to the | |
988 | * number of cards found in this machine. */ | |
aa337ef1 SS |
989 | { |
990 | if ( (rcode = find_PCI()) != DT_3155_SUCCESS ) | |
991 | { | |
992 | printk("DT3155 error: find_PCI() failed to find dt3155 board(s)\n"); | |
993 | unregister_chrdev( dt3155_major, "dt3155" ); | |
994 | return rcode; | |
995 | } | |
996 | } | |
997 | ||
998 | /* Ok, time to setup the frame buffers */ | |
999 | if( (rcode = dt3155_setup_buffers(&allocatorAddr)) < 0 ) | |
1000 | { | |
1001 | printk("DT3155: Error: setting up buffer not large enough."); | |
1002 | unregister_chrdev( dt3155_major, "dt3155" ); | |
1003 | return rcode; | |
1004 | } | |
1005 | ||
1006 | /* If we are this far, then there is enough RAM */ | |
1007 | /* for the buffers: Print the configuration. */ | |
1008 | for( index = 0; index < ndevices; index++ ) | |
1009 | { | |
1010 | printk("DT3155: Device = %d; acq_mode = %d; " | |
1011 | "continuous = %d; cols = %d; rows = %d;\n", | |
1012 | index , | |
1013 | dt3155_status[ index ].config.acq_mode, | |
1014 | dt3155_status[ index ].config.continuous, | |
1015 | dt3155_status[ index ].config.cols, | |
1016 | dt3155_status[ index ].config.rows); | |
1017 | printk("DT3155: m_addr = 0x%x; m_size = %ld; " | |
dcff74ce | 1018 | "state = %d; device_installed = %d\n", |
3a8954e8 | 1019 | dt3155_status[ index ].mem_addr, |
dcff74ce | 1020 | (long int)dt3155_status[ index ].mem_size, |
3a8954e8 | 1021 | dt3155_status[ index ].state, |
aa337ef1 SS |
1022 | dt3155_status[ index ].device_installed); |
1023 | } | |
1024 | ||
1025 | /* Disable ALL interrupts */ | |
1026 | int_csr_r.reg = 0; | |
1027 | for( index = 0; index < ndevices; index++ ) | |
1028 | { | |
1029 | WriteMReg( (dt3155_lbase[ index ] + INT_CSR), int_csr_r.reg ); | |
1030 | if( dt3155_status[ index ].device_installed ) | |
1031 | { | |
1032 | /* | |
1033 | * This driver *looks* like it can handle sharing interrupts, | |
1034 | * but I can't actually test myself. I've had reports that it | |
1035 | * DOES work so I'll enable it for now. This comment will remain | |
1036 | * as a reminder in case any problems arise. (SS) | |
1037 | */ | |
1038 | /* in older kernels flags are: SA_SHIRQ | SA_INTERRUPT */ | |
1039 | rcode = request_irq( dt3155_status[ index ].irq, (void *)dt3155_isr, | |
1040 | IRQF_SHARED | IRQF_DISABLED, devname[ index ], | |
1041 | (void*) &dt3155_status[index]); | |
1042 | if( rcode < 0 ) | |
1043 | { | |
1044 | printk("DT3155: minor %d request_irq failed for IRQ %d\n", | |
1045 | index, dt3155_status[index].irq); | |
1046 | unregister_chrdev( dt3155_major, "dt3155" ); | |
1047 | return rcode; | |
1048 | } | |
1049 | } | |
1050 | } | |
1051 | ||
1052 | printk("DT3155: finished loading\n"); | |
1053 | ||
1054 | return 0; | |
1055 | } | |
1056 | ||
1057 | /***************************************************** | |
1058 | * cleanup_module(void) | |
1059 | * | |
1060 | *****************************************************/ | |
1061 | void cleanup_module(void) | |
1062 | { | |
1063 | int index; | |
1064 | ||
1065 | printk("DT3155: cleanup_module called\n"); | |
1066 | ||
1067 | /* removed DMA allocated with the allocator */ | |
1068 | #ifdef STANDALONE_ALLOCATOR | |
1069 | if (allocatorAddr != 0) | |
1070 | allocator_free_dma(allocatorAddr); | |
1071 | #else | |
1072 | allocator_cleanup(); | |
1073 | #endif | |
1074 | ||
1075 | unregister_chrdev( dt3155_major, "dt3155" ); | |
1076 | ||
1077 | for( index = 0; index < ndevices; index++ ) | |
1078 | { | |
1079 | if( dt3155_status[ index ].device_installed == 1 ) | |
1080 | { | |
1081 | printk( "DT3155: Freeing irq %d for device %d\n", | |
1082 | dt3155_status[ index ].irq, index ); | |
1083 | free_irq( dt3155_status[ index ].irq, (void*)&dt3155_status[index] ); | |
1084 | } | |
1085 | } | |
aa337ef1 SS |
1086 | } |
1087 |