From 857a44f757abd93c2770175f820bf4871f8d3829 Mon Sep 17 00:00:00 2001 From: Frodo Looijaard Date: Thu, 3 Dec 1998 23:15:17 +0000 Subject: [PATCH] Simplification of /proc files functions in chip drivers I moved a large part of the proc and sysctl call-back functions into sensors.c. The result saved about 100 lines in lm78.c; it now uses only 2 pages according to lsmod, instead of 3! Also, other modules can use the same functions, saving coding efforts and keeping them small. I also fixed a fan_div display error. Expect more code to be moved to sensors.c: I am writing lm75.c, and most things which are in both modules (and so probably general purpose) will be moved to sensors.c. git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@28 7894878c-1315-0410-8ee3-d5d059ff63e0 --- kernel/chips/lm78.c | 431 +++++++++++++-------------------------- kernel/include/sensors.h | 48 ++++- kernel/sensors.c | 110 +++++++++- src/lm78.c | 431 +++++++++++++-------------------------- src/sensors.c | 110 +++++++++- src/sensors.h | 48 ++++- 6 files changed, 594 insertions(+), 584 deletions(-) diff --git a/kernel/chips/lm78.c b/kernel/chips/lm78.c index 39fd8d68..7d9c7674 100644 --- a/kernel/chips/lm78.c +++ b/kernel/chips/lm78.c @@ -33,7 +33,7 @@ #include "i2c.h" #include "compat.h" -/* Many LM78 constants needed below */ +/* Many LM78 constants specified below */ /* Length of ISA address segment */ #define LM78_EXTENT 8 @@ -81,7 +81,7 @@ static int lm78_in_conv[7] = {10000, 10000, 10000, 16892, 38000, (val)>=0x06?0:205-(val)*5) #define ALARMS_FROM_REG(val) (val) -#define DIV_FROM_REG(val) (1 >> (val)) +#define DIV_FROM_REG(val) (1 << (val)) #define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) /* Initial limits */ @@ -203,27 +203,19 @@ static int lm78_write_value(struct i2c_client *client, u8 register, u8 value); static void lm78_update_client(struct i2c_client *client); static void lm78_init_client(struct i2c_client *client); -static int lm78_sysctl (ctl_table *table, int *name, int nlen, void *oldval, - size_t *oldlenp, void *newval, size_t newlen, - void **context); -static int lm78_proc (ctl_table *ctl, int write, struct file * filp, - void *buffer, size_t *lenp); - - -static void write_in(struct i2c_client *client, int nr, int nrels, - long *results); -static void read_in(struct i2c_client *client, int nr, long *results); -static void write_fan(struct i2c_client *client, int nr, int nrels, - long *results); -static void read_fan(struct i2c_client *client, int nr, long *results); -static void write_temp(struct i2c_client *client, int nrels, long *results); -static void read_temp(struct i2c_client *client, long *results); -static void read_vid(struct i2c_client *client, long *results); -static void read_alarms(struct i2c_client *client, long *results); -static void write_fan_div(struct i2c_client *client, int nrels, long *results); -static void read_fan_div(struct i2c_client *client, long *results); - +void lm78_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +void lm78_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +void lm78_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +void lm78_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +void lm78_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +void lm78_fan_div(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); /* I choose here for semi-static LM78 allocation. Complete dynamic allocation could also be used; the code needed for this would probably @@ -254,20 +246,34 @@ static int lm78_initialized = 0; is done through one of the 'extra' fields which are initialized when a new copy is allocated. */ static ctl_table lm78_dir_table_template[] = { - { LM78_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_VID, "vid", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, + { LM78_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_in }, + { LM78_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_in }, + { LM78_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_in }, + { LM78_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_in }, + { LM78_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_in }, + { LM78_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_in }, + { LM78_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_in }, + { LM78_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_fan }, + { LM78_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_fan }, + { LM78_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_fan }, + { LM78_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_temp }, + { LM78_SYSCTL_VID, "vid", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_vid }, + { LM78_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_fan_div }, + { LM78_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_alarms }, { 0 } }; @@ -699,281 +705,138 @@ void lm78_update_client(struct i2c_client *client) up(&data->update_lock); } -/* This function is called when /proc/sys/dev/lm78-???/... is accessed */ -int lm78_proc (ctl_table *ctl, int write, struct file * filp, - void *buffer, size_t *lenp) + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void lm78_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) { - int nrels,mag; - long results[7]; - struct i2c_client *client = ctl -> extra1; + struct lm78_data *data = client->data; + int nr = ctl_name - LM78_SYSCTL_IN0; - /* If buffer is size 0, or we try to read when not at the start, we - return nothing. Note that I think writing when not at the start - does not work either, but anyway, this is straight from the kernel - sources. */ - if (!*lenp || (filp->f_pos && !write)) { - *lenp = 0; - return 0; - } - - /* How many numbers are found within these files, and how to scale them? */ - switch (ctl->ctl_name) { - case LM78_SYSCTL_IN0: case LM78_SYSCTL_IN1: case LM78_SYSCTL_IN2: - case LM78_SYSCTL_IN3: case LM78_SYSCTL_IN4: case LM78_SYSCTL_IN5: - case LM78_SYSCTL_IN6: - nrels=3; - mag=2; - break; - case LM78_SYSCTL_TEMP: - nrels=3; - mag=1; - break; - case LM78_SYSCTL_FAN_DIV: - nrels=3; - mag=0; - break; - case LM78_SYSCTL_FAN1: case LM78_SYSCTL_FAN2: case LM78_SYSCTL_FAN3: - nrels=2; - mag=0; - break; - case LM78_SYSCTL_VID: - nrels=1; - mag=2; - break; - case LM78_SYSCTL_ALARMS: - nrels=1; - mag=0; - break; - default: /* Should never be called */ - return -EINVAL; - } - - /* OK, try writing stuff. */ - if (write) { - sensors_parse_reals(&nrels,buffer,*lenp,results,mag); - if (nrels == 0) - return 0; - switch (ctl->ctl_name) { - case LM78_SYSCTL_IN0: write_in(client,0,nrels,results); break; - case LM78_SYSCTL_IN1: write_in(client,1,nrels,results); break; - case LM78_SYSCTL_IN2: write_in(client,2,nrels,results); break; - case LM78_SYSCTL_IN3: write_in(client,3,nrels,results); break; - case LM78_SYSCTL_IN4: write_in(client,4,nrels,results); break; - case LM78_SYSCTL_IN5: write_in(client,5,nrels,results); break; - case LM78_SYSCTL_IN6: write_in(client,6,nrels,results); break; - case LM78_SYSCTL_FAN1: write_fan(client,1,nrels,results); break; - case LM78_SYSCTL_FAN2: write_fan(client,2,nrels,results); break; - case LM78_SYSCTL_FAN3: write_fan(client,3,nrels,results); break; - case LM78_SYSCTL_FAN_DIV: write_fan_div(client,nrels,results);break; - case LM78_SYSCTL_TEMP: write_temp(client,nrels,results);break; - case LM78_SYSCTL_VID: case LM78_SYSCTL_ALARMS: break; - default: /* Should never be called */ *lenp=0; return -EINVAL; break; - } - filp->f_pos += *lenp; - return 0; - } else { /* read */ - /* Update all values in LM_Sensor_Data */ - - lm78_update_client((struct i2c_client *) (ctl->extra1)); - - /* Read the values to print into results */ - switch (ctl->ctl_name) { - case LM78_SYSCTL_IN0: read_in(client,0,results);break; - case LM78_SYSCTL_IN1: read_in(client,1,results);break; - case LM78_SYSCTL_IN2: read_in(client,2,results);break; - case LM78_SYSCTL_IN3: read_in(client,3,results);break; - case LM78_SYSCTL_IN4: read_in(client,4,results);break; - case LM78_SYSCTL_IN5: read_in(client,5,results);break; - case LM78_SYSCTL_IN6: read_in(client,6,results);break; - case LM78_SYSCTL_FAN1: read_fan(client,1,results);break; - case LM78_SYSCTL_FAN2: read_fan(client,2,results);break; - case LM78_SYSCTL_FAN3: read_fan(client,3,results);break; - case LM78_SYSCTL_TEMP: read_temp(client,results);break; - case LM78_SYSCTL_FAN_DIV: read_fan_div(client,results);break; - case LM78_SYSCTL_VID: read_vid(client,results);break; - case LM78_SYSCTL_ALARMS: read_alarms(client,results);break; - default: /* Should never be called */ return -EINVAL; - } - /* OK, print it now */ - sensors_write_reals(nrels,buffer,lenp,results,mag); - filp->f_pos += *lenp; - return 0; - } -} - -/* This function is called when a sysctl on a lm78 file is done */ -int lm78_sysctl (ctl_table *table, int *name, int nlen, void *oldval, - size_t *oldlenp, void *newval, size_t newlen, - void **context) -{ - long results[7]; - int nrels,oldlen; - struct i2c_client *client = table -> extra1; - - /* How many numbers are found within these files, and how to scale them? */ - switch (table->ctl_name) { - case LM78_SYSCTL_IN0: case LM78_SYSCTL_IN1: case LM78_SYSCTL_IN2: - case LM78_SYSCTL_IN3: case LM78_SYSCTL_IN4: case LM78_SYSCTL_IN5: - case LM78_SYSCTL_IN6: case LM78_SYSCTL_TEMP: - case LM78_SYSCTL_FAN_DIV: - nrels=3; - break; - case LM78_SYSCTL_FAN1: case LM78_SYSCTL_FAN2: case LM78_SYSCTL_FAN3: - nrels=2; - break; - case LM78_SYSCTL_VID: case LM78_SYSCTL_ALARMS: - nrels=1; - break; - default: /* Should never be called */ - return -EINVAL; - } - - /* Check if we need to output the old values */ - if (oldval && oldlenp && ! get_user_data(oldlen,oldlenp) && oldlen) { - - /* Update all values in LM_Sensor_Data */ - lm78_update_client((struct i2c_client *) (table->extra1)); - switch (table->ctl_name) { - case LM78_SYSCTL_IN0: read_in(client,0,results);break; - case LM78_SYSCTL_IN1: read_in(client,1,results);break; - case LM78_SYSCTL_IN2: read_in(client,2,results);break; - case LM78_SYSCTL_IN3: read_in(client,3,results);break; - case LM78_SYSCTL_IN4: read_in(client,4,results);break; - case LM78_SYSCTL_IN5: read_in(client,5,results);break; - case LM78_SYSCTL_IN6: read_in(client,6,results);break; - case LM78_SYSCTL_FAN1: read_fan(client,1,results);break; - case LM78_SYSCTL_FAN2: read_fan(client,2,results);break; - case LM78_SYSCTL_FAN3: read_fan(client,3,results);break; - case LM78_SYSCTL_TEMP: read_temp(client,results);break; - case LM78_SYSCTL_FAN_DIV: read_fan_div(client,results);break; - case LM78_SYSCTL_VID: read_vid(client,results);break; - case LM78_SYSCTL_ALARMS: read_alarms(client,results);break; - default: /* Should never be called */ return -EINVAL; - } - - /* Note the rounding factor! */ - if (nrels * sizeof(long) < oldlen) - oldlen = nrels * sizeof(long); - oldlen = (oldlen / sizeof(long)) * sizeof(long); - copy_to_user(oldval,results,oldlen); - put_user(oldlen,oldlenp); - } - - /* Check to see whether we need to read the new values */ - if (newval && newlen) { - if (nrels * sizeof(long) < newlen) - newlen = nrels * sizeof(long); - nrels = newlen / sizeof(long); - newlen = (newlen / sizeof(long)) * sizeof(long); - copy_from_user(results,newval,newlen); - - switch (table->ctl_name) { - case LM78_SYSCTL_IN0: write_in(client,0,nrels,results); break; - case LM78_SYSCTL_IN1: write_in(client,1,nrels,results); break; - case LM78_SYSCTL_IN2: write_in(client,2,nrels,results); break; - case LM78_SYSCTL_IN3: write_in(client,3,nrels,results); break; - case LM78_SYSCTL_IN4: write_in(client,4,nrels,results); break; - case LM78_SYSCTL_IN5: write_in(client,5,nrels,results); break; - case LM78_SYSCTL_IN6: write_in(client,6,nrels,results); break; - case LM78_SYSCTL_FAN1: write_fan(client,1,nrels,results); break; - case LM78_SYSCTL_FAN2: write_fan(client,2,nrels,results); break; - case LM78_SYSCTL_FAN3: write_fan(client,3,nrels,results); break; - case LM78_SYSCTL_TEMP: write_temp(client,nrels,results); break; - case LM78_SYSCTL_FAN_DIV: write_fan_div(client,nrels,results);break; - case LM78_SYSCTL_VID: case LM78_SYSCTL_ALARMS: break; - default: /* Should never be called */ return -EINVAL; break; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = IN_FROM_REG(data->in_min[nr],nr); + results[1] = IN_FROM_REG(data->in_max[nr],nr); + results[2] = IN_FROM_REG(data->in[nr],nr); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0],nr); + lm78_write_value(client,LM78_REG_IN_MIN(nr),data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1],nr); + lm78_write_value(client,LM78_REG_IN_MAX(nr),data->in_max[nr]); } } - return 1; /* We have done all the work */ } -void write_in(struct i2c_client *client, int nr, int nrels, long *results) +void lm78_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) { struct lm78_data *data = client->data; - if (nrels >= 1) { - data->in_min[nr] = IN_TO_REG(results[0],nr); - lm78_write_value(client,LM78_REG_IN_MIN(nr),data->in_min[nr]); - } - if (nrels >= 2) { - data->in_max[nr] = IN_TO_REG(results[1],nr); - lm78_write_value(client,LM78_REG_IN_MAX(nr),data->in_max[nr]); + int nr = ctl_name - LM78_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr-1]); + results[1] = FAN_FROM_REG(data->fan[nr-1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr-1] = FAN_TO_REG(results[0]); + lm78_write_value(client,LM78_REG_FAN_MIN(nr),data->fan_min[nr-1]); + } } } -void read_in(struct i2c_client *client, int nr, long *results) -{ - struct lm78_data *data = client->data; - results[0] = IN_FROM_REG(data->in_min[nr],nr); - results[1] = IN_FROM_REG(data->in_max[nr],nr); - results[2] = IN_FROM_REG(data->in[nr],nr); -} -void write_fan(struct i2c_client *client, int nr, int nrels, long *results) +void lm78_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) { struct lm78_data *data = client->data; - if (nrels >= 1) { - data->fan_min[nr-1] = FAN_TO_REG(results[0]); - lm78_write_value(client,LM78_REG_FAN_MIN(nr),data->fan_min[nr-1]); + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_over); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_over = TEMP_TO_REG(results[0]); + lm78_write_value(client,LM78_REG_TEMP_OVER,data->temp_over); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + lm78_write_value(client,LM78_REG_TEMP_HYST,data->temp_hyst); + } } } -void read_fan(struct i2c_client *client, int nr, long *results) +void lm78_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) { struct lm78_data *data = client->data; - results[0] = FAN_FROM_REG(data->fan_min[nr-1]); - results[1] = FAN_FROM_REG(data->fan[nr-1]); -} - -void write_temp(struct i2c_client *client, int nrels, long *results) -{ - struct lm78_data *data = client->data; - if (nrels >= 1) { - data->temp_over = TEMP_TO_REG(results[0]); - lm78_write_value(client,LM78_REG_TEMP_OVER,data->temp_over); - } - if (nrels >= 2) { - data->temp_hyst = TEMP_TO_REG(results[1]); - lm78_write_value(client,LM78_REG_TEMP_HYST,data->temp_hyst); + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; } } -void read_temp(struct i2c_client *client, long *results) +void lm78_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) { struct lm78_data *data = client->data; - results[0] = TEMP_FROM_REG(data->temp_over); - results[1] = TEMP_FROM_REG(data->temp_hyst); - results[2] = TEMP_FROM_REG(data->temp); + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } } -void read_vid(struct i2c_client *client, long *results) +void lm78_fan_div(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) { struct lm78_data *data = client->data; - results[0] = VID_FROM_REG(data->vid); -} - -void read_alarms(struct i2c_client *client, long *results) -{ - struct lm78_data *data = client->data; - results[0] = ALARMS_FROM_REG(data->alarms); -} - -void read_fan_div(struct i2c_client *client, long *results) -{ - struct lm78_data *data = client->data; - results[0] = DIV_FROM_REG(data->fan_div[0]); - results[1] = DIV_FROM_REG(data->fan_div[1]); - results[2] = 2; -} - -void write_fan_div(struct i2c_client *client, int nrels, long *results) -{ - struct lm78_data *data = client->data; - if (nrels >= 2) - data->fan_div[1] = DIV_TO_REG(results[1]); - if (nrels >= 1) { - data->fan_div[0] = DIV_TO_REG(results[0]); - lm78_write_value(client,LM78_REG_VID_FANDIV, - (data->fan_div[0] >> 4) | (data->fan_div[1] >> 6)); + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + results[2] = 2; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 2) + data->fan_div[1] = DIV_TO_REG(results[1]); + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + lm78_write_value(client,LM78_REG_VID_FANDIV, + (data->fan_div[0] >> 4) | (data->fan_div[1] >> 6)); + } } } @@ -1012,7 +875,7 @@ int lm78_cleanup(void) #ifdef MODULE MODULE_AUTHOR("Frodo Looijaard "); -MODULE_DESCRIPTION("LM78 driver"); +MODULE_DESCRIPTION("LM78, LM78-J and LM79 driver"); int init_module(void) { diff --git a/kernel/include/sensors.h b/kernel/include/sensors.h index 9bab6916..5f009a5a 100644 --- a/kernel/include/sensors.h +++ b/kernel/include/sensors.h @@ -23,13 +23,53 @@ #include #ifdef __KERNEL__ + +/* The type of callback functions used in sensors_{proc,sysctl}_real */ +typedef void (*sensors_real_callback) (struct i2c_client *client, + int operation, int ctl_name, + int *nrels_mag, long *results); + +/* Values for the operation field in the above function type */ +#define SENSORS_PROC_REAL_INFO 1 +#define SENSORS_PROC_REAL_READ 2 +#define SENSORS_PROC_REAL_WRITE 3 + +/* These funcion reads or writes a 'real' value (encoded by the combination + of an integer and a magnitude, the last is the power of ten the value + should be divided with) to a /proc/sys directory. To use these functions, + you must (before registering the ctl_table) set the extra2 field to the + client, and the extra1 field to a function of the form: + void func(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) + This last function can be called for three values of operation. If + operation equals SENSORS_PROC_REAL_INFO, the magnitude should be returned + in nrels_mag. If operation equals SENSORS_PROC_REAL_READ, values should + be read into results. nrels_mag should return the number of elements + read; the maximum number is put in it on entry. Finally, if operation + equals SENSORS_PROC_REAL_WRITE, the values in results should be + written to the chip. nrels_mag contains on entry the number of elements + found. + In all cases, client points to the client we wish to interact with, + and ctl_name is the SYSCTL id of the file we are accessing. */ +extern int sensors_sysctl_real (ctl_table *table, int *name, int nlen, + void *oldval, size_t *oldlenp, void *newval, + size_t newlen, void **context); +extern int sensors_proc_real(ctl_table *ctl, int write, struct file * filp, + void *buffer, size_t *lenp); + + + +/* These rather complex functions must be called when you want to add or + delete an entry in /proc/sys/dev/sensors/chips (not yet implemented). It + also creates a new directory within /proc/sys/dev/sensors/. + ctl_template should be a template of the newly created directory. It is + copied in memory. The extra2 field of each file is set to point to client. + If any driver wants subdirectories within the newly created directory, + these functions must be updated! */ extern int sensors_register_entry(struct i2c_client *client, const char *prefix, ctl_table *ctl_template); extern void sensors_deregister_entry(int id); -extern void sensors_parse_reals(int *nrels, void *buffer, int bufsize, - long *results, int magnitude); -extern void sensors_write_reals(int nrels,void *buffer,int *bufsize, - long *results, int magnitude); + #endif /* def __KERNEL__ */ diff --git a/kernel/sensors.c b/kernel/sensors.c index 2d3811bd..b7b90768 100644 --- a/kernel/sensors.c +++ b/kernel/sensors.c @@ -28,15 +28,19 @@ #include "isa.h" #include "sensors.h" + #ifdef MODULE extern int init_module(void); extern int cleanup_module(void); #endif /* MODULE */ -int sensors_register_entry(struct i2c_client *client ,const char *prefix, - ctl_table *ctl_template); static int sensors_create_name(char **name, const char *prefix, struct i2c_adapter * adapter, int addr); +static void sensors_parse_reals(int *nrels, void *buffer, int bufsize, + long *results, int magnitude); +static void sensors_write_reals(int nrels,void *buffer,int *bufsize, + long *results, int magnitude); + static int sensors_init(void); static int sensors_cleanup(void); @@ -77,7 +81,7 @@ int sensors_create_name(char **name, const char *prefix, to /proc/sys/dev/sensors/chips (not yet implemented). It also creates a new directory within /proc/sys/dev/sensors/. ctl_template should be a template of the newly created directory. It is - copied in memory. The extra1 field of each file is set to point to client. + copied in memory. The extra2 field of each file is set to point to client. If any driver wants subdirectories within the newly created directory, this function must be updated! */ int sensors_register_entry(struct i2c_client *client ,const char *prefix, @@ -121,7 +125,7 @@ int sensors_register_entry(struct i2c_client *client ,const char *prefix, new_table[4].ctl_name = id; memcpy(new_table+6,ctl_template,(len-6) * sizeof(ctl_table)); for (i = 6; i < len; i++) - new_table[i].extra1 = client; + new_table[i].extra2 = client; if (! (new_header = register_sysctl_table(new_table,0))) { kfree(new_table); @@ -147,6 +151,104 @@ void sensors_deregister_entry(int id) } } +/* This funcion reads or writes a 'real' value (encoded by the combination + of an integer and a magnitude, the last is the power of ten the value + should be divided with) to a /proc/sys directory. To use this function, + you must (before registering the ctl_table) set the extra2 field to the + client, and the extra1 field to a function of the form: + void func(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) + This function can be called for three values of operation. If operation + equals SENSORS_PROC_REAL_INFO, the magnitude should be returned in + nrels_mag. If operation equals SENSORS_PROC_REAL_READ, values should + be read into results. nrels_mag should return the number of elements + read; the maximum number is put in it on entry. Finally, if operation + equals SENSORS_PROC_REAL_WRITE, the values in results should be + written to the chip. nrels_mag contains on entry the number of elements + found. + In all cases, client points to the client we wish to interact with, + and ctl_name is the SYSCTL id of the file we are accessing. */ +int sensors_proc_real(ctl_table *ctl, int write, struct file * filp, + void *buffer, size_t *lenp) +{ +#define MAX_RESULTS 20 + int mag,nrels=MAX_RESULTS; + long results[MAX_RESULTS]; + sensors_real_callback callback = ctl -> extra1; + struct i2c_client *client = ctl -> extra2; + + /* If buffer is size 0, or we try to read when not at the start, we + return nothing. Note that I think writing when not at the start + does not work either, but anyway, this is straight from the kernel + sources. */ + if (!*lenp || (filp->f_pos && !write)) { + *lenp = 0; + return 0; + } + + /* Get the magnitude */ + callback(client,SENSORS_PROC_REAL_INFO,ctl->ctl_name,&mag,NULL); + + if (write) { + /* Read the complete input into results, converting to longs */ + sensors_parse_reals(&nrels,buffer,*lenp,results,mag); + + if (! nrels) + return 0; + + /* Now feed this information back to the client */ + callback(client,SENSORS_PROC_REAL_WRITE,ctl->ctl_name,&nrels,results); + + filp->f_pos += *lenp; + return 0; + } else { /* read */ + /* Get the information from the client into results */ + callback(client,SENSORS_PROC_REAL_READ,ctl->ctl_name,&nrels,results); + + /* And write them to buffer, converting to reals */ + sensors_write_reals(nrels,buffer,lenp,results,mag); + sensors_write_reals(nrels,buffer,lenp,results,mag); + filp->f_pos += *lenp; + return 0; + } +} + +/* This function is equivalent to sensors_proc_real, only it interacts with + the sysctl(2) syscall, and returns no reals, but integers */ +int sensors_sysctl_real (ctl_table *table, int *name, int nlen, void *oldval, + size_t *oldlenp, void *newval, size_t newlen, + void **context) +{ + long results[MAX_RESULTS]; + int oldlen,nrels=MAX_RESULTS; + sensors_real_callback callback = table -> extra1; + struct i2c_client *client = table -> extra2; + + /* Check if we need to output the old values */ + if (oldval && oldlenp && ! get_user_data(oldlen,oldlenp) && oldlen) { + callback(client,SENSORS_PROC_REAL_READ,table->ctl_name,&nrels,results); + + /* Note the rounding factor! */ + if (nrels * sizeof(long) < oldlen) + oldlen = nrels * sizeof(long); + oldlen = (oldlen / sizeof(long)) * sizeof(long); + copy_to_user(oldval,results,oldlen); + put_user(oldlen,oldlenp); + } + + if (newval && newlen) { + /* Note the rounding factor! */ + newlen -= newlen % sizeof(long); + nrels = newlen / sizeof(long); + copy_from_user(results,newval,newlen); + + /* Get the new values back to the client */ + callback(client,SENSORS_PROC_REAL_WRITE,table->ctl_name,&nrels,results); + } + return 0; +} + + /* nrels contains initially the maximum number of elements which can be put in results, and finally the number of elements actually put there. A magnitude of 1 will multiply everything with 10; etc. diff --git a/src/lm78.c b/src/lm78.c index 39fd8d68..7d9c7674 100644 --- a/src/lm78.c +++ b/src/lm78.c @@ -33,7 +33,7 @@ #include "i2c.h" #include "compat.h" -/* Many LM78 constants needed below */ +/* Many LM78 constants specified below */ /* Length of ISA address segment */ #define LM78_EXTENT 8 @@ -81,7 +81,7 @@ static int lm78_in_conv[7] = {10000, 10000, 10000, 16892, 38000, (val)>=0x06?0:205-(val)*5) #define ALARMS_FROM_REG(val) (val) -#define DIV_FROM_REG(val) (1 >> (val)) +#define DIV_FROM_REG(val) (1 << (val)) #define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) /* Initial limits */ @@ -203,27 +203,19 @@ static int lm78_write_value(struct i2c_client *client, u8 register, u8 value); static void lm78_update_client(struct i2c_client *client); static void lm78_init_client(struct i2c_client *client); -static int lm78_sysctl (ctl_table *table, int *name, int nlen, void *oldval, - size_t *oldlenp, void *newval, size_t newlen, - void **context); -static int lm78_proc (ctl_table *ctl, int write, struct file * filp, - void *buffer, size_t *lenp); - - -static void write_in(struct i2c_client *client, int nr, int nrels, - long *results); -static void read_in(struct i2c_client *client, int nr, long *results); -static void write_fan(struct i2c_client *client, int nr, int nrels, - long *results); -static void read_fan(struct i2c_client *client, int nr, long *results); -static void write_temp(struct i2c_client *client, int nrels, long *results); -static void read_temp(struct i2c_client *client, long *results); -static void read_vid(struct i2c_client *client, long *results); -static void read_alarms(struct i2c_client *client, long *results); -static void write_fan_div(struct i2c_client *client, int nrels, long *results); -static void read_fan_div(struct i2c_client *client, long *results); - +void lm78_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +void lm78_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +void lm78_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +void lm78_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +void lm78_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +void lm78_fan_div(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); /* I choose here for semi-static LM78 allocation. Complete dynamic allocation could also be used; the code needed for this would probably @@ -254,20 +246,34 @@ static int lm78_initialized = 0; is done through one of the 'extra' fields which are initialized when a new copy is allocated. */ static ctl_table lm78_dir_table_template[] = { - { LM78_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_VID, "vid", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, - { LM78_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, + { LM78_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_in }, + { LM78_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_in }, + { LM78_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_in }, + { LM78_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_in }, + { LM78_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_in }, + { LM78_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_in }, + { LM78_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_in }, + { LM78_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_fan }, + { LM78_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_fan }, + { LM78_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_fan }, + { LM78_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_temp }, + { LM78_SYSCTL_VID, "vid", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_vid }, + { LM78_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_fan_div }, + { LM78_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &lm78_alarms }, { 0 } }; @@ -699,281 +705,138 @@ void lm78_update_client(struct i2c_client *client) up(&data->update_lock); } -/* This function is called when /proc/sys/dev/lm78-???/... is accessed */ -int lm78_proc (ctl_table *ctl, int write, struct file * filp, - void *buffer, size_t *lenp) + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void lm78_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) { - int nrels,mag; - long results[7]; - struct i2c_client *client = ctl -> extra1; + struct lm78_data *data = client->data; + int nr = ctl_name - LM78_SYSCTL_IN0; - /* If buffer is size 0, or we try to read when not at the start, we - return nothing. Note that I think writing when not at the start - does not work either, but anyway, this is straight from the kernel - sources. */ - if (!*lenp || (filp->f_pos && !write)) { - *lenp = 0; - return 0; - } - - /* How many numbers are found within these files, and how to scale them? */ - switch (ctl->ctl_name) { - case LM78_SYSCTL_IN0: case LM78_SYSCTL_IN1: case LM78_SYSCTL_IN2: - case LM78_SYSCTL_IN3: case LM78_SYSCTL_IN4: case LM78_SYSCTL_IN5: - case LM78_SYSCTL_IN6: - nrels=3; - mag=2; - break; - case LM78_SYSCTL_TEMP: - nrels=3; - mag=1; - break; - case LM78_SYSCTL_FAN_DIV: - nrels=3; - mag=0; - break; - case LM78_SYSCTL_FAN1: case LM78_SYSCTL_FAN2: case LM78_SYSCTL_FAN3: - nrels=2; - mag=0; - break; - case LM78_SYSCTL_VID: - nrels=1; - mag=2; - break; - case LM78_SYSCTL_ALARMS: - nrels=1; - mag=0; - break; - default: /* Should never be called */ - return -EINVAL; - } - - /* OK, try writing stuff. */ - if (write) { - sensors_parse_reals(&nrels,buffer,*lenp,results,mag); - if (nrels == 0) - return 0; - switch (ctl->ctl_name) { - case LM78_SYSCTL_IN0: write_in(client,0,nrels,results); break; - case LM78_SYSCTL_IN1: write_in(client,1,nrels,results); break; - case LM78_SYSCTL_IN2: write_in(client,2,nrels,results); break; - case LM78_SYSCTL_IN3: write_in(client,3,nrels,results); break; - case LM78_SYSCTL_IN4: write_in(client,4,nrels,results); break; - case LM78_SYSCTL_IN5: write_in(client,5,nrels,results); break; - case LM78_SYSCTL_IN6: write_in(client,6,nrels,results); break; - case LM78_SYSCTL_FAN1: write_fan(client,1,nrels,results); break; - case LM78_SYSCTL_FAN2: write_fan(client,2,nrels,results); break; - case LM78_SYSCTL_FAN3: write_fan(client,3,nrels,results); break; - case LM78_SYSCTL_FAN_DIV: write_fan_div(client,nrels,results);break; - case LM78_SYSCTL_TEMP: write_temp(client,nrels,results);break; - case LM78_SYSCTL_VID: case LM78_SYSCTL_ALARMS: break; - default: /* Should never be called */ *lenp=0; return -EINVAL; break; - } - filp->f_pos += *lenp; - return 0; - } else { /* read */ - /* Update all values in LM_Sensor_Data */ - - lm78_update_client((struct i2c_client *) (ctl->extra1)); - - /* Read the values to print into results */ - switch (ctl->ctl_name) { - case LM78_SYSCTL_IN0: read_in(client,0,results);break; - case LM78_SYSCTL_IN1: read_in(client,1,results);break; - case LM78_SYSCTL_IN2: read_in(client,2,results);break; - case LM78_SYSCTL_IN3: read_in(client,3,results);break; - case LM78_SYSCTL_IN4: read_in(client,4,results);break; - case LM78_SYSCTL_IN5: read_in(client,5,results);break; - case LM78_SYSCTL_IN6: read_in(client,6,results);break; - case LM78_SYSCTL_FAN1: read_fan(client,1,results);break; - case LM78_SYSCTL_FAN2: read_fan(client,2,results);break; - case LM78_SYSCTL_FAN3: read_fan(client,3,results);break; - case LM78_SYSCTL_TEMP: read_temp(client,results);break; - case LM78_SYSCTL_FAN_DIV: read_fan_div(client,results);break; - case LM78_SYSCTL_VID: read_vid(client,results);break; - case LM78_SYSCTL_ALARMS: read_alarms(client,results);break; - default: /* Should never be called */ return -EINVAL; - } - /* OK, print it now */ - sensors_write_reals(nrels,buffer,lenp,results,mag); - filp->f_pos += *lenp; - return 0; - } -} - -/* This function is called when a sysctl on a lm78 file is done */ -int lm78_sysctl (ctl_table *table, int *name, int nlen, void *oldval, - size_t *oldlenp, void *newval, size_t newlen, - void **context) -{ - long results[7]; - int nrels,oldlen; - struct i2c_client *client = table -> extra1; - - /* How many numbers are found within these files, and how to scale them? */ - switch (table->ctl_name) { - case LM78_SYSCTL_IN0: case LM78_SYSCTL_IN1: case LM78_SYSCTL_IN2: - case LM78_SYSCTL_IN3: case LM78_SYSCTL_IN4: case LM78_SYSCTL_IN5: - case LM78_SYSCTL_IN6: case LM78_SYSCTL_TEMP: - case LM78_SYSCTL_FAN_DIV: - nrels=3; - break; - case LM78_SYSCTL_FAN1: case LM78_SYSCTL_FAN2: case LM78_SYSCTL_FAN3: - nrels=2; - break; - case LM78_SYSCTL_VID: case LM78_SYSCTL_ALARMS: - nrels=1; - break; - default: /* Should never be called */ - return -EINVAL; - } - - /* Check if we need to output the old values */ - if (oldval && oldlenp && ! get_user_data(oldlen,oldlenp) && oldlen) { - - /* Update all values in LM_Sensor_Data */ - lm78_update_client((struct i2c_client *) (table->extra1)); - switch (table->ctl_name) { - case LM78_SYSCTL_IN0: read_in(client,0,results);break; - case LM78_SYSCTL_IN1: read_in(client,1,results);break; - case LM78_SYSCTL_IN2: read_in(client,2,results);break; - case LM78_SYSCTL_IN3: read_in(client,3,results);break; - case LM78_SYSCTL_IN4: read_in(client,4,results);break; - case LM78_SYSCTL_IN5: read_in(client,5,results);break; - case LM78_SYSCTL_IN6: read_in(client,6,results);break; - case LM78_SYSCTL_FAN1: read_fan(client,1,results);break; - case LM78_SYSCTL_FAN2: read_fan(client,2,results);break; - case LM78_SYSCTL_FAN3: read_fan(client,3,results);break; - case LM78_SYSCTL_TEMP: read_temp(client,results);break; - case LM78_SYSCTL_FAN_DIV: read_fan_div(client,results);break; - case LM78_SYSCTL_VID: read_vid(client,results);break; - case LM78_SYSCTL_ALARMS: read_alarms(client,results);break; - default: /* Should never be called */ return -EINVAL; - } - - /* Note the rounding factor! */ - if (nrels * sizeof(long) < oldlen) - oldlen = nrels * sizeof(long); - oldlen = (oldlen / sizeof(long)) * sizeof(long); - copy_to_user(oldval,results,oldlen); - put_user(oldlen,oldlenp); - } - - /* Check to see whether we need to read the new values */ - if (newval && newlen) { - if (nrels * sizeof(long) < newlen) - newlen = nrels * sizeof(long); - nrels = newlen / sizeof(long); - newlen = (newlen / sizeof(long)) * sizeof(long); - copy_from_user(results,newval,newlen); - - switch (table->ctl_name) { - case LM78_SYSCTL_IN0: write_in(client,0,nrels,results); break; - case LM78_SYSCTL_IN1: write_in(client,1,nrels,results); break; - case LM78_SYSCTL_IN2: write_in(client,2,nrels,results); break; - case LM78_SYSCTL_IN3: write_in(client,3,nrels,results); break; - case LM78_SYSCTL_IN4: write_in(client,4,nrels,results); break; - case LM78_SYSCTL_IN5: write_in(client,5,nrels,results); break; - case LM78_SYSCTL_IN6: write_in(client,6,nrels,results); break; - case LM78_SYSCTL_FAN1: write_fan(client,1,nrels,results); break; - case LM78_SYSCTL_FAN2: write_fan(client,2,nrels,results); break; - case LM78_SYSCTL_FAN3: write_fan(client,3,nrels,results); break; - case LM78_SYSCTL_TEMP: write_temp(client,nrels,results); break; - case LM78_SYSCTL_FAN_DIV: write_fan_div(client,nrels,results);break; - case LM78_SYSCTL_VID: case LM78_SYSCTL_ALARMS: break; - default: /* Should never be called */ return -EINVAL; break; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = IN_FROM_REG(data->in_min[nr],nr); + results[1] = IN_FROM_REG(data->in_max[nr],nr); + results[2] = IN_FROM_REG(data->in[nr],nr); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0],nr); + lm78_write_value(client,LM78_REG_IN_MIN(nr),data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1],nr); + lm78_write_value(client,LM78_REG_IN_MAX(nr),data->in_max[nr]); } } - return 1; /* We have done all the work */ } -void write_in(struct i2c_client *client, int nr, int nrels, long *results) +void lm78_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) { struct lm78_data *data = client->data; - if (nrels >= 1) { - data->in_min[nr] = IN_TO_REG(results[0],nr); - lm78_write_value(client,LM78_REG_IN_MIN(nr),data->in_min[nr]); - } - if (nrels >= 2) { - data->in_max[nr] = IN_TO_REG(results[1],nr); - lm78_write_value(client,LM78_REG_IN_MAX(nr),data->in_max[nr]); + int nr = ctl_name - LM78_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr-1]); + results[1] = FAN_FROM_REG(data->fan[nr-1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr-1] = FAN_TO_REG(results[0]); + lm78_write_value(client,LM78_REG_FAN_MIN(nr),data->fan_min[nr-1]); + } } } -void read_in(struct i2c_client *client, int nr, long *results) -{ - struct lm78_data *data = client->data; - results[0] = IN_FROM_REG(data->in_min[nr],nr); - results[1] = IN_FROM_REG(data->in_max[nr],nr); - results[2] = IN_FROM_REG(data->in[nr],nr); -} -void write_fan(struct i2c_client *client, int nr, int nrels, long *results) +void lm78_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) { struct lm78_data *data = client->data; - if (nrels >= 1) { - data->fan_min[nr-1] = FAN_TO_REG(results[0]); - lm78_write_value(client,LM78_REG_FAN_MIN(nr),data->fan_min[nr-1]); + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_over); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_over = TEMP_TO_REG(results[0]); + lm78_write_value(client,LM78_REG_TEMP_OVER,data->temp_over); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + lm78_write_value(client,LM78_REG_TEMP_HYST,data->temp_hyst); + } } } -void read_fan(struct i2c_client *client, int nr, long *results) +void lm78_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) { struct lm78_data *data = client->data; - results[0] = FAN_FROM_REG(data->fan_min[nr-1]); - results[1] = FAN_FROM_REG(data->fan[nr-1]); -} - -void write_temp(struct i2c_client *client, int nrels, long *results) -{ - struct lm78_data *data = client->data; - if (nrels >= 1) { - data->temp_over = TEMP_TO_REG(results[0]); - lm78_write_value(client,LM78_REG_TEMP_OVER,data->temp_over); - } - if (nrels >= 2) { - data->temp_hyst = TEMP_TO_REG(results[1]); - lm78_write_value(client,LM78_REG_TEMP_HYST,data->temp_hyst); + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; } } -void read_temp(struct i2c_client *client, long *results) +void lm78_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) { struct lm78_data *data = client->data; - results[0] = TEMP_FROM_REG(data->temp_over); - results[1] = TEMP_FROM_REG(data->temp_hyst); - results[2] = TEMP_FROM_REG(data->temp); + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } } -void read_vid(struct i2c_client *client, long *results) +void lm78_fan_div(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) { struct lm78_data *data = client->data; - results[0] = VID_FROM_REG(data->vid); -} - -void read_alarms(struct i2c_client *client, long *results) -{ - struct lm78_data *data = client->data; - results[0] = ALARMS_FROM_REG(data->alarms); -} - -void read_fan_div(struct i2c_client *client, long *results) -{ - struct lm78_data *data = client->data; - results[0] = DIV_FROM_REG(data->fan_div[0]); - results[1] = DIV_FROM_REG(data->fan_div[1]); - results[2] = 2; -} - -void write_fan_div(struct i2c_client *client, int nrels, long *results) -{ - struct lm78_data *data = client->data; - if (nrels >= 2) - data->fan_div[1] = DIV_TO_REG(results[1]); - if (nrels >= 1) { - data->fan_div[0] = DIV_TO_REG(results[0]); - lm78_write_value(client,LM78_REG_VID_FANDIV, - (data->fan_div[0] >> 4) | (data->fan_div[1] >> 6)); + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + results[2] = 2; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 2) + data->fan_div[1] = DIV_TO_REG(results[1]); + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + lm78_write_value(client,LM78_REG_VID_FANDIV, + (data->fan_div[0] >> 4) | (data->fan_div[1] >> 6)); + } } } @@ -1012,7 +875,7 @@ int lm78_cleanup(void) #ifdef MODULE MODULE_AUTHOR("Frodo Looijaard "); -MODULE_DESCRIPTION("LM78 driver"); +MODULE_DESCRIPTION("LM78, LM78-J and LM79 driver"); int init_module(void) { diff --git a/src/sensors.c b/src/sensors.c index 2d3811bd..b7b90768 100644 --- a/src/sensors.c +++ b/src/sensors.c @@ -28,15 +28,19 @@ #include "isa.h" #include "sensors.h" + #ifdef MODULE extern int init_module(void); extern int cleanup_module(void); #endif /* MODULE */ -int sensors_register_entry(struct i2c_client *client ,const char *prefix, - ctl_table *ctl_template); static int sensors_create_name(char **name, const char *prefix, struct i2c_adapter * adapter, int addr); +static void sensors_parse_reals(int *nrels, void *buffer, int bufsize, + long *results, int magnitude); +static void sensors_write_reals(int nrels,void *buffer,int *bufsize, + long *results, int magnitude); + static int sensors_init(void); static int sensors_cleanup(void); @@ -77,7 +81,7 @@ int sensors_create_name(char **name, const char *prefix, to /proc/sys/dev/sensors/chips (not yet implemented). It also creates a new directory within /proc/sys/dev/sensors/. ctl_template should be a template of the newly created directory. It is - copied in memory. The extra1 field of each file is set to point to client. + copied in memory. The extra2 field of each file is set to point to client. If any driver wants subdirectories within the newly created directory, this function must be updated! */ int sensors_register_entry(struct i2c_client *client ,const char *prefix, @@ -121,7 +125,7 @@ int sensors_register_entry(struct i2c_client *client ,const char *prefix, new_table[4].ctl_name = id; memcpy(new_table+6,ctl_template,(len-6) * sizeof(ctl_table)); for (i = 6; i < len; i++) - new_table[i].extra1 = client; + new_table[i].extra2 = client; if (! (new_header = register_sysctl_table(new_table,0))) { kfree(new_table); @@ -147,6 +151,104 @@ void sensors_deregister_entry(int id) } } +/* This funcion reads or writes a 'real' value (encoded by the combination + of an integer and a magnitude, the last is the power of ten the value + should be divided with) to a /proc/sys directory. To use this function, + you must (before registering the ctl_table) set the extra2 field to the + client, and the extra1 field to a function of the form: + void func(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) + This function can be called for three values of operation. If operation + equals SENSORS_PROC_REAL_INFO, the magnitude should be returned in + nrels_mag. If operation equals SENSORS_PROC_REAL_READ, values should + be read into results. nrels_mag should return the number of elements + read; the maximum number is put in it on entry. Finally, if operation + equals SENSORS_PROC_REAL_WRITE, the values in results should be + written to the chip. nrels_mag contains on entry the number of elements + found. + In all cases, client points to the client we wish to interact with, + and ctl_name is the SYSCTL id of the file we are accessing. */ +int sensors_proc_real(ctl_table *ctl, int write, struct file * filp, + void *buffer, size_t *lenp) +{ +#define MAX_RESULTS 20 + int mag,nrels=MAX_RESULTS; + long results[MAX_RESULTS]; + sensors_real_callback callback = ctl -> extra1; + struct i2c_client *client = ctl -> extra2; + + /* If buffer is size 0, or we try to read when not at the start, we + return nothing. Note that I think writing when not at the start + does not work either, but anyway, this is straight from the kernel + sources. */ + if (!*lenp || (filp->f_pos && !write)) { + *lenp = 0; + return 0; + } + + /* Get the magnitude */ + callback(client,SENSORS_PROC_REAL_INFO,ctl->ctl_name,&mag,NULL); + + if (write) { + /* Read the complete input into results, converting to longs */ + sensors_parse_reals(&nrels,buffer,*lenp,results,mag); + + if (! nrels) + return 0; + + /* Now feed this information back to the client */ + callback(client,SENSORS_PROC_REAL_WRITE,ctl->ctl_name,&nrels,results); + + filp->f_pos += *lenp; + return 0; + } else { /* read */ + /* Get the information from the client into results */ + callback(client,SENSORS_PROC_REAL_READ,ctl->ctl_name,&nrels,results); + + /* And write them to buffer, converting to reals */ + sensors_write_reals(nrels,buffer,lenp,results,mag); + sensors_write_reals(nrels,buffer,lenp,results,mag); + filp->f_pos += *lenp; + return 0; + } +} + +/* This function is equivalent to sensors_proc_real, only it interacts with + the sysctl(2) syscall, and returns no reals, but integers */ +int sensors_sysctl_real (ctl_table *table, int *name, int nlen, void *oldval, + size_t *oldlenp, void *newval, size_t newlen, + void **context) +{ + long results[MAX_RESULTS]; + int oldlen,nrels=MAX_RESULTS; + sensors_real_callback callback = table -> extra1; + struct i2c_client *client = table -> extra2; + + /* Check if we need to output the old values */ + if (oldval && oldlenp && ! get_user_data(oldlen,oldlenp) && oldlen) { + callback(client,SENSORS_PROC_REAL_READ,table->ctl_name,&nrels,results); + + /* Note the rounding factor! */ + if (nrels * sizeof(long) < oldlen) + oldlen = nrels * sizeof(long); + oldlen = (oldlen / sizeof(long)) * sizeof(long); + copy_to_user(oldval,results,oldlen); + put_user(oldlen,oldlenp); + } + + if (newval && newlen) { + /* Note the rounding factor! */ + newlen -= newlen % sizeof(long); + nrels = newlen / sizeof(long); + copy_from_user(results,newval,newlen); + + /* Get the new values back to the client */ + callback(client,SENSORS_PROC_REAL_WRITE,table->ctl_name,&nrels,results); + } + return 0; +} + + /* nrels contains initially the maximum number of elements which can be put in results, and finally the number of elements actually put there. A magnitude of 1 will multiply everything with 10; etc. diff --git a/src/sensors.h b/src/sensors.h index 9bab6916..5f009a5a 100644 --- a/src/sensors.h +++ b/src/sensors.h @@ -23,13 +23,53 @@ #include #ifdef __KERNEL__ + +/* The type of callback functions used in sensors_{proc,sysctl}_real */ +typedef void (*sensors_real_callback) (struct i2c_client *client, + int operation, int ctl_name, + int *nrels_mag, long *results); + +/* Values for the operation field in the above function type */ +#define SENSORS_PROC_REAL_INFO 1 +#define SENSORS_PROC_REAL_READ 2 +#define SENSORS_PROC_REAL_WRITE 3 + +/* These funcion reads or writes a 'real' value (encoded by the combination + of an integer and a magnitude, the last is the power of ten the value + should be divided with) to a /proc/sys directory. To use these functions, + you must (before registering the ctl_table) set the extra2 field to the + client, and the extra1 field to a function of the form: + void func(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) + This last function can be called for three values of operation. If + operation equals SENSORS_PROC_REAL_INFO, the magnitude should be returned + in nrels_mag. If operation equals SENSORS_PROC_REAL_READ, values should + be read into results. nrels_mag should return the number of elements + read; the maximum number is put in it on entry. Finally, if operation + equals SENSORS_PROC_REAL_WRITE, the values in results should be + written to the chip. nrels_mag contains on entry the number of elements + found. + In all cases, client points to the client we wish to interact with, + and ctl_name is the SYSCTL id of the file we are accessing. */ +extern int sensors_sysctl_real (ctl_table *table, int *name, int nlen, + void *oldval, size_t *oldlenp, void *newval, + size_t newlen, void **context); +extern int sensors_proc_real(ctl_table *ctl, int write, struct file * filp, + void *buffer, size_t *lenp); + + + +/* These rather complex functions must be called when you want to add or + delete an entry in /proc/sys/dev/sensors/chips (not yet implemented). It + also creates a new directory within /proc/sys/dev/sensors/. + ctl_template should be a template of the newly created directory. It is + copied in memory. The extra2 field of each file is set to point to client. + If any driver wants subdirectories within the newly created directory, + these functions must be updated! */ extern int sensors_register_entry(struct i2c_client *client, const char *prefix, ctl_table *ctl_template); extern void sensors_deregister_entry(int id); -extern void sensors_parse_reals(int *nrels, void *buffer, int bufsize, - long *results, int magnitude); -extern void sensors_write_reals(int nrels,void *buffer,int *bufsize, - long *results, int magnitude); + #endif /* def __KERNEL__ */