static bool check_and_parse_filter(struct be_acct_req *data, const char *filter, const char *extra) { /* We will use sizeof() to determine the length of a string so we don't * call strlen over and over again with each request. Not a bottleneck, * but unnecessary and simple to avoid. */ static struct { const char *name; size_t lenght; uint32_t type; } types[] = {FILTER_TYPE("name", BE_FILTER_NAME), FILTER_TYPE("idnumber", BE_FILTER_IDNUM), FILTER_TYPE(DP_SEC_ID, BE_FILTER_SECID), FILTER_TYPE(DP_CERT, BE_FILTER_CERT), FILTER_TYPE(DP_WILDCARD, BE_FILTER_WILDCARD), {0, 0, 0}}; int i; if (filter == NULL) { return false; } for (i = 0; types[i].name != NULL; i++) { if (strncmp(filter, types[i].name, types[i].lenght) == 0) { data->filter_type = types[i].type; data->filter_value = &filter[types[i].lenght]; data->extra_value = extra; return true; } } if (strcmp(filter, ENUM_INDICATOR) == 0) { data->filter_type = BE_FILTER_ENUM; data->filter_value = NULL; data->extra_value = NULL; return true; } return false; }
static int sun4i_ts_probe(struct platform_device *pdev) { struct sun4i_ts_data *ts; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct device *hwmon; int error; u32 reg; bool ts_attached; u32 tp_sensitive_adjust = 15; u32 filter_type = 1; ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL); if (!ts) return -ENOMEM; ts->dev = dev; ts->ignore_fifo_data = true; ts->temp_data = -1; if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) { /* Allwinner SDK has temperature (C) = (value / 6) - 271 */ ts->temp_offset = 271000; ts->temp_step = 167; } else if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts")) { /* * The A10 temperature sensor has quite a wide spread, these * parameters are based on the averaging of the calibration * results of 4 completely different boards, with a spread of * temp_step from 0.096 - 0.170 and temp_offset from 176 - 331. */ ts->temp_offset = 257000; ts->temp_step = 133; } else { /* * The user manuals do not contain the formula for calculating * the temperature. The formula used here is from the AXP209, * which is designed by X-Powers, an affiliate of Allwinner: * * temperature (C) = (value * 0.1) - 144.7 * * Allwinner does not have any documentation whatsoever for * this hardware. Moreover, it is claimed that the sensor * is inaccurate and cannot work properly. */ ts->temp_offset = 144700; ts->temp_step = 100; } ts_attached = of_property_read_bool(np, "allwinner,ts-attached"); if (ts_attached) { ts->input = devm_input_allocate_device(dev); if (!ts->input) return -ENOMEM; ts->input->name = pdev->name; ts->input->phys = "sun4i_ts/input0"; ts->input->open = sun4i_ts_open; ts->input->close = sun4i_ts_close; ts->input->id.bustype = BUS_HOST; ts->input->id.vendor = 0x0001; ts->input->id.product = 0x0001; ts->input->id.version = 0x0100; ts->input->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS); __set_bit(BTN_TOUCH, ts->input->keybit); input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0); input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0); input_set_drvdata(ts->input, ts); } ts->base = devm_ioremap_resource(dev, platform_get_resource(pdev, IORESOURCE_MEM, 0)); if (IS_ERR(ts->base)) return PTR_ERR(ts->base); ts->irq = platform_get_irq(pdev, 0); error = devm_request_irq(dev, ts->irq, sun4i_ts_irq, 0, "sun4i-ts", ts); if (error) return error; /* * Select HOSC clk, clkin = clk / 6, adc samplefreq = clkin / 8192, * t_acq = clkin / (16 * 64) */ writel(ADC_CLK_SEL(0) | ADC_CLK_DIV(2) | FS_DIV(7) | T_ACQ(63), ts->base + TP_CTRL0); /* * tp_sensitive_adjust is an optional property * tp_mode = 0 : only x and y coordinates, as we don't use dual touch */ of_property_read_u32(np, "allwinner,tp-sensitive-adjust", &tp_sensitive_adjust); writel(TP_SENSITIVE_ADJUST(tp_sensitive_adjust) | TP_MODE_SELECT(0), ts->base + TP_CTRL2); /* * Enable median and averaging filter, optional property for * filter type. */ of_property_read_u32(np, "allwinner,filter-type", &filter_type); writel(FILTER_EN(1) | FILTER_TYPE(filter_type), ts->base + TP_CTRL3); /* Enable temperature measurement, period 1953 (2 seconds) */ writel(TEMP_ENABLE(1) | TEMP_PERIOD(1953), ts->base + TP_TPR); /* * Set stylus up debounce to aprox 10 ms, enable debounce, and * finally enable tp mode. */ reg = STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1); if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) reg |= SUN6I_TP_MODE_EN(1); else reg |= TP_MODE_EN(1); writel(reg, ts->base + TP_CTRL1); /* * The thermal core does not register hwmon devices for DT-based * thermal zone sensors, such as this one. */ hwmon = devm_hwmon_device_register_with_groups(ts->dev, "sun4i_ts", ts, sun4i_ts_groups); if (IS_ERR(hwmon)) return PTR_ERR(hwmon); devm_thermal_zone_of_sensor_register(ts->dev, 0, ts, &sun4i_ts_tz_ops); writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC); if (ts_attached) { error = input_register_device(ts->input); if (error) { writel(0, ts->base + TP_INT_FIFOC); return error; } } platform_set_drvdata(pdev, ts); return 0; }
int port_filter_apply(port_t *port, int value) { int i, *tmp_values; int len = port->filter_len; if (len >= port->filter_width) { // TODO this could be managed as a ring buffer for better performance /* shift filter values to left one place, * making space for the new value */ for (i = 0; i < len - 1; i++) { port->filter_values[i] = port->filter_values[i + 1]; } port->filter_values[len - 1] = value; } else { port->filter_values[len = port->filter_len++] = value; } if (len < 2) { return value; /* not enough values for filtering */ } if (port->type == PORT_TYPE_NUMBER) { switch (FILTER_TYPE(port)) { case FILTER_TYPE_MEDIAN: { /* copy the values to a temporary buffer, so that they can be sorted out-of-place */ tmp_values = malloc(sizeof(int) * len); memcpy(tmp_values, port->filter_values, sizeof(int) * len); qsort(tmp_values, len, sizeof(int), compare_numbers); value = tmp_values[len / 2]; free(tmp_values); return value; } case FILTER_TYPE_AVERAGE: { int avg_value = 0; for (i = 0; i < len; i++) { avg_value += port->filter_values[i]; } return avg_value / len; } default: return value; } } else { /* assuming port->type == PORT_TYPE_BOOLEAN */ // TODO a better approach would be to select an evenly-distributed // set of values (e.g. equal to the debounce factor) /* simply choose whatever the majority says */ int counts[2] = {0 /* false */ , 0 /* true */}; for (i = 0; i < len; i++) { counts[port->filter_values[i] & 1]++; } return counts[1] >= counts[0]; } }
void port_load(port_t *port, char *data) { char *base_ptr = data + CONFIG_OFFS_PORT_BASE + CONFIG_PORT_SIZE * port->port_no; char *expr_ptr = data + CONFIG_OFFS_STR_BASE; /* description */ memcpy(port->description, base_ptr + CONFIG_OFFS_PORT_DESC, PORT_MAX_DESC_LEN); port->description[PORT_MAX_DESC_LEN - 1] = 0; /* unit */ memcpy(port->unit, base_ptr + CONFIG_OFFS_PORT_UNIT, PORT_MAX_UNIT_LEN); port->unit[PORT_MAX_UNIT_LEN - 1] = 0; /* flags */ memcpy(&port->flags, base_ptr + CONFIG_OFFS_PORT_FLAGS, 4); /* value */ memcpy(&port->value, base_ptr + CONFIG_OFFS_PORT_VALUE, 4); /* filter */ port->filter_values = NULL; port->filter_width = 0; if (!IS_OUTPUT(port)) { memcpy(&port->filter_width, base_ptr + CONFIG_OFFS_PORT_FWIDTH, 4); if (!port->filter_width) { port->filter_width = 2; } port_filter_set(port, FILTER_TYPE(port), port->filter_width); } /* custom data */ memcpy(port->data, base_ptr + CONFIG_OFFS_PORT_DATA, PORT_CUSTOM_DATA_LEN); int expr_offs; int len; port->sexpr = NULL; port->expr = NULL; port->stransform_write = NULL; port->transform_write = NULL; port->stransform_read = NULL; port->transform_read = NULL; if (IS_OUTPUT(port)) { /* value expression */ memcpy(&expr_offs, base_ptr + CONFIG_OFFS_PORT_EXPR, 4); if (expr_offs) { len = strlen(expr_ptr + expr_offs); port->sexpr = malloc(len + 1); strcpy(port->sexpr, expr_ptr + expr_offs); /* the expression will be parsed later, * after all ports will have been loaded */ } /* write transform */ memcpy(&expr_offs, base_ptr + CONFIG_OFFS_PORT_TRANS_W, 4); if (expr_offs) { len = strlen(expr_ptr + expr_offs); port->stransform_write = malloc(len + 1); strcpy(port->stransform_write, expr_ptr + expr_offs); /* the expression will be parsed later, * after all ports will have been loaded */ } } /* read transform */ memcpy(&expr_offs, base_ptr + CONFIG_OFFS_PORT_TRANS_R, 4); if (expr_offs) { len = strlen(expr_ptr + expr_offs); port->stransform_read = malloc(len + 1); strcpy(port->stransform_read, expr_ptr + expr_offs); /* the expression will be parsed later, * after all ports will have been loaded */ } DEBUG("port %s loaded: description=\"%s\", unit=\"%s\", expression=\"%s\", transform_w=\"%s\", transform_r=\"%s\", flags=0x%08X", port->id, port->description, port->unit, port->sexpr ? port->sexpr : "", port->stransform_write ? port->stransform_write : "", port->stransform_read ? port->stransform_read : "", port->flags); /* sequence */ port->sequence_pos = -1; if (port->configure) { DEBUG("configuring port %s", port->id); port->configure(port); } if (IS_OUTPUT(port)) { if (IS_PERSISTED(port)) { port->value = port->value && 1; DEBUG("setting port %s to persisted value %d", port->id, port->value); } else { port->value = IS_PULL_UP(port) && 1; DEBUG("setting port %s to pull value %d", port->id, port->value); } port_set_value(port, port->value); } else { /* input */ int value = port->read_value(port); if (value != UNDEFINED_VALUE) { port->value = value; if (port->transform_read) { port->value = expr_eval(port->transform_read); } if (FILTER_TYPE(port) && !IS_OUTPUT(port)) { port->value = port_filter_apply(port, port->value); } DEBUG("initialized port %s with read value %d", port->id, port->value && 1); } else { port->value = 0; DEBUG("initialized port %s with value 0", port->id); } } }
static int sun4i_ts_probe(struct platform_device *pdev) { struct sun4i_ts_data *ts; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct device *hwmon; int error; bool ts_attached; ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL); if (!ts) return -ENOMEM; ts->dev = dev; ts->ignore_fifo_data = true; ts->temp_data = -1; ts_attached = of_property_read_bool(np, "allwinner,ts-attached"); if (ts_attached) { ts->input = devm_input_allocate_device(dev); if (!ts->input) return -ENOMEM; ts->input->name = pdev->name; ts->input->phys = "sun4i_ts/input0"; ts->input->open = sun4i_ts_open; ts->input->close = sun4i_ts_close; ts->input->id.bustype = BUS_HOST; ts->input->id.vendor = 0x0001; ts->input->id.product = 0x0001; ts->input->id.version = 0x0100; ts->input->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS); __set_bit(BTN_TOUCH, ts->input->keybit); input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0); input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0); input_set_drvdata(ts->input, ts); } ts->base = devm_ioremap_resource(dev, platform_get_resource(pdev, IORESOURCE_MEM, 0)); if (IS_ERR(ts->base)) return PTR_ERR(ts->base); ts->irq = platform_get_irq(pdev, 0); error = devm_request_irq(dev, ts->irq, sun4i_ts_irq, 0, "sun4i-ts", ts); if (error) return error; /* * Select HOSC clk, clkin = clk / 6, adc samplefreq = clkin / 8192, * t_acq = clkin / (16 * 64) */ writel(ADC_CLK_SEL(0) | ADC_CLK_DIV(2) | FS_DIV(7) | T_ACQ(63), ts->base + TP_CTRL0); /* * sensitive_adjust = 15 : max, which is not all that sensitive, * tp_mode = 0 : only x and y coordinates, as we don't use dual touch */ writel(TP_SENSITIVE_ADJUST(15) | TP_MODE_SELECT(0), ts->base + TP_CTRL2); /* Enable median filter, type 1 : 5/3 */ writel(FILTER_EN(1) | FILTER_TYPE(1), ts->base + TP_CTRL3); /* Enable temperature measurement, period 1953 (2 seconds) */ writel(TEMP_ENABLE(1) | TEMP_PERIOD(1953), ts->base + TP_TPR); /* * Set stylus up debounce to aprox 10 ms, enable debounce, and * finally enable tp mode. */ writel(STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1) | TP_MODE_EN(1), ts->base + TP_CTRL1); hwmon = devm_hwmon_device_register_with_groups(ts->dev, "sun4i_ts", ts, sun4i_ts_groups); if (IS_ERR(hwmon)) return PTR_ERR(hwmon); writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC); if (ts_attached) { error = input_register_device(ts->input); if (error) { writel(0, ts->base + TP_INT_FIFOC); return error; } } platform_set_drvdata(pdev, ts); return 0; }