X-Git-Url: http://rtime.felk.cvut.cz/gitweb/lincan.git/blobdiff_plain/11efb0a892b088eb448635058b2547ecc6f121fd..b29ebe97fb5acced24e8ce9bb30e5d4dcad58815:/lincan/src/proc.c diff --git a/lincan/src/proc.c b/lincan/src/proc.c index a851830..b9d3419 100644 --- a/lincan/src/proc.c +++ b/lincan/src/proc.c @@ -1,11 +1,36 @@ -/* proc.c - * Linux CAN-bus device driver. - * Written by Arnaud Westenberg email:arnaud@wanadoo.nl - * Rewritten for new CAN queues by Pavel Pisa - OCERA team member - * email:pisa@cmp.felk.cvut.cz - * This software is released under the GPL-License. - * Version lincan-0.3 17 Jun 2004 - */ +/**************************************************************************/ +/* 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" @@ -15,9 +40,11 @@ #define __NO_VERSION__ #include +#include int add_channel_to_procdir(struct candevice_t *candev); -int remove_channel_from_procdir(void); +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); @@ -35,11 +62,132 @@ 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; +DEFINE_MUTEX(proc_mutex); /* synchronize access to canproc_t array */ -/* 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 + +#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) @@ -54,14 +202,14 @@ static struct proc_dir_entry * can_create_proc_entry(const char *name, mode_t mo 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 *) + new_entry = (struct proc_dir_entry *) can_checked_malloc(sizeof(struct proc_dir_entry)+namelen+1); if (new_entry == NULL) @@ -86,10 +234,23 @@ static struct proc_dir_entry * can_create_proc_entry(const char *name, mode_t mo return new_entry; } -static 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); + proc_unregister(del->parent, del->low_ino); can_checked_free(del); return 0; } @@ -100,7 +261,7 @@ static int can_remove_proc_entry(struct proc_dir_entry *del, struct proc_dir_ent 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); } @@ -112,8 +273,8 @@ 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; @@ -122,31 +283,9 @@ static inline struct proc_dir_entry *can_proc_symlink(const char *name, return entry; } -#else /* Functions forwarded for kernel 2.4 and above */ - -static inline struct proc_dir_entry * can_create_proc_entry(const char *name, mode_t mode, - struct proc_dir_entry *parent) -{ - return create_proc_entry(name, mode, parent); -} - +#endif /* Linux 2.2 - 2.3 kernels */ -/* 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_remove_proc_entry(struct proc_dir_entry *del, struct proc_dir_entry *parent) -{ - if(!del) return -ENODEV; - remove_proc_entry(del->name,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); -} - -#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. @@ -155,7 +294,10 @@ int can_init_procdir(void) { int board; struct candevice_t *candev; - base->can_proc_entry = can_create_proc_entry("can", S_IFDIR | S_IRUGO | + + 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; @@ -163,41 +305,59 @@ int can_init_procdir(void) 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; /* name: "can" */ - if (can_remove_proc_entry(base->can_proc_entry, CAN_PROC_ROOT)) + if (can_proc_remove(base->can_proc_entry)) return -ENODEV; return 0; } -static int can_chip_procinfo(char *buf, char **start, off_t offset, - int count, int *eof, void *data) +/* can_delete_procentry removes device entries from CAN tree in the proc system */ +int can_delete_procentry(struct candevice_t *candev) { - struct canchip_t *chip=data; - int len=0; + 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 */ - 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 */ @@ -205,67 +365,115 @@ 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) { int i=0; - for (i=0; i < candev->nr_all_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 *) + base->channel[i] = (struct channelproc_t *) can_checked_malloc(sizeof(struct channelproc_t)); - if (base->channel[cc] == NULL) + if (base->channel[i] == NULL){ + mutex_unlock(&proc_mutex); return -ENOMEM; + } - sprintf(base->channel[cc]->ch_name, "channel%d",cc); - - base->channel[cc]->ch_entry = can_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); - if (base->channel[cc]->ch_entry == NULL) + if (base->channel[i]->ch_entry == NULL){ + mutex_unlock(&proc_mutex); return -ENODEV; + } - add_object_to_procdir(cc); + add_object_to_procdir(i); - create_proc_read_entry("chip_info", /* proc entry name */ + 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, - candev->chip[i]); - + 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(!base->channel[cc]) continue; - - remove_proc_entry("chip_info", base->channel[cc]->ch_entry); - - if (remove_object_from_procdir(cc)) - return -ENODEV; - + + 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; + } + + /* 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 remove_channel_from_procdir(struct candevice_t *candev) +{ + int i=0,j=0; + + 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; + + remove_proc_entry("chip_info", base->channel[i]->ch_entry); + + if (remove_object_from_procdir(i)){ + mutex_unlock(&proc_mutex); + return -ENODEV; + } + /* name: base->channel[cc]->ch_name */ - if (can_remove_proc_entry(base->channel[cc]->ch_entry, - base->can_proc_entry)) + if (can_proc_remove(base->channel[i]->ch_entry)){ + mutex_unlock(&proc_mutex); return -ENODEV; - - can_checked_free(base->channel[cc]); - base->channel[cc] = NULL; + } + + can_checked_free(base->channel[i]); + base->channel[i] = NULL; + + cc--; } + mutex_unlock(&proc_mutex); return 0; } @@ -286,8 +494,8 @@ int add_object_to_procdir(int chip_nr) 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_create_proc_entry( + + 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[chip_nr]->ch_entry); @@ -306,7 +514,7 @@ int add_object_to_procdir(int chip_nr) } return 0; -} +} int remove_object_from_procdir(int chip_nr) { @@ -316,15 +524,12 @@ int remove_object_from_procdir(int chip_nr) for (i=0; ichannel[chip_nr]->object[i]) continue; - + /* name: base->channel[chip_nr]->object[i]->lnk_name */ - if (can_remove_proc_entry( base->channel[chip_nr]->object[i]->lnk, - base->channel[chip_nr]->object[i]->obj_entry)) + if (can_proc_remove( base->channel[chip_nr]->object[i]->lnk)) return -ENODEV; /* name: base->channel[chip_nr]->object[i]->obj_name */ - if (can_remove_proc_entry( - base->channel[chip_nr]->object[i]->obj_entry, - base->channel[chip_nr]->ch_entry)) + if (can_proc_remove(base->channel[chip_nr]->object[i]->obj_entry)) return -ENODEV; can_checked_free(base->channel[chip_nr]->object[i]);