Linux設備駆動探究1日目ーーspi駆動(1)


この文章は転載を許可します.出所を明記してください.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
コード分析を参照してください.リンクを開くにはクリックしてください.
参照:
#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