| 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 | |
|---|
| 25 | #include "version.h" |
|---|
| 26 | #include "compat.h" |
|---|
| 27 | #include "i2c.h" |
|---|
| 28 | #include "isa.h" |
|---|
| 29 | #include "sensors.h" |
|---|
| 30 | |
|---|
| 31 | #ifdef MODULE |
|---|
| 32 | extern int init_module(void); |
|---|
| 33 | extern int cleanup_module(void); |
|---|
| 34 | #endif /* MODULE */ |
|---|
| 35 | |
|---|
| 36 | int sensors_register_entry(struct i2c_client *client ,const char *prefix, |
|---|
| 37 | ctl_table *ctl_template); |
|---|
| 38 | static int sensors_create_name(char **name, const char *prefix, |
|---|
| 39 | struct i2c_adapter * adapter, int addr); |
|---|
| 40 | static int sensors_init(void); |
|---|
| 41 | static int sensors_cleanup(void); |
|---|
| 42 | |
|---|
| 43 | #define SENSORS_ENTRY_MAX 20 |
|---|
| 44 | static struct ctl_table_header *sensors_entries[SENSORS_ENTRY_MAX]; |
|---|
| 45 | |
|---|
| 46 | static ctl_table sysctl_table[] = { |
|---|
| 47 | { CTL_DEV, "dev", NULL, 0, 0555 }, |
|---|
| 48 | { 0 }, |
|---|
| 49 | { DEV_SENSORS, "sensors", NULL, 0, 0555 }, |
|---|
| 50 | { 0 }, |
|---|
| 51 | { 0, NULL, NULL, 0, 0555 }, |
|---|
| 52 | { 0 } |
|---|
| 53 | }; |
|---|
| 54 | |
|---|
| 55 | /* This returns a nice name for a new directory; for example lm78-isa-0310 |
|---|
| 56 | (for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for |
|---|
| 57 | a LM75 chip on the third i2c bus at address 0x4e). |
|---|
| 58 | name is allocated first. */ |
|---|
| 59 | int sensors_create_name(char **name, const char *prefix, |
|---|
| 60 | struct i2c_adapter * adapter, int addr) |
|---|
| 61 | { |
|---|
| 62 | char name_buffer[50]; |
|---|
| 63 | int id; |
|---|
| 64 | if (i2c_is_isa_adapter(adapter)) |
|---|
| 65 | sprintf(name_buffer,"%s-isa-%d",prefix,addr); |
|---|
| 66 | else { |
|---|
| 67 | if ((id = i2c_adapter_id(adapter)) < 0); |
|---|
| 68 | return -ENOENT; |
|---|
| 69 | sprintf(name_buffer,"%s-i2c-%d-%d",prefix,id,addr); |
|---|
| 70 | } |
|---|
| 71 | *name = kmalloc(strlen(name_buffer)+1,GFP_KERNEL); |
|---|
| 72 | strcpy(*name,name_buffer); |
|---|
| 73 | return 0; |
|---|
| 74 | } |
|---|
| 75 | |
|---|
| 76 | /* This rather complex function must be called when you want to add an entry |
|---|
| 77 | to /proc/sys/dev/sensors/chips (not yet implemented). It also creates |
|---|
| 78 | a new directory within /proc/sys/dev/sensors/. |
|---|
| 79 | ctl_template should be a template of the newly created directory. It is |
|---|
| 80 | copied in memory. The extra1 field of each file is set to point to client. |
|---|
| 81 | If any driver wants subdirectories within the newly created directory, |
|---|
| 82 | this function must be updated! */ |
|---|
| 83 | int sensors_register_entry(struct i2c_client *client ,const char *prefix, |
|---|
| 84 | ctl_table *ctl_template) |
|---|
| 85 | { |
|---|
| 86 | int i,res,len,id; |
|---|
| 87 | ctl_table *new_table; |
|---|
| 88 | char *name; |
|---|
| 89 | struct ctl_table_header *new_header; |
|---|
| 90 | |
|---|
| 91 | if ((res = sensors_create_name(&name,prefix,client->adapter, |
|---|
| 92 | i2c_is_isa_client(client)? |
|---|
| 93 | ((struct isa_client *) client)->isa_addr: |
|---|
| 94 | client->addr))) |
|---|
| 95 | return res; |
|---|
| 96 | |
|---|
| 97 | for (id = 0; id < SENSORS_ENTRY_MAX; i++) |
|---|
| 98 | if (! sensors_entries[id]) { |
|---|
| 99 | break; |
|---|
| 100 | } |
|---|
| 101 | if (id == SENSORS_ENTRY_MAX) { |
|---|
| 102 | kfree(name); |
|---|
| 103 | return -ENOMEM; |
|---|
| 104 | } |
|---|
| 105 | id += 256; |
|---|
| 106 | |
|---|
| 107 | len = 0; |
|---|
| 108 | while (ctl_template[len].procname) |
|---|
| 109 | len++; |
|---|
| 110 | len += 7; |
|---|
| 111 | if (! (new_table = kmalloc(sizeof(ctl_table) * len,GFP_KERNEL))) { |
|---|
| 112 | kfree(name); |
|---|
| 113 | return -ENOMEM; |
|---|
| 114 | } |
|---|
| 115 | |
|---|
| 116 | memcpy(new_table,sysctl_table,6 * sizeof(ctl_table)); |
|---|
| 117 | new_table[0].child = &new_table[2]; |
|---|
| 118 | new_table[2].child = &new_table[4]; |
|---|
| 119 | new_table[4].child = &new_table[6]; |
|---|
| 120 | new_table[4].procname = name; |
|---|
| 121 | new_table[4].ctl_name = id; |
|---|
| 122 | memcpy(new_table+6,ctl_template,(len-6) * sizeof(ctl_table)); |
|---|
| 123 | for (i = 6; i < len; i++) |
|---|
| 124 | new_table[i].extra1 = client; |
|---|
| 125 | |
|---|
| 126 | if (! (new_header = register_sysctl_table(new_table,0))) { |
|---|
| 127 | kfree(new_table); |
|---|
| 128 | kfree(name); |
|---|
| 129 | return -ENOMEM; |
|---|
| 130 | } |
|---|
| 131 | |
|---|
| 132 | sensors_entries[i] = new_header; |
|---|
| 133 | |
|---|
| 134 | return id; |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | void sensors_deregister_entry(int id) |
|---|
| 138 | { |
|---|
| 139 | ctl_table *table; |
|---|
| 140 | id -= 256; |
|---|
| 141 | if (sensors_entries[id]) { |
|---|
| 142 | table = sensors_entries[id]->ctl_table; |
|---|
| 143 | unregister_sysctl_table(sensors_entries[id]); |
|---|
| 144 | kfree((void *) (table->procname)); |
|---|
| 145 | kfree(table); |
|---|
| 146 | sensors_entries[id] = 0; |
|---|
| 147 | } |
|---|
| 148 | } |
|---|
| 149 | |
|---|
| 150 | /* nrels contains initially the maximum number of elements which can be |
|---|
| 151 | put in results, and finally the number of elements actually put there. |
|---|
| 152 | A magnitude of 1 will multiply everything with 10; etc. |
|---|
| 153 | buffer, bufsize is the character buffer we read from and its length. |
|---|
| 154 | results will finally contain the parsed integers. |
|---|
| 155 | |
|---|
| 156 | Buffer should contain several reals, separated by whitespace. A real |
|---|
| 157 | has the following syntax: |
|---|
| 158 | [ Minus ] Digit* [ Dot Digit* ] |
|---|
| 159 | (everything between [] is optional; * means zero or more). |
|---|
| 160 | When the next character is unparsable, everything is skipped until the |
|---|
| 161 | next whitespace. |
|---|
| 162 | |
|---|
| 163 | WARNING! This is tricky code. I have tested it, but there may still be |
|---|
| 164 | hidden bugs in it, even leading to crashes and things! |
|---|
| 165 | */ |
|---|
| 166 | void sensors_parse_reals(int *nrels, void *buffer, int bufsize, |
|---|
| 167 | long *results, int magnitude) |
|---|
| 168 | { |
|---|
| 169 | int maxels,min,mag; |
|---|
| 170 | long res; |
|---|
| 171 | char nextchar=0; |
|---|
| 172 | |
|---|
| 173 | maxels = *nrels; |
|---|
| 174 | *nrels = 0; |
|---|
| 175 | |
|---|
| 176 | while (bufsize && (*nrels < maxels)) { |
|---|
| 177 | |
|---|
| 178 | /* Skip spaces at the start */ |
|---|
| 179 | while (bufsize && ! get_user_data(nextchar,(char *) buffer) && |
|---|
| 180 | isspace((int) nextchar)) { |
|---|
| 181 | bufsize --; |
|---|
| 182 | ((char *) buffer)++; |
|---|
| 183 | } |
|---|
| 184 | |
|---|
| 185 | /* Well, we may be done now */ |
|---|
| 186 | if (! bufsize) |
|---|
| 187 | return; |
|---|
| 188 | |
|---|
| 189 | /* New defaults for our result */ |
|---|
| 190 | min = 0; |
|---|
| 191 | res = 0; |
|---|
| 192 | mag = magnitude; |
|---|
| 193 | |
|---|
| 194 | /* Check for a minus */ |
|---|
| 195 | if (! get_user_data(nextchar,(char *) buffer) && (nextchar == '-')) { |
|---|
| 196 | min=1; |
|---|
| 197 | bufsize--; |
|---|
| 198 | ((char *) buffer)++; |
|---|
| 199 | } |
|---|
| 200 | |
|---|
| 201 | /* Digits before a decimal dot */ |
|---|
| 202 | while (bufsize && !get_user_data(nextchar,(char *) buffer) && |
|---|
| 203 | isdigit((int) nextchar)) { |
|---|
| 204 | res = res * 10 + nextchar - '0'; |
|---|
| 205 | bufsize--; |
|---|
| 206 | ((char *) buffer)++; |
|---|
| 207 | } |
|---|
| 208 | |
|---|
| 209 | /* If mag < 0, we must actually divide here! */ |
|---|
| 210 | while (mag < 0) { |
|---|
| 211 | res = res / 10; |
|---|
| 212 | mag++; |
|---|
| 213 | } |
|---|
| 214 | |
|---|
| 215 | if (bufsize && (nextchar == '.')) { |
|---|
| 216 | /* Skip the dot */ |
|---|
| 217 | bufsize--; |
|---|
| 218 | ((char *) buffer)++; |
|---|
| 219 | |
|---|
| 220 | /* Read digits while they are significant */ |
|---|
| 221 | while(bufsize && (mag > 0) && |
|---|
| 222 | !get_user_data(nextchar,(char *) buffer) && |
|---|
| 223 | isdigit((int) nextchar)) { |
|---|
| 224 | res = res * 10 + nextchar - '0'; |
|---|
| 225 | mag--; |
|---|
| 226 | bufsize--; |
|---|
| 227 | ((char *) buffer)++; |
|---|
| 228 | } |
|---|
| 229 | } |
|---|
| 230 | /* If we are out of data, but mag > 0, we need to scale here */ |
|---|
| 231 | while (mag > 0) { |
|---|
| 232 | res = res * 10; |
|---|
| 233 | mag --; |
|---|
| 234 | } |
|---|
| 235 | |
|---|
| 236 | /* Skip everything until we hit whitespace */ |
|---|
| 237 | while(bufsize && !get_user_data(nextchar,(char *) buffer) && |
|---|
| 238 | isspace ((int) nextchar)) { |
|---|
| 239 | bufsize --; |
|---|
| 240 | ((char *) buffer) ++; |
|---|
| 241 | } |
|---|
| 242 | |
|---|
| 243 | /* Put res in results */ |
|---|
| 244 | results[*nrels] = (min?-1:1)*res; |
|---|
| 245 | (*nrels)++; |
|---|
| 246 | } |
|---|
| 247 | |
|---|
| 248 | /* Well, there may be more in the buffer, but we need no more data. |
|---|
| 249 | Ignore anything that is left. */ |
|---|
| 250 | return; |
|---|
| 251 | } |
|---|
| 252 | |
|---|
| 253 | void sensors_write_reals(int nrels,void *buffer,int *bufsize,long *results, |
|---|
| 254 | int magnitude) |
|---|
| 255 | { |
|---|
| 256 | #define BUFLEN 20 |
|---|
| 257 | char BUF[BUFLEN+1]; /* An individual representation should fit in here! */ |
|---|
| 258 | char printfstr[10]; |
|---|
| 259 | int nr=0; |
|---|
| 260 | int buflen,mag,times; |
|---|
| 261 | int curbufsize=0; |
|---|
| 262 | |
|---|
| 263 | while ((nr < nrels) && (curbufsize < *bufsize)) { |
|---|
| 264 | mag=magnitude; |
|---|
| 265 | |
|---|
| 266 | if (nr != 0) { |
|---|
| 267 | put_user(' ', (char *) buffer); |
|---|
| 268 | curbufsize ++; |
|---|
| 269 | ((char *) buffer) ++; |
|---|
| 270 | } |
|---|
| 271 | |
|---|
| 272 | /* Fill BUF with the representation of the next string */ |
|---|
| 273 | if (mag <= 0) { |
|---|
| 274 | |
|---|
| 275 | buflen=sprintf(BUF,"%ld",results[nr]); |
|---|
| 276 | if (buflen < 0) { /* Oops, a sprintf error! */ |
|---|
| 277 | *bufsize=0; |
|---|
| 278 | return; |
|---|
| 279 | } |
|---|
| 280 | while ((mag < 0) && (buflen < BUFLEN)) { |
|---|
| 281 | BUF[buflen++]='0'; |
|---|
| 282 | mag++; |
|---|
| 283 | } |
|---|
| 284 | BUF[buflen]=0; |
|---|
| 285 | } else { |
|---|
| 286 | times=1; |
|---|
| 287 | for (times=1; mag-- > 0; times *= 10); |
|---|
| 288 | if (results[nr] < 0) { |
|---|
| 289 | BUF[0] = '-'; |
|---|
| 290 | buflen = 1; |
|---|
| 291 | } else |
|---|
| 292 | buflen=0; |
|---|
| 293 | strcpy(printfstr,"%ld.%0Xld"); |
|---|
| 294 | printfstr[6]=magnitude+'0'; |
|---|
| 295 | buflen+=sprintf(BUF+buflen,printfstr,abs(results[nr])/times, |
|---|
| 296 | abs(results[nr])%times); |
|---|
| 297 | if (buflen < 0) { /* Oops, a sprintf error! */ |
|---|
| 298 | *bufsize=0; |
|---|
| 299 | return; |
|---|
| 300 | } |
|---|
| 301 | } |
|---|
| 302 | |
|---|
| 303 | /* Now copy it to the user-space buffer */ |
|---|
| 304 | if (buflen + curbufsize > *bufsize) |
|---|
| 305 | buflen=*bufsize-curbufsize; |
|---|
| 306 | copy_to_user(buffer,BUF,buflen); |
|---|
| 307 | curbufsize += buflen; |
|---|
| 308 | (char *) buffer += buflen; |
|---|
| 309 | |
|---|
| 310 | nr ++; |
|---|
| 311 | } |
|---|
| 312 | if (curbufsize < *bufsize) { |
|---|
| 313 | put_user('\n', (char *) buffer); |
|---|
| 314 | curbufsize ++; |
|---|
| 315 | } |
|---|
| 316 | *bufsize=curbufsize; |
|---|
| 317 | } |
|---|
| 318 | |
|---|
| 319 | int sensors_init(void) |
|---|
| 320 | { |
|---|
| 321 | printk("sensors.o version %s (%s)\n",LM_VERSION,LM_DATE); |
|---|
| 322 | return 0; |
|---|
| 323 | } |
|---|
| 324 | |
|---|
| 325 | int sensors_cleanup(void) |
|---|
| 326 | { |
|---|
| 327 | return 0; |
|---|
| 328 | } |
|---|
| 329 | |
|---|
| 330 | #ifdef MODULE |
|---|
| 331 | |
|---|
| 332 | MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); |
|---|
| 333 | MODULE_DESCRIPTION("LM78 driver"); |
|---|
| 334 | |
|---|
| 335 | int init_module(void) |
|---|
| 336 | { |
|---|
| 337 | return sensors_init(); |
|---|
| 338 | } |
|---|
| 339 | |
|---|
| 340 | int cleanup_module(void) |
|---|
| 341 | { |
|---|
| 342 | return sensors_cleanup(); |
|---|
| 343 | } |
|---|
| 344 | |
|---|
| 345 | #endif /* MODULE */ |
|---|
| 346 | |
|---|