root/lm-sensors/trunk/kernel/sensors.c @ 51

Revision 51, 17.9 KB (checked in by frodo, 14 years ago)

Introduced /proc/sys/dev/sensors/chips file

See doc/sysctl for an explanation why it is needed.

Also fixed two very small cleanup bugs in lm75/lm78, which would probably be
impossible to trigger anyway, but still.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2    sensors.c - A Linux module for reading sensor data.
3    Copyright (c) 1998  Frodo Looijaard <frodol@dds.nl>
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18*/
19
20#include <linux/module.h>
21#include <linux/malloc.h>
22#include <linux/ctype.h>
23#include <linux/sysctl.h>
24#include <linux/proc_fs.h>
25
26#include "version.h"
27#include "compat.h"
28#include "i2c.h"
29#include "isa.h"
30#include "sensors.h"
31
32
33#ifdef MODULE
34extern int init_module(void);
35extern int cleanup_module(void);
36#endif /* MODULE */
37
38static int sensors_create_name(char **name, const char *prefix,
39                               struct i2c_adapter * adapter, int addr);
40static void sensors_parse_reals(int *nrels, void *buffer, int bufsize,
41                                long *results, int magnitude);
42static void sensors_write_reals(int nrels,void *buffer,int *bufsize,
43                                long *results, int magnitude);
44static int sensors_proc_chips(ctl_table *ctl, int write, struct file * filp,
45                              void *buffer, size_t *lenp);
46static int sensors_sysctl_chips (ctl_table *table, int *name, int nlen, 
47                                 void *oldval, size_t *oldlenp, void *newval,
48                                 size_t newlen, void **context);
49
50static int sensors_init(void);
51static int sensors_cleanup(void);
52
53#define SENSORS_ENTRY_MAX 20
54static struct ctl_table_header *sensors_entries[SENSORS_ENTRY_MAX];
55
56#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
57static struct i2c_client *sensors_clients[SENSORS_ENTRY_MAX];
58static unsigned short sensors_inodes[SENSORS_ENTRY_MAX];
59void sensors_fill_inode(struct inode *inode, int fill);
60#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */
61
62static ctl_table sysctl_table[] = {
63  { CTL_DEV, "dev", NULL, 0, 0555 },
64  { 0 },
65  { DEV_SENSORS, "sensors", NULL, 0, 0555 },
66  { 0 },
67  { 0, NULL, NULL, 0, 0555 },
68  { 0 }
69};
70
71static ctl_table sensors_proc_dev_sensors[] = {
72  { SENSORS_CHIPS, "chips", NULL, 0, 0644, NULL, &sensors_proc_chips, 
73    &sensors_sysctl_chips },
74  { 0 }
75};
76
77static ctl_table sensors_proc_dev[] = {
78  { DEV_SENSORS, "sensors", NULL, 0, 0555, sensors_proc_dev_sensors },
79  { 0 },
80};
81
82
83static ctl_table sensors_proc[] = {
84  { CTL_DEV, "dev", NULL, 0, 0555, sensors_proc_dev },
85  { 0 }
86};
87
88
89static struct ctl_table_header *sensors_proc_header;
90static int sensors_initialized;
91
92/* This returns a nice name for a new directory; for example lm78-isa-0310
93   (for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for
94   a LM75 chip on the third i2c bus at address 0x4e). 
95   name is allocated first. */
96int sensors_create_name(char **name, const char *prefix, 
97                        struct i2c_adapter * adapter, int addr)
98{
99  char name_buffer[50]; 
100  int id;
101  if (i2c_is_isa_adapter(adapter)) 
102    sprintf(name_buffer,"%s-isa-%04x",prefix,addr);
103  else {
104    if ((id = i2c_adapter_id(adapter)) < 0)
105      return -ENOENT;
106    sprintf(name_buffer,"%s-i2c-%d-%02x",prefix,id,addr);
107  }
108  *name = kmalloc(strlen(name_buffer)+1,GFP_KERNEL);
109  strcpy(*name,name_buffer);
110  return 0;
111}
112
113/* This rather complex function must be called when you want to add an entry
114   to /proc/sys/dev/sensors/chips (not yet implemented). It also creates
115   a new directory within /proc/sys/dev/sensors/.
116   ctl_template should be a template of the newly created directory. It is
117   copied in memory. The extra2 field of each file is set to point to client.
118   If any driver wants subdirectories within the newly created directory,
119   this function must be updated! */
120int sensors_register_entry(struct i2c_client *client ,const char *prefix, 
121                           ctl_table *ctl_template)
122{
123  int i,res,len,id;
124  ctl_table *new_table;
125  char *name;
126  struct ctl_table_header *new_header;
127
128  if ((res = sensors_create_name(&name,prefix,client->adapter,
129                                 i2c_is_isa_client(client)?
130                                 ((struct isa_client *) client)->isa_addr:
131                                 client->addr)))
132    return res;
133
134  for (id = 0; id < SENSORS_ENTRY_MAX; id++)
135    if (! sensors_entries[id]) {
136      break;
137    }
138  if (id == SENSORS_ENTRY_MAX) {
139    kfree(name);
140    return -ENOMEM;
141  }
142  id += 256;
143
144  len = 0;
145  while (ctl_template[len].procname)
146    len++;
147  len += 7;
148  if (! (new_table = kmalloc(sizeof(ctl_table) * len,GFP_KERNEL))) {
149    kfree(name);
150    return -ENOMEM;
151  }
152   
153  memcpy(new_table,sysctl_table,6 * sizeof(ctl_table));
154  new_table[0].child = &new_table[2];
155  new_table[2].child = &new_table[4];
156  new_table[4].child = &new_table[6];
157  new_table[4].procname = name;
158  new_table[4].ctl_name = id;
159  memcpy(new_table+6,ctl_template,(len-6) * sizeof(ctl_table));
160  for (i = 6; i < len; i++)
161    new_table[i].extra2 = client;
162
163  if (! (new_header = register_sysctl_table(new_table,0))) {
164    kfree(new_table);
165    kfree(name);
166    return -ENOMEM;
167  }
168
169  sensors_entries[id-256] = new_header;
170
171#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
172  sensors_clients[id-256] = client;
173#ifdef DEBUG
174  if (!new_header || !new_header->ctl_table || 
175      !new_header->ctl_table->child || 
176      !new_header->ctl_table->child->child ||
177      !new_header->ctl_table->child->child->de) {
178    printk("sensors.o: NULL pointer when trying to install fill_inode fix!\n");
179    return id; 
180  }
181#endif /* DEBUG */
182  sensors_inodes[id-256] = new_header->ctl_table->child->child->de->low_ino;
183  new_header->ctl_table->child->child->de->fill_inode = &sensors_fill_inode;
184#endif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
185
186  return id;
187}
188
189void sensors_deregister_entry(int id)
190{
191  ctl_table *table;
192  id -= 256;
193  if (sensors_entries[id]) {
194    table = sensors_entries[id]->ctl_table;
195    unregister_sysctl_table(sensors_entries[id]);
196    kfree((void *) (table[4].procname));
197    kfree(table);
198    sensors_entries[id] = NULL;
199#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
200    sensors_clients[id-256] = NULL;
201#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */
202  }
203}
204
205#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
206void sensors_fill_inode(struct inode *inode, int fill)
207{
208  int i;
209  struct i2c_client *client;
210
211#ifdef DEBUG
212  if (! inode) {
213    printk("sensors.o: Warning: inode NULL in fill_inode()\n");
214    return;
215  }
216#endif /* def DEBUG */
217 
218  for (i = 0; i < SENSORS_ENTRY_MAX; i++) 
219    if (sensors_clients[i] && (sensors_inodes[i] == inode->i_ino))
220      break;
221#ifdef DEBUG
222  if (i == SENSORS_ENTRY_MAX) {
223    printk("sensors.o: Warning: inode (%ld) not found in fill_inode()\n",
224           inode->i_ino);
225    return;
226  }
227#endif /* def DEBUG */
228  client = sensors_clients[i];
229  if (fill)
230    client->driver->inc_use(client);
231  else
232    client->driver->dec_use(client);
233}
234#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */
235
236int sensors_proc_chips(ctl_table *ctl, int write, struct file * filp,
237                       void *buffer, size_t *lenp)
238{
239  char BUF[SENSORS_PREFIX_MAX + 30];
240  int buflen,curbufsize,i;
241  struct ctl_table *client_tbl;
242
243  if (write)
244    return 0;
245
246  /* If buffer is size 0, or we try to read when not at the start, we
247     return nothing. Note that I think writing when not at the start
248     does not work either, but anyway, this is straight from the kernel
249     sources. */
250  if (!*lenp || (filp->f_pos && !write)) {
251    *lenp = 0;
252    return 0;
253  }
254  curbufsize = 0;
255  for (i = 0; i < SENSORS_ENTRY_MAX; i ++)
256    if (sensors_entries[i]) {
257      client_tbl = sensors_entries[i]->ctl_table->child->child;
258      buflen = sprintf(BUF,"%d\t%s\n",client_tbl->ctl_name,
259                       client_tbl->procname);
260      if (buflen + curbufsize > *lenp)
261        buflen=*lenp-curbufsize;
262      copy_to_user(buffer,BUF,buflen);
263      curbufsize += buflen;
264      (char *) buffer += buflen;
265    }
266  *lenp = curbufsize;
267  filp->f_pos += curbufsize;
268  return 0;
269}
270
271int sensors_sysctl_chips (ctl_table *table, int *name, int nlen, void *oldval,
272                          size_t *oldlenp, void *newval, size_t newlen,
273                          void **context)
274{
275  struct sensors_chips_data data;
276  int i,oldlen,nrels,maxels;
277  struct ctl_table *client_tbl;
278
279  if (oldval && oldlenp && ! get_user_data(oldlen,oldlenp) && oldlen) {
280    maxels = oldlen / sizeof(struct sensors_chips_data);
281    nrels = 0;
282    for (i = 0; (i < SENSORS_ENTRY_MAX) && (nrels < maxels); i++)
283      if (sensors_entries[i]) {
284        client_tbl = sensors_entries[i]->ctl_table->child->child;
285        data.sysctl_id = client_tbl->ctl_name;
286        strcpy(data.name,client_tbl->procname);
287        copy_to_user(oldval,&data,sizeof(struct sensors_chips_data));
288        (char *) oldval += sizeof(struct sensors_chips_data);
289        nrels++;
290      }
291    oldlen = nrels * sizeof(struct sensors_chips_data);
292    put_user(oldlen,oldlenp);
293  }
294  return 0;
295}
296
297
298/* This funcion reads or writes a 'real' value (encoded by the combination
299   of an integer and a magnitude, the last is the power of ten the value
300   should be divided with) to a /proc/sys directory. To use this function,
301   you must (before registering the ctl_table) set the extra2 field to the
302   client, and the extra1 field to a function of the form:
303      void func(struct i2c_client *client, int operation, int ctl_name,
304                int *nrels_mag, long *results)
305   This function can be called for three values of operation. If operation
306   equals SENSORS_PROC_REAL_INFO, the magnitude should be returned in
307   nrels_mag. If operation equals SENSORS_PROC_REAL_READ, values should
308   be read into results. nrels_mag should return the number of elements
309   read; the maximum number is put in it on entry. Finally, if operation
310   equals SENSORS_PROC_REAL_WRITE, the values in results should be
311   written to the chip. nrels_mag contains on entry the number of elements
312   found.
313   In all cases, client points to the client we wish to interact with,
314   and ctl_name is the SYSCTL id of the file we are accessing. */
315int sensors_proc_real(ctl_table *ctl, int write, struct file * filp,
316                      void *buffer, size_t *lenp)
317{
318#define MAX_RESULTS 20
319  int mag,nrels=MAX_RESULTS;
320  long results[MAX_RESULTS];
321  sensors_real_callback callback = ctl -> extra1;
322  struct i2c_client *client = ctl -> extra2;
323
324  /* If buffer is size 0, or we try to read when not at the start, we
325     return nothing. Note that I think writing when not at the start
326     does not work either, but anyway, this is straight from the kernel
327     sources. */
328  if (!*lenp || (filp->f_pos && !write)) {
329    *lenp = 0;
330    return 0;
331  }
332
333  /* Get the magnitude */
334  callback(client,SENSORS_PROC_REAL_INFO,ctl->ctl_name,&mag,NULL);
335
336  if (write) {
337    /* Read the complete input into results, converting to longs */
338    sensors_parse_reals(&nrels,buffer,*lenp,results,mag);
339
340    if (! nrels)
341      return 0;
342
343    /* Now feed this information back to the client */
344    callback(client,SENSORS_PROC_REAL_WRITE,ctl->ctl_name,&nrels,results);
345   
346    filp->f_pos += *lenp;
347    return 0;
348  } else { /* read */
349    /* Get the information from the client into results */
350    callback(client,SENSORS_PROC_REAL_READ,ctl->ctl_name,&nrels,results);
351
352    /* And write them to buffer, converting to reals */
353    sensors_write_reals(nrels,buffer,lenp,results,mag);
354    filp->f_pos += *lenp;
355    return 0;
356  }
357}
358
359/* This function is equivalent to sensors_proc_real, only it interacts with
360   the sysctl(2) syscall, and returns no reals, but integers */
361int sensors_sysctl_real (ctl_table *table, int *name, int nlen, void *oldval,
362               size_t *oldlenp, void *newval, size_t newlen,
363               void **context)
364{
365  long results[MAX_RESULTS];
366  int oldlen,nrels=MAX_RESULTS;
367  sensors_real_callback callback = table -> extra1;
368  struct i2c_client *client = table -> extra2;
369
370  /* Check if we need to output the old values */
371  if (oldval && oldlenp && ! get_user_data(oldlen,oldlenp) && oldlen) {
372    callback(client,SENSORS_PROC_REAL_READ,table->ctl_name,&nrels,results);
373
374    /* Note the rounding factor! */
375    if (nrels * sizeof(long) < oldlen)
376      oldlen = nrels * sizeof(long);
377    oldlen = (oldlen / sizeof(long)) * sizeof(long);
378    copy_to_user(oldval,results,oldlen);
379    put_user(oldlen,oldlenp);
380  }
381
382  if (newval && newlen) {
383    /* Note the rounding factor! */
384    newlen -= newlen % sizeof(long);
385    nrels = newlen / sizeof(long);
386    copy_from_user(results,newval,newlen);
387   
388    /* Get the new values back to the client */
389    callback(client,SENSORS_PROC_REAL_WRITE,table->ctl_name,&nrels,results);
390  }
391  return 0;
392}
393   
394
395/* nrels contains initially the maximum number of elements which can be
396   put in results, and finally the number of elements actually put there.
397   A magnitude of 1 will multiply everything with 10; etc.
398   buffer, bufsize is the character buffer we read from and its length.
399   results will finally contain the parsed integers.
400
401   Buffer should contain several reals, separated by whitespace. A real
402   has the following syntax:
403     [ Minus ] Digit* [ Dot Digit* ]
404   (everything between [] is optional; * means zero or more).
405   When the next character is unparsable, everything is skipped until the
406   next whitespace.
407
408   WARNING! This is tricky code. I have tested it, but there may still be
409            hidden bugs in it, even leading to crashes and things!
410*/
411void sensors_parse_reals(int *nrels, void *buffer, int bufsize, 
412                         long *results, int magnitude)
413{
414  int maxels,min,mag;
415  long res;
416  char nextchar=0;
417
418  maxels = *nrels;
419  *nrels = 0;
420
421  while (bufsize && (*nrels < maxels)) {
422
423    /* Skip spaces at the start */
424    while (bufsize && ! get_user_data(nextchar,(char *) buffer) && 
425           isspace((int) nextchar)) {
426      bufsize --;
427      ((char *) buffer)++;
428    }
429
430    /* Well, we may be done now */
431    if (! bufsize)
432      return;
433
434    /* New defaults for our result */
435    min = 0;
436    res = 0;
437    mag = magnitude;
438
439    /* Check for a minus */
440    if (! get_user_data(nextchar,(char *) buffer) && (nextchar == '-')) {
441      min=1;
442      bufsize--;
443      ((char *) buffer)++;
444    }
445
446    /* Digits before a decimal dot */
447    while (bufsize && !get_user_data(nextchar,(char *) buffer) && 
448           isdigit((int) nextchar)) {
449      res = res * 10 + nextchar - '0';
450      bufsize--;
451      ((char *) buffer)++;
452    }
453
454    /* If mag < 0, we must actually divide here! */
455    while (mag < 0) {
456      res = res / 10;
457      mag++;
458    }
459
460    if (bufsize && (nextchar == '.')) {
461      /* Skip the dot */
462      bufsize--;
463      ((char *) buffer)++;
464 
465      /* Read digits while they are significant */
466      while(bufsize && (mag > 0) && 
467            !get_user_data(nextchar,(char *) buffer) &&
468            isdigit((int) nextchar)) {
469        res = res * 10 + nextchar - '0';
470        mag--;
471        bufsize--;
472        ((char *) buffer)++;
473      }
474    }
475    /* If we are out of data, but mag > 0, we need to scale here */
476    while (mag > 0) {
477      res = res * 10;
478      mag --;
479    }
480
481    /* Skip everything until we hit whitespace */
482    while(bufsize && !get_user_data(nextchar,(char *) buffer) &&
483          isspace ((int) nextchar)) {
484      bufsize --;
485      ((char *) buffer) ++;
486    }
487
488    /* Put res in results */
489    results[*nrels] = (min?-1:1)*res;
490    (*nrels)++;
491  }   
492 
493  /* Well, there may be more in the buffer, but we need no more data.
494     Ignore anything that is left. */
495  return;
496}
497   
498void sensors_write_reals(int nrels,void *buffer,int *bufsize,long *results,
499                         int magnitude)
500{
501  #define BUFLEN 20
502  char BUF[BUFLEN+1]; /* An individual representation should fit in here! */
503  char printfstr[10];
504  int nr=0;
505  int buflen,mag,times;
506  int curbufsize=0;
507
508  while ((nr < nrels) && (curbufsize < *bufsize)) {
509    mag=magnitude;
510
511    if (nr != 0) {
512      put_user(' ', (char *) buffer);
513      curbufsize ++;
514      ((char *) buffer) ++;
515    }
516
517    /* Fill BUF with the representation of the next string */
518    if (mag <= 0) {
519
520      buflen=sprintf(BUF,"%ld",results[nr]);
521      if (buflen < 0) { /* Oops, a sprintf error! */
522        *bufsize=0;
523        return;
524      }
525      while ((mag < 0) && (buflen < BUFLEN)) {
526        BUF[buflen++]='0';
527        mag++;
528      }
529      BUF[buflen]=0;
530    } else {
531      times=1;
532      for (times=1; mag-- > 0; times *= 10);
533      if (results[nr] < 0) {
534        BUF[0] = '-';
535        buflen = 1;
536      } else
537        buflen=0;
538      strcpy(printfstr,"%ld.%0Xld");
539      printfstr[6]=magnitude+'0';
540      buflen+=sprintf(BUF+buflen,printfstr,abs(results[nr])/times,
541                      abs(results[nr])%times);
542      if (buflen < 0) { /* Oops, a sprintf error! */
543        *bufsize=0;
544        return;
545      }
546    }
547
548    /* Now copy it to the user-space buffer */
549    if (buflen + curbufsize > *bufsize)
550      buflen=*bufsize-curbufsize;
551    copy_to_user(buffer,BUF,buflen);
552    curbufsize += buflen;
553    (char *) buffer += buflen;
554
555    nr ++;
556  }
557  if (curbufsize < *bufsize) {
558    put_user('\n', (char *) buffer);
559    curbufsize ++;
560  }
561  *bufsize=curbufsize;
562}
563
564int sensors_init(void) 
565{
566  printk("sensors.o version %s (%s)\n",LM_VERSION,LM_DATE);
567  sensors_initialized = 0;
568  if (! (sensors_proc_header = register_sysctl_table(sensors_proc,0)))
569    return -ENOMEM;
570  sensors_initialized ++;
571  return 0;
572}
573
574int sensors_cleanup(void)
575{
576  if (sensors_initialized >= 1) {
577    unregister_sysctl_table(sensors_proc_header);
578    sensors_initialized --;
579  }
580  return 0;
581}
582
583#ifdef MODULE
584
585MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
586MODULE_DESCRIPTION("LM78 driver");
587
588int init_module(void)
589{
590  return sensors_init();
591}
592
593int cleanup_module(void)
594{
595  return sensors_cleanup();
596}
597
598#endif /* MODULE */
599
Note: See TracBrowser for help on using the browser.