Tim Waugh (tim@cyberelk.demon.co.uk)
Tue, 18 May 1999 21:04:25 +0100 (GMT)
Here's the patch I plan to send to Linus for 2.3.4 when he gets back from
Finland. Please test it out in case I messed something up. ;-)
In this patch:
o Phil's I2C-parport driver (plus a fix of mine in i2c.h)
o unified irq/dma module parameter parsing and checking
o register_driver/unregister_driver/announce_port API additions
o code for PCI support (without the actual support: I've sent a patch to
linux-pcisupport@cck.uni-kl.de with the necessary pci.h changes)
Thanks,
Tim.
*/
diff -durN linux-2.3.3/drivers/char/Config.in linux/drivers/char/Config.in
--- linux-2.3.3/drivers/char/Config.in Wed May 12 09:14:28 1999
+++ linux/drivers/char/Config.in Tue May 18 19:18:56 1999
@@ -159,6 +159,7 @@
if [ "$CONFIG_RADIO_ZOLTRIX" = "y" ]; then
hex ' ZOLTRIX I/O port (0x20c or 0x30c)' CONFIG_RADIO_ZOLTRIX_PORT 20c
fi
+ dep_tristate 'IIC on parallel port' CONFIG_I2C_PARPORT $CONFIG_PARPORT
fi
endmenu
diff -durN linux-2.3.3/drivers/char/Makefile linux/drivers/char/Makefile
--- linux-2.3.3/drivers/char/Makefile Wed May 12 09:14:28 1999
+++ linux/drivers/char/Makefile Tue May 18 19:18:11 1999
@@ -330,6 +330,16 @@
endif
endif
+ifeq ($(CONFIG_I2C_PARPORT),y)
+L_OBJS += i2c-parport.o
+L_I2C = y
+else
+ ifeq ($(CONFIG_I2C_PARPORT),m)
+ M_OBJS += i2c-parport.o
+ M_I2C = y
+ endif
+endif
+
ifeq ($(CONFIG_VIDEO_BWQCAM),y)
L_OBJS += bw-qcam.o
else
diff -durN linux-2.3.3/drivers/char/i2c-parport.c linux/drivers/char/i2c-parport.c
--- linux-2.3.3/drivers/char/i2c-parport.c Thu Jan 1 01:00:00 1970
+++ linux/drivers/char/i2c-parport.c Tue May 18 19:15:24 1999
@@ -0,0 +1,149 @@
+/*
+ * 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.h>
+#include <linux/init.h>
+#include <asm/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;
+
+#ifdef __SMP__
+static spinlock_t bus_list_lock = SPIN_LOCK_UNLOCKED;
+#endif
+
+/* 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);
+ b->i2c = parport_i2c_bus_template;
+ b->i2c.data = 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
diff -durN linux-2.3.3/drivers/misc/parport_init.c linux/drivers/misc/parport_init.c
--- linux-2.3.3/drivers/misc/parport_init.c Mon May 17 19:31:07 1999
+++ linux/drivers/misc/parport_init.c Tue May 18 19:35:23 1999
@@ -22,7 +22,7 @@
static int io[PARPORT_MAX+1] __initdata = { [0 ... PARPORT_MAX] = 0 };
static int io_hi[PARPORT_MAX+1] __initdata = { [0 ... PARPORT_MAX] = 0 };
static int irq[PARPORT_MAX] __initdata = { [0 ... PARPORT_MAX-1] = PARPORT_IRQ_PROBEONLY };
-static int dma[PARPORT_MAX] __initdata = { [0 ... PARPORT_MAX-1] = PARPORT_DMA_NONE };
+static int dma[PARPORT_MAX] __initdata = { [0 ... PARPORT_MAX-1] = PARPORT_DMA_AUTO };
extern int parport_pc_init(int *io, int *io_hi, int *irq, int *dma);
extern int parport_ax_init(void);
@@ -146,8 +146,11 @@
EXPORT_SYMBOL(parport_claim_or_block);
EXPORT_SYMBOL(parport_release);
EXPORT_SYMBOL(parport_register_port);
+EXPORT_SYMBOL(parport_announce_port);
EXPORT_SYMBOL(parport_unregister_port);
EXPORT_SYMBOL(parport_quiesce);
+EXPORT_SYMBOL(parport_register_driver);
+EXPORT_SYMBOL(parport_unregister_driver);
EXPORT_SYMBOL(parport_register_device);
EXPORT_SYMBOL(parport_unregister_device);
EXPORT_SYMBOL(parport_enumerate);
@@ -157,6 +160,7 @@
EXPORT_SYMBOL(parport_proc_unregister);
EXPORT_SYMBOL(parport_probe_hook);
EXPORT_SYMBOL(parport_parse_irqs);
+EXPORT_SYMBOL(parport_parse_dmas);
void inc_parport_count(void)
{
diff -durN linux-2.3.3/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
--- linux-2.3.3/drivers/misc/parport_pc.c Mon May 17 19:31:07 1999
+++ linux/drivers/misc/parport_pc.c Tue May 18 19:36:31 1999
@@ -44,6 +44,7 @@
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/malloc.h>
+#include <linux/pci.h>
#include <asm/io.h>
@@ -847,9 +848,59 @@
if (parport_probe_hook)
(*parport_probe_hook)(p);
+ /* Now that we've told the sharing engine about the port, and
+ found out its characteristics, let the high-level drivers
+ know about it. */
+ parport_announce_port (p);
+
return 1;
}
+/* Look for PCI parallel port cards. */
+static int __init parport_pc_init_pci (int irq, int dma)
+{
+ int count = 0;
+#ifdef CONFIG_PCI
+ int i;
+ struct {
+ unsigned int vendor;
+ unsigned int device;
+ unsigned int numports;
+ struct {
+ unsigned int lo;
+ unsigned int hi; /* -ve if not there */
+ } addr[4];
+ } cards[] = {
+ { 0, }
+ };
+
+ if (!pci_present ())
+ return 0;
+
+ for (i = 0; cards[i].vendor; i++) {
+ struct pci_dev *pcidev = NULL;
+ while ((pcidev = pci_find_device (cards[i].vendor,
+ cards[i].device,
+ pcidev)) != NULL) {
+ int n;
+ for (n = 0; n < cards[i].numports; n++) {
+ int lo = cards[i].addr[n].lo;
+ int hi = cards[i].addr[n].hi;
+ int io_lo = pcidev->base_address[lo];
+ int io_hi = ((hi < 0) ? 0 :
+ pcidev->base_address[hi]);
+ io_lo &= PCI_BASE_ADDRESS_IO_MASK;
+ io_hi &= PCI_BASE_ADDRESS_IO_MASK;
+ count += probe_one_port (io_lo, io_hi,
+ irq, dma);
+ }
+ }
+ }
+#endif /* CONFIG_PCI */
+
+ return count;
+}
+
int __init parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
{
int count = 0, i = 0;
@@ -866,6 +917,7 @@
count += probe_one_port(0x3bc, 0x7bc, irq[0], dma[0]);
count += probe_one_port(0x378, 0x778, irq[0], dma[0]);
count += probe_one_port(0x278, 0x678, irq[0], dma[0]);
+ count += parport_pc_init_pci (irq[0], dma[0]);
}
return count;
@@ -874,13 +926,22 @@
#ifdef MODULE
static int io[PARPORT_PC_MAX_PORTS+1] = { [0 ... PARPORT_PC_MAX_PORTS] = 0 };
static int io_hi[PARPORT_PC_MAX_PORTS+1] = { [0 ... PARPORT_PC_MAX_PORTS] = 0 };
-static int dma[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_DMA_NONE };
+static int dmaval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_DMA_AUTO };
static int irqval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY };
static const char *irq[PARPORT_PC_MAX_PORTS] = { NULL, };
+static const char *dma[PARPORT_PC_MAX_PORTS] = { NULL, };
+
+MODULE_PARM_DESC(io, "base address");
MODULE_PARM(io, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i");
+
+MODULE_PARM_DESC(io_hi, "base address for ECR");
MODULE_PARM(io_hi, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i");
+
+MODULE_PARM_DESC(irq, "irq line to use (or 'auto' or 'none')");
MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
-MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i");
+
+MODULE_PARM_DESC(dma, "dma channel to use (or 'auto' or 'none')");
+MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
int init_module(void)
{
@@ -888,9 +949,30 @@
the irq values. */
unsigned int i;
for (i = 0; i < PARPORT_PC_MAX_PORTS && io[i]; i++);
- parport_parse_irqs(i, irq, irqval);
+ if (i) {
+ if (parport_parse_irqs(i, irq, irqval)) return 1;
+ if (parport_parse_dmas(i, dma, dmaval)) return 1;
+ }
+ else {
+ /* The user can make us use any IRQs or DMAs we find. */
+ int val;
- return (parport_pc_init(io, io_hi, irqval, dma)?0:1);
+ if (irq[0] && !parport_parse_irqs (1, irq, &val))
+ switch (val) {
+ case PARPORT_IRQ_NONE:
+ case PARPORT_IRQ_AUTO:
+ irqval[0] = val;
+ }
+
+ if (dma[0] && !parport_parse_dmas (1, dma, &val))
+ switch (val) {
+ case PARPORT_DMA_NONE:
+ case PARPORT_DMA_AUTO:
+ dmaval[0] = val;
+ }
+ }
+
+ return (parport_pc_init(io, io_hi, irqval, dmaval)?0:1);
}
void cleanup_module(void)
diff -durN linux-2.3.3/drivers/misc/parport_share.c linux/drivers/misc/parport_share.c
--- linux-2.3.3/drivers/misc/parport_share.c Fri May 14 08:39:38 1999
+++ linux/drivers/misc/parport_share.c Tue May 18 19:33:55 1999
@@ -40,6 +40,55 @@
static struct parport *portlist = NULL, *portlist_tail = NULL;
spinlock_t parportlist_lock = SPIN_LOCK_UNLOCKED;
+static struct parport_driver *driver_chain = NULL;
+spinlock_t driverlist_lock = SPIN_LOCK_UNLOCKED;
+
+static void call_driver_chain (int attach, struct parport *port)
+{
+ struct parport_driver *drv;
+
+ for (drv = driver_chain; drv; drv = drv->next) {
+ if (attach)
+ drv->attach (port);
+ else
+ drv->detach (port);
+ }
+}
+
+int parport_register_driver (struct parport_driver *drv)
+{
+ struct parport *port;
+
+ spin_lock (&driverlist_lock);
+ drv->next = driver_chain;
+ driver_chain = drv;
+ spin_unlock (&driverlist_lock);
+
+ for (port = portlist; port; port = port->next)
+ drv->attach (port);
+
+ return 0;
+}
+
+void parport_unregister_driver (struct parport_driver *arg)
+{
+ struct parport_driver *drv = driver_chain, *olddrv = NULL;
+
+ while (drv) {
+ if (drv == arg) {
+ spin_lock (&driverlist_lock);
+ if (olddrv)
+ olddrv->next = drv->next;
+ else
+ driver_chain = drv->next;
+ spin_unlock (&driverlist_lock);
+ return;
+ }
+ olddrv = drv;
+ drv = drv->next;
+ }
+}
+
void (*parport_probe_hook)(struct parport *port) = NULL;
/* Return a list of all the ports we know about. */
@@ -138,10 +187,19 @@
return tmp;
}
+void parport_announce_port (struct parport *port)
+{
+ /* Let drivers know that a new port has arrived. */
+ call_driver_chain (1, port);
+}
+
void parport_unregister_port(struct parport *port)
{
struct parport *p;
+ /* Spread the word. */
+ call_driver_chain (0, port);
+
spin_lock(&parportlist_lock);
if (portlist == port) {
if ((portlist = port->next) == NULL)
@@ -517,23 +575,38 @@
}
}
-void parport_parse_irqs(int nports, const char *irqstr[], int irqval[])
+static int parport_parse_params (int nports, const char *str[], int val[],
+ int automatic, int none)
{
unsigned int i;
- for (i = 0; i < nports && irqstr[i]; i++) {
- if (!strncmp(irqstr[i], "auto", 4))
- irqval[i] = PARPORT_IRQ_AUTO;
- else if (!strncmp(irqstr[i], "none", 4))
- irqval[i] = PARPORT_IRQ_NONE;
+ for (i = 0; i < nports && str[i]; i++) {
+ if (!strncmp(str[i], "auto", 4))
+ val[i] = automatic;
+ else if (!strncmp(str[i], "none", 4))
+ val[i] = none;
else {
char *ep;
- unsigned long r = simple_strtoul(irqstr[i], &ep, 0);
- if (ep != irqstr[i])
- irqval[i] = r;
+ unsigned long r = simple_strtoul(str[i], &ep, 0);
+ if (ep != str[i])
+ val[i] = r;
else {
- printk("parport: bad irq specifier `%s'\n", irqstr[i]);
- return;
+ printk("parport: bad specifier `%s'\n", str[i]);
+ return -1;
}
}
}
+
+ return 0;
+}
+
+int parport_parse_irqs(int nports, const char *irqstr[], int irqval[])
+{
+ return parport_parse_params (nports, irqstr, irqval, PARPORT_IRQ_AUTO,
+ PARPORT_IRQ_NONE);
+}
+
+int parport_parse_dmas(int nports, const char *dmastr[], int dmaval[])
+{
+ return parport_parse_params (nports, dmastr, dmaval, PARPORT_DMA_AUTO,
+ PARPORT_DMA_NONE);
}
diff -durN linux-2.3.3/include/linux/i2c.h linux/include/linux/i2c.h
--- linux-2.3.3/include/linux/i2c.h Fri Jan 15 22:36:21 1999
+++ linux/include/linux/i2c.h Tue May 18 19:52:10 1999
@@ -22,6 +22,8 @@
*
*/
+#include <linux/version.h>
+
#define I2C_BUS_MAX 4 /* max # of bus drivers */
#define I2C_DRIVER_MAX 8 /* max # of chip drivers */
#define I2C_DEVICE_MAX 8 /* max # if devices per bus/driver */
@@ -35,6 +37,7 @@
#define I2C_DRIVERID_VIDEOTEXT 3
#define I2C_BUSID_BT848 1 /* I2C bus on a BT848 */
+#define I2C_BUSID_PARPORT 2 /* Bit banging on a parallel port */
/*
* struct for a driver for a i2c chip (tuner, soundprocessor,
diff -durN linux-2.3.3/include/linux/parport.h linux/include/linux/parport.h
--- linux-2.3.3/include/linux/parport.h Mon May 17 19:31:14 1999
+++ linux/include/linux/parport.h Tue May 18 19:46:04 1999
@@ -212,6 +212,13 @@
rwlock_t cad_lock;
};
+struct parport_driver {
+ const char *name;
+ void (*attach) (struct parport *);
+ void (*detach) (struct parport *);
+ struct parport_driver *next;
+};
+
/* parport_register_port registers a new parallel port at the given address (if
* one does not already exist) and returns a pointer to it. This entails
* claiming the I/O region, IRQ and DMA.
@@ -220,6 +227,13 @@
struct parport *parport_register_port(unsigned long base, int irq, int dma,
struct parport_operations *ops);
+/* Once a registered port is ready for high-level drivers to use, the
+ low-level driver that registered it should announce it. This will
+ call the high-level drivers' attach() functions (after things like
+ determining the IEEE 1284.3 topology of the port and collecting
+ DeviceIDs). */
+void parport_announce_port (struct parport *port);
+
/* Unregister a port. */
extern void parport_unregister_port(struct parport *port);
@@ -235,6 +249,12 @@
*/
struct parport *parport_enumerate(void);
+/* Register a new high-level driver. */
+extern int parport_register_driver (struct parport_driver *);
+
+/* Unregister a high-level driver. */
+extern void parport_unregister_driver (struct parport_driver *);
+
/* parport_register_device declares that a device is connected to a port, and
* tells the kernel all it needs to know.
* pf is the preemption function (may be NULL for no callback)
@@ -322,7 +342,8 @@
#define PARPORT_FLAG_COMA (1<<0)
#define PARPORT_FLAG_EXCL (1<<1) /* EXCL driver registered. */
-extern void parport_parse_irqs(int, const char *[], int irqval[]);
+extern int parport_parse_irqs(int, const char *[], int irqval[]);
+extern int parport_parse_dmas(int, const char *[], int irqval[]);
extern int parport_ieee1284_nibble_mode_ok(struct parport *, unsigned char);
extern int parport_wait_peripheral(struct parport *, unsigned char, unsigned
char);
-- To unsubscribe, send mail to: linux-parport-request@torque.net --
-- with the single word "unsubscribe" in the body of the message. --
This archive was generated by hypermail 2.0b3 on Tue 18 May 1999 - 16:05:45 EDT