i2c设备驱动框架模型实例

来源:互联网 发布:securecrt mac 破解 编辑:程序博客网 时间:2024/05/17 22:17

微笑驱动层:

#include <linux/kernel.h>#include <linux/module.h>#include <linux/i2c.h>#include <linux/cdev.h>#include <linux/slab.h>#include <linux/fs.h>#include <linux/delay.h>#include <asm/uaccess.h>#include "mpu6050.h"MODULE_LICENSE("GPL");#define SMPLRT_DIV0x19#define CONFIG0x1A#define GYRO_CONFIG0x1B#define ACCEL_CONFIG0x1C#define ACCEL_XOUT_H0x3B#define ACCEL_XOUT_L0x3C#define ACCEL_YOUT_H0x3D#define ACCEL_YOUT_L0x3E#define ACCEL_ZOUT_H0x3F#define ACCEL_ZOUT_L0x40#define TEMP_OUT_H0x41#define TEMP_OUT_L0x42#define GYRO_XOUT_H0x43#define GYRO_XOUT_L0x44#define GYRO_YOUT_H0x45#define GYRO_YOUT_L0x46#define GYRO_ZOUT_H0x47#define GYRO_ZOUT_L0x48#define PWR_MGMT_10x6B#define MPU6050_MAJOR 500#define MPU6050_MINOR 0/** * struct i2c_client - represent an I2C slave device * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address; *I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking * @addr: Address used on the I2C bus connected to the parent adapter. * @name: Indicates the type of the device, usually a chip name that's *generic enough to hide second-sourcing and compatible revisions. * @adapter: manages the bus segment hosting this I2C device * @dev: Driver model device node for the slave. * @irq: indicates the IRQ generated by this device (if any) * @detected: member of an i2c_driver.clients list or i2c-core's *userspace_devices list * @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter *calls it to pass on slave events to the slave driver. * * An i2c_client identifies a single device (i.e. chip) connected to an * i2c bus. The behaviour exposed to Linux is defined by the driver * managing the device. */ /** * struct i2c_client { *unsigned short flags;// div., see below *unsigned short addr;// chip address - NOTE: 7bit *// addresses are stored in the *// _LOWER_ 7 bits *char name[I2C_NAME_SIZE]; *struct i2c_adapter *adapter;// the adapter we sit on(坐落,依附于) *struct device dev;// the device structure(i2c设备是一个设备-继承) *int irq;// irq issued by device设备对应的中断 *struct list_head detected; *  #if IS_ENABLED(CONFIG_I2C_SLAVE) *i2c_slave_cb_t slave_cb;// callback for slave mode *  #endif * };*/struct mpu6050_device {struct cdev cdev;// i2c客户端// represent an I2C slave devicestruct i2c_client *client;};struct mpu6050_device *mpu6050; /** *参数: *@client 代表具体的i2c设备 *  @reg 具体的寄存器值 */ /** * struct i2c_msg - an I2C transaction segment beginning with START * @addr: Slave address, either seven or ten bits.  When this is a ten *bit address, I2C_M_TEN must be set in @flags and the adapter *must support I2C_FUNC_10BIT_ADDR. * @flags: I2C_M_RD is handled by all adapters.  No other flags may be *provided unless the adapter exported the relevant I2C_FUNC_* *flags through i2c_check_functionality(). * @len: Number of data bytes in @buf being read from or written to the *I2C slave address.  For read transactions where I2C_M_RECV_LEN *is set, the caller guarantees that this buffer can hold up to *32 bytes in addition to the initial length byte sent by the *slave (plus, if used, the SMBus PEC); and this value will be *incremented by the number of block data bytes received. * @buf: The buffer into which data is read, or from which it's written. * * An i2c_msg is the low level representation of one segment of an I2C * transaction.  It is visible to drivers in the @i2c_transfer() procedure, * to userspace from i2c-dev, and to I2C adapter drivers through the * @i2c_adapter.@master_xfer() method. * * Except when I2C "protocol mangling" is used, all I2C adapters implement * the standard rules for I2C transactions.  Each transaction begins with a * START.  That is followed by the slave address, and a bit encoding read * versus write.  Then follow all the data bytes, possibly including a byte * with SMBus PEC.  The transfer terminates with a NAK, or when all those * bytes have been transferred and ACKed.  If this is the last message in a * group, it is followed by a STOP.  Otherwise it is followed by the next * @i2c_msg transaction segment, beginning with a (repeated) START. * * Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then * passing certain @flags may have changed those standard protocol behaviors. * Those flags are only for use with broken/nonconforming slaves, and with * adapters which are known to support the specific mangling options they * need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR). */ /*struct i2c_msg {__u16 addr;// slave address 从设备地址__u16 flags; // 标志 下面宏所示#define I2C_M_RD0x0001// read data, from slave to master 主读从// I2C_M_RD is guaranteed to be 0x0001! #define I2C_M_TEN0x0010// this is a ten bit chip address 10位的从机地址#define I2C_M_RECV_LEN0x0400// length will be first received byte 首次收到的数据字节长度#define I2C_M_NO_RD_ACK0x0800// if I2C_FUNC_PROTOCOL_MANGLING 无读应答#define I2C_M_IGNORE_NAK0x1000// if I2C_FUNC_PROTOCOL_MANGLING 忽略NAK#define I2C_M_REV_DIR_ADDR0x2000// if I2C_FUNC_PROTOCOL_MANGLING 接受方向地址#define I2C_M_NOSTART0x4000// if I2C_FUNC_NOSTART 无起始信号#define I2C_M_STOP0x8000// if I2C_FUNC_PROTOCOL_MANGLING 停止信号__u16 len;// msg length 消息长度__u8 *buf;// pointer to msg data 存放消息的地址};*/static int mpu6050_read_byte(struct i2c_client *client, unsigned char reg){int ret;char txbuf[1] = { reg };char rxbuf[1];// 消息帧struct i2c_msg msg[2] = {{client->addr, 0, 1, txbuf}, // 主发送{client->addr, I2C_M_RD, 1, rxbuf} // 主接收};/** * i2c_transfer - execute a single or combined I2C message * @adap: Handle to I2C bus * @msgs: One or more messages to execute before STOP is issued to *terminate the operation; each message begins with a START. * @num: Number of messages to be executed. * * Returns negative errno, else the number of messages executed. * * Note that there is no requirement that each message be sent to * the same slave address, although that is the most common model. *//*  * i2c_adapter is the structure used to identify a physical i2c bus along  * with the access algorithms necessary to access it.  * i2c_adapter 是一个类,这个类用来确定一个物理的i2c总线,并且其中包含了访  *问该物理i2c总线adapter的访问接口机制方法*//*struct i2c_adapter {struct module *owner;unsigned int class;  // classes to allow probing for const struct i2c_algorithm *algo; //the algorithm to access the bus 总线的访问机制方法void *algo_data;/* data fields that are valid for all devicesconst struct i2c_lock_operations *lock_ops;struct rt_mutex bus_lock;struct rt_mutex mux_lock;int timeout;//in jiffies int retries;struct device dev;// the adapter device 适配器设备这里,adapter直接被抽象为设备int nr;char name[48];struct completion dev_released;struct mutex userspace_clients_lock;struct list_head userspace_clients;struct i2c_bus_recovery_info *bus_recovery_info;const struct i2c_adapter_quirks *quirks;struct irq_domain *host_notify_domain;};*/ /** * struct i2c_algorithm - represent I2C transfer method * @master_xfer: Issue a set of i2c transactions to the given I2C adapter *   defined by the msgs array, with num messages available to transfer via *   the adapter specified by adap. * @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this *   is not present, then the bus layer will try and convert the SMBus calls *   into I2C transfers instead. * @functionality: Return the flags that this algorithm/adapter pair supports *   from the I2C_FUNC_* flags. * @reg_slave: Register given client to I2C slave mode of this adapter * @unreg_slave: Unregister given client from I2C slave mode of this adapter * * The following structs are for those who like to implement new bus drivers: * i2c_algorithm is the interface to a class of hardware solutions which can * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 * to name two of the most common. * * The return codes from the @master_xfer field should indicate the type of * error code that occurred during the transfer, as documented in the kernel * Documentation file Documentation/i2c/fault-codes. *//*struct i2c_algorithm {// If an adapter algorithm can't do I2C-level access, set master_xfer// to NULL. If an adapter algorithm can do SMBus access, set// smbus_xfer. If set to NULL, the SMBus protocol is simulated// using common I2C messages // master_xfer should return the number of messages successfully// processed, or a negative value on error int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,   int num);int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,   unsigned short flags, char read_write,   u8 command, int size, union i2c_smbus_data *data);// To determine what the adapter supports u32 (*functionality) (struct i2c_adapter *);#if IS_ENABLED(CONFIG_I2C_SLAVE)int (*reg_slave)(struct i2c_client *client);int (*unreg_slave)(struct i2c_client *client);#endif};*/// 发送消息 --> 调用 i2c-core.c 的 int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)// 调用--> adap->algo->master_xfer(adap, msgs, num);// --> 调用板级支持的samsung的i2c控制器硬件实现发送接收数据ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); // i2c-core.cif (ret < 0) {printk("ret = %d\n", ret);return ret;}return rxbuf[0];}// reg: 要操作的寄存器// val: 发送给寄存器的值static int mpu6050_write_byte(struct i2c_client *client, unsigned char reg, unsigned char val){char txbuf[2] = {reg, val};struct i2c_msg msg[2] = {{client->addr, 0, 2, txbuf}, 主发送,从接收};i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));return 0;}static int mpu6050_open(struct inode *inode, struct file *file) {return 0;}static int mpu6050_release(struct inode *inode, struct file *file) {return 0;}static long mpu6050_ioctl(struct file *file, unsigned int cmd, unsigned long arg){union mpu6050_data data;struct i2c_client *client = mpu6050->client;switch(cmd) {case GET_ACCEL:data.accel.x = mpu6050_read_byte(client, ACCEL_XOUT_L);data.accel.x |= mpu6050_read_byte(client, ACCEL_XOUT_H) << 8;data.accel.y = mpu6050_read_byte(client, ACCEL_YOUT_L);data.accel.y |= mpu6050_read_byte(client, ACCEL_YOUT_H) << 8;data.accel.z = mpu6050_read_byte(client, ACCEL_ZOUT_L);data.accel.z |= mpu6050_read_byte(client, ACCEL_ZOUT_H) << 8;break;case GET_GYRO:data.gyro.x = mpu6050_read_byte(client, GYRO_XOUT_L);data.gyro.x |= mpu6050_read_byte(client, GYRO_XOUT_H) << 8;data.gyro.y = mpu6050_read_byte(client, GYRO_YOUT_L);data.gyro.y |= mpu6050_read_byte(client, GYRO_YOUT_H) << 8;data.gyro.z = mpu6050_read_byte(client, GYRO_ZOUT_L);data.gyro.z |= mpu6050_read_byte(client, GYRO_ZOUT_H) << 8;break;case GET_TEMP:data.temp = mpu6050_read_byte(client, TEMP_OUT_L);data.temp |= mpu6050_read_byte(client, TEMP_OUT_H) << 8;break;default:printk("invalid argument\n");return -EINVAL;}if (copy_to_user((void *)arg, &data, sizeof(data)))return -EFAULT;return sizeof(data);}struct file_operations mpu6050_fops = {.owner = THIS_MODULE,.open= mpu6050_open,.release = mpu6050_release,.unlocked_ioctl = mpu6050_ioctl,};static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id){int ret;dev_t devno = MKDEV(MPU6050_MAJOR, MPU6050_MINOR);printk("match OK!\n");mpu6050 = kzalloc(sizeof(*mpu6050), GFP_KERNEL);if (mpu6050 == NULL) {return -ENOMEM;}mpu6050->client = client;ret = register_chrdev_region(devno, 1, "mpu6050");if (ret < 0) {printk("failed to register char device region!\n");goto err1;}cdev_init(&mpu6050->cdev, &mpu6050_fops);mpu6050->cdev.owner = THIS_MODULE;ret = cdev_add(&mpu6050->cdev, devno, 1);if (ret < 0) {printk("failed to add device\n");goto err2;}mpu6050_write_byte(client, PWR_MGMT_1, 0x00);mpu6050_write_byte(client, SMPLRT_DIV, 0x07);mpu6050_write_byte(client, CONFIG, 0x06);mpu6050_write_byte(client, GYRO_CONFIG, 0xF8);mpu6050_write_byte(client, ACCEL_CONFIG, 0x19);return 0;err2:unregister_chrdev_region(devno, 1);err1:kfree(mpu6050);return ret;}static int mpu6050_remove(struct i2c_client *client){dev_t devno = MKDEV(MPU6050_MAJOR, MPU6050_MINOR);cdev_del(&mpu6050->cdev);unregister_chrdev_region(devno, 1);kfree(mpu6050);return 0;}static const struct i2c_device_id mpu6050_id[] = {{ .name = "mpu6050"},{}}; static struct of_device_id mpu6050_dt_match[] = {{.compatible = "invensense,mpu6050" },{/*northing to be done*/},};struct i2c_driver mpu6050_driver = {.driver = {.name = "mpu6050",.owner = THIS_MODULE,.of_match_table = of_match_ptr(mpu6050_dt_match),},.probe = mpu6050_probe,.remove = mpu6050_remove,.id_table = mpu6050_id,};module_i2c_driver(mpu6050_driver);

微笑应用层:

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#include <sys/ioctl.h>#include "mpu6050.h"int main(int argc, const char *argv[]){int fd;union mpu6050_data data; fd = open("/dev/mpu6050", O_RDWR);if (fd < 0) {perror("open");exit(1);}while(1) {ioctl(fd, GET_ACCEL, &data);printf("\racceleration data: x = %04x, y = %04x, z = %04x", data.accel.x, data.accel.y, data.accel.z);}close(fd);return 0;}


0 0
原创粉丝点击