Linux 2.3.1pre3
[davej-history.git] / drivers / sbus / char / vfc_dev.c
blob9ebc20c7189842f02ecf3882a4f6905e2e20ca93
1 /*
2 * drivers/sbus/char/vfc_dev.c
4 * Driver for the Videopix Frame Grabber.
5 *
6 * In order to use the VFC you need to program the video controller
7 * chip. This chip is the Phillips SAA9051. You need to call their
8 * documentation ordering line to get the docs.
10 * There is very little documentation on the VFC itself. There is
11 * some useful info that can be found in the manuals that come with
12 * the card. I will hopefully write some better docs at a later date.
14 * Copyright (C) 1996 Manish Vachharajani (mvachhar@noc.rutgers.edu)
15 * */
17 #include <linux/module.h>
18 #include <linux/kernel.h>
19 #include <linux/string.h>
20 #include <linux/malloc.h>
21 #include <linux/errno.h>
22 #include <linux/sched.h>
23 #include <linux/fs.h>
25 #include <asm/openprom.h>
26 #include <asm/oplib.h>
27 #include <asm/io.h>
28 #include <asm/system.h>
29 #include <asm/sbus.h>
30 #include <asm/delay.h>
31 #include <asm/page.h>
32 #include <asm/pgtable.h>
33 #include <asm/uaccess.h>
35 #define VFC_MAJOR (60)
37 #if 0
38 #define VFC_IOCTL_DEBUG
39 #endif
41 #include "vfc.h"
42 #include <asm/vfc_ioctls.h>
44 struct vfc_dev **vfc_dev_lst;
45 static char vfcstr[]="vfc";
46 static unsigned char saa9051_init_array[VFC_SAA9051_NR]=
47 { 0x00, 0x64, 0x72, 0x52,
48 0x36, 0x18, 0xff, 0x20,
49 0xfc, 0x77, 0xe3, 0x50,
50 0x3e};
52 void vfc_lock_device(struct vfc_dev *dev) {
53 down(&dev->device_lock_sem);
56 void vfc_unlock_device(struct vfc_dev *dev) {
57 up(&dev->device_lock_sem);
61 void vfc_captstat_reset(struct vfc_dev *dev)
63 dev->control_reg |= VFC_CONTROL_CAPTRESET;
64 dev->regs->control=dev->control_reg;
65 dev->control_reg &= ~VFC_CONTROL_CAPTRESET;
66 dev->regs->control=dev->control_reg;
67 dev->control_reg |= VFC_CONTROL_CAPTRESET;
68 dev->regs->control=dev->control_reg;
69 return;
72 void vfc_memptr_reset(struct vfc_dev *dev)
74 dev->control_reg |= VFC_CONTROL_MEMPTR;
75 dev->regs->control = dev->control_reg;
76 dev->control_reg &= ~VFC_CONTROL_MEMPTR;
77 dev->regs->control = dev->control_reg;
78 dev->control_reg |= VFC_CONTROL_MEMPTR;
79 dev->regs->control = dev->control_reg;
80 return;
83 int vfc_csr_init(struct vfc_dev *dev)
85 dev->control_reg = 0x80000000;
86 dev->regs->control = dev->control_reg;
87 udelay(200);
88 dev->control_reg &= ~0x80000000;
89 dev->regs->control = dev->control_reg;
90 udelay(100);
91 dev->regs->i2c_magic2 = 0x0f000000;
93 vfc_memptr_reset(dev);
95 dev->control_reg &= ~VFC_CONTROL_DIAGMODE;
96 dev->control_reg &= ~VFC_CONTROL_CAPTURE;
97 dev->control_reg |= 0x40000000;
98 dev->regs->control=dev->control_reg;
100 vfc_captstat_reset(dev);
102 return 0;
105 int vfc_saa9051_init(struct vfc_dev *dev)
107 int i;
108 for(i=0;i<VFC_SAA9051_NR;i++) {
109 dev->saa9051_state_array[i]=saa9051_init_array[i];
111 vfc_i2c_sendbuf(dev,VFC_SAA9051_ADDR,
112 dev->saa9051_state_array, VFC_SAA9051_NR);
113 return 0;
116 int init_vfc_hw(struct vfc_dev *dev)
118 vfc_lock_device(dev);
119 vfc_csr_init(dev);
121 vfc_pcf8584_init(dev);
122 vfc_init_i2c_bus(dev); /* hopefully this doesn't undo the magic
123 sun code above*/
124 vfc_saa9051_init(dev);
125 vfc_unlock_device(dev);
126 return 0;
129 int init_vfc_devstruct(struct vfc_dev *dev, int instance)
131 dev->instance=instance;
132 dev->device_lock_sem=MUTEX;
133 dev->control_reg=0;
134 dev->poll_wait=NULL;
135 dev->busy=0;
136 return 0;
139 int init_vfc_device(struct linux_sbus_device *sdev,struct vfc_dev *dev,
140 int instance) {
141 if(!dev) {
142 printk(KERN_ERR "VFC: Bogus pointer passed\n");
143 return -ENOMEM;
145 printk("Initializing vfc%d\n",instance);
146 dev->regs=NULL;
147 prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0], sdev->num_registers, sdev);
148 dev->regs=sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
149 sizeof(struct vfc_regs), vfcstr,
150 sdev->reg_addrs[0].which_io, 0x0);
151 dev->which_io=sdev->reg_addrs[0].which_io;
152 dev->phys_regs=(struct vfc_regs *)sdev->reg_addrs[0].phys_addr;
153 if(!dev->regs) return -EIO;
155 printk("vfc%d: registers mapped at phys_addr: 0x%lx\n virt_addr: 0x%lx\n",
156 instance,(unsigned long)sdev->reg_addrs[0].phys_addr,(unsigned long)dev->regs);
158 if(init_vfc_devstruct(dev,instance)) return -EINVAL;
159 if(init_vfc_hw(dev)) return -EIO;
161 return 0;
165 struct vfc_dev *vfc_get_dev_ptr(int instance)
167 return vfc_dev_lst[instance];
170 static int vfc_open(struct inode *inode, struct file *file)
172 struct vfc_dev *dev;
173 dev=vfc_get_dev_ptr(MINOR(inode->i_rdev));
174 if(!dev) return -ENODEV;
175 if(dev->busy) return -EBUSY;
176 dev->busy=1;
177 MOD_INC_USE_COUNT;
178 vfc_lock_device(dev);
180 vfc_csr_init(dev);
181 vfc_pcf8584_init(dev);
182 vfc_init_i2c_bus(dev);
183 vfc_saa9051_init(dev);
184 vfc_memptr_reset(dev);
185 vfc_captstat_reset(dev);
187 vfc_unlock_device(dev);
188 return 0;
191 static void vfc_release(struct inode *inode,struct file *file)
193 struct vfc_dev *dev;
194 dev=vfc_get_dev_ptr(MINOR(inode->i_rdev));
195 if(!dev) return;
196 if(!dev->busy) return;
197 dev->busy=0;
198 MOD_DEC_USE_COUNT;
199 return;
202 static int vfc_debug(struct vfc_dev *dev, int cmd, unsigned long arg)
204 struct vfc_debug_inout inout;
205 unsigned char *buffer;
206 int ret;
208 if(!capable(CAP_SYS_ADMIN)) return -EPERM;
210 switch(cmd) {
211 case VFC_I2C_SEND:
212 if(copy_from_user(&inout, (void *)arg, sizeof(inout))) {
213 return -EFAULT;
216 buffer = kmalloc(inout.len*sizeof(char), GFP_KERNEL);
217 if (!buffer)
218 return -ENOMEM;
220 if(copy_from_user(buffer, inout.buffer,
221 inout.len*sizeof(char))) {
222 kfree_s(buffer,inout.len*sizeof(char));
223 return -EFAULT;
227 vfc_lock_device(dev);
228 inout.ret=
229 vfc_i2c_sendbuf(dev,inout.addr & 0xff,
230 inout.buffer,inout.len);
232 if (copy_to_user((void *)arg,&inout,sizeof(inout))) {
233 kfree_s(buffer, inout.len);
234 return -EFAULT;
236 vfc_unlock_device(dev);
238 break;
239 case VFC_I2C_RECV:
240 if (copy_from_user(&inout, (void *)arg, sizeof(inout))) {
241 return -EFAULT;
244 buffer = kmalloc(inout.len, GFP_KERNEL);
245 if (!buffer)
246 return -ENOMEM;
247 memset(buffer,0,inout.len*sizeof(char));
248 vfc_lock_device(dev);
249 inout.ret=
250 vfc_i2c_recvbuf(dev,inout.addr & 0xff
251 ,buffer,inout.len);
252 vfc_unlock_device(dev);
254 if (copy_to_user(inout.buffer, buffer, inout.len)) {
255 kfree_s(buffer,inout.len);
256 return -EFAULT;
258 if (copy_to_user((void *)arg,&inout,sizeof(inout))) {
259 kfree_s(buffer,inout.len);
260 return -EFAULT;
262 kfree_s(buffer,inout.len);
263 break;
264 default:
265 return -EINVAL;
267 return 0;
270 int vfc_capture_start(struct vfc_dev *dev)
272 vfc_captstat_reset(dev);
273 dev->control_reg=dev->regs->control;
274 if((dev->control_reg & VFC_STATUS_CAPTURE)) {
275 printk(KERN_ERR "vfc%d: vfc capture status not reset\n",
276 dev->instance);
277 return -EIO;
280 vfc_lock_device(dev);
281 dev->control_reg &= ~VFC_CONTROL_CAPTURE;
282 dev->regs->control=dev->control_reg;
283 dev->control_reg |= VFC_CONTROL_CAPTURE;
284 dev->regs->control=dev->control_reg;
285 dev->control_reg &= ~VFC_CONTROL_CAPTURE;
286 dev->regs->control=dev->control_reg;
287 vfc_unlock_device(dev);
289 return 0;
292 int vfc_capture_poll(struct vfc_dev *dev)
294 int timeout=1000;
295 while(!timeout--) {
296 if((dev->regs->control & VFC_STATUS_CAPTURE)) break;
297 vfc_i2c_delay_no_busy(dev,100);
299 if(!timeout) {
300 printk(KERN_WARNING "vfc%d: capture timed out\n", dev->instance);
301 return -ETIMEDOUT;
303 return 0;
308 static int vfc_set_control_ioctl(struct inode *inode, struct file *file,
309 struct vfc_dev *dev, unsigned long arg)
311 int setcmd,ret=0;
312 if (copy_from_user(&setcmd,(void *)arg,sizeof(unsigned int)))
313 return -EFAULT;
314 VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCSCTRL) arg=0x%x\n",
315 dev->instance,setcmd));
316 switch(setcmd) {
317 case MEMPRST:
318 vfc_lock_device(dev);
319 vfc_memptr_reset(dev);
320 vfc_unlock_device(dev);
321 ret=0;
322 break;
323 case CAPTRCMD:
324 vfc_capture_start(dev);
325 vfc_capture_poll(dev);
326 break;
327 case DIAGMODE:
328 if(suser()) {
329 vfc_lock_device(dev);
330 dev->control_reg |= VFC_CONTROL_DIAGMODE;
331 dev->regs->control = dev->control_reg;
332 vfc_unlock_device(dev);
333 ret=0;
334 } else ret=-EPERM;
335 break;
336 case NORMMODE:
337 vfc_lock_device(dev);
338 dev->control_reg &= ~VFC_CONTROL_DIAGMODE;
339 dev->regs->control = dev->control_reg;
340 vfc_unlock_device(dev);
341 ret=0;
342 break;
343 case CAPTRSTR:
344 vfc_capture_start(dev);
345 ret=0;
346 break;
347 case CAPTRWAIT:
348 vfc_capture_poll(dev);
349 ret=0;
350 break;
351 default:
352 ret=-EINVAL;
354 return ret;
358 int vfc_port_change_ioctl(struct inode *inode, struct file *file,
359 struct vfc_dev *dev, unsigned long arg)
361 int ret=0;
362 int cmd;
364 if(copy_from_user(&cmd, (void *)arg, sizeof(unsigned int))) {
365 VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to "
366 "vfc_port_change_ioctl\n",dev->instance));
367 return -EFAULT;
370 VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCPORTCHG) arg=0x%x\n",
371 dev->instance,cmd));
373 switch(cmd) {
374 case 1:
375 case 2:
376 VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_START) = 0x72;
377 VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_STOP) = 0x52;
378 VFC_SAA9051_SA(dev,VFC_SAA9051_HC_START) = 0x36;
379 VFC_SAA9051_SA(dev,VFC_SAA9051_HC_STOP) = 0x18;
380 VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) = VFC_SAA9051_BP2;
381 VFC_SAA9051_SA(dev,VFC_SAA9051_C3) = VFC_SAA9051_CT | VFC_SAA9051_SS3;
382 VFC_SAA9051_SA(dev,VFC_SAA9051_SECAM_DELAY) = 0x3e;
383 break;
384 case 3:
385 VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_START) = 0x3a;
386 VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_STOP) = 0x17;
387 VFC_SAA9051_SA(dev,VFC_SAA9051_HC_START) = 0xfa;
388 VFC_SAA9051_SA(dev,VFC_SAA9051_HC_STOP) = 0xde;
389 VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) = VFC_SAA9051_BY | VFC_SAA9051_PF | VFC_SAA9051_BP2;
390 VFC_SAA9051_SA(dev,VFC_SAA9051_C3) = VFC_SAA9051_YC;
391 VFC_SAA9051_SA(dev,VFC_SAA9051_SECAM_DELAY) = 0;
392 VFC_SAA9051_SA(dev,VFC_SAA9051_C2) &= ~(VFC_SAA9051_SS0 | VFC_SAA9051_SS1);
393 break;
394 default:
395 ret=-EINVAL;
396 return ret;
397 break;
400 switch(cmd) {
401 case 1:
402 VFC_SAA9051_SA(dev,VFC_SAA9051_C2) |= VFC_SAA9051_SS0 | VFC_SAA9051_SS1;
403 break;
404 case 2:
405 VFC_SAA9051_SA(dev,VFC_SAA9051_C2) &= ~(VFC_SAA9051_SS0 | VFC_SAA9051_SS1);
406 VFC_SAA9051_SA(dev,VFC_SAA9051_C2) |= VFC_SAA9051_SS0;
407 break;
408 case 3:
409 break;
410 default:
411 ret=-EINVAL;
412 return ret;
413 break;
415 VFC_SAA9051_SA(dev,VFC_SAA9051_C3) &= ~(VFC_SAA9051_SS2);
416 ret=vfc_update_saa9051(dev);
417 udelay(500);
418 VFC_SAA9051_SA(dev,VFC_SAA9051_C3) |= (VFC_SAA9051_SS2);
419 ret=vfc_update_saa9051(dev);
420 return ret;
423 int vfc_set_video_ioctl(struct inode *inode, struct file *file,
424 struct vfc_dev *dev, unsigned long arg)
426 int ret=0;
427 int cmd;
428 if(copy_from_user(&cmd, (void *)arg, sizeof(unsigned int))) {
429 VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to "
430 "vfc_set_video_ioctl\n",dev->instance));
431 return ret;
434 VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCSVID) arg=0x%x\n",
435 dev->instance,cmd));
436 switch(cmd) {
438 case STD_NTSC:
439 VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~VFC_SAA9051_ALT;
440 VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_YPN |
441 VFC_SAA9051_CCFR0 | VFC_SAA9051_CCFR1 | VFC_SAA9051_FS;
442 ret=vfc_update_saa9051(dev);
443 break;
444 case STD_PAL:
445 VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~(VFC_SAA9051_YPN |
446 VFC_SAA9051_CCFR1 |
447 VFC_SAA9051_CCFR0 |
448 VFC_SAA9051_FS);
449 VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_ALT;
450 ret=vfc_update_saa9051(dev);
451 break;
453 case COLOR_ON:
454 VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_CO;
455 VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) &= ~(VFC_SAA9051_BY | VFC_SAA9051_PF);
456 ret=vfc_update_saa9051(dev);
457 break;
458 case MONO:
459 VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~(VFC_SAA9051_CO);
460 VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) |= VFC_SAA9051_BY | VFC_SAA9051_PF;
461 ret=vfc_update_saa9051(dev);
462 break;
463 default:
464 ret=-EINVAL;
465 break;
467 return ret;
470 int vfc_get_video_ioctl(struct inode *inode, struct file *file,
471 struct vfc_dev *dev, unsigned long arg)
473 int ret=0;
474 unsigned int status=NO_LOCK;
475 unsigned char buf[1];
477 if(vfc_i2c_recvbuf(dev,VFC_SAA9051_ADDR,buf,1)) {
478 printk(KERN_ERR "vfc%d: Unable to get status\n",dev->instance);
479 return -EIO;
482 if(buf[0] & VFC_SAA9051_HLOCK) {
483 status = NO_LOCK;
484 } else if(buf[0] & VFC_SAA9051_FD) {
485 if(buf[0] & VFC_SAA9051_CD)
486 status=NTSC_COLOR;
487 else
488 status=NTSC_NOCOLOR;
489 } else {
490 if(buf[0] & VFC_SAA9051_CD)
491 status=PAL_COLOR;
492 else
493 status=PAL_NOCOLOR;
495 VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCGVID) returning status 0x%x; buf[0]=%x\n",dev->instance,
496 status,buf[0]));
497 if (copy_to_user((void *)arg,&status,sizeof(unsigned int))) {
498 VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to "
499 "vfc_get_video_ioctl\n",dev->instance));
500 return ret;
502 return ret;
505 static int vfc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
506 unsigned long arg)
508 int ret=0;
509 unsigned int tmp;
510 struct vfc_dev *dev;
512 dev=vfc_get_dev_ptr(MINOR(inode->i_rdev));
513 if(!dev) return -ENODEV;
515 switch(cmd & 0x0000ffff) {
516 case VFCGCTRL:
517 #if 0
518 VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCGCTRL)\n",dev->instance));
519 #endif
520 tmp=dev->regs->control;
521 if(copy_to_user((void *)arg,&tmp, sizeof(unsigned int))) {
522 ret=-EFAULT;
523 break;
525 ret=0;
526 break;
527 case VFCSCTRL:
528 ret=vfc_set_control_ioctl(inode, file, dev, arg);
529 break;
530 case VFCGVID:
531 ret=vfc_get_video_ioctl(inode,file,dev,arg);
532 break;
533 case VFCSVID:
534 ret=vfc_set_video_ioctl(inode,file,dev,arg);
535 break;
536 case VFCHUE:
537 VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCHUE)\n",dev->instance));
538 if(copy_from_user(&tmp,(void *)arg,sizeof(unsigned int))) {
539 VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer "
540 "to IOCTL(VFCHUE)",dev->instance));
541 ret=-EFAULT;
542 } else {
543 VFC_SAA9051_SA(dev,VFC_SAA9051_HUE)=tmp;
544 vfc_update_saa9051(dev);
545 ret=0;
547 break;
548 case VFCPORTCHG:
549 ret=vfc_port_change_ioctl(inode, file, dev, arg);
550 break;
551 case VFCRDINFO:
552 ret=-EINVAL;
553 VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCRDINFO)\n",dev->instance));
554 break;
555 default:
556 ret=vfc_debug(vfc_get_dev_ptr(MINOR(inode->i_rdev)),
557 cmd,arg);
558 break;
560 return ret;
563 static int vfc_mmap(struct inode *inode, struct file *file,
564 struct vm_area_struct *vma)
566 unsigned int map_size,ret,map_offset;
567 struct vfc_dev *dev;
569 dev=vfc_get_dev_ptr(MINOR(inode->i_rdev));
570 if(!dev) return -ENODEV;
572 map_size=vma->vm_end - vma->vm_start;
573 if(map_size > sizeof(struct vfc_regs))
574 map_size=sizeof(struct vfc_regs);
577 if(vma->vm_offset & ~PAGE_MASK) return -ENXIO;
578 vma->vm_flags |= VM_SHM | VM_LOCKED | VM_IO | VM_MAYREAD | VM_MAYWRITE | VM_MAYSHARE;
579 map_offset=(unsigned int)dev->phys_regs;
580 ret = io_remap_page_range(vma->vm_start,map_offset,map_size,
581 vma->vm_page_prot, dev->which_io);
582 if(ret)
583 return -EAGAIN;
585 return 0;
589 static int vfc_lseek(struct inode *inode, struct file *file,
590 off_t offset, int origin)
592 return -ESPIPE;
595 static struct file_operations vfc_fops = {
596 vfc_lseek, /* vfc_lseek */
597 NULL, /* vfc_write */
598 NULL, /* vfc_read */
599 NULL, /* vfc_readdir */
600 NULL, /* vfc_select */
601 vfc_ioctl,
602 vfc_mmap,
603 vfc_open,
604 NULL, /* flush */
605 vfc_release,
608 static int vfc_probe(void)
610 struct linux_sbus *bus;
611 struct linux_sbus_device *sdev = NULL;
612 int ret;
613 int instance=0,cards=0;
615 for_all_sbusdev(sdev,bus) {
616 if (strcmp(sdev->prom_name,"vfc") == 0) {
617 cards++;
618 continue;
622 if (!cards)
623 return -ENODEV;
625 vfc_dev_lst=(struct vfc_dev **)kmalloc(sizeof(struct vfc_dev *)*
626 (cards+1),
627 GFP_KERNEL);
628 if(!vfc_dev_lst)
629 return -ENOMEM;
630 memset(vfc_dev_lst,0,sizeof(struct vfc_dev *)*(cards+1));
631 vfc_dev_lst[cards]=NULL;
633 ret=register_chrdev(VFC_MAJOR,vfcstr,&vfc_fops);
634 if(ret) {
635 printk(KERN_ERR "Unable to get major number %d\n",VFC_MAJOR);
636 kfree(vfc_dev_lst);
637 return -EIO;
640 instance=0;
641 for_all_sbusdev(sdev,bus) {
642 if (strcmp(sdev->prom_name,"vfc") == 0) {
643 vfc_dev_lst[instance]=(struct vfc_dev *)
644 kmalloc(sizeof(struct vfc_dev), GFP_KERNEL);
645 if(vfc_dev_lst[instance] == NULL) return -ENOMEM;
646 ret=init_vfc_device(sdev,
647 vfc_dev_lst[instance],
648 instance);
649 if(ret) {
650 printk(KERN_ERR "Unable to initialize"
651 " vfc%d device\n",instance);
652 } else {
655 instance++;
656 continue;
660 return 0;
663 #ifdef MODULE
664 int init_module(void)
665 #else
666 int vfc_init(void)
667 #endif
669 return vfc_probe();
672 #ifdef MODULE
673 static void deinit_vfc_device(struct vfc_dev *dev)
675 if(!dev) return;
676 sparc_free_io((void *)dev->regs,sizeof(struct vfc_regs));
677 kfree(dev);
680 void cleanup_module(void)
682 struct vfc_dev **devp;
683 unregister_chrdev(VFC_MAJOR,vfcstr);
684 for(devp=vfc_dev_lst;*devp;devp++) {
685 deinit_vfc_device(*devp);
687 kfree(vfc_dev_lst);
688 return;
690 #endif