X-Git-Url: http://rtime.felk.cvut.cz/gitweb/lincan.git/blobdiff_plain/4cf24de229090b1ab6279570a564d224e13dd706..3c47a7cd435662f7e2daa006596722271f5dd090:/lincan/src/proc.c diff --git a/lincan/src/proc.c b/lincan/src/proc.c index 401148e..b9d3419 100644 --- a/lincan/src/proc.c +++ b/lincan/src/proc.c @@ -1,51 +1,193 @@ -/* proc.c - * Linux CAN-bus device driver. - * Written by Arnaud Westenberg email:arnaud@wanadoo.nl - * This software is released under the GPL-License. - * Version 0.7 6 Aug 2001 - */ +/**************************************************************************/ +/* File: proc.c - proc filesystem entries for CAN driver */ +/* */ +/* LinCAN - (Not only) Linux CAN bus driver */ +/* Copyright (C) 2002-2009 DCE FEE CTU Prague */ +/* Copyright (C) 2002-2009 Pavel Pisa */ +/* Funded by OCERA and FRESCOR IST projects */ +/* Based on CAN driver code by Arnaud Westenberg */ +/* */ +/* LinCAN is free software; you can redistribute it and/or modify it */ +/* under terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2, or (at your option) any */ +/* later version. LinCAN is distributed in the hope that it will be */ +/* useful, but WITHOUT ANY WARRANTY; without even the implied warranty */ +/* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. You should have received a */ +/* copy of the GNU General Public License along with LinCAN; see file */ +/* COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, */ +/* Cambridge, MA 02139, USA. */ +/* */ +/* To allow use of LinCAN in the compact embedded systems firmware */ +/* and RT-executives (RTEMS for example), main authors agree with next */ +/* special exception: */ +/* */ +/* Including LinCAN header files in a file, instantiating LinCAN generics */ +/* or templates, or linking other files with LinCAN objects to produce */ +/* an application image/executable, does not by itself cause the */ +/* resulting application image/executable to be covered by */ +/* the GNU General Public License. */ +/* This exception does not however invalidate any other reasons */ +/* why the executable file might be covered by the GNU Public License. */ +/* Publication of enhanced or derived LinCAN files is required although. */ +/**************************************************************************/ + +#include "../include/can.h" +#include "../include/can_sysdep.h" +#include "../include/main.h" +#include "../include/proc.h" +#include "../include/setup.h" #define __NO_VERSION__ #include +#include -#include -#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) -#define MODVERSIONS -#endif +int add_channel_to_procdir(struct candevice_t *candev); +int remove_channels_from_procdir(void); +int remove_channel_from_procdir(struct candevice_t *candev); +int add_object_to_procdir(int chip_nr); +int remove_object_from_procdir(int chip_nr); -#if defined (MODVERSIONS) -#include +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,0)) +static int can_proc_readlink(struct proc_dir_entry *ent, char *page); #endif -#include -#include -#include -#include +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,25) +#define CAN_PROC_ROOT (&proc_root) +#else /* >= 2.6.26 */ +#define CAN_PROC_ROOT (NULL) +#endif /* >= 2.6.26 */ -#include "../include/main.h" -#include "../include/proc.h" -#include "../include/setup.h" +static int cc=0; /* static counter for each CAN chip */ -int add_channel_to_procdir(void); -int remove_channel_from_procdir(void); -int add_object_to_procdir(void); -int remove_object_from_procdir(void); +static struct canproc_t can_proc_base; +static struct canproc_t *base=&can_proc_base; +DEFINE_MUTEX(proc_mutex); /* synchronize access to canproc_t array */ -#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19)) -static int candev_readlink(struct proc_dir_entry *de, char *page); -#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)) -static int bc=0; /* static counter for each hardware board */ -static int cc=0; /* static counter for each CAN chip */ -static int oc=0; /* static counter for each message object */ +#include -struct canproc_t can_proc_base; -struct canproc_t *base=&can_proc_base; +#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, \ +}; -/* 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,2,19)) +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) @@ -54,23 +196,36 @@ static void can_fill_inode(struct inode *inode, int fill) MOD_DEC_USE_COUNT; } -static struct proc_dir_entry * new_can_proc_entry(unsigned short inode, - const char *name, mode_t mode, nlink_t nlink, struct proc_dir_entry *parent) +static struct proc_dir_entry * can_create_proc_entry(const char *name, mode_t mode, + struct proc_dir_entry *parent) { struct proc_dir_entry *new_entry = NULL; + char *namestore; + int namelen; + + if(!name || !parent) + return NULL; + namelen=strlen(name); + if(!namelen) + return NULL; + + new_entry = (struct proc_dir_entry *) + can_checked_malloc(sizeof(struct proc_dir_entry)+namelen+1); - new_entry = (struct proc_dir_entry *) kmalloc(sizeof(struct - proc_dir_entry), GFP_KERNEL); if (new_entry == NULL) return NULL; memset(new_entry, 0, sizeof(struct proc_dir_entry)); - new_entry->low_ino = inode; - new_entry->namelen = strlen(name); - new_entry->name = name; + /* Store copy of the proc entry name */ + namestore = ((char *) new_entry) + sizeof(struct proc_dir_entry); + memcpy(namestore, name, namelen + 1); + + new_entry->low_ino = 0; + new_entry->namelen = namelen; + new_entry->name = namestore; new_entry->mode = mode; - new_entry->nlink = nlink; + new_entry->nlink = 0; new_entry->fill_inode = can_fill_inode; new_entry->parent = parent; @@ -79,228 +234,308 @@ static struct proc_dir_entry * new_can_proc_entry(unsigned short inode, return new_entry; } -int can_remove_proc_entry(struct proc_dir_entry *del, struct proc_dir_entry *parent) +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 can_create_proc_entry(name, S_IFDIR | mode, parent); +} + +static int can_proc_remove(struct proc_dir_entry *del) { if (del != NULL) { - proc_unregister(parent, del->low_ino); - kfree(del); - del = NULL; + proc_unregister(del->parent, del->low_ino); + can_checked_free(del); return 0; } else return -ENODEV; } -#endif // Functions required for kernel 2.2 + + +static int can_proc_readlink(struct proc_dir_entry *ent, char *page) +{ + char *link_dest = (char*)ent->data; + + strcpy(page, link_dest); + return strlen(link_dest); +} + + + +/* This compatibility version of proc_symlink does not store local copy of destination */ +static inline struct proc_dir_entry *can_proc_symlink(const char *name, + struct proc_dir_entry *parent, const char *dest) +{ + struct proc_dir_entry *entry; + + + entry = can_create_proc_entry(name, S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, parent); + if (entry == NULL) + return NULL; + entry->readlink_proc = can_proc_readlink; + entry->data = dest; + return entry; +} + +#endif /* Linux 2.2 - 2.3 kernels */ + +#endif /* Linux kernel 3.6 and 3.10+ */ /* can_init_procdir registers the entire CAN directory tree recursively at * the proc system. */ int can_init_procdir(void) { -#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19)) - base->can_proc_entry = new_can_proc_entry(0, "can", S_IFDIR | S_IRUGO | - S_IXUGO, 0, &proc_root); -#else - base->can_proc_entry = create_proc_entry("can", S_IFDIR | S_IRUGO | - S_IXUGO, &proc_root); -#endif + int board; + struct candevice_t *candev; + + mutex_init(&proc_mutex); + + base->can_proc_entry = can_proc_mkdir_mode("can", S_IFDIR | S_IRUGO | + S_IXUGO, CAN_PROC_ROOT); if (base->can_proc_entry == NULL) return -ENODEV; - for (bc=0; bcnr_boards; bc++) { - add_channel_to_procdir(); - } + for (board=0; boardnr_boards; board++) { + candev=hardware_p->candevice[board]; + if(candev) add_channel_to_procdir(candev); + } return 0; } +/* can_init_procentry registers entry of a new board in CAN directory tree at + * the proc system. + */ +int can_init_procentry(int board) +{ + struct candevice_t *candev; + candev=hardware_p->candevice[board]; + if(candev) + return add_channel_to_procdir(candev); + return -ENODEV; +} + /* can_delete_procdir removes the entire CAN tree from the proc system */ int can_delete_procdir(void) { - if (remove_channel_from_procdir()) + if (remove_channels_from_procdir()) return -ENODEV; -#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19)) - if (can_remove_proc_entry(base->can_proc_entry, &proc_root)) + /* name: "can" */ + if (can_proc_remove(base->can_proc_entry)) return -ENODEV; -#else - remove_proc_entry("can", &proc_root); + + return 0; +} + +/* can_delete_procentry removes device entries from CAN tree in the proc system */ +int can_delete_procentry(struct candevice_t *candev) +{ + if (remove_channel_from_procdir(candev)) + return -ENODEV; + + return 0; +} + +static int can_chip_procinfo_show(can_proc_seq_file_t *sqf, void *data) +{ + struct canchip_t *chip=can_seq_data(sqf); + + /* Generic chip info */ + 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)); + can_seq_printf(sqf, "config : %s\n", + (chip->flags & CHIP_CONFIGURED) ? "yes":"no"); + 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 */ + if(chip->chipspecops->get_info) + len += (chip->chipspecops->get_info)(chip,buf+len); #endif return 0; } -int add_channel_to_procdir(void) +CAN_PROC_SHOW_SINGLE_OPS_DECLARE(can_chip_procinfo_ops, can_chip_procinfo_show); + +int add_channel_to_procdir(struct candevice_t *candev) { int i=0; - for (i=0; i < candevices_p[bc]->nr_82527_chips + - candevices_p[bc]->nr_sja1000_chips; i++) { + mutex_lock(&proc_mutex); + for (i=0; i < MAX_TOT_CHIPS; i++){ + if (!chips_p[i]) continue; + if (chips_p[i]->hostdevice != candev) continue; - base->channel[cc] = (struct channelproc_t *) - kmalloc(sizeof(struct channelproc_t), GFP_KERNEL); - if (base->channel[cc] == NULL) - return -ENOMEM; - else if (add_mem_to_list(base->channel[cc])) + base->channel[i] = (struct channelproc_t *) + can_checked_malloc(sizeof(struct channelproc_t)); + if (base->channel[i] == NULL){ + mutex_unlock(&proc_mutex); return -ENOMEM; + } - sprintf(base->channel[cc]->ch_name, "channel%d",cc); - -#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19)) - base->channel[cc]->ch_entry = new_can_proc_entry(0, - base->channel[cc]->ch_name, - S_IFDIR | S_IRUGO | S_IXUGO, 0, - base->can_proc_entry); -#else - base->channel[cc]->ch_entry = create_proc_entry( - base->channel[cc]->ch_name, + sprintf(base->channel[i]->ch_name, "channel%d",i); + + base->channel[i]->ch_entry = can_proc_mkdir_mode( + base->channel[i]->ch_name, S_IFDIR | S_IRUGO |S_IXUGO, base->can_proc_entry); -#endif - if (base->channel[cc]->ch_entry == NULL) + + if (base->channel[i]->ch_entry == NULL){ + mutex_unlock(&proc_mutex); return -ENODEV; + } - add_object_to_procdir(); + add_object_to_procdir(i); + can_create_proc_read_entry("chip_info", /* proc entry name */ + 0, /* protection mask, 0->default */ + base->channel[i]->ch_entry, /* parent dir, NULL->/proc */ + &can_chip_procinfo_ops, + chips_p[i]); cc++; - } + } + mutex_unlock(&proc_mutex); return 0; } -int remove_channel_from_procdir(void) +int remove_channels_from_procdir(void) { - - while (cc != 0) { + int i=0; + + mutex_lock(&proc_mutex); + for (i=0; i < MAX_TOT_CHIPS; i++){ + if (!chips_p[i]) continue; + cc--; -#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19)) - if (can_remove_proc_entry(base->channel[cc]->ch_entry, - base->can_proc_entry)) + + if(!base->channel[i]) continue; + + remove_proc_entry("chip_info", base->channel[i]->ch_entry); + + if (remove_object_from_procdir(i)){ + mutex_unlock(&proc_mutex); return -ENODEV; -#else - remove_proc_entry(base->channel[cc]->ch_name, - base->can_proc_entry); -#endif - if (remove_object_from_procdir()) - return -ENODEV; + } + + /* name: base->channel[cc]->ch_name */ + if (can_proc_remove(base->channel[i]->ch_entry)){ + mutex_unlock(&proc_mutex); + return -ENODEV; + } + + can_checked_free(base->channel[i]); + base->channel[i] = NULL; } + mutex_unlock(&proc_mutex); return 0; } - -int add_object_to_procdir(void) +int remove_channel_from_procdir(struct candevice_t *candev) { - int i=0, obj=0; + int i=0,j=0; - if (!strcmp(chips_p[cc]->chip_type,"i82527")) - obj=15; - if (!strcmp(chips_p[cc]->chip_type,"sja1000")) - obj=1; + mutex_lock(&proc_mutex); + for (i=0; i < MAX_TOT_CHIPS; i++){ + if (!chips_p[i]) continue; + if (chips_p[i]->hostdevice != candev) continue; + if (!base->channel[i]) continue; - for (i=0; ichannel[cc]->object[i] = (struct objectproc_t *) - kmalloc(sizeof(struct objectproc_t),GFP_KERNEL); - - - if (base->channel[cc]->object[i] == NULL) - return -ENOMEM; - else if (add_mem_to_list( base->channel[cc]->object[i])) - return -ENOMEM; + remove_proc_entry("chip_info", base->channel[i]->ch_entry); - sprintf(base->channel[cc]->object[i]->obj_name,"object%d",i); - sprintf(base->channel[cc]->object[i]->lnk_name,"dev"); - -#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19)) - base->channel[cc]->object[i]->obj_entry=new_can_proc_entry( - 0, base->channel[cc]->object[i]->obj_name, - S_IFDIR | S_IRUGO | S_IXUGO, 0, - base->channel[cc]->ch_entry); - if (base->channel[cc]->object[i]->obj_entry == NULL) + if (remove_object_from_procdir(i)){ + mutex_unlock(&proc_mutex); return -ENODEV; - base->channel[cc]->object[i]->lnk = new_can_proc_entry( - 0, base->channel[cc]->object[i]->lnk_name, - S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, - 0, base->channel[cc]->object[i]->obj_entry); - if (base->channel[cc]->object[i]->lnk == NULL) + } + + /* name: base->channel[cc]->ch_name */ + if (can_proc_remove(base->channel[i]->ch_entry)){ + mutex_unlock(&proc_mutex); return -ENODEV; - sprintf(base->channel[cc]->object[i]->lnk_dev,"/dev/can"); - base->channel[cc]->object[i]->lnk->readlink_proc = - candev_readlink; + } + + can_checked_free(base->channel[i]); + base->channel[i] = NULL; + + cc--; + } + mutex_unlock(&proc_mutex); + + return 0; +} + + +int add_object_to_procdir(int chip_nr) +{ + int i, max_objects; -#else - base->channel[cc]->object[i]->obj_entry = create_proc_entry( - base->channel[cc]->object[i]->obj_name, + max_objects=chips_p[chip_nr]->max_objects; + + for (i=0; ichannel[chip_nr]->object[i] = (struct objectproc_t *) + can_checked_malloc(sizeof(struct objectproc_t)); + + if (base->channel[chip_nr]->object[i] == NULL) + return -ENOMEM; + + sprintf(base->channel[chip_nr]->object[i]->obj_name,"object%d",i); + sprintf(base->channel[chip_nr]->object[i]->lnk_name,"dev"); + + base->channel[chip_nr]->object[i]->obj_entry = can_proc_mkdir_mode( + base->channel[chip_nr]->object[i]->obj_name, S_IFDIR | S_IRUGO | S_IXUGO, - base->channel[cc]->ch_entry); - if (base->channel[cc]->object[i]->obj_entry == NULL) + base->channel[chip_nr]->ch_entry); + if (base->channel[chip_nr]->object[i]->obj_entry == NULL) return -ENODEV; - sprintf(base->channel[cc]->object[i]->lnk_dev,"/dev/can%d", - chips_p[cc]->msgobj[i]->minor); - base->channel[cc]->object[i]->lnk = proc_symlink( - base->channel[cc]->object[i]->lnk_name, - base->channel[cc]->object[i]->obj_entry, - base->channel[cc]->object[i]->lnk_dev); - if (base->channel[cc]->object[i]->lnk == NULL) + + sprintf(base->channel[chip_nr]->object[i]->lnk_dev,"/dev/can%d", + chips_p[chip_nr]->msgobj[i]->minor); + + base->channel[chip_nr]->object[i]->lnk = can_proc_symlink( + base->channel[chip_nr]->object[i]->lnk_name, + base->channel[chip_nr]->object[i]->obj_entry, + base->channel[chip_nr]->object[i]->lnk_dev); + if (base->channel[chip_nr]->object[i]->lnk == NULL) return -ENODEV; -#endif } return 0; -} +} -int remove_object_from_procdir(void) +int remove_object_from_procdir(int chip_nr) { int i=0, obj=0; - if (!strcmp(chips_p[cc]->chip_type,"i82527")) - obj=15; - if (!strcmp(chips_p[cc]->chip_type,"sja1000")) - obj=1; + obj=chips_p[chip_nr]->max_objects; for (i=0; ichannel[cc]->object[i]->lnk, - base->channel[cc]->object[i]->obj_entry)) + if(!base->channel[chip_nr]->object[i]) continue; + + /* name: base->channel[chip_nr]->object[i]->lnk_name */ + if (can_proc_remove( base->channel[chip_nr]->object[i]->lnk)) return -ENODEV; - if (can_remove_proc_entry( - base->channel[cc]->object[i]->obj_entry, - base->channel[cc]->ch_entry)) + /* name: base->channel[chip_nr]->object[i]->obj_name */ + if (can_proc_remove(base->channel[chip_nr]->object[i]->obj_entry)) return -ENODEV; -#else - remove_proc_entry(base->channel[cc]->object[i]->lnk_name, - base->channel[cc]->object[i]->obj_entry); - remove_proc_entry(base->channel[cc]->object[i]->obj_name, - base->channel[cc]->ch_entry); -#endif - + + can_checked_free(base->channel[chip_nr]->object[i]); + + base->channel[chip_nr]->object[i]=NULL; } return 0; } -#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19)) -static int candev_readlink(struct proc_dir_entry *de, char *page) -{ - int i=0, nchip=0, nobj=0; - char chip[20], object[20], tmp[6]; - - sprintf(chip, de->parent->parent->name+7); - sprintf(object, de->parent->name+6); - - for (i=0; imsgobj[nobj]->minor ); -} -#endif //End of candev_readlink for kernel 2.2