S 3 C 2440に基づく埋め込みLinux駆動−SPIサブシステム解読(三)


転載先: http://blog.csdn.net/yj4231/article/details/7751279
このシリーズの記事は、次の4つのセクションに分かれています.
   第1部では、SPIサブシステム全体について説明するとともに、SPIに関するデータ構造を与え、最後にSPIバスの登録について説明する.S 3 C 2440に基づく埋め込みLinux駆動-SPIサブシステム解読(一)
   第2部では、SPIのマスターコントローラ駆動について説明する.S 3 C 2440に基づく埋め込みLinux駆動−SPIサブシステム解読(二)
   第3部、すなわち本編では、SPIデバイス駆動、protocol駆動とも呼ばれ、説明する.
   第4部では、SPIデバイスによってユーザ層に残されたAPIを駆動し、SPIのprotocolによってデータがどのように駆動され、bitbangによって中継され、最後にmaster駆動によってデータが出力されるかについて説明する. 
                   行きます. S 3 C 2440に基づく埋め込みLinux駆動−SPIサブシステム解読(四)
本文は第3部に属する.
5.SPIデバイス駆動
    メインコントローラ駆動でspi_デバイスは登録されていますが、デバイスドライバでは、まずspi_を登録します.driverは、ユーザ層に対応するAPIを提供する.
5.1 SPIデバイスドライバの登録
次のデータ構造および関数はdrivers/spi/spidev.cにあります.
static 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,
    .open =        spidev_open,
    .release =    spidev_release,
};

/* The main reason to have this class is to make mdev/udev create the
 * /dev/spidevB.C character device nodes exposing our userspace API.
 * It also simplifies memory management.
 */

static struct class *spidev_class;

static struct spi_driver spidev_spi = {
    .driver = {
        .name =        "spidev",
        .owner =    THIS_MODULE,
    },
    .probe =    spidev_probe,
    .remove =    __devexit_p(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.
     */
};

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);	/*      */
	status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops); /*      ,major=153*/
	if (status < 0)
		return status;

	spidev_class = class_create(THIS_MODULE, "spidev");		/*  sysfs  ,spidev */
	if (IS_ERR(spidev_class)) {
		unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
		return PTR_ERR(spidev_class);
	}

	status = spi_register_driver(&spidev_spi);		/*  spi_driver,   probe  */
	if (status < 0) {
		class_destroy(spidev_class);                    /*  sysfs   spidev_class  */
		unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
	}
	return status;
}
module_init(spidev_init);

static void __exit spidev_exit(void)
{
	spi_unregister_driver(&spidev_spi);			/*  spi_driver*/
	class_destroy(spidev_class);				/*   */
	unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);/*      */
}
module_exit(spidev_exit);

この関数では、ユーザー層にAPIを提供する文字デバイスが作成され、spidevクラスが作成され、spi_が登録されます.driverをカーネルに挿入します.
ここでは,SPIデバイスドライバがユーザ層にAPIをどのように提供するかを見た.それは,これ以上熟知していない文字デバイスを通じてである.文字デバイスにより,ユーザ層に5つのAPI:open,release,write,read,ioctlを提供した.本稿ではopenとcloseについて後述し,残りの3つは本シリーズの4番目の記事で紹介する.
次にspi_を見てregister_driver関数.drivers/spi/spidev.cにあります.
/**
 * spi_register_driver - register a SPI driver
 * @sdrv: the driver to register
 * Context: can sleep
 */
int spi_register_driver(struct spi_driver *sdrv)
{
	sdrv->driver.bus = &spi_bus_type;
	if (sdrv->probe)
		sdrv->driver.probe = spi_drv_probe;
	if (sdrv->remove)
		sdrv->driver.remove = spi_drv_remove;
	if (sdrv->shutdown)
		sdrv->driver.shutdown = spi_drv_shutdown;
	return driver_register(&sdrv->driver);
}
EXPORT_SYMBOL_GPL(spi_register_driver);

driverを呼び出すregisterの過程でdriver.nameとspi_を使用します.デバイスのmodaliasフィールドを比較し、両者が等しい場合spi_driverとspi_デバイスをバインドします.
spi_driver登録に成功するとprobeメソッド:spidev_が呼び出されますprobe関数.
5.2 probeメソッド
spidevを見てみましょうprobeという関数で、drivers/spi/spidev.cに位置します.
#define SPIDEV_MAJOR            153    /* assigned */
#define N_SPI_MINORS            32    /* ... up to 256 */

static unsigned long    minors[N_SPI_MINORS / BITS_PER_LONG];    /**/

static LIST_HEAD(device_list);
static DEFINE_MUTEX(device_list_lock);

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);	/* kmalloc    ,  0*/
	if (!spidev)
		return -ENOMEM;

	/* Initialize the driver data */
	spidev->spi = spi; 					/*  spi_device*/
	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);	/*             */
		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);/* list entry*/ } mutex_unlock(&device_list_lock);/* */ if (status == 0) spi_set_drvdata(spi, spidev);/*spi->dev.driver_data=spidev*/ else kfree(spidev); return status; }

使用する
struct spidev_data
構造は次のとおりです.
struct spidev_data {
    dev_t            devt;
    spinlock_t        spi_lock;
    struct spi_device    *spi;
    struct list_head    device_entry;

    /* buffer is NULL unless this device is open (users > 0) */
    struct mutex        buf_lock;
    unsigned        users;
    u8            *buffer;
};

この関数にはspidev_が割り当てられていますDataとセカンダリデバイス番号は、その後、プライマリセカンダリデバイス番号に基づいてデバイスノードを作成します.デバイスノードの名前はspidev「bus_num」.chip_select」とは、デバイスがいくつかのSPIインタフェース上のいくつかのデバイスであることを意味する.
   さらに、spidevをdevice_listに追加することで、spidevの検索が容易になります.
5.3 removeメソッド
次の関数はdrivers/spi/spidev.cにあります.
static int spidev_remove(struct spi_device *spi)
{
	struct spidev_data	*spidev = spi_get_drvdata(spi);

	/* make sure ops on existing fds can abort cleanly */
	spin_lock_irq(&spidev->spi_lock);
	spidev->spi = NULL;
	spi_set_drvdata(spi, NULL);
	spin_unlock_irq(&spidev->spi_lock);

	/* prevent new opens */
	mutex_lock(&device_list_lock);
	list_del(&spidev->device_entry);	/*  entry*/
	device_destroy(spidev_class, spidev->devt);	/*      */
	clear_bit(MINOR(spidev->devt), minors);/*           */
	if (spidev->users == 0)
		kfree(spidev);
	mutex_unlock(&device_list_lock);

	return 0;
}

6.openとrelease
次にopenとreleaseシステムが呼び出すAPIインタフェースを見てみましょう.残りの3つのインタフェースは、このシリーズの4番目の記事で与えられます.
6.1 openメソッド
次の関数はdrivers/spi/spidev.cにあります.
static int spidev_open(struct inode *inode, struct file *filp)
{
	struct spidev_data	*spidev;
	int			status = -ENXIO;

	lock_kernel();			/*      ,    ,          */
	mutex_lock(&device_list_lock);	/*     */

	list_for_each_entry(spidev, &device_list, device_entry) {/* list    entry,      spidev*/
		if (spidev->devt == inode->i_rdev) {	/*         */
			status = 0;							/*     spi  */
			break;
		}
	}
	if (status == 0) {
		/*NOTE:      open  ,       buffer,   buufer        */
		if (!spidev->buffer) {			/*buffer  */ 
			spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);/*  buffer   ,  4KB*/
			if (!spidev->buffer) {
				dev_dbg(&spidev->spi->dev, "open/ENOMEM
"); status = -ENOMEM; } } if (status == 0) { spidev->users++; /* open , */ filp->private_data = spidev; /* spidev */ nonseekable_open(inode, filp); /* lseek*/ } } else pr_debug("spidev: nothing for minor %d
", iminor(inode)); mutex_unlock(&device_list_lock);/* */ unlock_kernel(); /* */ return status; }

ここで、device_listをチェーンヘッダとして、すべてのspidev_data構造を巡回し、デバイスノードのデバイス番号とspidev_dataに保存されたデバイス番号とを一致させることで、そのデバイスノードに属するspiデバイスを見つける.その後、spiデバイスドライバ層で使用されるバッファが割り当てられ、最後にオープンカウントが増加する.
6.2 releaseメソッド
次の関数はdrivers/spi/spidev.cにあります.
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*/
	filp->private_data = NULL;		

	/* last close? */
	spidev->users--;				/*      ,      */
	if (!spidev->users) {			/*      0*/
		int		dofree;

		kfree(spidev->buffer);		/*     */
		spidev->buffer = NULL;

		/* ... after we unbound from the underlying device? */
		spin_lock_irq(&spidev->spi_lock);	/*     */
		dofree = (spidev->spi == NULL);		/*????*/
		spin_unlock_irq(&spidev->spi_lock);	/*     */

		if (dofree)
			kfree(spidev);			/*  spidev, probe   */
	}
	mutex_unlock(&device_list_lock);/*     */

	return status;
}

 そこでprotocol駆動層のフレームワークについて簡単に分析し、次の記事ではその駆動層の未分析の多くの関数について説明します.次の記事の内容はとても重要ですよ.