patch-2.4.0-test2 linux/drivers/char/ppdev.c

Next file: linux/drivers/char/qpmouse.c
Previous file: linux/drivers/char/pcwd.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test1/linux/drivers/char/ppdev.c linux/drivers/char/ppdev.c
@@ -83,6 +83,63 @@
 /* ROUND_UP macro from fs/select.c */
 #define ROUND_UP(x,y) (((x)+(y)-1)/(y))
 
+struct pp_port_list_struct {
+	struct parport *port;
+	struct pp_port_list_struct *next;
+};
+static struct pp_port_list_struct *pp_port_list;
+static DECLARE_MUTEX(pp_port_list_lock);
+
+/* pp_attach and pp_detach are for keeping a list of currently
+ * available ports, held under a mutex.  We do this rather than
+ * using parport_enumerate because it stops a load of races.
+ */
+
+static void pp_attach (struct parport *port)
+{
+	struct pp_port_list_struct *add;
+
+	add = kmalloc (sizeof (struct pp_port_list_struct), GFP_KERNEL);
+	if (!add) {
+		printk (KERN_WARNING CHRDEV ": memory squeeze\n");
+		return;
+	}
+
+	add->next = pp_port_list;
+	down (&pp_port_list_lock);
+	pp_port_list = add;
+	up (&pp_port_list_lock);
+}
+
+static void pp_detach (struct parport *port)
+{
+	struct pp_port_list_struct *del;
+
+	down (&pp_port_list_lock);
+	del = pp_port_list;
+	if (del->port == port)
+		pp_port_list = del->next;
+	else {
+		struct pp_port_list_struct *prev;
+		do {
+			prev = del;
+			del = del->next;
+		} while (del && del->port != port);
+		if (del)
+			prev->next = del->next;
+	}
+	up (&pp_port_list_lock);
+
+	if (del)
+		kfree (del);
+}
+
+static struct parport_driver ppdev_driver = {
+	name:	CHRDEV,
+	attach:	pp_attach,
+	detach:	pp_detach
+};
+
 static inline void pp_enable_irq (struct pp_struct *pp)
 {
 	struct parport *port = pp->pdev->port;
@@ -216,7 +273,7 @@
 
 static int register_device (int minor, struct pp_struct *pp)
 {
-	struct parport * port;
+	struct pp_port_list_struct *ports;
 	struct pardevice * pdev = NULL;
 	char *name;
 	int fl;
@@ -226,20 +283,24 @@
 		return -ENOMEM;
 
 	sprintf (name, CHRDEV "%x", minor);
-	port = parport_enumerate (); /* FIXME: use attach/detach */
 
-	while (port && port->number != minor)
-		port = port->next;
+	down (&pp_port_list_lock);
+	ports = pp_port_list;
+	while (ports && ports->port->number != minor)
+		ports = ports->next;
+	if (ports->port) {
+		fl = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0;
+		pdev = parport_register_device (ports->port, name, NULL,
+						NULL, pp_irq, fl, pp);
+	}
+	up (&pp_port_list_lock);
 
-	if (!port) {
+	if (!ports->port) {
 		printk (KERN_WARNING "%s: no associated port!\n", name);
 		kfree (name);
 		return -ENXIO;
 	}
 
-	fl = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0;
-	pdev = parport_register_device (port, name, NULL, NULL, pp_irq, fl,
-					pp);
 
 	if (!pdev) {
 		printk (KERN_WARNING "%s: failed to register device!\n", name);
@@ -507,13 +568,9 @@
 	if (minor >= PARPORT_MAX)
 		return -ENXIO;
 
-	MOD_INC_USE_COUNT;
-
 	pp = kmalloc (sizeof (struct pp_struct), GFP_KERNEL);
-	if (!pp) {
-		MOD_DEC_USE_COUNT;
+	if (!pp)
 		return -ENOMEM;
-	}
 
 	pp->state.mode = IEEE1284_MODE_COMPAT;
 	pp->state.phase = init_phase (pp->state.mode);
@@ -565,7 +622,6 @@
 
 	kfree (pp);
 
-	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -583,6 +639,7 @@
 }
 
 static struct file_operations pp_fops = {
+	owner:		THIS_MODULE,
 	llseek:		pp_lseek,
 	read:		pp_read,
 	write:		pp_write,
@@ -596,6 +653,10 @@
 
 static int __init ppdev_init (void)
 {
+	if (parport_register_driver (&ppdev_driver)) {
+		printk (KERN_WARNING CHRDEV ": unable to register driver\n");
+		return -EIO;
+	}
 	if (devfs_register_chrdev (PP_MAJOR, CHRDEV, &pp_fops)) {
 		printk (KERN_WARNING CHRDEV ": unable to get major %d\n",
 			PP_MAJOR);
@@ -604,7 +665,7 @@
 	devfs_handle = devfs_mk_dir (NULL, "parports", 0, NULL);
 	devfs_register_series (devfs_handle, "%u", PARPORT_MAX,
 			       DEVFS_FL_DEFAULT, PP_MAJOR, 0,
-			       S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+			       S_IFCHR | S_IRUGO | S_IWUGO,
 			       &pp_fops, NULL);
 
 	printk (KERN_INFO PP_VERSION "\n");
@@ -616,6 +677,7 @@
 	/* Clean up all parport stuff */
 	devfs_unregister (devfs_handle);
 	devfs_unregister_chrdev (PP_MAJOR, CHRDEV);
+	parport_unregister_driver (&ppdev_driver);
 }
 
 module_init(ppdev_init);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)