linux系统总线SPI总线四之SPI设备驱动程序

前篇文章分析了SPI主控制器驱动,也就是SPI总线驱动,既然总线有了,根据linux设备驱动模型,还得有SPI设备和SPI设备驱动。SPI设备是在板级文件中注册,SPI设备驱动需要用户自己实现,好在内核为我们提供了一个通用的SPI设备驱动spidev.c,下面就来分析一下这个文件,该文件位于kernel3.0.15/drivers/spi/spidev.c。

1. 模块初始化和注销:spidev_init & spidev_exit

  1. static const struct file_operations spidev_fops = {  
  2.     .owner =    THIS_MODULE,  
  3.     /* REVISIT switch to aio primitives, so that userspace 
  4.      * gets more complete API coverage.  It'll simplify things 
  5.      * too, except for the locking. 
  6.      */  
  7.     .write =    spidev_write,  
  8.     .read =     spidev_read,  
  9.     .unlocked_ioctl = spidev_ioctl,  
  10.     .compat_ioctl = spidev_compat_ioctl,  
  11.     .open =     spidev_open,  
  12.     .release =  spidev_release,  
  13.     .llseek =   no_llseek,  
  14. };  
  1. static struct spi_driver spidev_spi_driver = {  
  2.     .driver = {  
  3.         .name =     "spidev",  
  4.         .owner =    THIS_MODULE,  
  5.     },  
  6.     .probe =    spidev_probe,  
  7.     .remove =   __devexit_p(spidev_remove),  
  8.   
  9.     /* NOTE:  suspend/resume methods are not necessary here. 
  10.      * We don't do anything except pass the requests to/from 
  11.      * the underlying controller.  The refrigerator handles 
  12.      * most issues; the controller driver handles the rest. 
  13.      */  
  14. };  
  15.   
  16. /*-------------------------------------------------------------------------*/  
  17.   
  18. static int __init spidev_init(void)  
  19. {  
  20.     int status;  
  21.   
  22.     /* Claim our 256 reserved device numbers.  Then register a class 
  23.      * that will key udev/mdev to add/remove /dev nodes.  Last, register 
  24.      * the driver which manages those device numbers. 
  25.      */  
  26.     BUILD_BUG_ON(N_SPI_MINORS > 256);  
  27.     //注册字符设备,参数spidev_fops是struct file_operations的实例,这里就可以知道,用户程序的open、write等操作最终会调用这里面的函数  
  28.     status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);  
  29.     if (status < 0)  
  30.         return status;  
  31.   
  32.     spidev_class = class_create(THIS_MODULE, "spidev"); //创建spidev这一类设备,为后面自动生成设备节点做准备  
  33.     if (IS_ERR(spidev_class)) {  
  34.         unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);  
  35.         return PTR_ERR(spidev_class);  
  36.     }  
  37.   
  38.     status = spi_register_driver(&spidev_spi_driver); //注册spi设备驱动  
  39.     if (status < 0) {  
  40.         class_destroy(spidev_class);  
  41.         unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);  
  42.     }  
  43.     return status;  
  44. }  
  45. module_init(spidev_init);  
  46.   
  47. static void __exit spidev_exit(void)  
  48. {  
  49.     spi_unregister_driver(&spidev_spi_driver);  
  50.     class_destroy(spidev_class);  
  51.     unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);  
  52. }  
  53. module_exit(spidev_exit);  
  54.   
  55. MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");  
  56. MODULE_DESCRIPTION("User mode SPI device interface");  
  57. MODULE_LICENSE("GPL");  
  58. MODULE_ALIAS("spi:spidev");  

在模块初始化函数中,创建了一个字符设备以提供API给用户层,同时创建了一个spidev类,最后注册spi_driver到内核中。在这里我们看到了SPI设备驱动是如何提供API给用户层的,那就是通过再熟悉不过的字符设备。通过字符设备,给用户层提供了5个API:open,release,write,read和ioctl。

接下来分析一下spi_register_driver函数,该函数位于kernel3.0.15/drivers/spi/spi.c

  1. int spi_register_driver(struct spi_driver *sdrv)  
  2. {  
  3.     sdrv->driver.bus = &spi_bus_type; //该驱动所属的总线  
  4.     if (sdrv->probe)  
  5.         sdrv->driver.probe = spi_drv_probe;  
  6.     if (sdrv->remove)  
  7.         sdrv->driver.remove = spi_drv_remove;  
  8.     if (sdrv->shutdown)  
  9.         sdrv->driver.shutdown = spi_drv_shutdown;  
  10.     //将驱动注册进设备模型,注册成功的话就会在总线上寻找设备,调用总线上的match函数,看能否与之匹配起来,匹配成功的话,驱动中的probe函数就会被调用  
  11.     return driver_register(&sdrv->driver);  
  12. }  
在调用driver_register的过程中,将用driver.name和spi_device的modalias字段进行比较,两者相等则将该spi_driver和spi_device进行绑定。当spi_driver注册成功以后,将调用probe方法:spidev_probe函数。

2. 探测和移除函数:spidev_probe & spidev_remove

  1. static int __devinit spidev_probe(struct spi_device *spi)  
  2. {  
  3.     struct spidev_data  *spidev;  
  4.     int         status;  
  5.     unsigned long       minor;  
  6.   
  7.     /* Allocate driver data */  
  8.     spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); //分配内存,注意对象的类型是struct spidev_data  
  9.     if (!spidev)  
  10.         return -ENOMEM;  
  11.   
  12.     /* Initialize the driver data */  
  13.     spidev->spi = spi;  
  14.     spin_lock_init(&spidev->spi_lock); //一些锁和链表的初始化  
  15.     mutex_init(&spidev->buf_lock);  
  16.   
  17.     INIT_LIST_HEAD(&spidev->device_entry);  
  18.   
  19.     /* If we can allocate a minor number, hook up this device. 
  20.      * Reusing minors is fine so long as udev or mdev is working. 
  21.      */  
  22.     mutex_lock(&device_list_lock);  
  23.     minor = find_first_zero_bit(minors, N_SPI_MINORS); //从名字上就可以知道,就是找到第一个为0的位,分析见下面  
  24.     if (minor < N_SPI_MINORS) {  
  25.         struct device *dev;  
  26.   
  27.         spidev->devt = MKDEV(SPIDEV_MAJOR, minor); //如果找到了非0位,就将它作为次设备号与之前注册的主设备号生成设备号  
  28.         dev = device_create(spidev_class, &spi->dev, spidev->devt,//创建设备,并生成设备节点,设备节点在/dev目录下,名字的形式为“spidevx.x”  
  29.                     spidev, "spidev%d.%d",  
  30.                     spi->master->bus_num, spi->chip_select);  
  31.         status = IS_ERR(dev) ? PTR_ERR(dev) : 0;  
  32.     } else {  
  33.         dev_dbg(&spi->dev, "no minor number available!\n");  
  34.         status = -ENODEV;  
  35.     }  
  36.     if (status == 0) { //创建设备成功后,将相应的位置1,表示该次设备号已经被使用,同时将该设备加入到设备链表  
  37.         set_bit(minor, minors);  
  38.         list_add(&spidev->device_entry, &device_list);  
  39.     }  
  40.     mutex_unlock(&device_list_lock);  
  41.   
  42.     if (status == 0)  
  43.         spi_set_drvdata(spi, spidev); //将设备的私有数据指针指向该设备  
  44.     else  
  45.         kfree(spidev);  
  46.   
  47.     return status;  
  48. }  
  49.   
  50. static int __devexit spidev_remove(struct spi_device *spi)  
  51. {  
  52.     struct spidev_data  *spidev = spi_get_drvdata(spi);  
  53.   
  54.     /* make sure ops on existing fds can abort cleanly */  
  55.     spin_lock_irq(&spidev->spi_lock);  
  56.     spidev->spi = NULL;  
  57.     spi_set_drvdata(spi, NULL);  
  58.     spin_unlock_irq(&spidev->spi_lock);  
  59.   
  60.     /* prevent new opens */  
  61.     mutex_lock(&device_list_lock);  
  62.     list_del(&spidev->device_entry);  
  63.     device_destroy(spidev_class, spidev->devt);  
  64.     clear_bit(MINOR(spidev->devt), minors);  
  65.     if (spidev->users == 0)  
  66.         kfree(spidev);  
  67.     mutex_unlock(&device_list_lock);  
  68.   
  69.     return 0;  
  70. }  

spidev_data(kernel3.0.15/driver/spi/spidev.c)

  1. struct spidev_data {  
  2.     dev_t           devt; //设备号  
  3.     spinlock_t      spi_lock;  
  4.     struct spi_device   *spi;  
  5.     struct list_head    device_entry; //设备链表,所有采用此驱动的设备将连成一个链表  
  6.   
  7.     /* buffer is NULL unless this device is open (users > 0) */  
  8.     struct mutex        buf_lock;  
  9.     unsigned        users; //计数,也即是此设备被open的次数  
  10.     u8          *buffer;  
  11. };  
find_first_zero_bit(minors, N_SPI_MINORS)
第一个参数minors的定义:

kernel3.0.15/driver/spi/spidev.c

  1. #define N_SPI_MINORS            32  /* ... up to 256 */  
  2.   
  3. static DECLARE_BITMAP(minors, N_SPI_MINORS);  
DECLARE_BITMAP是一个宏,定义如下:

kernel3.0.15/include/linux/types.h

  1. #define DECLARE_BITMAP(name,bits) \  
  2.     unsigned long name[BITS_TO_LONGS(bits)]  
将宏展开后是这样的,unsigned long minors[1],其实就是定义一个只有一个元素的无符号长整形数组miniors。

3. 打开和释放函数:spidev_open & spidev_release

  1. static int spidev_open(struct inode *inode, struct file *filp)  
  2. {  
  3.     struct spidev_data  *spidev;  
  4.     int         status = -ENXIO;  
  5.   
  6.     mutex_lock(&device_list_lock);  
  7.   
  8.     list_for_each_entry(spidev, &device_list, device_entry) {  
  9.         if (spidev->devt == inode->i_rdev) { //遍历设备链表,每找到一个设备就将它的设备号与打开文件的设备号进行比较,相等的话表示查找成功  
  10.             status = 0;  
  11.             break;  
  12.         }  
  13.     }  
  14.     //查找成功后就分配读写数据内存,使用计数加1,设置文件私有数据指针指向查找到的设备,以后在驱动的write、read函数里就可以把它取出来  
  15.     if (status == 0) {  
  16.         if (!spidev->buffer) {  
  17.             spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);  
  18.             if (!spidev->buffer) {  
  19.                 dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");  
  20.                 status = -ENOMEM;  
  21.             }  
  22.         }  
  23.         if (status == 0) {  
  24.             spidev->users++;  
  25.             filp->private_data = spidev;  
  26.             nonseekable_open(inode, filp);  
  27.         }  
  28.     } else  
  29.         pr_debug("spidev: nothing for minor %d\n", iminor(inode));  
  30.   
  31.     mutex_unlock(&device_list_lock);  
  32.     return status;  
  33. }  
  34.   
  35. static int spidev_release(struct inode *inode, struct file *filp)  
  36. {  
  37.     struct spidev_data  *spidev;  
  38.     int         status = 0;  
  39.   
  40.     mutex_lock(&device_list_lock);  
  41.     spidev = filp->private_data;  
  42.     filp->private_data = NULL;  
  43.   
  44.     /* last close? */  
  45.     spidev->users--;  
  46.     if (!spidev->users) {  
  47.         int     dofree;  
  48.   
  49.         kfree(spidev->buffer);  
  50.         spidev->buffer = NULL;  
  51.   
  52.         /* ... after we unbound from the underlying device? */  
  53.         spin_lock_irq(&spidev->spi_lock);  
  54.         dofree = (spidev->spi == NULL);  
  55.         spin_unlock_irq(&spidev->spi_lock);  
  56.   
  57.         if (dofree)  
  58.             kfree(spidev);  
  59.     }  
  60.     mutex_unlock(&device_list_lock);  
  61.   
  62.     return status;  
  63. }  

4. 读和写函数:spidev_read & spidev_write

  1. /* Read-only message with current device setup */  
  2. static ssize_t  
  3. spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)  
  4. {  
  5.     struct spidev_data  *spidev;  
  6.     ssize_t         status = 0;  
  7.   
  8.     /* chipselect only toggles at start or end of operation */  
  9.     if (count > bufsiz)  
  10.         return -EMSGSIZE;  
  11.   
  12.     spidev = filp->private_data;  
  13.   
  14.     mutex_lock(&spidev->buf_lock);  
  15.     status = spidev_sync_read(spidev, count);  
  16.     if (status > 0) {  
  17.         unsigned long   missing;  
  18.   
  19.         missing = copy_to_user(buf, spidev->buffer, status);  
  20.         if (missing == status)  
  21.             status = -EFAULT;  
  22.         else  
  23.             status = status - missing;  
  24.     }  
  25.     mutex_unlock(&spidev->buf_lock);  
  26.   
  27.     return status;  
  28. }  
  29.   
  30. /* Write-only message with current device setup */  
  31. static ssize_t  
  32. spidev_write(struct file *filp, const char __user *buf,  
  33.         size_t count, loff_t *f_pos)  
  34. {  
  35.     struct spidev_data  *spidev;  
  36.     ssize_t         status = 0;  
  37.     unsigned long       missing;  
  38.   
  39.     /* chipselect only toggles at start or end of operation */  
  40.     if (count > bufsiz) //应用程序写入的数据不能大于驱动中缓冲区的大小,默认为4096个字节  
  41.         return -EMSGSIZE;  
  42.   
  43.     spidev = filp->private_data; //指向文件的私有数据  
  44.   
  45.     mutex_lock(&spidev->buf_lock);  
  46.     missing = copy_from_user(spidev->buffer, buf, count); //拷贝用户空间的数据到内核空间  
  47.     if (missing == 0) {  
  48.         status = spidev_sync_write(spidev, count);  
  49.     } else  
  50.         status = -EFAULT;  
  51.     mutex_unlock(&spidev->buf_lock);  
  52.   
  53.     return status;  
  54. }  

5.ioctl函数:spidev_ioctl

  1. static long  
  2. spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  
  3. {  
  4.     int         err = 0;  
  5.     int         retval = 0;  
  6.     struct spidev_data  *spidev;  
  7.     struct spi_device   *spi;  
  8.     u32         tmp;  
  9.     unsigned        n_ioc;  
  10.     struct spi_ioc_transfer *ioc;  
  11.   
  12.     /* Check type and command number */  
  13.     if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)  
  14.         return -ENOTTY;  
  15.   
  16.     /* Check access direction once here; don't repeat below. 
  17.      * IOC_DIR is from the user perspective, while access_ok is 
  18.      * from the kernel perspective; so they look reversed. 
  19.      */  
  20.     if (_IOC_DIR(cmd) & _IOC_READ)  
  21.         err = !access_ok(VERIFY_WRITE,  
  22.                 (void __user *)arg, _IOC_SIZE(cmd));  
  23.     if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)  
  24.         err = !access_ok(VERIFY_READ,  
  25.                 (void __user *)arg, _IOC_SIZE(cmd));  
  26.     if (err)  
  27.         return -EFAULT;  
  28.   
  29.     /* guard against device removal before, or while, 
  30.      * we issue this ioctl. 
  31.      */  
  32.     spidev = filp->private_data;  
  33.     spin_lock_irq(&spidev->spi_lock);  
  34.     spi = spi_dev_get(spidev->spi);  
  35.     spin_unlock_irq(&spidev->spi_lock);  
  36.   
  37.     if (spi == NULL)  
  38.         return -ESHUTDOWN;  
  39.   
  40.     /* use the buffer lock here for triple duty: 
  41.      *  - prevent I/O (from us) so calling spi_setup() is safe; 
  42.      *  - prevent concurrent SPI_IOC_WR_* from morphing 
  43.      *    data fields while SPI_IOC_RD_* reads them; 
  44.      *  - SPI_IOC_MESSAGE needs the buffer locked "normally". 
  45.      */  
  46.     mutex_lock(&spidev->buf_lock);  
  47.   
  48.     switch (cmd) {  
  49.     /* read requests */  
  50.     case SPI_IOC_RD_MODE:  
  51.         retval = __put_user(spi->mode & SPI_MODE_MASK,  
  52.                     (__u8 __user *)arg);  
  53.         break;  
  54.     case SPI_IOC_RD_LSB_FIRST:  
  55.         retval = __put_user((spi->mode & SPI_LSB_FIRST) ?  1 : 0,  
  56.                     (__u8 __user *)arg);  
  57.         break;  
  58.     case SPI_IOC_RD_BITS_PER_WORD:  
  59.         retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);  
  60.         break;  
  61.     case SPI_IOC_RD_MAX_SPEED_HZ:  
  62.         retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);  
  63.         break;  
  64.   
  65.     /* write requests */  
  66.     case SPI_IOC_WR_MODE:  
  67.         retval = __get_user(tmp, (u8 __user *)arg);  
  68.         if (retval == 0) {  
  69.             u8  save = spi->mode;  
  70.   
  71.             if (tmp & ~SPI_MODE_MASK) {  
  72.                 retval = -EINVAL;  
  73.                 break;  
  74.             }  
  75.   
  76.             tmp |= spi->mode & ~SPI_MODE_MASK;  
  77.             spi->mode = (u8)tmp;  
  78.             retval = spi_setup(spi);  
  79.             if (retval < 0)  
  80.                 spi->mode = save;  
  81.             else  
  82.                 dev_dbg(&spi->dev, "spi mode %02x\n", tmp);  
  83.         }  
  84.         break;  
  85.     case SPI_IOC_WR_LSB_FIRST:  
  86.         retval = __get_user(tmp, (__u8 __user *)arg);  
  87.         if (retval == 0) {  
  88.             u8  save = spi->mode;  
  89.   
  90.             if (tmp)  
  91.                 spi->mode |= SPI_LSB_FIRST;  
  92.             else  
  93.                 spi->mode &= ~SPI_LSB_FIRST;  
  94.             retval = spi_setup(spi);  
  95.             if (retval < 0)  
  96.                 spi->mode = save;  
  97.             else  
  98.                 dev_dbg(&spi->dev, "%csb first\n",  
  99.                         tmp ? 'l' : 'm');  
  100.         }  
  101.         break;  
  102.     case SPI_IOC_WR_BITS_PER_WORD:  
  103.         retval = __get_user(tmp, (__u8 __user *)arg);  
  104.         if (retval == 0) {  
  105.             u8  save = spi->bits_per_word;  
  106.   
  107.             spi->bits_per_word = tmp;  
  108.             retval = spi_setup(spi);  
  109.             if (retval < 0)  
  110.                 spi->bits_per_word = save;  
  111.             else  
  112.                 dev_dbg(&spi->dev, "%d bits per word\n", tmp);  
  113.         }  
  114.         break;  
  115.     case SPI_IOC_WR_MAX_SPEED_HZ:  
  116.         retval = __get_user(tmp, (__u32 __user *)arg);  
  117.         if (retval == 0) {  
  118.             u32 save = spi->max_speed_hz;  
  119.   
  120.             spi->max_speed_hz = tmp;  
  121.             retval = spi_setup(spi);  
  122.             if (retval < 0)  
  123.                 spi->max_speed_hz = save;  
  124.             else  
  125.                 dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);  
  126.         }  
  127.         break;  
  128.   
  129.     default:  
  130.         /* segmented and/or full-duplex I/O request */  
  131.         if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))  
  132.                 || _IOC_DIR(cmd) != _IOC_WRITE) {  
  133.             retval = -ENOTTY;  
  134.             break;  
  135.         }  
  136.   
  137.         tmp = _IOC_SIZE(cmd);  
  138.         if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {  
  139.             retval = -EINVAL;  
  140.             break;  
  141.         }  
  142.         n_ioc = tmp / sizeof(struct spi_ioc_transfer);  
  143.         if (n_ioc == 0)  
  144.             break;  
  145.   
  146.         /* copy into scratch area */  
  147.         ioc = kmalloc(tmp, GFP_KERNEL);  
  148.         if (!ioc) {  
  149.             retval = -ENOMEM;  
  150.             break;  
  151.         }  
  152.         if (__copy_from_user(ioc, (void __user *)arg, tmp)) {  
  153.             kfree(ioc);  
  154.             retval = -EFAULT;  
  155.             break;  
  156.         }  
  157.   
  158.         /* translate to spi_message, execute */  
  159.         retval = spidev_message(spidev, ioc, n_ioc);  
  160.         kfree(ioc);  
  161.         break;  
  162.     }  
  163.   
  164.     mutex_unlock(&spidev->buf_lock);  
  165.     spi_dev_put(spi);  
  166.     return retval;  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基本信息 原书名: Essential Linux Device Drivers 原出版社: Prentice Hall 作者: (印)Sreekrishnan Venkateswaran 译者: 宋宝华 何昭然 史海滨 吴国成 丛书名: 图灵程序设计丛书 操作系统 出版社:人民邮电出版社 ISBN:9787115221674 出版日期:2010 年6月 页码:468 内容简介   本书是linux设备驱动程序开发领域的权威著作。全书基于2.6内核,不仅透彻讲解了基本概念和技术,更深入探讨了其他书没有涵盖或浅尝辄止的许多重要主题和关键难点,如pcmcia、i2c和usb等外部总线以及视频、音频、无线连网和闪存等驱动程序的开发,并讲解了相关的内核源码文件,给出了完整的开发实例。   本书适合中高级linux开发人员阅读。 目录 第1章 引言1 1.1 演进1 1.2 gnu copyleft2 1.3 kernel.org2 1.4 邮件列表和论坛3 1.5 linux发行版3 1.6 查看源代码4 1.7 编译内核7 1.8 可加载的模块8 1.9 整装待发9 第2章 内核11 2.1 启动过程11 2.1.1 bios-provided physical ram map12 2.1.2 758mb lowmem available14 2.1.3 kernel command line: ro root=/dev/hda114 2.1.4 calibrating delay...1197.46 .bogomips (lpj=2394935)15 2.1.5 checking hlt instruction16 2.1.6 net: registered protocol family 217 2.1.7 freeing initrd memory: 387k freed17 2.1.8 io scheduler anticipatory registered (default)18 2.1.9 setting up standard pci resources18 2.1.10 ext3-fs: mounted filesystem19 2.1.11 init: version 2.85 booting19 2.2 内核模式和用户模式20 2.3 进程上下文和中断上下文20 2.4 内核定时器21 2.4.1 hz和jiffies21 2.4.2 长延时22 2.4.3 短延时24 2.4.4 pentium时间戳计数器24 2.4.5 实时钟25 2.5 内核中的并发26 2.5.1 自旋锁和互斥体26 2.5.2 原子操作30 2.5.3 读—写锁31 2.5.4 调试32 2.6 proc文件系统32 2.7 内存分配33 2.8 查看源代码34 第3章 内核组件37 3.1 内核线程37 3.1.1 创建内核线程37 3.1.2 进程状态和等待队列41 3.1.3 用户模式辅助程序42 3.2 辅助接口43 3.2.1 链表44 3.2.2 散列链表49 3.2.3 工作队列49 3.2.4 通知链51 3.2.5 完成接口54 3.2.6 kthread辅助接口56 3.2.7 错误处理助手57 3.3 查看源代码58 第4章 基本概念61 4.1 设备驱动程序介绍61 4.2 中断处理63 4.2.1 中断上下文63 4.2.2 分配irq号64 4.2.3 设备实例:导航杆65 4.2.4 softirq和tasklet68 4.3 linux设备模型71 4.3.1 udev71 4.3.2 sysfs、kobject和设备类73 4.3.3 热插拔和冷插拔76 4.3.4 微码下载76 4.3.5 模块自动加载77 4.4 内存屏障78 4.5 电源管理79 4.6 查看源代码79 第5章 字符设备驱动程序81 5.1 字符设备驱动程序基础81 5.2 设备实例:系统cmos82 5.2.1 驱动程序初始化83 5.2.2 打开与释放86 5.2.3 数据交换88 5.2.4 查找92 5.2.5 控制94 5.3 检测数据可用性95 5.3.1 轮询95 5.3.2 fasync98 5.4 和并行端口交互99 5.5 rtc子系统108 5.6 伪字符驱动程序109 5.7 混杂驱动程序110 5.8 字符设备驱动程序注意事项115 5.9 查看源代码115 第6章 串行设备驱动程序118 6.1 层次架构119 6.2 uart驱动程序121 6.2.1 设备实例:手机122 6.2.2 rs-485132 6.3 tty驱动程序132 6.4 线路规程134 6.5 查看源代码141 第7章 输入设备驱动程序143 7.1 输入事件驱动程序144 7.2 输入设备驱动程序150 7.2.1 serio150 7.2.2 键盘150 7.2.3 鼠标152 7.2.4 触摸控制器157 7.2.5 加速度传感器158 7.2.6 输出事件158 7.3 调试159 7.4 查看源代码160 第8章 i2c协议161 8.1 i2c/smbus是什么161 8.2 i2c核心162 8.3 总线事务164 8.4 设备实例:eeprom164 8.4.1 初始化165 8.4.2 探测设备167 8.4.3 检查适配器的功能169 8.4.4 访问设备169 8.4.5 其他函数170 8.5 设备实例:实时时钟171 8.6 i2c-dev174 8.7 使用lm-sensors监控硬件174 8.8 spi总线174 8.9 1-wire总线176 8.10 调试176 8.11 查看源代码176 第9章 pcmcia和cf179 9.1 pcmcia/cf是什么179 9.2 linux-pcmcia子系统181 9.3 主机控制器驱动程序183 9.4 pcmcia核心183 9.5 驱动程序服务183 9.6 客户驱动程序183 9.6.1 数据结构184 9.6.2 设备实例:pcmcia卡185 9.7 将零件组装在一起188 9.8 pcmcia存储189 9.9 串行pcmcia189 9.10 调试191 9.11 查看源代码191 第10章 pci193 10.1 pci系列193 10.2 寻址和识别195 10.3 访问pci198 10.3.1 配置区198 10.3.2 i/o和内存199 10.4 dma200 10.5 设备实例:以太网—调制解调器卡203 10.5.1 初始化和探测203 10.5.2 数据传输209 10.6 调试214 10.7 查看源代码214 第11章 usb216 11.1 usb体系架构216 11.1.1 总线速度218 11.1.2 主机控制器218 11.1.3 传输模式219 11.1.4 寻址219 11.2 linux-usb子系统220 11.3 驱动程序的数据结构221 11.3.1 usb_device结构体221 11.3.2 urb222 11.3.3 管道223 11.3.4 描述符结构223 11.4 枚举225 11.5 设备实例:遥测卡225 11.5.1 初始化和探测过程226 11.5.2 卡寄存器的访问230 11.5.3 数据传输233 11.6 类驱动程序236 11.6.1 大容量存储设备236 11.6.2 usb-串行端口转换器241 11.6.3 人机接口设备243 11.6.4 蓝牙243 11.7 gadget驱动程序243 11.8 调试244 11.9 查看源代码245 第12章 视频驱动程序247 12.1 显示架构247 12.2 linux视频子系统249 12.3 显示参数251 12.4 帧缓冲api252 12.5 帧缓冲驱动程序254 12.6 控制台驱动程序265 12.6.1 设备实例:手机266 12.6.2 启动logo270 12.7 调试270 12.8 查看源代码271 第13章 音频驱动程序273 13.1 音频架构273 13.2 linux声音子系统275 13.3 设备实例:mp3播放器277 13.3.1 驱动程序函数和结构体278 13.3.2 alsa编程287 13.4 调试288 13.5 查看源代码289 第14章 块设备驱动程序291 14.1 存储技术291 14.2 linux块i/o层295 14.3 i/o调度器295 14.4 块驱动程序数据结构和方法296 14.5 设备实例:简单存储控制器298 14.5.1 初始化299 14.5.2 块设备操作301 14.5.3 磁盘访问302 14.6 高级主题304 14.7 调试306 14.8 查看源代码306 第15章 网络接口卡308 15.1 驱动程序数据结构308 15.1.1 套接字缓冲区309 15.1.2 网络设备接口310 15.1.3 激活311 15.1.4 数据传输311 15.1.5 看门狗311 15.1.6 统计312 15.1.7 配置313 15.1.8 总线相关内容314 15.2 与协议层会话314 15.2.1 接收路径314 15.2.2 发送路径315 15.2.3 流量控制315 15.3 缓冲区管理和并发控制315 15.4 设备实例:以太网nic316 15.5 isa网络驱动程序321 15.6 atm321 15.7 网络吞吐量322 15.7.1 驱动程序性能322 15.7.2 协议性能323 15.8 查看源代码324 第16章 linux无线设备驱动326 16.1 蓝牙327 16.1.1 bluez328 16.1.2 设备实例:cf卡329 16.1.3 设备实例:usb适配器330 16.1.4 rfcomm331 16.1.5 网络332 16.1.6 hid334 16.1.7 音频334 16.1.8 调试334 16.1.9 关于源代码334 16.2 红外335 16.2.1 linux-irda335 16.2.2 设备实例:超级i/o芯片337 16.2.3 设备实例:ir dongle338 16.2.4 ircomm340 16.2.5 联网340 16.2.6 irda套接字341 16.2.7 lirc341 16.2.8 查看源代码342 16.3 wifi343 16.3.1 配置343 16.3.2 设备驱动程序346 16.3.3 查看源代码347 16.4 蜂窝网络347 16.4.1 gprs347 16.4.2 cdma349 16.5 当前趋势350 第17章 存储技术设备352 17.1 什么是闪存352 17.2 linux-mtd子系统353 17.3 映射驱动程序353 17.4 nor芯片驱动程序358 17.5 nand芯片驱动程序359 17.6 用户模块361 17.6.1 块设备模拟361 17.6.2 字符设备模拟361 17.6.3 jffs2362 17.6.4 yaffs2363 17.7 mtd工具363 17.8 配置mtd363 17.9 xip364 17.10 fwh364 17.11 调试367 17.12 查看源代码367 第18章 嵌入式linux369 18.1 挑战369 18.2 元器件选择370 18.3 工具链371 18.4 bootloader372 18.5 内存布局374 18.6 内核移植375 18.7 嵌入式驱动程序376 18.7.1 闪存377 18.7.2 uart377 18.7.3 按钮和滚轮378 18.7.4 pcmcia/cf378 18.7.5 sd/mmc378 18.7.6 usb378 18.7.7 rtc378 18.7.8 音频378 18.7.9 触摸屏379 18.7.10 视频379 18.7.11 cpld/fpga379 18.7.12 连接性379 18.7.13 专用领域电子器件380 18.7.14 更多驱动程序380 18.8 根文件系统380 18.8.1 nfs挂载的根文件系统381 18.8.2 紧凑型中间件382 18.9 测试基础设施383 18.10 调试383 18.10.1 电路板返工384 18.10.2 调试器385 第19章 用户空间的驱动程序386 19.1 进程调度和响应时间387 19.1.1 原先的调度器387 19.1.2 o(1)调度器387 19.1.3 cfs388 19.1.4 响应时间388 19.2 访问i/o区域390 19.3 访问内存区域393 19.4 用户模式scsi395 19.5 用户模式usb397 19.6 用户模式i2c400 19.7 uio401 19.8 查看源代码402 第20章 其他设备驱动程序403 20.1 ecc报告403 20.2 频率调整407 20.3 嵌入式控制器408 20.4 acpi408 20.5 isa与mca410 20.6 火线410 20.7 智能输入/输出411 20.8 业余无线电411 20.9 voip411 20.10 高速互联412 20.10.1 infiniband413 20.10.2 rapidio413 20.10.3 光纤通道413 20.10.4 iscsi413 第21章 调试设备驱动程序414 21.1 kdb414 21.1.1 进入调试器415 21.1.2 kdb415 21.1.3 kgdb417 21.1.4 gdb420 21.1.5 jtag调试器421 21.1.6 下载423 21.2 内核探测器423 21.2.1 kprobe423 21.2.2 jprobe427 21.2.3 返回探针429 21.2.4 局限性431 21.2.5 查看源代码431 21.3 kexec与kdump431 21.3.1 kexec432 21.3.2 kdump与kexec协同工作432 21.3.3 kdump433 21.3.4 查看源代码437 21.4 性能剖析437 21.4.1 利用oprofile剖析内核性能438 21.4.2 利用gprof剖析应用程序性能440 21.5 跟踪441 21.6 ltp444 21.7 uml444 21.8 诊断工具444 21.9 内核修改配置选项444 21.10 测试设备445 第22章 维护与发布446 22.1 代码风格446 22.2 修改标记446 22.3 版本控制447 22.4 一致性检查447 22.5 构建脚本448 22.6 可移植代码450 第23章 结束语451 23.1 流程一览表451 23.2 下一步该做什么452 附录a linux汇编453 附录b linux与bios457 附录c seq文件461
目  录 第1章 引言 1 1.1 演进 1 1.2 gnu copyleft 2 1.3 kernel.org 2 1.4 邮件列表和论坛 3 1.5 linux发行版 3 1.6 查看源代码 4 1.7 编译内核 7 1.8 可加载的模块 8 1.9 整装待发 9 第2章 内核 11 2.1 启动过程 11 2.1.1 bios-provided physical ram map 12 2.1.2 758mb lowmem available 14 2.1.3 kernel command line: ro root=/dev/hda1 14 2.1.4 calibrating delay...1197.46 .bogomips (lpj=2394935) 15 2.1.5 checking hlt instruction 16 2.1.6 net: registered protocol family 2 17 2.1.7 freeing initrd memory: 387k freed 17 2.1.8 io scheduler anticipatory registered (default) 18 2.1.9 setting up standard pci resources 18 2.1.10 ext3-fs: mounted filesystem 19 2.1.11 init: version 2.85 booting 19 2.2 内核模式和用户模式 20 2.3 进程上下文和中断上下文 20 2.4 内核定时器 21 2.4.1 hz和jiffies 21 2.4.2 长延时 22 2.4.3 短延时 24 2.4.4 pentium时间戳计数器 24 2.4.5 实时钟 25 2.5 内核中的并发 26 2.5.1 自旋锁和互斥体 26 2.5.2 原子操作 30 2.5.3 读—写锁 31 2.5.4 调试 32 2.6 proc文件系统 32 2.7 内存分配 33 2.8 查看源代码 34 第3章 内核组件 37 3.1 内核线程 37 3.1.1 创建内核线程 37 3.1.2 进程状态和等待队列 41 3.1.3 用户模式辅助程序 42 3.2 辅助接口 43 3.2.1 链表 44 3.2.2 散列链表 49 3.2.3 工作队列 49 3.2.4 通知链 51 3.2.5 完成接口 54 3.2.6 kthread辅助接口 56 3.2.7 错误处理助手 57 3.3 查看源代码 58 第4章 基本概念 61 4.1 设备驱动程序介绍 61 4.2 中断处理 63 4.2.1 中断上下文 63 4.2.2 分配irq号 64 4.2.3 设备实例:导航杆 65 4.2.4 softirq和tasklet 68 4.3 linux设备模型 71 4.3.1 udev 71 4.3.2 sysfs、kobject和设备类 73 4.3.3 热插拔和冷插拔 76 4.3.4 微码下载 76 4.3.5 模块自动加载 77 4.4 内存屏障 78 4.5 电源管理 79 4.6 查看源代码 79 第5章 字符设备驱动程序 81 5.1 字符设备驱动程序基础 81 5.2 设备实例:系统cmos 82 5.2.1 驱动程序初始化 83 5.2.2 打开与释放 86 5.2.3 数据交换 88 5.2.4 查找 92 5.2.5 控制 94 5.3 检测数据可用性 95 5.3.1 轮询 95 5.3.2 fasync 98 5.4 和并行端口交互 99 5.5 rtc子系统 108 5.6 伪字符驱动程序 109 5.7 混杂驱动程序 110 5.8 字符设备驱动程序注意事项 115 5.9 查看源代码 115 第6章 串行设备驱动程序 118 6.1 层次架构 119 6.2 uart驱动程序 121 6.2.1 设备实例:手机 122 6.2.2 rs-485 132 6.3 tty驱动程序 132 6.4 线路规程 134 6.5 查看源代码 141 第7章 输入设备驱动程序 143 7.1 输入事件驱动程序 144 7.2 输入设备驱动程序 150 7.2.1 serio 150 7.2.2 键盘 150 7.2.3 鼠标 152 7.2.4 触摸控制器 157 7.2.5 加速度传感器 158 7.2.6 输出事件 158 7.3 调试 159 7.4 查看源代码 160 第8章 i2c协议 161 8.1 i2c/smbus是什么 161 8.2 i2c核心 162 8.3 总线事务 164 8.4 设备实例:eeprom 164 8.4.1 初始化 165 8.4.2 探测设备 167 8.4.3 检查适配器的功能 169 8.4.4 访问设备 169 8.4.5 其他函数 170 8.5 设备实例:实时时钟 171 8.6 i2c-dev 174 8.7 使用lm-sensors监控硬件 174 8.8 spi总线 174 8.9 1-wire总线 176 8.10 调试 176 8.11 查看源代码 176 第9章 pcmcia和cf 179 9.1 pcmcia/cf是什么 179 9.2 linux-pcmcia子系统 181 9.3 主机控制器驱动程序 183 9.4 pcmcia核心 183 9.5 驱动程序服务 183 9.6 客户驱动程序 183 9.6.1 数据结构 184 9.6.2 设备实例:pcmcia卡 185 9.7 将零件组装在一起 188 9.8 pcmcia存储 189 9.9 串行pcmcia 189 9.10 调试 191 9.11 查看源代码 191 第10章 pci 193 10.1 pci系列 193 10.2 寻址和识别 195 10.3 访问pci 198 10.3.1 配置区 198 10.3.2 i/o和内存 199 10.4 dma 200 10.5 设备实例:以太网—调制解调器卡 203 10.5.1 初始化和探测 203 10.5.2 数据传输 209 10.6 调试 214 10.7 查看源代码 214 第11章 usb 216 11.1 usb体系架构 216 11.1.1 总线速度 218 11.1.2 主机控制器 218 11.1.3 传输模式 219 11.1.4 寻址 219 11.2 linux-usb子系统 220 11.3 驱动程序的数据结构 221 11.3.1 usb_device结构体 221 11.3.2 urb 222 11.3.3 管道 223 11.3.4 描述符结构 223 11.4 枚举 225 11.5 设备实例:遥测卡 225 11.5.1 初始化和探测过程 226 11.5.2 卡寄存器的访问 230 11.5.3 数据传输 233 11.6 类驱动程序 236 11.6.1 大容量存储设备 236 11.6.2 usb-串行端口转换器 241 11.6.3 人机接口设备 243 11.6.4 蓝牙 243 11.7 gadget驱动程序 243 11.8 调试 244 11.9 查看源代码 245 第12章 视频驱动程序 247 12.1 显示架构 247 12.2 linux视频子系统 249 12.3 显示参数 251 12.4 帧缓冲api 252 12.5 帧缓冲驱动程序 254 12.6 控制台驱动程序 265 12.6.1 设备实例:手机 266 12.6.2 启动logo 270 12.7 调试 270 12.8 查看源代码 271 第13章 音频驱动程序 273 13.1 音频架构 273 13.2 linux声音子系统 275 13.3 设备实例:mp3播放器 277 13.3.1 驱动程序函数和结构体 278 13.3.2 alsa编程 287 13.4 调试 288 13.5 查看源代码 289 第14章 块设备驱动程序 291 14.1 存储技术 291 14.2 linux块i/o层 295 14.3 i/o调度器 295 14.4 块驱动程序数据结构和方法 296 14.5 设备实例:简单存储控制器 298 14.5.1 初始化 299 14.5.2 块设备操作 301 14.5.3 磁盘访问 302 14.6 高级主题 304 14.7 调试 306 14.8 查看源代码 306 第15章 网络接口卡 308 15.1 驱动程序数据结构 308 15.1.1 套接字缓冲区 309 15.1.2 网络设备接口 310 15.1.3 激活 311 15.1.4 数据传输 311 15.1.5 看门狗 311 15.1.6 统计 312 15.1.7 配置 313 15.1.8 总线相关内容 314 15.2 与协议层会话 314 15.2.1 接收路径 314 15.2.2 发送路径 315 15.2.3 流量控制 315 15.3 缓冲区管理和并发控制 315 15.4 设备实例:以太网nic 316 15.5 isa网络驱动程序 321 15.6 atm 321 15.7 网络吞吐量 322 15.7.1 驱动程序性能 322 15.7.2 协议性能 323 15.8 查看源代码 324 第16章 linux无线设备驱动 326 16.1 蓝牙 327 16.1.1 bluez 328 16.1.2 设备实例:cf卡 329 16.1.3 设备实例:usb适配器 330 16.1.4 rfcomm 331 16.1.5 网络 332 16.1.6 hid 334 16.1.7 音频 334 16.1.8 调试 334 16.1.9 关于源代码 334 16.2 红外 335 16.2.1 linux-irda 335 16.2.2 设备实例:超级i/o芯片 337 16.2.3 设备实例:ir dongle 338 16.2.4 ircomm 340 16.2.5 联网 340 16.2.6 irda套接字 341 16.2.7 lirc 341 16.2.8 查看源代码 342 16.3 wifi 343 16.3.1 配置 343 16.3.2 设备驱动程序 346 16.3.3 查看源代码 347 16.4 蜂窝网络 347 16.4.1 gprs 347 16.4.2 cdma 349 16.5 当前趋势 350 第17章 存储技术设备 352 17.1 什么是闪存 352 17.2 linux-mtd子系统 353 17.3 映射驱动程序 353 17.4 nor芯片驱动程序 358 17.5 nand芯片驱动程序 359 17.6 用户模块 361 17.6.1 块设备模拟 361 17.6.2 字符设备模拟 361 17.6.3 jffs2 362 17.6.4 yaffs2 363 17.7 mtd工具 363 17.8 配置mtd 363 17.9 xip 364 17.10 fwh 364 17.11 调试 367 17.12 查看源代码 367 第18章 嵌入式linux 369 18.1 挑战 369 18.2 元器件选择 370 18.3 工具链 371 18.4 bootloader 372 18.5 内存布局 374 18.6 内核移植 375 18.7 嵌入式驱动程序 376 18.7.1 闪存 377 18.7.2 uart 377 18.7.3 按钮和滚轮 378 18.7.4 pcmcia/cf 378 18.7.5 sd/mmc 378 18.7.6 usb 378 18.7.7 rtc 378 18.7.8 音频 378 18.7.9 触摸屏 379 18.7.10 视频 379 18.7.11 cpld/fpga 379 18.7.12 连接性 379 18.7.13 专用领域电子器件 380 18.7.14 更多驱动程序 380 18.8 根文件系统 380 18.8.1 nfs挂载的根文件系统 381 18.8.2 紧凑型中间件 382 18.9 测试基础设施 383 18.10 调试 383 18.10.1 电路板返工 384 18.10.2 调试器 385 第19章 用户空间的驱动程序 386 19.1 进程调度和响应时间 387 19.1.1 原先的调度器 387 19.1.2 o(1)调度器 387 19.1.3 cfs 388 19.1.4 响应时间 388 19.2 访问i/o区域 390 19.3 访问内存区域 393 19.4 用户模式scsi 395 19.5 用户模式usb 397 19.6 用户模式i2c 400 19.7 uio 401 19.8 查看源代码 402 第20章 其他设备驱动程序 403 20.1 ecc报告 403 20.2 频率调整 407 20.3 嵌入式控制器 408 20.4 acpi 408 20.5 isa与mca 410 20.6 火线 410 20.7 智能输入/输出 411 20.8 业余无线电 411 20.9 voip 411 20.10 高速互联 412 20.10.1 infiniband 413 20.10.2 rapidio 413 20.10.3 光纤通道 413 20.10.4 iscsi 413 第21章 调试设备驱动程序 414 21.1 kdb 414 21.1.1 进入调试器 415 21.1.2 kdb 415 21.1.3 kgdb 417 21.1.4 gdb 420 21.1.5 jtag调试器 421 21.1.6 下载 423 21.2 内核探测器 423 21.2.1 kprobe 423 21.2.2 jprobe 427 21.2.3 返回探针 429 21.2.4 局限性 431 21.2.5 查看源代码 431 21.3 kexec与kdump 431 21.3.1 kexec 432 21.3.2 kdump与kexec协同工作 432 21.3.3 kdump 433 21.3.4 查看源代码 437 21.4 性能剖析 437 21.4.1 利用oprofile剖析内核性能 438 21.4.2 利用gprof剖析应用程序性能 440 21.5 跟踪 441 21.6 ltp 444 21.7 uml 444 21.8 诊断工具 444 21.9 内核修改配置选项 444 21.10 测试设备 445 第22章 维护与发布 446 22.1 代码风格 446 22.2 修改标记 446 22.3 版本控制 447 22.4 一致性检查 447 22.5 构建脚本 448 22.6 可移植代码 450 第23章 结束语 451 23.1 流程一览表 451 23.2 下一步该做什么 452 附录a linux汇编 453 附录b linux与bios 457 附录c seq文件 461

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值