linux iio 驱动

   

    IIO 全称是 Industrial I/O ,是 linux内核对ADC,DAC类型传感器设备的抽象, 目的是为上层提供统一的接口

主要功能包括读取传感器的值以及设置控制参数.

struct iio_dev 结构体代表一个iio设备.

/**

 * struct iio_dev - industrial I/O device

 * @id:         [INTERN] used to identify device internally

 * @modes:      [DRIVER] operating modes supported by device

 * @currentmode:    [DRIVER] current operating mode

 * @dev:        [DRIVER] device structure, should be assigned a parent

 *          and owner

 * @event_interface:    [INTERN] event chrdevs associated with interrupt lines

 * @buffer:     [DRIVER] any buffer present

 * @buffer_list:    [INTERN] list of all buffers currently attached

 * @scan_bytes:     [INTERN] num bytes captured to be fed to buffer demux

 * @mlock:      [DRIVER] lock used to prevent simultaneous device state

 *          changes

 * @available_scan_masks: [DRIVER] optional array of allowed bitmasks

 * @masklength:     [INTERN] the length of the mask established from

 *          channels

 * @active_scan_mask:   [INTERN] union of all scan masks requested by buffers

 * @scan_timestamp: [INTERN] set if any buffers have requested timestamp

 * @scan_index_timestamp:[INTERN] cache of the index to the timestamp

 * @trig:       [INTERN] current device trigger (buffer modes)

 * @trig_readonly:  [INTERN] mark the current trigger immutable

 * @pollfunc:       [DRIVER] function run on trigger being received

 * @pollfunc_event: [DRIVER] function run on events trigger being received

 * @channels:       [DRIVER] channel specification structure table

 * @num_channels:   [DRIVER] number of channels specified in @channels.

 * @channel_attr_list:  [INTERN] keep track of automatically created channel

 *          attributes

 * @chan_attr_group:    [INTERN] group for all attrs in base directory

 * @name:       [DRIVER] name of the device.

 * @info:       [DRIVER] callbacks and constant info from driver

 * @clock_id:       [INTERN] timestamping clock posix identifier

 * @info_exist_lock:    [INTERN] lock to prevent use during removal

 * @setup_ops:      [DRIVER] callbacks to call before and after buffer

 *          enable/disable

 * @chrdev:     [INTERN] associated character device

 * @groups:     [INTERN] attribute groups

 * @groupcounter:   [INTERN] index of next attribute group

 * @flags:      [INTERN] file ops related flags including busy flag.

 * @debugfs_dentry: [INTERN] device specific debugfs dentry.

 * @cached_reg_addr:    [INTERN] cached register address for debugfs reads.

 */

struct iio_dev {

    int             id;

    int             modes;

    int             currentmode;

    struct device           dev;

    struct iio_event_interface  *event_interface;

    struct iio_buffer       *buffer;

    struct list_head        buffer_list;

    int             scan_bytes;

    struct mutex            mlock;

    /*如果使能了触发缓冲区,该变量是可用通道的掩码位,仅仅是可用,是否开启是active_scan_mask指定*/

    const unsigned long     *available_scan_masks;

    unsigned            masklength;   /*掩码位长度*/

    /*开启那些通道,这些通道触发时会把水写到触发缓冲区.*/

    const unsigned long     *active_scan_mask;

    /*set if any buffers have requested timestamp*/

    bool                scan_timestamp;

    /*时间戳的编号*/

    unsigned            scan_index_timestamp;

    /*开启触发的情况下,指定该设备使用的触发器*/

    struct iio_trigger      *trig;

    bool                trig_readonly; //标记触发器不可变

    struct iio_poll_func        *pollfunc;  //收到触发事件后执行的函数

    struct iio_poll_func        *pollfunc_event; //收到 event 触发事件后执行的函数

    /*重点,设备用的通道和通道数*/

    struct iio_chan_spec const  *channels;  

    int             num_channels;

    /*内部管理使用.*/

    struct list_head        channel_attr_list;

    struct attribute_group      chan_attr_group; //根目录的sysfs文件节点.

    const char          *name;  //设备名

    const struct iio_info       *info;   // 核心, 驱动要实现的接口,编程重点.

    clockid_t           clock_id;  //时钟id, 这个决定了时间戳的时间类型

    struct mutex            info_exist_lock;

    const struct iio_buffer_setup_ops   *setup_ops; /*如果使能了iio buffer, 这里指定buffer的一些操作函数*/

    struct cdev         chrdev;

#define IIO_MAX_GROUPS 6

    const struct attribute_group    *groups[IIO_MAX_GROUPS + 1];

    int             groupcounter;

    unsigned long           flags;

#if defined(CONFIG_DEBUG_FS)

    struct dentry           *debugfs_dentry;

    unsigned            cached_reg_addr;

#endif

};

部分参数介绍:

@modes: 设备支持的工作模式,有如下工作模式

/* Device operating modes */

#define INDIO_DIRECT_MODE       0x01

#define INDIO_BUFFER_TRIGGERED      0x02

#define INDIO_BUFFER_SOFTWARE       0x04

#define INDIO_BUFFER_HARDWARE       0x08

#define INDIO_EVENT_TRIGGERED       0x10

一般不使用buffer和触发的情况选择INDIO_DIRECT_MODE普通模式即可.

@name: 设备的名字;

@dev: device structure, should be assigned a parent and owner, 必须指定父设备

@channels: 核心参数,描述通道,包括通道的功能,通道的设置参数.不同设备支持的功能就体现在这里.

@num_channels: 通道数量.

@info: 核心接口,channels 参数描述了通道支持的功能,这些功能的底层实现就定义在info指定的操作函数中,这是驱动实现的重点. channel和info是iio子系统两个核心结构体.



 

struct iio_chan_spec结构体

/**

 * struct iio_chan_spec - specification of a single channel

 * @type:       What type of measurement is the channel making.

 * @channel:        What number do we wish to assign the channel.

 * @channel2:       If there is a second number for a differential

 *          channel then this is it. If modified is set then the

 *          value here specifies the modifier.

 * @address:        Driver specific identifier.

 * @scan_index:     Monotonic index to give ordering in scans when read

 *          from a buffer.

 * @scan_type:      sign:       's' or 'u' to specify signed or unsigned

 *          realbits:   Number of valid bits of data

 *          storagebits:    Realbits + padding

 *          shift:      Shift right by this before masking out

 *                  realbits.

 *          repeat:     Number of times real/storage bits

 *                  repeats. When the repeat element is

 *                  more than 1, then the type element in

 *                  sysfs will show a repeat value.

 *                  Otherwise, the number of repetitions is

 *                  omitted.

 *          endianness: little or big endian

 * @info_mask_separate: What information is to be exported that is specific to

 *          this channel.

 * @info_mask_separate_available: What availability information is to be

 *          exported that is specific to this channel.

 * @info_mask_shared_by_type: What information is to be exported that is shared

 *          by all channels of the same type.

 * @info_mask_shared_by_type_available: What availability information is to be

 *          exported that is shared by all channels of the same

 *          type.

 * @info_mask_shared_by_dir: What information is to be exported that is shared

 *          by all channels of the same direction.

 * @info_mask_shared_by_dir_available: What availability information is to be

 *          exported that is shared by all channels of the same

 *          direction.

 * @info_mask_shared_by_all: What information is to be exported that is shared

 *          by all channels.

 * @info_mask_shared_by_all_available: What availability information is to be

 *          exported that is shared by all channels.

 * @event_spec:     Array of events which should be registered for this

 *          channel.

 * @num_event_specs:    Size of the event_spec array.

 * @ext_info:       Array of extended info attributes for this channel.

 *          The array is NULL terminated, the last element should

 *          have its name field set to NULL.

 * @extend_name:    Allows labeling of channel attributes with an

 *          informative name. Note this has no effect codes etc,

 *          unlike modifiers.

 * @datasheet_name: A name used in in-kernel mapping of channels. It should

 *          correspond to the first name that the channel is referred

 *          to by in the datasheet (e.g. IND), or the nearest

 *          possible compound name (e.g. IND-INC).

 * @modified:       Does a modifier apply to this channel. What these are

 *          depends on the channel type.  Modifier is set in

 *          channel2. Examples are IIO_MOD_X for axial sensors about

 *          the 'x' axis.

 * @indexed:        Specify the channel has a numerical index. If not,

 *          the channel index number will be suppressed for sysfs

 *          attributes but not for event codes.

 * @output:     Channel is output.

 * @differential:   Channel is differential.

 */

struct iio_chan_spec {

    enum iio_chan_type  type;

    int         channel;

    int         channel2;

    unsigned long       address;

    int         scan_index;

    struct {

        char    sign;

        u8  realbits;

        u8  storagebits;

        u8  shift;

        u8  repeat;

        enum iio_endian endianness;

    } scan_type;

    long            info_mask_separate;

    long            info_mask_separate_available;

    long            info_mask_shared_by_type;

    long            info_mask_shared_by_type_available;

    long            info_mask_shared_by_dir;

    long            info_mask_shared_by_dir_available;

    long            info_mask_shared_by_all;

    long            info_mask_shared_by_all_available;

    const struct iio_event_spec *event_spec;

    unsigned int        num_event_specs;

    const struct iio_chan_spec_ext_info *ext_info;

    const char      *extend_name;

    const char      *datasheet_name;

    unsigned        modified:1;

    unsigned        indexed:1;

    unsigned        output:1;

    unsigned        differential:1;

};

@info_mask_separate 参数指定了通道支持的功能,如下:

enum iio_chan_info_enum {

    IIO_CHAN_INFO_RAW = 0,

    IIO_CHAN_INFO_PROCESSED,

    IIO_CHAN_INFO_SCALE,

    IIO_CHAN_INFO_OFFSET,

    IIO_CHAN_INFO_CALIBSCALE,

    IIO_CHAN_INFO_CALIBBIAS,

    IIO_CHAN_INFO_PEAK,

    IIO_CHAN_INFO_PEAK_SCALE,

    IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW,

    IIO_CHAN_INFO_AVERAGE_RAW,

    IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY,

    IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY,

    IIO_CHAN_INFO_SAMP_FREQ,

    IIO_CHAN_INFO_FREQUENCY,

    IIO_CHAN_INFO_PHASE,

    IIO_CHAN_INFO_HARDWAREGAIN,

    IIO_CHAN_INFO_HYSTERESIS,

    IIO_CHAN_INFO_INT_TIME,

    IIO_CHAN_INFO_ENABLE,

    IIO_CHAN_INFO_CALIBHEIGHT,

    IIO_CHAN_INFO_CALIBWEIGHT,

    IIO_CHAN_INFO_DEBOUNCE_COUNT,

    IIO_CHAN_INFO_DEBOUNCE_TIME,

    IIO_CHAN_INFO_CALIBEMISSIVITY,

    IIO_CHAN_INFO_OVERSAMPLING_RATIO,

};

一个通道支持那些功能这些功能就要在struct iio_dev->info 指定的函数中实现.

struct iio_info结构体

在 iio_chan_spec->info_mask_separate 通道描述中支持的那些功能就需要再这里一一实现

主要的实现函数是 read_raw()函数和write_raw()函数.

/**

 * struct iio_info - constant information about device

 * @driver_module:  module structure used to ensure correct

 *          ownership of chrdevs etc

 * @event_attrs:    event control attributes

 * @attrs:      general purpose device attributes

 * @read_raw:       function to request a value from the device.

 *          mask specifies which value. Note 0 means a reading of

 *          the channel in question.  Return value will specify the

 *          type of value returned by the device. val and val2 will

 *          contain the elements making up the returned value.

 * @read_raw_multi: function to return values from the device.

 *          mask specifies which value. Note 0 means a reading of

 *          the channel in question.  Return value will specify the

 *          type of value returned by the device. vals pointer

 *          contain the elements making up the returned value.

 *          max_len specifies maximum number of elements

 *          vals pointer can contain. val_len is used to return

 *          length of valid elements in vals.

 * @read_avail:     function to return the available values from the device.

 *          mask specifies which value. Note 0 means the available

 *          values for the channel in question.  Return value

 *          specifies if a IIO_AVAIL_LIST or a IIO_AVAIL_RANGE is

 *          returned in vals. The type of the vals are returned in

 *          type and the number of vals is returned in length. For

 *          ranges, there are always three vals returned; min, step

 *          and max. For lists, all possible values are enumerated.

 * @write_raw:      function to write a value to the device.

 *          Parameters are the same as for read_raw.

 * @write_raw_get_fmt:  callback function to query the expected

 *          format/precision. If not set by the driver, write_raw

 *          returns IIO_VAL_INT_PLUS_MICRO.

 * @read_event_config:  find out if the event is enabled.

 * @write_event_config: set if the event is enabled.

 * @read_event_value:   read a configuration value associated with the event.

 * @write_event_value:  write a configuration value for the event.

 * @validate_trigger:   function to validate the trigger when the

 *          current trigger gets changed.

 * @update_scan_mode:   function to configure device and scan buffer when

 *          channels have changed

 * @debugfs_reg_access: function to read or write register value of device

 * @of_xlate:       function pointer to obtain channel specifier index.

 *          When #iio-cells is greater than '0', the driver could

 *          provide a custom of_xlate function that reads the

 *          *args* and returns the appropriate index in registered

 *          IIO channels array.

 * @hwfifo_set_watermark: function pointer to set the current hardware

 *          fifo watermark level; see hwfifo_* entries in

 *          Documentation/ABI/testing/sysfs-bus-iio for details on

 *          how the hardware fifo operates

 * @hwfifo_flush_to_buffer: function pointer to flush the samples stored

 *          in the hardware fifo to the device buffer. The driver

 *          should not flush more than count samples. The function

 *          must return the number of samples flushed, 0 if no

 *          samples were flushed or a negative integer if no samples

 *          were flushed and there was an error.

 **/

struct iio_info {

    struct module           *driver_module;

    const struct attribute_group    *event_attrs;

    const struct attribute_group    *attrs;

    int (*read_raw)(struct iio_dev *indio_dev,

            struct iio_chan_spec const *chan,

            int *val,

            int *val2,

            long mask);

    int (*read_raw_multi)(struct iio_dev *indio_dev,

            struct iio_chan_spec const *chan,

            int max_len,

            int *vals,

            int *val_len,

            long mask);

    int (*read_avail)(struct iio_dev *indio_dev,

              struct iio_chan_spec const *chan,

              const int **vals,

              int *type,

              int *length,

              long mask);

    int (*write_raw)(struct iio_dev *indio_dev,

             struct iio_chan_spec const *chan,

             int val,

             int val2,

             long mask);

    int (*write_raw_get_fmt)(struct iio_dev *indio_dev,

             struct iio_chan_spec const *chan,

             long mask);

    int (*read_event_config)(struct iio_dev *indio_dev,

                 const struct iio_chan_spec *chan,

                 enum iio_event_type type,

                 enum iio_event_direction dir);

    int (*write_event_config)(struct iio_dev *indio_dev,

                  const struct iio_chan_spec *chan,

                  enum iio_event_type type,

                  enum iio_event_direction dir,

                  int state);

    int (*read_event_value)(struct iio_dev *indio_dev,

                const struct iio_chan_spec *chan,

                enum iio_event_type type,

                enum iio_event_direction dir,

                enum iio_event_info info, int *val, int *val2);

    int (*write_event_value)(struct iio_dev *indio_dev,

                 const struct iio_chan_spec *chan,

                 enum iio_event_type type,

                 enum iio_event_direction dir,

                 enum iio_event_info info, int val, int val2);

    int (*validate_trigger)(struct iio_dev *indio_dev,

                struct iio_trigger *trig);

    int (*update_scan_mode)(struct iio_dev *indio_dev,

                const unsigned long *scan_mask);

    int (*debugfs_reg_access)(struct iio_dev *indio_dev,

                  unsigned reg, unsigned writeval,

                  unsigned *readval);

    int (*of_xlate)(struct iio_dev *indio_dev,

            const struct of_phandle_args *iiospec);

    int (*hwfifo_set_watermark)(struct iio_dev *indio_dev, unsigned val);

    int (*hwfifo_flush_to_buffer)(struct iio_dev *indio_dev,

                      unsigned count);

};

    int (*read_raw)(struct iio_dev *indio_dev,

            struct iio_chan_spec const *chan,

            int *val,

            int *val2,

            long mask);

函数参数:

@indio_dev , 要操作的dev,

@chan, 要操作的通道

@mask, 要读的是哪个功能,这和iio_chan_spec->info_mask_separate 指定的功能是一致的,即enum iio_chan_info_enum枚举

    类型指定的那些功能.

@int *val,

@int *val2,  这两个参数很重要, 这两个参数的含义和返回值类型有关,

函数返回值又如下值:

#define IIO_VAL_INT 1    //表示读的是一个整数,val2无效

#define IIO_VAL_INT_PLUS_MICRO 2  //val表示整数部分, val2表示小数部分*1000 000 , 后的整数部分

#define IIO_VAL_INT_PLUS_NANO 3   //同上,差异是 val2表示小数部分*1000 000 000

#define IIO_VAL_INT_PLUS_MICRO_DB 4 //未知

#define IIO_VAL_INT_MULTIPLE 5  //多值,这里用不到

#define IIO_VAL_FRACTIONAL 10  //分数值, 实际传递的值是 val/val2

#define IIO_VAL_FRACTIONAL_LOG2 11 //左移,实际传的值是val >> val2

上层读写通道值时根据返回值类型把数据重新转换一下.

下面是内核其中一个实现代码,可以看到和描述是一致的:

static int ads1015_read_raw(struct iio_dev *indio_dev,

                struct iio_chan_spec const *chan, int *val,

                int *val2, long mask)

{

    int ret, idx;

    struct ads1015_data *data = iio_priv(indio_dev);

    mutex_lock(&data->lock);

    switch (mask) {

    case IIO_CHAN_INFO_RAW: {

        int shift = chan->scan_type.shift;

        ret = iio_device_claim_direct_mode(indio_dev);

        if (ret)

            break;

        if (ads1015_event_channel_enabled(data) &&

                data->event_channel != chan->address) {

            ret = -EBUSY;

            goto release_direct;

        }

        ret = ads1015_set_power_state(data, true);

        if (ret < 0)

            goto release_direct;

        ret = ads1015_get_adc_result(data, chan->address, val);

        if (ret < 0) {

            ads1015_set_power_state(data, false);

            goto release_direct;

        }

        *val = sign_extend32(*val >> shift, 15 - shift);

        ret = ads1015_set_power_state(data, false);

        if (ret < 0)

            goto release_direct;

        ret = IIO_VAL_INT;

release_direct:

        iio_device_release_direct_mode(indio_dev);

        break;

    }

    case IIO_CHAN_INFO_SCALE:

        idx = data->channel_data[chan->address].pga;

        *val = ads1015_fullscale_range[idx];

        *val2 = chan->scan_type.realbits - 1;

        ret = IIO_VAL_FRACTIONAL_LOG2;

        break;

    case IIO_CHAN_INFO_SAMP_FREQ:

        idx = data->channel_data[chan->address].data_rate;

        *val = data->data_rate[idx];

        ret = IIO_VAL_INT;

        break;

    default:

        ret = -EINVAL;

        break;

    }

    mutex_unlock(&data->lock);

    return ret;

}

int (*write_raw)(struct iio_dev *indio_dev,

             struct iio_chan_spec const *chan,

             int val,

             int val2,

             long mask);

相对来说 write_raw 就简单很多,因为我们配置数据输出的地方比较少,很少写的很复杂同样以ads1015为例,

实现代码就比较简单

static int ads1015_write_raw(struct iio_dev *indio_dev,

                 struct iio_chan_spec const *chan, int val,

                 int val2, long mask)

{

    struct ads1015_data *data = iio_priv(indio_dev);

    int ret;

    mutex_lock(&data->lock);

    switch (mask) {

    case IIO_CHAN_INFO_SCALE:

        ret = ads1015_set_scale(data, chan, val, val2);

        break;

    case IIO_CHAN_INFO_SAMP_FREQ:

        ret = ads1015_set_data_rate(data, chan->address, val);

        break;

    default:

        ret = -EINVAL;

        break;

    }

    mutex_unlock(&data->lock);

    return ret;

}

基于IIO子系统的ADC驱动.

ADC驱动一般由芯片厂商编写.源码位于/dev/iio/xxx.c目录下. 我们如何使用这些ADC呢?

如果是在应用层访问,则直接按照规则访问/sys/class/iio/xxxx目录下对应的sysfs节点即可.

如果驱动中要读取一个ADC通道的值如何实现?

在设备树里添加通道:

    xxx {

        io-channels = <&adc 1>, <&adc 0>;

        io-channel-names = "adc_xxxx", "adc_nnnn";

    };

int  adc_value;

struct iio_channel * channles = iio_channel_get(data->dev, "adc_xxxx");

iio_read_channel_processed(channles,&adc_value);

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值