Linux下MCP2515驱动解析

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

[cpp]  view plain copy
  1. static int __init mcp251x_init(void)  
  2. {  
  3.     int ret;  
  4.     can_class = class_create(THIS_MODULE, "can");  
  5.     if (IS_ERR(can_class))  
  6.         return PTR_ERR(can_class);  
  7.     ret = alloc_chrdev_region(&devid, 0, CAN_DEV_MAX, DRIVER_NAME);  
  8.     if (ret < 0)  
  9.     {  
  10.         printk(KERN_ERR "%s: failed to allocate char dev region\n", __FILE__);  
  11.         class_destroy(can_class);  
  12.         return ret;  
  13.     }  
  14.   
  15.     return spi_register_driver(&mcp251x_driver);  
  16. }  

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

[cpp]  view plain copy
  1. /** 
  2.  * spi_register_driver - register a SPI driver 
  3.  * @sdrv: the driver to register 
  4.  * Context: can sleep 
  5.  */  
  6. int spi_register_driver(struct spi_driver *sdrv)  
  7. {  
  8.     sdrv->driver.bus = &spi_bus_type;  
  9.     if (sdrv->probe)  
  10.         sdrv->driver.probe = spi_drv_probe;  
  11.     if (sdrv->remove)  
  12.         sdrv->driver.remove = spi_drv_remove;  
  13.     if (sdrv->shutdown)  
  14.         sdrv->driver.shutdown = spi_drv_shutdown;  
  15.     return driver_register(&sdrv->driver);  
  16. }  

spi_register_driver()完成了驱动在总线的挂载以及spi驱动函数probe, remove, shutdown的赋值。那mcp251x_driver是什么样的结构体,里面又存储了什么内容呢?

[cpp]  view plain copy
  1. static struct spi_driver mcp251x_driver = {  
  2.     .driver = {  
  3.                .name = DRIVER_NAME,  
  4.                .bus = &spi_bus_type,  
  5.                .owner = THIS_MODULE,  
  6.                },  
  7.     .probe = mcp251x_probe,  
  8.     .remove = __devexit_p(mcp251x_remove),  
  9. #ifdef CONFIG_PM  
  10.     .suspend = mcp251x_suspend,  
  11.     .resume = mcp251x_resume,  
  12. #endif  
  13. };  

mcp251x_driver是结构体spi_driver的实例,在mcp251x_driver里面完成了mcp251x驱动函数probe, remove, suspend, resume的赋值。

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

[cpp]  view plain copy
  1. struct mcp251x  
  2. {  
  3.     struct cdev cdev;  
  4.     struct class_device *class_dev;  
  5.     struct semaphore lock;      /* semaphore for spi bus share. */  
  6.     struct semaphore rxblock;   /* semaphore for ring buffer of receive. */  
  7.     struct semaphore txblock;   /* semaphore for ring buffer of send. */  
  8.   
  9.     uint8_t *spi_transfer_buf;  /* temp buffer for spi bus transfer. */  
  10.   
  11.     struct can_frame rxb[MCP251X_BUF_LEN];  /* ring buffer for receive. */  
  12.     struct can_frame txb[MCP251X_BUF_LEN];  /* ring buffer for send. */  
  13.   
  14.     int txbin;                  /* pos of in for ring buffer of sned. */  
  15.     int txbout;                 /* pos of out for ring buffer of send. */  
  16.     int rxbin;                  /* pos of in for ring buffer of receive. */  
  17.     int rxbout;                 /* pos of out for ring buffer of receive. */  
  18.   
  19.     int bit_rate;               /* save bit rate of current set. */  
  20.     int count;                  /* count of the device opened. */  
  21.   
  22.     wait_queue_head_t wq;       /* queue for read process. */  
  23.   
  24.     struct work_struct irq_work;    /* bottom half of interrupt task. */  
  25.   
  26.     struct spi_device *spi;     /* save the point of struce spi_device. */  
  27.     struct can_filter filter;   /* save the filter data of current set. */  
  28. };  
其中的很多结构体,我们暂时不管。下面开始进入正题。

[cpp]  view plain copy
  1. static int __devinit mcp251x_probe(struct spi_device *spi)  
  2. {  
  3.     struct mcp251x *chip;  
  4.     int ret = 0;  
  5.   
  6.     dev_dbg(&spi->dev, "%s: start\n", __FUNCTION__);  
  7.   
  8.     /* 申请内存资源 */  
  9.     chip = kmalloc(sizeof(struct mcp251x), GFP_KERNEL);  
  10.     if (!chip)  
  11.     {  
  12.         ret = -ENOMEM;  
  13.         goto error_alloc;  
  14.     }  
  15.   
  16.     /* 将mcp251x的设备信息保存到spi的设备结构体中 */  
  17.     dev_set_drvdata(&spi->dev, chip);  
  18.   
  19.     /* mcp251x结构体初始化 */  
  20.     chip->txbin = chip->txbout = 0;  
  21.     chip->rxbin = chip->rxbout = 0;  
  22.     chip->count = 0;  
  23.     chip->spi = spi;  
  24.     init_MUTEX(&chip->lock);  
  25.     init_MUTEX(&chip->txblock);  
  26.     init_MUTEX(&chip->rxblock);  
  27.     init_waitqueue_head(&chip->wq);  
  28.   
  29. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))  
  30.     INIT_WORK(&chip->irq_work, mcp251x_irq_handler);  
  31. #else  
  32.     INIT_WORK(&chip->irq_work, mcp251x_irq_handler, spi);  
  33. #endif  
  34.   
  35.     /* 为spi的buf分配空间 */  
  36.     chip->spi_transfer_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);  
  37.     if (!chip->spi_transfer_buf)  
  38.     {  
  39.         ret = -ENOMEM;  
  40.         goto error_buf;  
  41.     }  
  42.       
  43.     /* 输入模式,不使用内部上拉电阻 */  
  44.     at91_set_gpio_input(spi->irq, 0);  
  45.     /* 绑定输入函数mcp251x_irq,传递参数spi */  
  46.     /* mcp251x_irq函数我们等下再看,先放一放 */  
  47.     ret = request_irq(spi->irq, mcp251x_irq, IRQF_SAMPLE_RANDOM, DRIVER_NAME, spi);  
  48.     if (ret < 0)  
  49.     {  
  50.         dev_err(&spi->dev, "request irq %d failed (ret = %d)\n", spi->irq, ret);  
  51.         goto error_irq;  
  52.     }  
  53.   
  54.     if (can_minor > CAN_DEV_MAX)  
  55.         goto error_register;  
  56.   
  57.     if (can_major)  
  58.     {  
  59.         devid = MKDEV(can_major, can_minor++);  
  60.         ret = register_chrdev_region(devid, 0, DRIVER_NAME);  
  61.     }  
  62.     else  
  63.     {  
  64.         ret = alloc_chrdev_region(&devid, can_minor, 0, DRIVER_NAME);  
  65.         can_major = MAJOR(devid);  
  66.     }  
  67.   
  68.     if (ret < 0)  
  69.     {  
  70.         dev_err(&spi->dev, "register char device region (%d:%d) failed (ret = %d)\n", MAJOR(devid),  
  71.                 MINOR(devid), ret);  
  72.         goto error_register;  
  73.     }  
  74.   
  75.     /* 字符设备的初始化以及添加到内核 */  
  76.     cdev_init(&chip->cdev, &mcp251x_fops);  
  77.     chip->cdev.owner = THIS_MODULE;  
  78.     ret = cdev_add(&chip->cdev, devid, 1);  
  79.     if (ret < 0)  
  80.     {  
  81.         dev_err(&spi->dev, "register char device failed (ret = %d)\n", ret);  
  82.         goto error_devadd;  
  83.     }  
  84.   
  85.     dev_info(&spi->dev, "device register at dev(%d:%d)\n", MAJOR(devid), MINOR(devid));  
  86.   
  87.     /* 自动创建设备文件 */  
  88.     chip->class_dev = device_create(can_class, NULL,  
  89.                                     MKDEV(MAJOR(devid), can_minor), &spi->dev, "can%d", can_minor);  
  90.     if (IS_ERR(chip->class_dev))  
  91.     {  
  92.         dev_err(&spi->dev, "cannot create CAN class device\n");  
  93.         ret = PTR_ERR(chip->class_dev);  
  94.         goto error_class_reg;  
  95.     }  
  96.   
  97.     /* mcp251x初始化设置 */  
  98.     mcp251x_hw_init(spi);  
  99.     mcp251x_set_bit_rate(spi, 125000);  /* A reasonable default */  
  100.     mcp251x_hw_sleep(spi);  
  101.   
  102.     return 0;  
  103.   
  104.   error_class_reg:  
  105.     cdev_del(&chip->cdev);  
  106.   error_devadd:  
  107.     unregister_chrdev_region(devid, 0);  
  108.   error_register:  
  109.     free_irq(spi->irq, spi);  
  110.   error_irq:  
  111.     kfree(chip->spi_transfer_buf);  
  112.   error_buf:  
  113.     kfree(chip);  
  114.   error_alloc:  
  115.     return ret;  
  116. }  
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
由于mcp2515是一种CAN控制器,因此我们需要一个CAN总线控制器来和mcp2515交互,这里以socketCAN为例进行说明。socketCAN是一个基于AF_CAN协议簇的套接字接口,可以用它来实现一个虚拟的CAN总线,并通过can-utils等工具来进行CAN数据的发送和接收。 下面是一个简单的mcp2515驱动代码示例: ```c #include <linux/module.h> #include <linux/version.h> #include <linux/kernel.h> #include <linux/spi/spi.h> #include <linux/can.h> #include <linux/skbuff.h> #include <linux/netlink.h> static struct spi_device *spi_dev; static struct net_device *can_dev; static struct can_priv *can_priv; static const struct spi_device_id mcp2515_id[] = { {"mcp2515", 0}, {} }; MODULE_DEVICE_TABLE(spi, mcp2515_id); static int mcp2515_probe(struct spi_device *spi) { int ret; struct net_device *dev; struct can_priv *priv; spi_dev = spi; dev = alloc_can_dev(&spi->dev); if (!dev) { dev_err(&spi->dev, "Failed to allocate CAN device\n"); return -ENOMEM; } priv = netdev_priv(dev); can_priv = priv; /* 修改设备名称 */ strcpy(dev->name, "can0"); /* 设置设备参数 */ priv->can.state = CAN_STATE_STOPPED; priv->can.bittiming = &mcp2515_bittiming_const; priv->can.do_set_mode = mcp2515_do_set_mode; priv->can.echo_skb = mcp2515_echo_skb; priv->can.do_tx = mcp2515_do_tx; priv->can.do_rx = mcp2515_do_rx; /* 初始化SPI设备 */ ret = spi_setup(spi); if (ret < 0) { dev_err(&spi->dev, "Failed to setup SPI device\n"); free_can_dev(dev); return ret; } ret = register_netdev(dev); if (ret < 0) { dev_err(&spi->dev, "Failed to register CAN device\n"); free_can_dev(dev); return ret; } can_dev = dev; return 0; } static int mcp2515_remove(struct spi_device *spi) { unregister_netdev(can_dev); free_can_dev(can_dev); return 0; } static const struct of_device_id mcp2515_of_match[] = { { .compatible = "microchip,mcp2515", }, {}, }; MODULE_DEVICE_TABLE(of, mcp2515_of_match); static struct spi_driver mcp2515_driver = { .driver = { .name = "mcp2515", .owner = THIS_MODULE, .of_match_table = mcp2515_of_match, }, .probe = mcp2515_probe, .remove = mcp2515_remove, .id_table = mcp2515_id, }; module_spi_driver(mcp2515_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Linux_Studio"); MODULE_DESCRIPTION("MCP2515 CAN driver"); ``` 在这里我们主要是定义了一个SPI控制器的驱动程序,并在其probe函数中完成了CAN设备的初始化,包括设备的名称、参数和相关的回调函数等。在这里我们只是演示了一些基本的操作,实际的驱动程序应该还需要实现数据的接收和发送等核心功能。 需要注意的是,在这个驱动程序中,我们使用了一个can_priv结构体来存储CAN控制器的私有数据,这个结构体的定义如下: ```c struct can_priv { struct can_dev can; struct net_device *dev; struct net_device_stats stats; spinlock_t lock; struct sk_buff_head rx_queue; }; ``` 我们还需要实现一些CAN控制器的回调函数,包括mcp2515_bittiming_const、mcp2515_do_set_mode、mcp2515_echo_skb、mcp2515_do_tx和mcp2515_do_rx等函数,这里就不再给出具体的代码了。需要注意的是,这些回调函数可以根据实际需求来修改或扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值