Skip to contentSkip to conversation

cyclades.c sysfs statistics support

PATCH In review Marcelo Tosatti 2 participants 3 messages Sep 28, 2004 Char and misc drivers

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 cyclades.c sysfs statistics support

Hi Linus,

The following patch from Germano Barreiro adds support for sysfs based extended statistics support to the Cyclades multiserial driver.

It is used by CyMonitor tool, distributed with Cyclades async driver package.

This has passed our QA procedure.

Please apply

drivers/char/cyclades.c +336 -7
@@ -2,7 +2,7 @@
 #define	Z_WAKE
 #undef	Z_EXT_CHARS_IN_BUFFER
 static char rcsid[] =
-"$Revision: 2.3.2.20 $$Date: 2004/02/25 18:14:16 $";
+"$Revision: 2.3.2.21 $$Date: 2004/09/15 13:10:10 $";
 
 /*
  *  linux/drivers/char/cyclades.c
@@ -25,6 +25,33 @@
  * This version supports shared IRQ's (only for PCI boards).
  *
  * $Log: cyclades.c,v $
+ * Revision 1.11  2004/09/15 13:10:10  germano
+ * bug correction: wrong number of ports showed at startup
+ *
+ * Revision 1.8  2004/09/08 14:26:46  germano
+ * removed debugging messages, commented code, and such stuff that normally appears during the developing process
+ *
+ * Revision 1.7  2004/09/03 22:55:55  germano
+ * made the module read the correct number of boards from the firmware when using a Z board
+ *
+ * Revision 1.6  2004/09/02 13:10:51  germano
+ * gave a more correct name to the function that exports information under /sys/device
+ *
+ * Revision 1.5  2004/09/01 21:38:23  germano
+ * old procfs code ported to new sysfs
+ *
+ * Revision 1.4  2004/08/30 18:46:05  germano
+ * new try to implement sysfs for async
+ *
+ * Revision 1.1  2004/08/27 20:55:55  germano
+ * sysfs work on async
+ *
+ * Revision 1.2  2004/08/26 20:30:11  germano
+ * daily global commit
+ *
+ * Revision 1.1  2004/08/20 20:13:19  germano
+ * started sysfs support coding
+ *
  * Prevent users from opening non-existing Z ports.
  *
  * Revision 2.3.2.8   2000/07/06 18:14:16 ivan
@@ -646,7 +673,6 @@
 #include <linux/string.h>
 #include <linux/fcntl.h>
 #include <linux/ptrace.h>
-#include <linux/cyclades.h>
 #include <linux/mm.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
@@ -672,9 +697,9 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/pci.h>
-
 #include <linux/stat.h>
 #include <linux/proc_fs.h>
+#include <linux/cyclades.h>
 
 static void cy_throttle (struct tty_struct *tty);
 static void cy_send_xchar (struct tty_struct *tty, char ch);
@@ -700,6 +725,7 @@
 
 #define	JIFFIES_DIFF(n, j)	((j) - (n))
 
+static char _version[80];
 static struct tty_driver *cy_serial_driver;
 
 #ifdef CONFIG_ISA
@@ -2528,6 +2554,13 @@
   int retval, line;
   unsigned long page;
 
+  uclong numberports=0;
+
+  /* these were included to make the necessary code for this
+     module to report the correct number of ports when using a Z-board */
+  struct ZFW_CTRL *zfw_ctrl;
+  struct BOARD_CTRL *board_ctrl;
+  
     line = tty->index;
     if ((line < 0) || (NR_PORTS <= line)){
         return -ENODEV;
@@ -2546,6 +2579,14 @@
 	struct FIRM_ID *firm_id = (struct FIRM_ID *)
         	(cinfo->base_addr + ID_ADDRESS);
 
+        /* find out the number of ports registered by the firmware*/
+   	zfw_ctrl = (struct ZFW_CTRL *)(cy_card[info->card].base_addr + (cy_readl(&firm_id->zfwctrl_addr) & 0xfffff));
+        board_ctrl = &(zfw_ctrl->board_ctrl);
+        numberports = cy_readl(&board_ctrl->n_channel); 
+        
+	/* prints the port opened together with the number of ports for that board */
+	printk(KERN_INFO "Opening port %d of %lu in this board\n",line,numberports);
+
         if (!ISZLOADED(*cinfo)) {
 	    if (((ZE_V1 ==cy_readl(&((struct RUNTIME_9060 *)
 		(cinfo->ctl_addr))->mail_box_0)) &&
@@ -2647,9 +2688,12 @@
     info->throttle = 0;
 
 #ifdef CY_DEBUG_OPEN
-    printk(" cyc:cy_open done\n");/**/
+    printk(" cyc:cy_open done\n");
 #endif
+    /*records line struct pointer for recovering when reading sysfs*/
+    cy_card[info->card].pdev->dev.driver_data=info; 
 
+    device_create_file(&(cy_card[info->card].pdev->dev), &_cydas[line]);            
     return 0;
 } /* cy_open */
 
@@ -2874,6 +2918,7 @@
 #endif
 
     CY_UNLOCK(info, flags);
+    device_remove_file(&cy_card[info->card].pdev->dev, &_cydas[info->line]);
     return;
 } /* cy_close */
 
@@ -4831,6 +4876,13 @@
   uclong		Ze_phys0[NR_CARDS], Ze_phys2[NR_CARDS];
   unsigned char		Ze_irq[NR_CARDS];
   struct pci_dev	*Ze_pdev[NR_CARDS];
+  uclong numberports=0;
+
+  /* these were included to make the necessary code for this
+     module to report the correct number of ports when using a Z-board */
+  struct FIRM_ID *firm_id;
+  struct ZFW_CTRL *zfw_ctrl;
+  struct BOARD_CTRL *board_ctrl;
 
         for (i = 0; i < NR_CARDS; i++) {
                 /* look for a Cyclades card by vendor and device id */
@@ -5229,6 +5281,12 @@
                 cy_card[j].num_chips = -1;
 		cy_card[j].pdev = pdev;
 
+                /* find out the number of ports registered by the firmware*/
+                firm_id = (struct FIRM_ID *)(cy_pci_addr2 + ID_ADDRESS);
+   		zfw_ctrl = (struct ZFW_CTRL *)(cy_pci_addr2 + (cy_readl(&firm_id->zfwctrl_addr) & 0xfffff));
+                board_ctrl = &(zfw_ctrl->board_ctrl);
+                numberports = cy_readl(&board_ctrl->n_channel); 
+
                 /* print message */
 #ifdef CONFIG_CYZ_INTR
 		/* don't report IRQ if board is no IRQ */
@@ -5243,8 +5301,6 @@
 			j+1,(ulong)cy_pci_phys2,
 			(ulong)(cy_pci_phys2 + CyPCI_Ze_win - 1));
 
-                printk("%d channels starting from port %d.\n",
-		    cy_pci_nchan,cy_next_channel);
                 cy_next_channel += cy_pci_nchan;
         }
 	if (ZeIndex != 0) {
@@ -5276,6 +5332,8 @@
         rcsvers, rcsdate);
     printk("        built %s %s\n",
 	__DATE__, __TIME__);
+    /* _version is later used by show_sys_data */
+    sprintf(_version,"Cyclades driver %s %s\n         built %s %s\n", rcsvers, rcsdate, __DATE__, __TIME__);
 } /* show_version */
 
 static int 
@@ -5393,7 +5450,6 @@
     show_version();
 
     /* Initialize the tty_driver structure */
-    
     cy_serial_driver->owner = THIS_MODULE;
     cy_serial_driver->driver_name = "cyclades";
     cy_serial_driver->name = "ttyC";
@@ -5668,4 +5724,275 @@
 } /* cy_setup */
 #endif /* MODULE */
 
+/*****************************************************************************************************
+ SYSFS SUPPORT FUNCTIONS
+ Germano Barreiro - 2/09/2004
+ ****************************************************************************************************/
+
+ /***************************************************************************************************
+  show_sys_data - shows the data exported to sysfs/device, mostly the signals status involved in the
+  serial communication such as CTS,RTS,DTS,etc
+  This function is called by the kernel whenever any user mode process tries to read a file created
+  in the virtual filesystem /sys/device/.../ttyC?
+  The code internal to this function, with few changes, was extracted from the original procfs 
+  implementation in the cyclades async serial driver, made by Ivan. 
+
+  p - points to the struct device associated with this channel. Besides some other potentially useful
+      information for future debugging tasks, cy_open() saves there a pointer to the cyclades_port
+      associated with this channel, which in recovered in this function.
+  buf - points to the buffer where the data to be available in the file mentioned above must be written
+
+  Returns the number of bytes written to user space 
+  ***************************************************************************************************/
+ static ssize_t show_sys_data(struct device *p, char *buf)
+ {  
+     
+	int len, z;
+	int	flow; // 0:none, 1:soft ctr, 2: hard ctr
+	int chip;
+        //cy_open recorded the cyclades_port pointer here
+	struct cyclades_port *info = p->driver_data;
+        //ind will be an index to the cyclades_port struct associated to this device in the array cy_port[]
+        int ind = info - cy_port; 
+	struct tty_struct * tty = info->tty;
+	unsigned char *base_addr = (unsigned char *) cy_card[info->card].base_addr;
+	struct FIRM_ID *firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS);
+	struct ZFW_CTRL *zfw_ctrl = (struct ZFW_CTRL *) (base_addr + (cy_readl(&firm_id->zfwctrl_addr) & 0xfffff));
+	struct CH_CTRL *ch_ctrl = zfw_ctrl->ch_ctrl;
+	struct BUF_CTRL *buf_ctrl = &(zfw_ctrl->buf_ctrl[ind - cy_card[info->card].first_line]);
+	int card = info->card;
+	int channel = (info->line) - (cy_card[card].first_line);
+	int index = cy_card[card].bus_index;
+	uclong control;
+
+        //refer to cyclades.h to many of the symbols below	
+	if ( tty == 0 ) {
+		len = sprintf(buf, "%s:%s\n", cysys_state, cysys_close);
+		return len;
+	} 
+	len = sprintf(buf, "%s:%s\n", cysys_state, cysys_open);
+	
+	//Printing tty and board type
+	if (IS_CYC_Z(cy_card[card])) {
+		z = 1;
+		len += sprintf(buf+len, "%s:%s\n", cysys_ct, cysys_z);
+	} else {
+		z = 0;
+		len += sprintf(buf+len, "%s:%s\n", cysys_ct, cysys_y);
+	}
+		
+	//Printing the baud rate	
+	len += sprintf(buf+len, "%s:%d\n", cysys_baud, tty_get_baud_rate(tty) );
+	
+	//Printing the flow control type
+	if (I_IXOFF(tty)) { 
+		flow = 1;	
+		len += sprintf(buf+len, "%s:%s\n", cysys_fc, cysys_fc_soft);
+	} else if ( C_CRTSCTS(tty) ) {
+		flow = 2;	
+		len += sprintf(buf+len, "%s:%s\n", cysys_fc, cysys_fc_hard);
+	} else {
+		flow = 0;	
+		len += sprintf(buf+len, "%s:%s\n", cysys_fc, cysys_fc_none);
+	}
+		
+	//printing the flow status
+	if (z == 1) { 					// Cyclades-Z board
+		if ( flow == 2 ) { 			//hard flow control
+			uclong control = cy_readl(&ch_ctrl[channel].rs_status);
+			if (control & C_RS_RTS) {
+				len += sprintf(buf+len, "%s:%s\n", cysys_rxfcs, cysys_on);
+			} else {
+				len += sprintf(buf+len, "%s:%s\n", cysys_rxfcs, cysys_off);
+			}	
+			
+			if (control & C_RS_CTS) {
+				len += sprintf(buf+len, "%s:%s\n", cysys_txfcs, cysys_on);
+			} else {
+				len += sprintf(buf+len, "%s:%s\n", cysys_txfcs, cysys_off);
+			}
+		} else if (flow == 1) { 	//soft flow control
+			// Fix me. Find how to discover the software flow control state
+		}
+	} else {						// Cyclades-Y board
+		uclong aux;
+		chip = channel >> 2;
+		channel &= 0x03;
+		base_addr = (unsigned char *)(cy_card[card].base_addr
+					 + (cy_chip_offset[chip] << index));
+		
+		if ( flow == 1 ) {			// Software flow control
+			/*			
+			int ccsr;	
+ 			unsigned long flags;
+			CY_LOCK(info, flags);
+			cy_writeb((u_long) base_addr + (CyCAR << index), (u_char) channel);
+			ccsr = cy_readb((u_long) base_addr + (CyCCSR << index));
+			CY_UNLOCK(info, flags);
+			len += sprintf(buf+len, "ccsr:%x\n", ccsr);
+			*/
+			// Fix me. It seems the CCSR register is not responding properly
+		}
+
+		if ( flow == 2 ) {			// Hardware flow control
+			uclong control = cy_readb((u_long) base_addr + (CyMSVR1 << index));
+
+			if (info->rtsdtr_inv) {
+				aux = CyDTR;
+			} else {
+				aux = CyRTS;
+			}
+			
+			if (control & aux) {
+				len += sprintf(buf+len, "%s:%s\n", cysys_rxfcs, cysys_on);
+			} else {
+				len += sprintf(buf+len, "%s:%s\n", cysys_rxfcs, cysys_off);
+			}	
+		}
+	}
+
+	//Printing status of the signals: DCD, DSR, CTS, RTS, DTR
+	if (z == 1) {					// Cyclades-Z board
+		control = cy_readl(&ch_ctrl[channel].rs_status);
+	} else {
+		control = cy_readb((u_long) base_addr + (CyMSVR1 << index));
+	}
+	
+	// DCD status
+	len += sprintf(buf+len, "%s:%s\n", cysys_dcd, 
+			   (control & (z ? C_RS_DCD:CyDCD) ) ? cysys_on : cysys_off);
+
+	// DSR Status
+	len += sprintf(buf+len, "%s:%s\n", cysys_dsr, 
+			   (control & (z ? C_RS_DSR:CyDSR) ) ? cysys_on : cysys_off);
+
+	// CTS status
+	len += sprintf(buf+len, "%s:%s\n", cysys_cts, 
+			   (control & (z ? C_RS_CTS:CyCTS) ) ? cysys_on : cysys_off);
+				
+	// RTS status
+	len += sprintf(buf+len, "%s:%s\n", cysys_rts, 
+			   (control & (z ? C_RS_RTS:CyRTS) ) ? cysys_on : cysys_off);
+
+	// DTR status
+	len += sprintf(buf+len, "%s:%s\n", cysys_dtr,
+                           (control & (z ? C_RS_DTR:(info->rtsdtr_inv ? CyRTS : CyDTR)) ) ? cysys_on : cysys_off);
+	
+	
+	//Printing status of tty buffer
+	len += sprintf(buf+len,"%s:%d/%d\n", cysys_ttyrxbuf, 
+					tty->read_cnt,N_TTY_BUF_SIZE);
+	len += sprintf(buf+len, "%s:%d/%ld\n", cysys_ttytxbuf, 
+					info->xmit_cnt, SERIAL_XMIT_SIZE);
+
+	//Printing the status of onboard buffer (for cyclades z only)
+	if ( z == 1) {
+		volatile uclong put, get, bufsize;
+		volatile int char_count;
+		//rx
+		get = cy_readl(&buf_ctrl->rx_get);
+		put = cy_readl(&buf_ctrl->rx_put);
+		bufsize = cy_readl(&buf_ctrl->rx_bufsize);
+		if (put >= get) {
+			char_count = put - get;
+		} else {
+			char_count = put - get + bufsize;
+		}
+		len += sprintf(buf+len, "%s:%d/%ld\n", cysys_boardrxbuf,
+						char_count, bufsize);
+
+		//tx
+		get = cy_readl(&buf_ctrl->tx_get);
+		put = cy_readl(&buf_ctrl->tx_put);
+		bufsize = cy_readl(&buf_ctrl->tx_bufsize);
+		if (put >= get) {
+			char_count = put - get;
+		} else {
+			char_count = put - get + bufsize;
+		}
+		len += sprintf(buf+len, "%s:%d/%ld\n", cysys_boardtxbuf,
+						char_count, bufsize);
+	}
+
+	//Printing communication data len
+	if (z == 1){
+		uclong data_len, pari;
+		char par;
+		int nstop, char_len;
+		
+		data_len = cy_readl(&ch_ctrl[channel].comm_data_l);
+		pari = cy_readl(&ch_ctrl[channel].comm_parity);
+
+		switch (pari) {
+			case C_PR_NONE:
+				par = 'n';
+				break;
+			case C_PR_ODD:
+				par = 'o';
+				break;
+			case C_PR_EVEN:
+				par = 'e';
+				break;
+			default:
+				par = 'n';
+				break;
+		}
+	
+		if ( data_len & C_DL_2STOP) {
+			nstop = 2;
+		} else {
+			nstop = 1;
+		}
+			
+		switch (data_len & 0x0f) {
+			case 1:
+				char_len = 5;
+				break;
+			case 2:
+				char_len = 6;
+				break;
+			case 4:
+				char_len = 7;
+				break;
+			case 8:
+				char_len = 8;
+				break;
+			default:
+				char_len = 8;
+				break;
+		}
+				
+		len += sprintf(buf+len, "%s:%d%c%d\n", cysys_charlen, 
+						char_len, par, nstop); 
+	} else {
+		int nstop;
+		char par;
+
+		switch ( info->cor1 & 0xc0) {
+			case CyPARITY_NONE:
+				par = 'n';
+				break;
+			case CyPARITY_O:
+				par = 'o';
+				break;
+			case CyPARITY_E:
+				par = 'e';
+				break;
+			default:
+				par = 'n';
+				break;
+		}
+
+		if ( info->cor1 & Cy_2_STOP) {
+			nstop = 2;
+		} else {
+			nstop = 1;
+		}
+		
+		len += sprintf(buf+len, "%s:%d%c%d\n", cysys_charlen, 
+						(info->cor1 & 0x03) + 5, par, nstop);
+	}	
+	return len;
+ }
+
 MODULE_LICENSE("GPL");
include/linux/cyclades.h +318 -1

339 lines not on this page · raw message

#1 lore raw
Quoted 1 line · Marcelo Tosatti

+ device_create_file(&(cy_card[info->card].pdev->dev), &_cydas[line]);

Why the array of attributes? As you only have one (which is wrong...) you only need one attribute structure.

drivers/char/cyclades.c line 5734

Quoted 2 lines
drivers/char/cyclades.c +2 -0
+  show_sys_data - shows the data exported to sysfs/device, mostly the signals status involved in the
+  serial communication such as CTS,RTS,DTS,etc

NO! sysfs is 1 value per file. Not a whole bunch of values per one file. Please change this to create a whole bunch of little files, not one big one.

thanks,

greg k-h

#2 lore raw
Quoted 5 lines · Greg KH
Quoted 1 line · Marcelo Tosatti

+ device_create_file(&(cy_card[info->card].pdev->dev), &_cydas[line]);

Why the array of attributes? As you only have one (which is wrong...) you only need one attribute structure.

Because each of them has a different name. I talked to him and he will probably stick the attribute inside each device structure.

Quoted 6 lines

drivers/char/cyclades.c line 5734

Quoted 2 lines
drivers/char/cyclades.c +2 -0
+  show_sys_data - shows the data exported to sysfs/device, mostly the signals status involved in the
+  serial communication such as CTS,RTS,DTS,etc

NO! sysfs is 1 value per file. Not a whole bunch of values per one file. Please change this to create a whole bunch of little files, not one big one.

Germano will do so.

We really appreciate your comments! Thanks.