Linux設備駆動探究1日目ーーspi駆動(1)
17147 ワード
この文章は転載を許可します.出所を明記してください.http://blog.csdn.net/fulinus
Linuxカーネルコードは大きすぎます.小さいモジュールも困惑します.今日の午後はspiドライバをよく見てみます.
まず、spideav.cファイルを分析して、このファイルの中でstruct fileを定義します.operations構成のメンバー.メンバーにはスペシャルビデオがありますwrite、spidiv_readとspidv_ioctlは、前半の両者が半二重通信を実現し、後者は全二重通信を実現する.もちろんopenやreleaseなど関連メンバーもいますので、とりあえず無視しましょう.
spidv_write---->spidev_sync
spidv_write---->spidev_sync
spidv_ioctl ------> spidv_message--->spidev_sync
詳細文書をご覧ください.リンクを開くにはクリックしてください.
struct spi_も充填されています.driveデータ構造体のメンバーの関数は、spidv_があります.probe,spidv_remove関数およびデバイスドライバの初期化と終了関数:spidv_initとspidv_exit
コード分析を参照してください.リンクを開くにはクリックしてください.
参照:
Linuxカーネルコードは大きすぎます.小さいモジュールも困惑します.今日の午後はspiドライバをよく見てみます.
まず、spideav.cファイルを分析して、このファイルの中でstruct fileを定義します.operations構成のメンバー.メンバーにはスペシャルビデオがありますwrite、spidiv_readとspidv_ioctlは、前半の両者が半二重通信を実現し、後者は全二重通信を実現する.もちろんopenやreleaseなど関連メンバーもいますので、とりあえず無視しましょう.
spidv_write---->spidev_sync
spidv_write---->spidev_sync
spidv_ioctl ------> spidv_message--->spidev_sync
詳細文書をご覧ください.リンクを開くにはクリックしてください.
struct spi_も充填されています.driveデータ構造体のメンバーの関数は、spidv_があります.probe,spidv_remove関数およびデバイスドライバの初期化と終了関数:spidv_initとspidv_exit
コード分析を参照してください.リンクを開くにはクリックしてください.
参照:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SPIDEV_MAJOR 153 //spidev
#define N_SPI_MINORS 32 /* ... up to 256 */
static DECLARE_BITMAP(minors, N_SPI_MINORS); //
#define SPI_MODE_MASK (SPI_CPHA|SPI_CPOL|SPI_CS_HIGH|SPI_LSB_FIRST|SPI_3WIRE|SPI_LOOP|SPI_NO_CS|SPI_READY)
struct spidev_data {
dev_t devt; //
spinlock_t spi_lock; //
struct spi_device *spi; //spi
struct list_head device_entry;
struct mutex buf_lock; //
unsigned users; //
u8 *buffer; //
};
static LIST_HEAD(device_list); // spi
static DEFINE_MUTEX(device_list_lock); //
static unsigned bufsiz = 4096; //
module_param(bufsiz, uint, S_IRUGO);
MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
static void spidev_complete(void *arg)
{
complete(arg); // complete
}
static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
message->complete = spidev_complete; // spi complete
message->context = &done;
spin_lock_irq(&spidev->spi_lock);
if (spidev->spi == NULL) // spi
status = -ESHUTDOWN;
else
status = spi_async(spidev->spi, message); //spi
spin_unlock_irq(&spidev->spi_lock);
if (status == 0) {
wait_for_completion(&done); //
status = message->status; // spi
if (status == 0)
status = message->actual_length; //status
}
return status; //
}
static inline ssize_t spidev_sync_write(struct spidev_data *spidev, size_t len)
{
struct spi_transfer t = {
.tx_buf = spidev->buffer, //
.len = len, //
};
struct spi_message m;
spi_message_init(&m); // spi ( spi )
spi_message_add_tail(&t, &m); // spr
return spidev_sync(spidev, &m); //
}
static inline ssize_t spidev_sync_read(struct spidev_data *spidev, size_t len)
{
struct spi_transfer t = {
.rx_buf = spidev->buffer, //
.len = len, //
};
struct spi_message m;
spi_message_init(&m); // spi ( spi )
spi_message_add_tail(&t, &m); // spr
return spidev_sync(spidev, &m); //
}
static ssize_t spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
struct spidev_data *spidev;
ssize_t status = 0;
if (count > bufsiz) //
return -EMSGSIZE;
spidev = filp->private_data; // spidev_data
mutex_lock(&spidev->buf_lock); //
status = spidev_sync_read(spidev, count); // ,
if (status > 0) {
unsigned long missing; //
missing = copy_to_user(buf, spidev->buffer, status); //
if (missing == status) //
status = -EFAULT;
else
status = status - missing; //
}
mutex_unlock(&spidev->buf_lock);//
return status; //
}
static ssize_t spidev_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;
if (count > bufsiz) //
return -EMSGSIZE;
spidev = filp->private_data; // spidev_data
mutex_lock(&spidev->buf_lock); //
missing = copy_from_user(spidev->buffer, buf, count); //
if (missing == 0) { // 0
status = spidev_sync_write(spidev, count); // ,
}
else
status = -EFAULT;
mutex_unlock(&spidev->buf_lock);//
return status; //
}
static int spidev_message(struct spidev_data *spidev,struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
{
struct spi_message msg;
struct spi_transfer *k_xfers;
struct spi_transfer *k_tmp;
struct spi_ioc_transfer *u_tmp;
unsigned n, total;
u8 *buf;
int status = -EFAULT;
spi_message_init(&msg); // spi ( spi )
k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL); // spi
if (k_xfers == NULL)
return -ENOMEM;
buf = spidev->buffer; // spidev_data
total = 0;
//n=xfers spi_ioc_transfer ,u_tmp = u_xfers spi_ioc_transfer
for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;n;n--, k_tmp++, u_tmp++) {
k_tmp->len = u_tmp->len; //
total += k_tmp->len; //
if (total > bufsiz) { // bufsiz
status = -EMSGSIZE;
goto done;
}
if (u_tmp->rx_buf) { //
k_tmp->rx_buf = buf; // buf
if (!access_ok(VERIFY_WRITE, (u8 __user *)(uintptr_t) u_tmp->rx_buf,u_tmp->len))
goto done;
}
if (u_tmp->tx_buf) { //
k_tmp->tx_buf = buf; // buf
if (copy_from_user(buf, (const u8 __user *)(uintptr_t) u_tmp->tx_buf,u_tmp->len)) // buf
goto done;
}
buf += k_tmp->len; //
k_tmp->cs_change = !!u_tmp->cs_change; // cs_change
k_tmp->bits_per_word = u_tmp->bits_per_word; // bits_per_word
k_tmp->delay_usecs = u_tmp->delay_usecs; // delay_usecs
k_tmp->speed_hz = u_tmp->speed_hz; // speed_hz
#ifdef VERBOSE
dev_dbg(&spidev->spi->dev," xfer len %zd %s%s%s%dbits %u usec %uHz
",
u_tmp->len,u_tmp->rx_buf ? "rx " : "",u_tmp->tx_buf ? "tx " : "",u_tmp->cs_change ? "cs " : "",
u_tmp->bits_per_word ? : spidev->spi->bits_per_word,u_tmp->delay_usecs,u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
#endif
spi_message_add_tail(k_tmp, &msg); // spr
}
//for spi_ioc_transfer spi spi_transfer, spi
status = spidev_sync(spidev, &msg); //
if (status < 0)
goto done;
buf = spidev->buffer; // spidev_data
for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) { // spi_ioc_transfer
if (u_tmp->rx_buf) { //
if (__copy_to_user((u8 __user *)(uintptr_t) u_tmp->rx_buf, buf,u_tmp->len)) {
status = -EFAULT;
goto done;
}
}
buf += u_tmp->len; //buf spi_ioc_transfer
}
status = total; //status
done:
kfree(k_xfers); // k_xfers
return status; //
}
static long spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int err = 0;
int retval = 0;
struct spidev_data *spidev;
struct spi_device *spi;
u32 tmp;
unsigned n_ioc;
struct spi_ioc_transfer *ioc;
if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC) //
return -ENOTTY;
if (_IOC_DIR(cmd) & _IOC_READ) // read
err = !access_ok(VERIFY_WRITE,(void __user *)arg, _IOC_SIZE(cmd)); //
if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) // write
err = !access_ok(VERIFY_READ,(void __user *)arg, _IOC_SIZE(cmd)); //
if (err)
return -EFAULT;
spidev = filp->private_data; // spidev_data
spin_lock_irq(&spidev->spi_lock); //
spi = spi_dev_get(spidev->spi); // spi
spin_unlock_irq(&spidev->spi_lock); //
if (spi == NULL) // spi
return -ESHUTDOWN; //
mutex_lock(&spidev->buf_lock); //
switch (cmd) {
case SPI_IOC_RD_MODE: // spi
retval = __put_user(spi->mode & SPI_MODE_MASK,(__u8 __user *)arg);
break;
case SPI_IOC_RD_LSB_FIRST: // spi
retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,(__u8 __user *)arg);
break;
case SPI_IOC_RD_BITS_PER_WORD: // spi
retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);
break;
case SPI_IOC_RD_MAX_SPEED_HZ: // spi
retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);
break;
case SPI_IOC_WR_MODE: // spi
retval = __get_user(tmp, (u8 __user *)arg);
if (retval == 0) {
u8 save = spi->mode; // spi
if (tmp & ~SPI_MODE_MASK) {
retval = -EINVAL;
break;
}
tmp |= spi->mode & ~SPI_MODE_MASK;
spi->mode = (u8)tmp;
retval = spi_setup(spi); // spi
if (retval < 0)
spi->mode = save;
else
dev_dbg(&spi->dev, "spi mode %02x
", tmp);
}
break;
case SPI_IOC_WR_LSB_FIRST: // spi
retval = __get_user(tmp, (__u8 __user *)arg);
if (retval == 0) {
u8 save = spi->mode; // spi
if (tmp)
spi->mode |= SPI_LSB_FIRST;
else
spi->mode &= ~SPI_LSB_FIRST;
retval = spi_setup(spi); // spi
if (retval < 0)
spi->mode = save;
else
dev_dbg(&spi->dev, "%csb first
",tmp ? 'l' : 'm');
}
break;
case SPI_IOC_WR_BITS_PER_WORD: // spi
retval = __get_user(tmp, (__u8 __user *)arg); //
if (retval == 0) {
u8 save = spi->bits_per_word; // spi
spi->bits_per_word = tmp; // spi
retval = spi_setup(spi); // spi
if (retval < 0) //
spi->bits_per_word = save; // spi
else
dev_dbg(&spi->dev, "%d bits per word
", tmp);
}
break;
case SPI_IOC_WR_MAX_SPEED_HZ: // spi
retval = __get_user(tmp, (__u32 __user *)arg); //
if (retval == 0) {
u32 save = spi->max_speed_hz; // spi
spi->max_speed_hz = tmp; // spi
retval = spi_setup(spi); // spi
if (retval < 0) //
spi->max_speed_hz = save; // spi
else
dev_dbg(&spi->dev, "%d Hz (max)
", tmp);
}
break;
default:
// , SPI_IOC_MESSAGE()
if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))|| _IOC_DIR(cmd) != _IOC_WRITE) {
retval = -ENOTTY;
break;
}
tmp = _IOC_SIZE(cmd); //
if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) { // spi_ioc_transfer
retval = -EINVAL;
break;
}
n_ioc = tmp / sizeof(struct spi_ioc_transfer); // spi_ioc_transfer
if (n_ioc == 0)
break;
ioc = kmalloc(tmp, GFP_KERNEL); // spi_ioc_transfer ioc
if (!ioc) {
retval = -ENOMEM;
break;
}
if (__copy_from_user(ioc, (void __user *)arg, tmp)) { //
kfree(ioc); // ioc
retval = -EFAULT;
break;
}
retval = spidev_message(spidev, ioc, n_ioc); //spidev
kfree(ioc); // ioc
break;
}
mutex_unlock(&spidev->buf_lock); //
spi_dev_put(spi); // spi
return retval;
}
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) { // device_list
if (spidev->devt == inode->i_rdev) { //
status = 0; // 0
break;
}
}
if (status == 0) { //
if (!spidev->buffer) { //spidev_data
spidev->buffer = kmalloc(bufsiz, GFP_KERNEL); //
if (!spidev->buffer) { //
dev_dbg(&spidev->spi->dev, "open/ENOMEM
"); //
status = -ENOMEM;
}
}
if (status == 0) { //
spidev->users++; //spidev_data ++
filp->private_data = spidev; //spidev_data
nonseekable_open(inode, filp); // ( )
}
}
else
pr_debug("spidev: nothing for minor %d
", iminor(inode));
mutex_unlock(&device_list_lock); //
return status;
}
static int spidev_release(struct inode *inode, struct file *filp)
{
struct spidev_data *spidev;
int status = 0;
mutex_lock(&device_list_lock);
spidev = filp->private_data; // spidev_data
filp->private_data = NULL; //
spidev->users--; // --
if (!spidev->users) { // 0
int dofree;
kfree(spidev->buffer); // spidev_data
spidev->buffer = NULL; // spidev_data
spin_lock_irq(&spidev->spi_lock); //
dofree = (spidev->spi == NULL); // spi spidev_data
spin_unlock_irq(&spidev->spi_lock); //
if (dofree) // spi
kfree(spidev); // spidev_data
}
mutex_unlock(&device_list_lock);
return status;
}
static const struct file_operations spidev_fops = { //
.owner = THIS_MODULE,
.write = spidev_write, // write
.read = spidev_read, // read
.unlocked_ioctl = spidev_ioctl, // ioctl
.open = spidev_open, // open
.release = spidev_release, // release
.llseek = no_llseek, // no_llseek
};
static struct class *spidev_class;
static int __devinit spidev_probe(struct spi_device *spi)
{
struct spidev_data *spidev;
int status;
unsigned long minor;
spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); // spidev_data
if (!spidev)
return -ENOMEM;
spidev->spi = spi; // spidev_data->spi(spi )
spin_lock_init(&spidev->spi_lock);
mutex_init(&spidev->buf_lock);
INIT_LIST_HEAD(&spidev->device_entry); // spidev_data
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); //
// /dev/spidev%d.%d(spidev . )
dev = device_create(spidev_class, &spi->dev, spidev->devt,spidev, "spidev%d.%d",spi->master->bus_num, spi->chip_select);
status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
}
else {
dev_dbg(&spi->dev, "no minor number available!
");
status = -ENODEV;
}
if (status == 0) { //
set_bit(minor, minors); //
list_add(&spidev->device_entry, &device_list); //
}
mutex_unlock(&device_list_lock);
if (status == 0)
spi_set_drvdata(spi, spidev); //spi->dev->p->driver_data=spidev
else
kfree(spidev);
return status;
}
static int __devexit spidev_remove(struct spi_device *spi)
{
struct spidev_data *spidev = spi_get_drvdata(spi); // spi spidev_data
spin_lock_irq(&spidev->spi_lock); //
spidev->spi = NULL; // spidev_data->spi
spi_set_drvdata(spi, NULL); //spi->dev->p->driver_data=NULL
spin_unlock_irq(&spidev->spi_lock); //
mutex_lock(&device_list_lock); //
list_del(&spidev->device_entry); // spidev_data
device_destroy(spidev_class, spidev->devt); // /dev/spidev%d.%d
clear_bit(MINOR(spidev->devt), minors); //
if (spidev->users == 0) // 0
kfree(spidev); // spidev_data
mutex_unlock(&device_list_lock); //
return 0;
}
static struct spi_driver spidev_spi_driver = { //spi
.driver = {
.name = "spidev",
.owner = THIS_MODULE,
},
.probe = spidev_probe, //spidev probe ( modalias "spidev" spi , probe )
.remove = __devexit_p(spidev_remove), //spidev remove
};
static int __init spidev_init(void) //spidev
{
int status;
BUILD_BUG_ON(N_SPI_MINORS > 256);
// , SPIDEV_MAJOR=153, spidev_fops
status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
if (status < 0)
return status;
spidev_class = class_create(THIS_MODULE, "spidev"); // spidev_class
if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
return PTR_ERR(spidev_class);
}
status = spi_register_driver(&spidev_spi_driver); // spi spidev_spi_driver
if (status < 0) {
class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
}
return status;
}
module_init(spidev_init); //
static void __exit spidev_exit(void) //spidev
{
spi_unregister_driver(&spidev_spi_driver); // spi spidev_spi_driver
class_destroy(spidev_class); // spidev_class
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); //
}
module_exit(spidev_exit); //
MODULE_AUTHOR("Andrea Paterniani, ");
MODULE_DESCRIPTION("User mode SPI device interface");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:spidev");
ここでioctlの命令を整理します.SPI_IOC_RD_MODE //
SPI_IOC_RD_LSB_FIRST // LSB
SPI_IOC_RD_BITS_PER_WORD //
SPI_IOC_RD_MAX_SPEED_HZ //
SPI_IOC_WR_MODE //
SPI_IOC_WR_LSB_FIRST // LSB
SPI_IOC_WR_BITS_PER_WORD //
SPI_IOC_WR_MAX_SPEED_HZ //
SPI_IOC_MESSAGE(n) // n