📄 mcp2515.c
字号:
dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __FUNCTION__, ret); frame->header.id = (rx_buf[0] << 3) | (rx_buf[1] >> 5); frame->header.ide = (rx_buf[1] >> 3) & 0x1; frame->header.srr = (rx_buf[1] >> 4) & 0x1; if (frame->header.ide == 1) frame->header.eid = (rx_buf[1] << 16) | (rx_buf[2] << 8) | rx_buf[3]; else frame->header.eid = 0; frame->header.rtr = (rx_buf[4] >> 6) & 0x1; frame->header.rb1 = (rx_buf[4] >> 5) & 0x1; frame->header.rb0 = (rx_buf[4] >> 4) & 0x1; frame->header.dlc = rx_buf[4] & 0x0f; memcpy(frame->data, rx_buf + 5, CAN_FRAME_MAX_DATA_LEN); up(&chip->lock); /* update pos of ring buffer */ chip->rxbin++; if (chip->rxbin >= MCP251X_BUF_LEN) chip->rxbin = 0; up(&chip->rxblock); return 0;}/* ........................................................................ *//* bottom half task for interrupt */#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))static void mcp251x_irq_handler(struct work_struct *work){ struct mcp251x *chip = container_of(work, struct mcp251x, irq_work); struct spi_device *spi = chip->spi;#elsestatic void mcp251x_irq_handler(void *dev_id){ struct spi_device *spi = dev_id; struct mcp251x *chip = dev_get_drvdata(&spi->dev);#endif uint8_t intf, rxs; for(;;) { intf = mcp251x_read_reg(spi, CANINTF); if (intf == 0x00) break; dev_dbg(&spi->dev, "interrupt:%s%s%s%s%s%s%s%s\n", (intf & CANINTF_MERRF) ? " MERR":"", (intf & CANINTF_WAKIF) ? " WAK":"", (intf & CANINTF_ERRIF) ? " ERR":"", (intf & CANINTF_TX2IF) ? " TX2":"", (intf & CANINTF_TX1IF) ? " TX1":"", (intf & CANINTF_TX0IF) ? " TX0":"", (intf & CANINTF_RX1IF) ? " RX1":"", (intf & CANINTF_RX0IF) ? " RX0":""); rxs = mcp251x_read_state(spi, INSTRUCTION_RX_STATE); dev_dbg(&spi->dev, "rx_state:%s%s\n", (rxs & RX_STATE_IDE) ? " IDE":"", (rxs & RX_STATE_RTR) ? " RTR":""); if (intf & CANINTF_MERRF) {#if 0 uint8_t txbnctrl; /* if there are no pending Tx buffers, restart queue */ txbnctrl = mcp251x_read_reg(spi, TXBCTRL(0)); if (!(txbnctrl & TXBCTRL_TXREQ)) netif_wake_queue(&chip->can->ndev);#endif } if (intf & CANINTF_ERRIF) { uint8_t eflg = mcp251x_read_reg(spi, EFLG); dev_dbg(&spi->dev, "EFLG = 0x%02x\n", eflg); if (eflg & (EFLG_RX0OVR | EFLG_RX1OVR)) {#if 0 if (eflg & EFLG_RX0OVR) chip->stats.rx_over_errors++; if (eflg & EFLG_RX1OVR) chip->stats.rx_over_errors++;#endif mcp251x_write_reg(spi, EFLG, 0x00); } } if (intf & CANINTF_TX0IF) /* If ready to send, copy data to send buffer. */ mcp251x_hw_tx(spi, 0); if (intf & CANINTF_TX1IF) mcp251x_hw_tx(spi, 1); if (intf & CANINTF_TX2IF) mcp251x_hw_tx(spi, 2); if (intf & CANINTF_RX0IF) /* If received data, copy data to ring buffer. */ mcp251x_hw_rx(spi, 0); if (intf & CANINTF_RX1IF) mcp251x_hw_rx(spi, 1); mcp251x_write_bits(spi, CANINTF, intf, 0x00); /* If ring buffer of receive is not empty, wake up the read queue. */ if (chip->rxbin != chip->rxbout) wake_up_interruptible(&chip->wq); }}static irqreturn_t mcp251x_irq(int irq, void *dev_id, struct pt_regs *regs){ struct spi_device *spi = dev_id; struct mcp251x *chip = dev_get_drvdata(&spi->dev); /* Can't do anything in interrupt context so fire of the interrupt * handling workqueue. */ schedule_work(&chip->irq_work); return IRQ_HANDLED;}/* ........................................................................ */static int mcp251x_open(struct inode *inode, struct file *file){ struct mcp251x *chip = container_of(inode->i_cdev, struct mcp251x, cdev); struct spi_device *spi = chip->spi; struct mcp251x_platform_data *pdata = spi->dev.platform_data; file->private_data = chip; if (!chip->count) { if (pdata->transceiver_enable) pdata->transceiver_enable(1); mcp251x_hw_wakeup(spi); /* Enable interrupts */ mcp251x_write_reg(spi, CANINTE, CANINTE_ERRIE | CANINTE_TX2IE | CANINTE_TX1IE | CANINTE_TX0IE | CANINTE_RX1IE | CANINTE_RX0IE); /* put device into normal mode */ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_NORMAL); mcp251x_write_reg(spi, RXBCTRL(0), RXBCTRL_BULK); } chip->count++; return 0;}static int mcp251x_release(struct inode *inode, struct file *file){ struct mcp251x *chip = container_of(inode->i_cdev, struct mcp251x, cdev); struct spi_device *spi = chip->spi; struct mcp251x_platform_data *pdata = spi->dev.platform_data; chip->count--; if (chip->count) return 0; /* disable and clear pending interrupts */ mcp251x_write_reg(spi, CANINTE, 0x00); mcp251x_write_reg(spi, CANINTF, 0x00); /* go to sleep */ mcp251x_hw_sleep(spi); if (pdata->transceiver_enable) pdata->transceiver_enable(0); return 0;}static int mcp251x_write(struct file *file, const char __user *buf, size_t count, loff_t *ofs){ struct mcp251x *chip = file->private_data; struct spi_device *spi = chip->spi; struct can_frame *frame; int ret; uint8_t txreq; if (count < sizeof(struct can_frame)) return -EINVAL; if (down_interruptible(&chip->txblock)) return -ERESTARTSYS; frame = &chip->txb[chip->txbin]; ret = copy_from_user(frame, buf, sizeof(struct can_frame)); chip->txbin++; if (chip->txbin >= MCP251X_BUF_LEN) chip->txbin = 0; up(&chip->txblock); txreq = mcp251x_read_state(spi, INSTRUCTION_CAN_STATE); if (!(txreq & CAN_STATE_TX0REQ)) mcp251x_hw_tx(spi, 0); if (!(txreq & CAN_STATE_TX1REQ)) mcp251x_hw_tx(spi, 1); if (!(txreq & CAN_STATE_TX2REQ)) mcp251x_hw_tx(spi, 2); return count;}static ssize_t mcp251x_read(struct file *file, char __user *buf, size_t count, loff_t *ofs){ struct mcp251x *chip = file->private_data; struct can_frame *frame; if (count != sizeof(struct can_frame)) return -EINVAL; if (down_interruptible(&chip->rxblock)) return -ERESTARTSYS; while (chip->rxbin == chip->rxbout) { up(&chip->rxblock); if (file->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(chip->wq, (chip->rxbin != chip->rxbout))) return -ERESTARTSYS; if (down_interruptible(&chip->rxblock)) return -ERESTARTSYS; } frame = &chip->rxb[chip->rxbout]; if (copy_to_user(buf, frame, sizeof(struct can_frame))) { up(&chip->rxblock); return -EFAULT; } chip->rxbout++; if(chip->rxbout >= MCP251X_BUF_LEN) chip->rxbout = 0; up(&chip->rxblock); return count; #if 0retry: if (chip->rxbin != chip->rxbout) { down(&chip->rxblock); frame = &chip->rxb[chip->rxbout]; if (copy_to_user(buf, frame, sizeof(struct can_frame))) { up(&chip->rxblock); return -EFAULT; } chip->rxbout++; if(chip->rxbout >= MCP251X_BUF_LEN) chip->rxbout = 0; up(&chip->rxblock); return count; } else { if (file->f_flags & O_NONBLOCK) return -EAGAIN; interruptible_sleep_on(&chip->wq); if (signal_pending(current)) return -ERESTARTSYS; goto retry; }#endif}static int mcp251x_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct mcp251x *chip = container_of(inode->i_cdev, struct mcp251x, cdev); struct spi_device *spi = chip->spi; int ret = 0; switch(cmd) { case CAN_IOCTRESET: /* reset devcie */ mcp251x_hw_reset(spi); break; case CAN_IOCTWAKEUP: /* wake up device */ mcp251x_hw_wakeup(spi); break; case CAN_IOCSRATE: /* set bit rate */ ret = mcp251x_set_bit_rate(spi, (int)arg); mdelay(10); break; case CAN_IOCGRATE: /* get bit rate */ *((int *)arg) = mcp251x_get_bit_rate(spi); break; case CAN_IOCSFILTER: /* set filter */ ret = mcp251x_set_filter(spi, (struct can_filter *)arg); break; case CAN_IOCGFILTER: /* get filter */ ret = mcp251x_get_filter(spi, (struct can_filter *)arg); break; case CAN_IOCTNORMALMODE: /* turn to normal mode */ mcp251x_write_bits(spi, CANCTRL, CANCTRL_REQOP_MASK, CANCTRL_REQOP_NORMAL); break; case CAN_IOCTLOOPBACKMODE: /* turn to loopback mode */ mcp251x_write_bits(spi, CANCTRL, CANCTRL_REQOP_MASK, CANCTRL_REQOP_LOOPBACK); break; case CAN_IOCTLISTENONLYMODE: /* turn to listen only mode */ mcp251x_write_bits(spi, CANCTRL, CANCTRL_REQOP_MASK, CANCTRL_REQOP_LISTEN_ONLY); break; case CAN_IOCTSLEEPMODE: /* turn to sleep mode */ mcp251x_hw_sleep(spi); break; default: return -ENOTTY; } return ret;}static const struct file_operations mcp251x_fops = { .owner = THIS_MODULE, .read = mcp251x_read, .write = mcp251x_write, .ioctl = mcp251x_ioctl, .open = mcp251x_open, .release = mcp251x_release,};/* ........................................................................ */static int mcp251x_remove(struct spi_device *spi){ struct mcp251x *chip = dev_get_drvdata(&spi->dev); dev_dbg(&spi->dev, "%s: stop\n", __FUNCTION__); cdev_del(&chip->cdev); free_irq(spi->irq, spi); kfree(chip->spi_transfer_buf); return 0;}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; } dev_set_drvdata(&spi->dev, chip); 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 chip->spi_transfer_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL); if (!chip->spi_transfer_buf) { ret = -ENOMEM; goto error_buf; } ret = request_irq(spi->irq, mcp251x_irq, SA_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_MAX_DEV) 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)); mcp251x_hw_init(spi); mcp251x_set_bit_rate(spi, 125000); /* A reasonable default */ mcp251x_hw_sleep(spi); return 0; 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;}#ifdef CONFIG_PMstatic int mcp251x_suspend(struct spi_device *spi, pm_message_t mesg){ struct mcp251x *chip = dev_get_drvdata(&spi->dev); struct mcp251x_platform_data *pdata = spi->dev.platform_data; if (chip->count) return 0; mcp251x_hw_sleep(spi); if (pdata->transceiver_enable) pdata->transceiver_enable(0); return 0;}static int mcp251x_resume(struct spi_device *spi){ struct mcp251x *chip = dev_get_drvdata(&spi->dev); struct mcp251x_platform_data *pdata = spi->dev.platform_data; if (!chip->count) return 0; if (pdata->transceiver_enable) pdata->transceiver_enable(1); mcp251x_hw_wakeup(spi); return 0;}#endifstatic 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};static int __init mcp251x_init(void){ return spi_register_driver(&mcp251x_driver);}module_init(mcp251x_init);static void __exit mcp251x_exit(void){ spi_unregister_driver(&mcp251x_driver);}module_exit(mcp251x_exit);MODULE_DESCRIPTION("MCP251x CAN controller driver");MODULE_LICENSE("GPL");MODULE_AUTHOR("Renzhou.Meng");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -