linux SPI驱动框架(二) -- 设备驱动
来源:互联网 发布:亚马逊订单管理 软件 编辑:程序博客网 时间:2024/05/22 06:23
转载请标明出处floater的csdn blog,http://blog.csdn.net/flaoter
之前文章linux SPI驱动框架(一)— 控制器驱动http://blog.csdn.net/flaoter/article/details/50001133对进行了介绍,本节内容对SPI从设备的设备驱动进行讲解。
设备驱动关注的结构体主要有两个,struct spi_device描述spi从设备,struct spi_driver是从设备的设备驱动。
struct spi_device { struct device dev; struct spi_master *master; u32 max_speed_hz; u8 chip_select; u8 bits_per_word; u16 mode;#define SPI_CPHA 0x01 /* clock phase */#define SPI_CPOL 0x02 /* clock polarity */#define SPI_MODE_0 (0|0) /* (original MicroWire) */#define SPI_MODE_1 (0|SPI_CPHA)#define SPI_MODE_2 (SPI_CPOL|0)#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)#define SPI_CS_HIGH 0x04 /* chipselect active high? */#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */#define SPI_3WIRE 0x10 /* SI/SO signals shared */#define SPI_LOOP 0x20 /* loopback mode */#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */#define SPI_READY 0x80 /* slave pulls low to pause */#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */#define SPI_RX_DUAL 0x400 /* receive with 2 wires */#define SPI_RX_QUAD 0x800 /* receive with 4 wires */ int irq; void *controller_state; void *controller_data; char modalias[SPI_NAME_SIZE]; int cs_gpio; /* chip select gpio */ /* * likely need more hooks for more protocol options affecting how * the controller talks to each chip, like: * - memory packing (12 bit samples into low bits, others zeroed) * - priority * - drop chipselect after each word * - chipselect delays * - ... */};struct spi_driver { const struct spi_device_id *id_table; int (*probe)(struct spi_device *spi); int (*remove)(struct spi_device *spi); void (*shutdown)(struct spi_device *spi); int (*suspend)(struct spi_device *spi, pm_message_t mesg); int (*resume)(struct spi_device *spi); struct device_driver driver;};
初始化函数如下,代码解释参见代码注释。此代码中包含了设备注册的代码,但是在dts普遍应用后,此种设备注册的方式已经被设备树的方式代替了,此处的设备注册的应用场景多出现在以模块方式编译的驱动设计中。因此请不要被设备注册产生混淆。
static int __init spidev_init(void){ int status; /* Claim our 256 reserved device numbers. Then register a class * that will key udev/mdev to add/remove /dev nodes. Last, register * the driver which manages those device numbers. */ BUILD_BUG_ON(N_SPI_MINORS > 256);//字符设备注册,主设备号是SPIDEV_MAJOR,设备名是"spi",文件操作ops是spidev_fops,注册后/proc/devices下有相关设备,但不会向udev发送uevent,如需要/dev下设备节,需要手动配置 status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops); if (status < 0) return status;//创建spidev class, /sys/class/spidev spidev_class = class_create(THIS_MODULE, "spidev"); if (IS_ERR(spidev_class)) { status = PTR_ERR(spidev_class); goto error_class; }//设备驱动spidev_spi_driver注册 status = spi_register_driver(&spidev_spi_driver); if (status < 0) goto error_register;//设备注册,一般是作为module driver的时候用这种注册方式,busnum, chipselect可作为MODULE_PARAM传入;//正常情况下,设备的注册会在控制器驱动spi_master注册过程中,对dts进行解析后进行spi slave设备spi_device的注册 if (busnum != -1 && chipselect != -1) { struct spi_board_info chip = { .modalias = "spidev", //与device_driver进行match使用 .mode = spimode, //spi工作模式 .bus_num = busnum, //spi master busnum .chip_select = chipselect, .max_speed_hz = maxspeed, }; struct spi_master *master; master = spi_busnum_to_master(busnum); //根据busnum找到注册的spi_master if (!master) { status = -ENODEV; goto error_busnum; }//spi slave设备spi_device注册的实现,并建立master和slave之间的关系 /* We create a virtual device that will sit on the bus */ spi = spi_new_device(master, &chip); if (!spi) { status = -EBUSY; goto error_mem; } dev_dbg(&spi->dev, "busnum=%d cs=%d bufsiz=%d maxspeed=%d", busnum, chipselect, bufsiz, maxspeed); } return 0;error_mem:error_busnum: spi_unregister_driver(&spidev_spi_driver);error_register: class_destroy(spidev_class);error_class: unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); return status;}module_init(spidev_init);
下面是设备驱动的定义,当compatible与设备注册的属性一致时,spidev_probe会被调用。
static const struct of_device_id spidev_dt_ids[] = { { .compatible = "rohm,dh2228fv", }, { .compatible = "qcom,spi-msm-codec-slave", }, {},};MODULE_DEVICE_TABLE(of, spidev_dt_ids);static struct spi_driver spidev_spi_driver = { .driver = { .name = "spidev", .owner = THIS_MODULE, .of_match_table = of_match_ptr(spidev_dt_ids), }, .probe = spidev_probe, .remove = spidev_remove, /* NOTE: suspend/resume methods are not necessary here. * We don't do anything except pass the requests to/from * the underlying controller. The refrigerator handles * most issues; the controller driver handles the rest. */};
probe函数定义如下,
static int spidev_probe(struct spi_device *spi){ struct spidev_data *spidev; int status; unsigned long minor; /* Allocate driver data */ spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); if (!spidev) return -ENOMEM; /* Initialize the driver data */ spidev->spi = spi; spin_lock_init(&spidev->spi_lock); mutex_init(&spidev->buf_lock); INIT_LIST_HEAD(&spidev->device_entry); /* If we can allocate a minor number, hook up this device. * Reusing minors is fine so long as udev or mdev is working. */ mutex_lock(&device_list_lock);//找到最小的次设备号 minor = find_first_zero_bit(minors, N_SPI_MINORS); if (minor < N_SPI_MINORS) { struct device *dev; spidev->devt = MKDEV(SPIDEV_MAJOR, minor);//设备创建,/sys/class/spidev下会有spidev%d.%d相应设备,同时udev也会在/dev下创建相应的节点 dev = device_create(spidev_class, &spi->dev, spidev->devt, spidev, "spidev%d.%d", spi->master->bus_num, spi->chip_select); status = PTR_ERR_OR_ZERO(dev); } else { dev_dbg(&spi->dev, "no minor number available!\n"); status = -ENODEV; } if (status == 0) { set_bit(minor, minors); list_add(&spidev->device_entry, &device_list); //将spidev加入device_list链表 } mutex_unlock(&device_list_lock); if (status == 0) spi_set_drvdata(spi, spidev); //spi->dev->drvdata=spidev else kfree(spidev); return status;}
文件操作函数定义如下,
static const struct file_operations spidev_fops = { .owner = THIS_MODULE, /* REVISIT switch to aio primitives, so that userspace * gets more complete API coverage. It'll simplify things * too, except for the locking. */ .write = spidev_write, .read = spidev_read, .unlocked_ioctl = spidev_ioctl, .compat_ioctl = spidev_compat_ioctl, .open = spidev_open, .release = spidev_release, .llseek = no_llseek,};
下面以open和write函数为例,对设备操作函数进行说明,
static int spidev_open(struct inode *inode, struct file *filp){ struct spidev_data *spidev; int status = -ENXIO; mutex_lock(&device_list_lock); list_for_each_entry(spidev, &device_list, device_entry) { //根据节点inode->i_rdev在device_list链表中找到spidev if (spidev->devt == inode->i_rdev) { status = 0; break; } } if (status) { pr_debug("spidev: nothing for minor %d\n", iminor(inode)); goto err_find_dev; } if (!spidev->tx_buffer) { spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL); //申请tx buffer if (!spidev->tx_buffer) { dev_dbg(&spidev->spi->dev, "open/ENOMEM\n"); status = -ENOMEM; goto err_find_dev; } } if (!spidev->rx_buffer) { spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL); //申请rx buffer if (!spidev->rx_buffer) { dev_dbg(&spidev->spi->dev, "open/ENOMEM\n"); status = -ENOMEM; goto err_alloc_rx_buf; } } spidev->users++; //增加引用次数 filp->private_data = spidev; //将spidev传给filp nonseekable_open(inode, filp); mutex_unlock(&device_list_lock); return 0;err_alloc_rx_buf: kfree(spidev->tx_buffer); spidev->tx_buffer = NULL;err_find_dev: mutex_unlock(&device_list_lock); return status;}
static ssize_tspidev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos){ struct spidev_data *spidev; ssize_t status = 0; unsigned long missing; /* chipselect only toggles at start or end of operation */ if (count > bufsiz) return -EMSGSIZE; spidev = filp->private_data; //从传入参数filp获得spidev mutex_lock(&spidev->buf_lock); missing = copy_from_user(spidev->tx_buffer, buf, count); //将用户态buf拷贝到spidev->tx_buf if (missing == 0) status = spidev_sync_write(spidev, count); //继续调用spidev_sync_write实现 else status = -EFAULT; mutex_unlock(&spidev->buf_lock); return status;}
write函数的具体实现如下,
static inline ssize_tspidev_sync_write(struct spidev_data *spidev, size_t len){ struct spi_transfer t = { .tx_buf = spidev->tx_buffer, .len = len, }; struct spi_message m; spi_message_init(&m); //初始化spi_message spi_message_add_tail(&t, &m); //将spi_transfer加入spi_message的队列 return spidev_sync(spidev, &m); //继续调用spidev_sync}static ssize_tspidev_sync(struct spidev_data *spidev, struct spi_message *message){ DECLARE_COMPLETION_ONSTACK(done); int status; message->complete = spidev_complete; message->context = &done; //spi_message的完成量赋值 spin_lock_irq(&spidev->spi_lock); if (spidev->spi == NULL) status = -ESHUTDOWN; else status = spi_async(spidev->spi, message); //调用spi_async异步传输 spin_unlock_irq(&spidev->spi_lock); if (status == 0) { wait_for_completion(&done); //等待完成量 status = message->status; if (status == 0) status = message->actual_length; } return status;}int spi_async(struct spi_device *spi, struct spi_message *message){ struct spi_master *master = spi->master; int ret; unsigned long flags; ret = __spi_validate(spi, message); if (ret != 0) return ret; spin_lock_irqsave(&master->bus_lock_spinlock, flags); if (master->bus_lock_flag) ret = -EBUSY; else ret = __spi_async(spi, message); //继续调用__spi_async spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); return ret;}static int __spi_async(struct spi_device *spi, struct spi_message *message){ struct spi_master *master = spi->master; //获取master message->spi = spi; //spi_message发送到的设备 trace_spi_message_submit(message); return master->transfer(spi, message); //调用master->transfer进行传输}
transfer函数的实现在控制器驱动章节中有详细的描述,在此处不再进行重复。
阅读全文
0 0
- linux SPI驱动框架(二) -- 设备驱动
- Linux SPI设备驱动框架
- Linux设备驱动模型SPI之二
- linux SPI 设备驱动
- Linux spi设备驱动
- Linux spi 设备驱动
- Linux设备驱动剖析之SPI(二)
- Linux设备驱动剖析之SPI(二)
- Linux设备驱动剖析之SPI(二)
- Linux spi驱动(二)
- linux字符设备驱动框架(二)
- 三、Linux spi 设备驱动
- [Linux] SPI 设备驱动模型(SPI 协议基础)
- linux spi 设备驱动简析 二(基于s5pv210)
- Linux SPI驱动框架剖析
- linux中spi驱动框架
- Linux SPI驱动框架剖析
- Linux spi驱动分析(四)----SPI设备驱动(W25Q32BV)
- Spark面试经典系列之数据倾斜解决方案的“银弹”是什么? 本节我们对Spark数据倾斜解决方案进行回顾和总结
- 设计模式(二十三)interpreter
- 【2-SAT】hihoCoder#1467 音乐节
- 嵌入式面试题
- python异常处理整理
- linux SPI驱动框架(二) -- 设备驱动
- 单例模式
- ndroid系统广播大全及开机自启动的Service
- Java集合之Map类型的集合
- HTTP介绍
- NAT技术与代理服务器讲解
- 解析文件时出现不可见字符
- Redis数据类型常用指令
- 从入门到入门-Spring Boot-第一个Spring Boot应用