LinCAN /proc file-system support updated to be compatible even with 3.12+ kernels.
authorPavel Pisa <pisa@cmp.felk.cvut.cz>
Sat, 30 Nov 2013 15:40:17 +0000 (16:40 +0100)
committerPavel Pisa <pisa@cmp.felk.cvut.cz>
Sat, 30 Nov 2013 15:40:17 +0000 (16:40 +0100)
Code variant for older 2.6.x and 3.x Linux kernels tested on 3.2 version.
Support for 2.2 and 2.4 kernels updated but not tested. It would be
most probably removed after next release.

Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz>
lincan/src/proc.c

index 16dda35..34ecc43 100644 (file)
@@ -61,10 +61,130 @@ static int cc=0; /* static counter for each CAN chip */
 static struct canproc_t can_proc_base;
 static struct canproc_t *base=&can_proc_base;
 
-/* The following functions are needed only for kernel version 2.2. Kernel
- * version 2.4 already defines them for us.
- */
-#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,0))
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
+
+#include <linux/seq_file.h>
+
+#define CAN_PROC_SHOW_SINGLE_OPS_DECLARE(ops_name, show_function) \
+static int ops_name##_open(struct inode *inode, struct file *file) \
+{ \
+       return single_open(file, show_function, PDE_DATA(inode)); \
+} \
+static const struct file_operations ops_name = { \
+       .owner          = THIS_MODULE, \
+       .open           = ops_name##_open, \
+       .read           = seq_read, \
+       .llseek         = seq_lseek, \
+       .release        = single_release, \
+};
+
+typedef struct file_operations can_proc_read_entry_ops_t;
+typedef struct seq_file can_proc_seq_file_t;
+
+static inline void *can_seq_data(can_proc_seq_file_t *sqf)
+{
+       return sqf->private;
+}
+
+#define can_seq_printf seq_printf
+
+struct proc_dir_entry *can_create_proc_read_entry(const char *name,
+       mode_t mode, struct proc_dir_entry *parent,
+       const can_proc_read_entry_ops_t *proc_read_entry_ops, void * data)
+{
+       return proc_create_data(name, mode, parent, proc_read_entry_ops, data);
+}
+
+struct proc_dir_entry *can_proc_mkdir_mode(const char *name, umode_t mode,
+                                       struct proc_dir_entry *parent)
+{
+       return proc_mkdir_mode(name, mode, parent);
+}
+
+static int can_proc_remove(struct proc_dir_entry *del)
+{
+       if(!del) return -ENODEV;
+       proc_remove(del);
+       return 0;
+}
+
+static inline struct proc_dir_entry *can_proc_symlink(const char *name,
+                struct proc_dir_entry *parent, const char *dest)
+{
+       return proc_symlink(name, parent, dest);
+}
+
+#else /* kernel older than 3.10 */
+
+typedef read_proc_t *can_proc_read_entry_ops_t;
+typedef struct {
+       int len;
+       int limit;
+       char *buf;
+       void *private;
+} can_proc_seq_file_t;
+
+#define CAN_PROC_SHOW_SINGLE_OPS_DECLARE(ops_name, show_function) \
+static int ops_name##_show_wrapper(char *buf, char **start, off_t offset, \
+                int count, int *eof, void *data) \
+{ \
+       can_proc_seq_file_t seq_pos = { .len = 0, .buf = buf, .limit = PAGE_SIZE, \
+                                       .private = data}; \
+       *eof = 1; \
+       show_function(&seq_pos, data); \
+       return seq_pos.len; \
+} \
+const static can_proc_read_entry_ops_t ops_name = ops_name##_show_wrapper;
+
+static inline void *can_seq_data(can_proc_seq_file_t *sqf)
+{
+       return sqf->private;
+}
+
+static inline int can_seq_printf(can_proc_seq_file_t *sqf, const char *f, ...)
+{
+       int ret;
+       va_list args;
+       va_start(args, f);
+       ret = vsnprintf(sqf->buf + sqf->len, sqf->limit - sqf->len, f, args);
+       sqf->len += ret;
+       if (sqf->len > sqf->limit)
+               sqf->len = sqf->limit;
+       va_end(args);
+       return ret;
+}
+
+struct proc_dir_entry *can_create_proc_read_entry(const char *name,
+       mode_t mode, struct proc_dir_entry *parent,
+       const can_proc_read_entry_ops_t *proc_read_entry_ops, void * data)
+{
+       return create_proc_read_entry(name, mode, parent, *proc_read_entry_ops, data);
+}
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0))
+
+struct proc_dir_entry *can_proc_mkdir_mode(const char *name, umode_t mode,
+                                       struct proc_dir_entry *parent)
+{
+       return create_proc_entry(name, S_IFDIR | mode, parent);
+}
+
+/* This does not fully follow linux 2.4 and 2.6 prototype to simplify 2.2.x compatibility */
+/* The newer kernels use entry name instead of pointer to the entry */
+static int can_proc_remove(struct proc_dir_entry *del)
+{
+       if(!del) return -ENODEV;
+       remove_proc_entry(del->name, del->parent);
+       return 0;
+}
+
+static inline struct proc_dir_entry *can_proc_symlink(const char *name,
+                struct proc_dir_entry *parent, const char *dest)
+{
+       return proc_symlink(name, parent, dest);
+}
+
+#else /* ancient Linux kernel older than 2.3.0 */
 static void can_fill_inode(struct inode *inode, int fill)
 {
        if (fill)
@@ -160,87 +280,9 @@ static inline struct proc_dir_entry *can_proc_symlink(const char *name,
        return entry;
 }
 
-#elif (LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0))
-
-struct proc_dir_entry *can_create_proc_read_entry(const char *name,
-       mode_t mode, struct proc_dir_entry *parent,
-       read_proc_t *read_proc, void * data)
-{
-       return create_proc_read_entry(name, mode, parent, read_proc, data);
-}
-
-struct proc_dir_entry *can_proc_mkdir_mode(const char *name, umode_t mode,
-                                       struct proc_dir_entry *parent)
-{
-       return create_proc_entry(name, S_IFDIR | mode, parent);
-}
-
-/* This does not fully follow linux 2.4 and 2.6 prototype to simplify 2.2.x compatibility */
-/* The newer kernels use entry name instead of pointer to the entry */
-static int can_proc_remove(struct proc_dir_entry *del)
-{
-       if(!del) return -ENODEV;
-       remove_proc_entry(del->name, del->parent);
-       return 0;
-}
-
-static inline struct proc_dir_entry *can_proc_symlink(const char *name,
-                struct proc_dir_entry *parent, const char *dest)
-{
-       return proc_symlink(name, parent, dest);
-}
-
-#else /* Linux kernel 3.10+ */
+#endif /* Linux 2.2 - 2.3 kernels */
 
-#include <linux/seq_file.h>
-
-static int can_status_proc_show(struct seq_file *m, void *v)
-{
-       seq_printf(m, "dummy CAN can_create_proc_read_entry implementation\n");
-       return 0;
-}
-
-static int can_status_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, can_status_proc_show, PDE_DATA(inode));
-}
-
-static const struct file_operations can_status_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = can_status_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-struct proc_dir_entry *can_create_proc_read_entry(const char *name,
-       mode_t mode, struct proc_dir_entry *parent,
-       void *read_proc, void * data)
-{
-       /*return proc_read_entry(name, mode, parent, read_proc, data);*/
-       return proc_create_data(name, mode, parent, &can_status_proc_fops, data);
-}
-
-struct proc_dir_entry *can_proc_mkdir_mode(const char *name, umode_t mode,
-                                       struct proc_dir_entry *parent)
-{
-       return proc_mkdir_mode(name, mode, parent);
-}
-
-static int can_proc_remove(struct proc_dir_entry *del)
-{
-       if(!del) return -ENODEV;
-       proc_remove(del);
-       return 0;
-}
-
-static inline struct proc_dir_entry *can_proc_symlink(const char *name,
-                struct proc_dir_entry *parent, const char *dest)
-{
-       return proc_symlink(name, parent, dest);
-}
-
-#endif /* Functions required for kernel 2.2 */
+#endif /* Linux kernel 3.6 and 3.10+ */
 
 /* can_init_procdir registers the entire CAN directory tree recursively at
  * the proc system.
@@ -274,24 +316,21 @@ int can_delete_procdir(void)
        return 0;
 }
 
-static int can_chip_procinfo(char *buf, char **start, off_t offset,
-                int count, int *eof, void *data)
+static int can_chip_procinfo_show(can_proc_seq_file_t *sqf, void *data)
 {
-       struct canchip_t *chip=data;
-       int len=0;
+       struct canchip_t *chip=can_seq_data(sqf);
 
        /* Generic chip info */
-       len += sprintf(buf+len,"type    : %s\n",chip->chip_type);
-       len += sprintf(buf+len,"index   : %d\n",chip->chip_idx);
-       len += sprintf(buf+len,"irq     : %d\n",chip->chip_irq);
-       len += sprintf(buf+len,"addr    : %lu\n",
+       can_seq_printf(sqf, "type    : %s\n",chip->chip_type);
+       can_seq_printf(sqf, "index   : %d\n",chip->chip_idx);
+       can_seq_printf(sqf, "irq     : %d\n",chip->chip_irq);
+       can_seq_printf(sqf, "addr    : %lu\n",
                        can_ioptr2ulong(chip->chip_base_addr));
-       len += sprintf(buf+len,"config  : %s\n",
+       can_seq_printf(sqf, "config  : %s\n",
                        (chip->flags & CHIP_CONFIGURED) ? "yes":"no");
-       len += sprintf(buf+len,"clock   : %ld Hz\n",chip->clock);
-       len += sprintf(buf+len,"baud    : %ld\n",chip->baudrate);
-       len += sprintf(buf+len,"num obj : %d\n",chip->max_objects);
-
+       can_seq_printf(sqf, "clock   : %ld Hz\n",chip->clock);
+       can_seq_printf(sqf, "baud    : %ld\n",chip->baudrate);
+       can_seq_printf(sqf, "num obj : %d\n",chip->max_objects);
 
 #if 0
        /* Chip specific info if available */
@@ -299,10 +338,10 @@ static int can_chip_procinfo(char *buf, char **start, off_t offset,
                len += (chip->chipspecops->get_info)(chip,buf+len);
 #endif
 
-       *eof = 1;
-       return len;
+       return 0;
 }
 
+CAN_PROC_SHOW_SINGLE_OPS_DECLARE(can_chip_procinfo_ops, can_chip_procinfo_show);
 
 int add_channel_to_procdir(struct candevice_t *candev)
 {
@@ -330,7 +369,7 @@ int add_channel_to_procdir(struct candevice_t *candev)
                can_create_proc_read_entry("chip_info",        /* proc entry name */
                                       0,                  /* protection mask, 0->default */
                                       base->channel[cc]->ch_entry,  /* parent dir, NULL->/proc */
-                                      can_chip_procinfo,
+                                      &can_chip_procinfo_ops,
                                       candev->chip[i]);
 
                cc++;