Chinaunix首页 | 论坛 | 博客
  • 博客访问: 117683
  • 博文数量: 19
  • 博客积分: 50
  • 博客等级: 民兵
  • 技术积分: 140
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-08 23:04
文章分类

全部博文(19)

文章存档

2016年(1)

2015年(1)

2014年(4)

2013年(10)

2012年(3)

我的朋友

分类: 嵌入式

2013-11-04 22:26:00

拿到一个设备驱动,首先要看的是设备初始化函数。

static int __init mcp251x_init(void)  
{  
    int ret;  
    can_class = class_create(THIS_MODULE, "can");  
    if (IS_ERR(can_class))  
        return PTR_ERR(can_class);  
    ret = alloc_chrdev_region(&devid, 0, CAN_DEV_MAX, DRIVER_NAME);  
    if (ret < 0)  
    {  
        printk(KERN_ERR "%s: failed to allocate char dev region\n", __FILE__);  
        class_destroy(can_class);  
        return ret;  
    }  
  
    return spi_register_driver(&mcp251x_driver);  

class_create()用于自动创建设备节点,我们可以暂时不看,有兴趣的可以看看Linux源码。alloc_chrdev_region()自动为DRIVER_NAME分配设备号。在这里,我们真正关心的是spi_register_driver()函数和mcp251x_driver结构体的内容。
我们先看spi_register_driver()的内容。
/** 
 * 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);  

spi_register_driver()完成了驱动在总线的挂载以及spi驱动函数probe, remove, shutdown的赋值。那mcp251x_driver是什么样的结构体,里面又存储了什么内容呢?
static struct spi_driver mcp251x_driver = {  
    .driver = {  
               .name = DRIVER_NAME,  
               .bus = &spi_bus_type,  
               .owner = THIS_MODULE,  
               },  
    .probe = mcp251x_probe,  
    .remove = __devexit_p(mcp251x_remove),  
#ifdef CONFIG_PM   
    .suspend = mcp251x_suspend,  
    .resume = mcp251x_resume,  
#endif   
}; 
mcp251x_driver是结构体spi_driver的实例,在mcp251x_driver里面完成了mcp251x驱动函数probe, remove, suspend, resume的赋值。


在进入mcp251x的驱动函数之前,我们还是先看看mcp251x的结构吧!

struct mcp251x  
{  
    struct cdev cdev;  
    struct class_device *class_dev;  
    struct semaphore lock;      /* semaphore for spi bus share. */  
    struct semaphore rxblock;   /* semaphore for ring buffer of receive. */  
    struct semaphore txblock;   /* semaphore for ring buffer of send. */  
  
    uint8_t *spi_transfer_buf;  /* temp buffer for spi bus transfer. */  
  
    struct can_frame rxb[MCP251X_BUF_LEN];  /* ring buffer for receive. */  
    struct can_frame txb[MCP251X_BUF_LEN];  /* ring buffer for send. */  
  
    int txbin;                  /* pos of in for ring buffer of sned. */  
    int txbout;                 /* pos of out for ring buffer of send. */  
    int rxbin;                  /* pos of in for ring buffer of receive. */  
    int rxbout;                 /* pos of out for ring buffer of receive. */  
  
    int bit_rate;               /* save bit rate of current set. */  
    int count;                  /* count of the device opened. */  
  
    wait_queue_head_t wq;       /* queue for read process. */  
  
    struct work_struct irq_work;    /* bottom half of interrupt task. */  
  
    struct spi_device *spi;     /* save the point of struce spi_device. */  
    struct can_filter filter;   /* save the filter data of current set. */  
}; 
其中的很多结构体,我们暂时不管。下面开始进入正题。
static int __devinit mcp251x_probe(struct spi_device *spi)  
{  
    struct mcp251x *chip;  
    int ret = 0;  
  
    dev_dbg(&spi->dev, "%s: start\n", __FUNCTION__);  
  
    /* 申请内存资源 */  
    chip = kmalloc(sizeof(struct mcp251x), GFP_KERNEL);  
    if (!chip)  
    {  
        ret = -ENOMEM;  
        goto error_alloc;  
    }  
  
    /* 将mcp251x的设备信息保存到spi的设备结构体中 */  
    dev_set_drvdata(&spi->dev, chip);  
  
    /* mcp251x结构体初始化 */  
    chip->txbin = chip->txbout = 0;  
    chip->rxbin = chip->rxbout = 0;  
    chip->count = 0;  
    chip->spi = spi;  
    init_MUTEX(&chip->lock);  
    init_MUTEX(&chip->txblock);  
    init_MUTEX(&chip->rxblock);  
    init_waitqueue_head(&chip->wq);  
  
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))   
    INIT_WORK(&chip->irq_work, mcp251x_irq_handler);  
#else   
    INIT_WORK(&chip->irq_work, mcp251x_irq_handler, spi);  
#endif   
  
    /* 为spi的buf分配空间 */  
    chip->spi_transfer_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);  
    if (!chip->spi_transfer_buf)  
    {  
        ret = -ENOMEM;  
        goto error_buf;  
    }  
      
    /* 输入模式,不使用内部上拉电阻 */  
    at91_set_gpio_input(spi->irq, 0);  
    /* 绑定输入函数mcp251x_irq,传递参数spi */  
    /* mcp251x_irq函数我们等下再看,先放一放 */  
    ret = request_irq(spi->irq, mcp251x_irq, IRQF_SAMPLE_RANDOM, DRIVER_NAME, spi);  
    if (ret < 0)  
    {  
        dev_err(&spi->dev, "request irq %d failed (ret = %d)\n", spi->irq, ret);  
        goto error_irq;  
    }  
  
    if (can_minor > CAN_DEV_MAX)  
        goto error_register;  
  
    if (can_major)  
    {  
        devid = MKDEV(can_major, can_minor++);  
        ret = register_chrdev_region(devid, 0, DRIVER_NAME);  
    }  
    else  
    {  
        ret = alloc_chrdev_region(&devid, can_minor, 0, DRIVER_NAME);  
        can_major = MAJOR(devid);  
    }  
  
    if (ret < 0)  
    {  
        dev_err(&spi->dev, "register char device region (%d:%d) failed (ret = %d)\n", MAJOR(devid),  
                MINOR(devid), ret);  
        goto error_register;  
    }  
  
    /* 字符设备的初始化以及添加到内核 */  
    cdev_init(&chip->cdev, &mcp251x_fops);  
    chip->cdev.owner = THIS_MODULE;  
    ret = cdev_add(&chip->cdev, devid, 1);  
    if (ret < 0)  
    {  
        dev_err(&spi->dev, "register char device failed (ret = %d)\n", ret);  
        goto error_devadd;  
    }  
  
    dev_info(&spi->dev, "device register at dev(%d:%d)\n", MAJOR(devid), MINOR(devid));  
  
    /* 自动创建设备文件 */  
    chip->class_dev = device_create(can_class, NULL,  
                                    MKDEV(MAJOR(devid), can_minor), &spi->dev, "can%d", can_minor);  
    if (IS_ERR(chip->class_dev))  
    {  
        dev_err(&spi->dev, "cannot create CAN class device\n");  
        ret = PTR_ERR(chip->class_dev);  
        goto error_class_reg;  
    }  
  
    /* mcp251x初始化设置 */  
    mcp251x_hw_init(spi);  
    mcp251x_set_bit_rate(spi, 125000);  /* A reasonable default */  
    mcp251x_hw_sleep(spi);  
  
    return 0;  
  
  error_class_reg:  
    cdev_del(&chip->cdev);  
  error_devadd:  
    unregister_chrdev_region(devid, 0);  
  error_register:  
    free_irq(spi->irq, spi);  
  error_irq:  
    kfree(chip->spi_transfer_buf);  
  error_buf:  
    kfree(chip);  
  error_alloc:  
    return ret;  

阅读(3098) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~