/*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MODULE_NAME "avm-i2c" MODULE_DESCRIPTION("AVM I2C Driver"); MODULE_VERSION("1.0"); MODULE_LICENSE("\n(C) Copyright 2004, 2005, AVM"); /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int i2c_open(struct inode *, struct file *); static int i2c_close(struct inode *, struct file *); static int i2c_ioctl(struct inode *inode, struct file *filp, unsigned int ioctl_param, unsigned long argv); static void i2c_cleanup(void); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct _i2c { /*--- int major; ---*/ /*--- int minor; ---*/ /*--- devfs_handle_t devfs_handle; ---*/ struct char_device_struct *device_region; dev_t device; struct cdev *cdev; }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct _i2c i2c; struct semaphore i2c_sema; extern int i2c_debug; #define DEB_INFO(...) if(i2c_debug) printk(KERN_INFO __VA_ARGS__) #define DEB_ERR(...) printk(KERN_ERR __VA_ARGS__) int i2c_debug = 0; MODULE_PARM(i2c_debug, "i"); MODULE_PARM_DESC(i2c_debug, "Enable I2C Debug "); int i2c_frequency = 100000; MODULE_PARM(i2c_frequency, "i"); MODULE_PARM_DESC(i2c_frequency, "I2C Frequency"); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct file_operations i2c_fops = { owner: THIS_MODULE, open: i2c_open, release: i2c_close, /*--- read: i2c_read, ---*/ /*--- write: i2c_write, ---*/ ioctl: i2c_ioctl, /*--- fasync: i2c_fasync, ---*/ /*--- poll: i2c_poll, ---*/ }; /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ int __init i2c_init(void) { int reason; i2c.device = MKDEV(AVM_I2C_MAJOR, 0); reason = register_chrdev_region(i2c.device, 1, "watchdog"); if(reason) { printk("[i2c] register_chrdev_region failed: reason %d!\n", reason); return -ERESTARTSYS; } i2c.cdev = cdev_alloc(); if (!i2c.cdev) { unregister_chrdev_region(i2c.device, 1); printk("[i2c] cdev_alloc failed!\n"); return -ERESTARTSYS; } i2c.cdev->owner = i2c_fops.owner; i2c.cdev->ops = &i2c_fops; kobject_set_name(&(i2c.cdev->kobj), "i2c"); if (cdev_add(i2c.cdev, i2c.device, 1)) { kobject_put(&i2c.cdev->kobj); unregister_chrdev_region(i2c.device, 1); printk("[i2c] cdev_add failed!\n"); return -ERESTARTSYS; } sema_init(&i2c_sema, 1); return 0; } module_init(i2c_init); /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ void __exit i2c_cleanup(void) { DEB_INFO("[%s]: unregister_chrdev(%u)\n", "i2c", MAJOR(i2c.device)); i2c_hw_deinit(); if(i2c.cdev) { cdev_del(i2c.cdev); /* Delete char device */ unregister_chrdev_region(i2c.device, 1); } return; } module_exit(i2c_cleanup); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int i2c_open(struct inode *inode, struct file *filp) { /*--- struct _i2c_open_data *open_data; ---*/ DEB_INFO("[%s]: i2c_open:\n", "i2c"); /*-------------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------------*/ if(filp->f_flags & O_APPEND) { DEB_ERR("[%s]: i2c_open: open O_APPEND not supported\n", "i2c"); return -EFAULT; } /*-------------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------------*/ if(down_interruptible(&i2c_sema)) { DEB_ERR("[%s] down_interruptible() failed\n", "i2c"); return -ERESTARTSYS; } up(&i2c_sema); DEB_INFO("[%s]: i2c_open: open success flags=0x%x\n", "i2c", filp->f_flags); return 0; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int i2c_close(struct inode *inode, struct file *filp) { DEB_INFO("[%s]: i2c_close:\n", "i2c"); if(down_interruptible(&i2c_sema)) { DEB_ERR("[%s] down_interruptible() failed\n", "i2c"); return -ERESTARTSYS; } /*--- achtung auf ind wartende "gefreien" und warten bis alle fertig ---*/ up(&i2c_sema); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int i2c_ioctl(struct inode *inode, struct file *filp, unsigned int ioctl_param, unsigned long argv) { unsigned int status; struct _i2c_cmd Cmd; unsigned int number = _IOC_NR(ioctl_param); DEB_INFO("[i2c]: ioctl_param=0x%x i2c_i2ctl: type 0x%x number 0x%x argv 0x%p size 0x%x\n", ioctl_param, _IOC_TYPE(ioctl_param), number, (void *)argv, _IOC_SIZE(ioctl_param)); if(argv == 0) { printk(KERN_ERR "[i2c]: i2c_ioctl: no data pointer for cmd number %u\n", number); return -EFAULT; } if(_IOC_SIZE(ioctl_param) != sizeof(Cmd)) { DEB_ERR("[i2c]: i2c_ioctl: invallid data pointer size (%u expected)\n", sizeof(Cmd)); return -EFAULT; } if(copy_from_user(&Cmd, (void *)argv, sizeof(Cmd))) { DEB_ERR("[i2c]: i2c_ioctl: copy_from_user failed\n"); return -EFAULT; } if(Cmd.data_length > sizeof(Cmd.Buffer)) { DEB_ERR("[i2c]: i2c_ioctl: invallid data length\n"); return -EFAULT; } if(down_interruptible(&i2c_sema)) { DEB_ERR("[i2c]: down_interruptible() failed\n"); return -ERESTARTSYS; } /*--------------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------------*/ switch(number) { case _I2C_READ_ID: DEB_INFO("[i2c]: i2c_i2ctl: block read type %s device 0x%x addr 0x%x max bytes 0x%x\n", Cmd.device_type == avm_i2c_type_16_bit ? "avm_i2c_type_16_bit" : Cmd.device_type == avm_i2c_type_12_bit ? "avm_i2c_type_12_bit" : Cmd.device_type == avm_i2c_type_8_bit ? "avm_i2c_type_8_bit" : "avm_i2c_type_unknown", Cmd.device_address, Cmd.register_address, Cmd.data_length ); status = i2c_block_read(&Cmd); if(status) { up(&i2c_sema); DEB_ERR("[i2c]: i2c_ioctl: read failed\n"); return -EFAULT; } break; case _I2C_WRITE_ID: DEB_INFO("[i2c]: i2c_i2ctl: block write type %s device 0x%x addr 0x%x max bytes 0x%x\n", Cmd.device_type == avm_i2c_type_16_bit ? "avm_i2c_type_16_bit" : Cmd.device_type == avm_i2c_type_12_bit ? "avm_i2c_type_12_bit" : Cmd.device_type == avm_i2c_type_8_bit ? "avm_i2c_type_8_bit" : "avm_i2c_type_unknown", Cmd.device_address, Cmd.register_address, Cmd.data_length ); status = i2c_block_write(&Cmd); if(status) { up(&i2c_sema); DEB_ERR("[i2c]: i2c_ioctl: write failed\n"); return -EFAULT; } break; case _I2C_RAW_READ_ID: DEB_INFO("[i2c]: i2c_i2ctl: raw read type %s device 0x%x max bytes 0x%x\n", Cmd.device_type == avm_i2c_type_16_bit ? "avm_i2c_type_16_bit" : Cmd.device_type == avm_i2c_type_12_bit ? "avm_i2c_type_12_bit" : Cmd.device_type == avm_i2c_type_8_bit ? "avm_i2c_type_8_bit" : "avm_i2c_type_unknown", Cmd.device_address, Cmd.data_length); status = i2c_hw_read(Cmd.device_address, Cmd.Buffer, Cmd.data_length); if(status) { up(&i2c_sema); DEB_ERR("[i2c]: i2c_ioctl: raw read failed\n"); return -EFAULT; } break; case _I2C_RAW_WRITE_ID: DEB_INFO("[i2c]: i2c_i2ctl: raw write type %s device 0x%x max bytes 0x%x\n", Cmd.device_type == avm_i2c_type_16_bit ? "avm_i2c_type_16_bit" : Cmd.device_type == avm_i2c_type_12_bit ? "avm_i2c_type_12_bit" : Cmd.device_type == avm_i2c_type_8_bit ? "avm_i2c_type_8_bit" : "avm_i2c_type_unknown", Cmd.device_address, Cmd.data_length); status = i2c_hw_write(Cmd.device_address, Cmd.Buffer, Cmd.data_length); if(status) { up(&i2c_sema); DEB_ERR("[i2c]: i2c_ioctl: raw write failed\n"); return -EFAULT; } break; case _I2C_SET_SPEED_ID: status = i2c_hw_set_speed(*((unsigned int *)Cmd.Buffer)); if(status) { up(&i2c_sema); DEB_ERR("[i2c]: i2c_ioctl: set speed failed\n"); return -EFAULT; } break; default: printk(KERN_ERR "[i2c]: i2c_ioctl: unknown number %u\n", number); } up(&i2c_sema); if(copy_to_user((void *)argv, &Cmd, sizeof(Cmd))) { printk(KERN_ERR "[i2c]: tffs_ioctl: copy_to_user failed\n"); return -EFAULT; } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/