| /* |
| * I2C driver for parallel port |
| * |
| * Author: Phil Blundell <philb@gnu.org> |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version |
| * 2 of the License, or (at your option) any later version. |
| * |
| * This driver implements a simple I2C protocol by bit-twiddling some |
| * signals on the parallel port. Since the outputs on the parallel port |
| * aren't open collector, three lines rather than two are used: |
| * |
| * D0 clock out |
| * D1 data out |
| * BUSY data in |
| */ |
| |
| #include <linux/parport.h> |
| #include <linux/module.h> |
| #include <linux/delay.h> |
| #include <linux/i2c-old.h> |
| #include <linux/init.h> |
| #include <linux/spinlock.h> |
| |
| #define I2C_DELAY 10 |
| |
| static int debug = 0; |
| |
| struct parport_i2c_bus |
| { |
| struct i2c_bus i2c; |
| struct parport_i2c_bus *next; |
| }; |
| |
| static struct parport_i2c_bus *bus_list; |
| |
| static spinlock_t bus_list_lock = SPIN_LOCK_UNLOCKED; |
| |
| /* software I2C functions */ |
| |
| static void i2c_setlines(struct i2c_bus *bus, int clk, int data) |
| { |
| struct parport *p = bus->data; |
| parport_write_data(p, (clk?1:0) | (data?2:0)); |
| udelay(I2C_DELAY); |
| } |
| |
| static int i2c_getdataline(struct i2c_bus *bus) |
| { |
| struct parport *p = bus->data; |
| return (parport_read_status(p) & PARPORT_STATUS_BUSY) ? 0 : 1; |
| } |
| |
| static struct i2c_bus parport_i2c_bus_template = |
| { |
| "...", |
| I2C_BUSID_PARPORT, |
| NULL, |
| |
| SPIN_LOCK_UNLOCKED, |
| |
| NULL, |
| NULL, |
| |
| i2c_setlines, |
| i2c_getdataline, |
| NULL, |
| NULL, |
| }; |
| |
| static void i2c_parport_attach(struct parport *port) |
| { |
| struct parport_i2c_bus *b = kmalloc(sizeof(struct parport_i2c_bus), |
| GFP_KERNEL); |
| if (!b) { |
| printk(KERN_ERR "i2c_parport: Memory allocation failed. Not attaching.\n"); |
| return; |
| } |
| b->i2c = parport_i2c_bus_template; |
| b->i2c.data = parport_get_port (port); |
| strncpy(b->i2c.name, port->name, 32); |
| spin_lock(&bus_list_lock); |
| b->next = bus_list; |
| bus_list = b; |
| spin_unlock(&bus_list_lock); |
| i2c_register_bus(&b->i2c); |
| if (debug) |
| printk(KERN_DEBUG "i2c: attached to %s\n", port->name); |
| } |
| |
| static void i2c_parport_detach(struct parport *port) |
| { |
| struct parport_i2c_bus *b, *old_b = NULL; |
| spin_lock(&bus_list_lock); |
| b = bus_list; |
| while (b) |
| { |
| if (b->i2c.data == port) |
| { |
| if (old_b) |
| old_b->next = b->next; |
| else |
| bus_list = b->next; |
| i2c_unregister_bus(&b->i2c); |
| kfree(b); |
| break; |
| } |
| old_b = b; |
| b = b->next; |
| } |
| spin_unlock(&bus_list_lock); |
| if (debug) |
| printk(KERN_DEBUG "i2c: detached from %s\n", port->name); |
| } |
| |
| static struct parport_driver parport_i2c_driver = |
| { |
| "i2c", |
| i2c_parport_attach, |
| i2c_parport_detach |
| }; |
| |
| #ifdef MODULE |
| int init_module(void) |
| #else |
| int __init i2c_parport_init(void) |
| #endif |
| { |
| printk("I2C: driver for parallel port v0.1 philb@gnu.org\n"); |
| parport_register_driver(&parport_i2c_driver); |
| return 0; |
| } |
| |
| #ifdef MODULE |
| MODULE_PARM(debug, "i"); |
| |
| void cleanup_module(void) |
| { |
| struct parport_i2c_bus *b = bus_list; |
| while (b) |
| { |
| struct parport_i2c_bus *next = b->next; |
| i2c_unregister_bus(&b->i2c); |
| kfree(b); |
| b = next; |
| } |
| parport_unregister_driver(&parport_i2c_driver); |
| } |
| #endif |
| MODULE_LICENSE("GPL"); |