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

Revision 46, 14.9 KB (checked in by frodo, 14 years ago)

Module use count fix

This fix will only work for kernels >= 2.1.58.

* lm75, lm78: {INC,DEC}_MOD_USE_COUNT called for {inc,dec}_mod_use hooks
* sensors: For new kernels, fill_inode is called, which in turn calls

{inc,dec}_mod_use for a certain client

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