mcpcan.c
上传用户:plenty2008
上传日期:2022-08-02
资源大小:11k
文件大小:20k
源码类别:

驱动编程

开发平台:

Unix_Linux

  1. #include <linux/unistd.h> #include <linux/module.h> #include <asm-arm/arch-s3c2410/S3C2410.h> #include <asm-arm/arch-s3c2410/irqs.h> #if defined(CONFIG_SMP) #define __SMP__ #endif #if defined(CONFIG_MODVERSIONS) #define MODVERSIONS #endif #include <linux/sched.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/errno.h>  /* for -EBUSY */ //#include <linux/malloc.h> #include <linux/slab.h> #include <linux/ioport.h> /* for verify_area */ #include <linux/mm.h> #include <linux/interrupt.h> #include <linux/tqueue.h> #include <linux/timer.h> #include <linux/delay.h> #include <linux/types.h> #include <linux/init.h> /* for module_init */ #include <linux/module.h> #include <asm/io.h> #include <asm/segment.h> #include <asm/irq.h> #include <asm/signal.h> #include <asm/siginfo.h> #include <asm/uaccess.h>   /* for get_user and put_user */ #include <asm/fcntl.h> #include <asm/cache.h> #include <asm/atomic.h> //#include <asm-arm/arch-s3c2410/S3C2410_2.h> #include <sys/syscall.h> //#include <asm-arm/arch-s3c2410/S3C2410_2.h> //#include <asm-arm/arch-s3c2410/S3C2410_2.h> //#include "S3C2410_2.h" #include "mcpcan.h" #include "spi_cmd.h"
  2. #include    "spi.h" #define MCP2510_FLAG
  3. #define BUF_SIZE sizeof(struct MCP_device) * 100 //#define DEBUG #ifdef DEBUG #define DbgPrintk(S) printk(S) #else #define DbgPrintk(S) #endif struct MCP_device {        volatile unsigned long RecvBuf;        volatile unsigned long RecvHead;        volatile unsigned long RecvTail;        volatile unsigned long RecvNum;                                                                                                                                                        wait_queue_head_t inq;   //or use as a global variable                                                                                                                                                        struct semaphore sem;    //semaphore used for mutex                                                                                                                                                        unsigned int      IrqNum;        unsigned int      MinorNum;/*device minor number*/        unsigned int      BaudRate;                                                                                                                                                        unsigned char     *SendBuf;        unsigned int      SendNum;        volatile unsigned long SendHead;        volatile unsigned long SendTail; }__attribute__((aligned(L1_CACHE_BYTES),packed));                                                                                                                                                 /*****************************Variable define*******************************/ unsigned int MCP_major = MCP_MAJOR; struct MCP_device *Device[MCP_NUM]; unsigned int MCP_irq[MCP_NUM] = {IRQ_EINT4_7,IRQ_EINT8_23}; static char *MCP_name[MCP_NUM] = {"mcpcan0","mcpcan1"}; /*****************************SJA device initialize*************************/ //这个函数用来对struct MCP_device进行初始化 static int MCP_device_init(void) {         int res,i;         for(i=0;i<MCP_NUM;i++) {                 Device[i] = kmalloc(sizeof(struct MCP_device),GFP_KERNEL);  //malloc the device struct                 if(Device[i] == NULL) {                         printk(KERN_ALERT"allocate device memory failed.n");                         return(-ENOMEM);                 }                 memset((char *)Device[i],0,sizeof(struct MCP_device));                 //Device[i]->RecvBuf = (unsigned char *)get_free_page(GFP_KERNEL);  //malloc the recv buffer                 Device[i]->RecvBuf = (unsigned long)kmalloc(sizeof(struct MCP_device) * 100,GFP_KERNEL);  //malloc the recv buffer                 if(Device[i]->RecvBuf == 0) {                         printk(KERN_ALERT"allocate Recv memory failed.n");                         return(-ENOMEM);                 }                 memset((char *)Device[i]->RecvBuf,0,sizeof(struct MCP_device) * 100);                 Device[i]->RecvHead = Device[i]->RecvBuf;                 Device[i]->RecvTail = Device[i]->RecvBuf;                 Device[i]->RecvNum = 0;                 //Device[i]->RecvQueue = NULL;                                  //Device[i]->SendBuf = (unsigned char *)get_free_page(GFP_KERNEL);   //malloc the send buffer                 Device[i]->SendBuf = kmalloc(sizeof(struct MCP_device) * 100,GFP_KERNEL);  //malloc the send buffer                 if(Device[i]->SendBuf == NULL) {                         printk(KERN_ALERT"allocate Send memory failed.n");                         return(-ENOMEM);                 }                 memset((char *)Device[i]->SendBuf,0,sizeof(struct MCP_device) * 100);                 Device[i]->MinorNum = i;                 //Device[i]->FrameMode = 1;                 Device[i]->IrqNum = MCP_irq[i];                 //Device[i]->BaudRate = MCP_baudrate[i]; init_waitqueue_head(&(Device[i]->inq)); //init_waitqueue_head(&(Device[i]->outq)); init_MUTEX(&(Device[i]->sem)); }         return 0; } /*****************************MCP device initialize*************************/ /* Initialize the mcp2510 */
  4. void MCP2510_Init(int MinorNum )
  5. {
  6. // Configure the SPI Interface in MX1
  7. //int canstat = 0; SPI_Init( ); 
  8. // Configure the mcp2510 through spi interface
  9. // Reset controller
  10. CAN_SPI_CMD( SPI_CMD_RESET, ARG_UNUSED, ARG_UNUSED, ARG_UNUSED );
  11. CAN_SPI_CMD( SPI_CMD_BITMOD, TOLONG(&(MCP2510_MAP->CANCTRL)), 0xe0, 0x80 ); // CONFIG MODE
  12. // make sure we are in configuration mode
  13. while( (CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->CANSTAT)), ARG_UNUSED, ARG_UNUSED )>>5)!=0x04 );
  14. // start configuration
  15. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->BFPCTRL)),  BFPCTRL_INIT_VAL,  ARG_UNUSED);
  16. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->TXRTSCTRL)),  TXRTSCTRL_INIT_VAL, ARG_UNUSED);
  17. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->CNF3)),  CNF3_INIT_VAL,  ARG_UNUSED);
  18. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->CNF2)),  CNF2_INIT_VAL,  ARG_UNUSED);
  19. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->CNF1)), CNF1_INIT_VAL,  ARG_UNUSED);
  20. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->CANINTE)), CANINTE_INIT_VAL,  ARG_UNUSED);
  21. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->CANINTF)),  CANINTF_INIT_VAL,  ARG_UNUSED);
  22. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->EFLG)),  EFLG_INIT_VAL,  ARG_UNUSED);
  23. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->TXB0CTRL)),  TXBnCTRL_INIT_VAL,  ARG_UNUSED);
  24. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->TXB1CTRL)),  TXBnCTRL_INIT_VAL,  ARG_UNUSED);
  25. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->TXB2CTRL)),  TXBnCTRL_INIT_VAL,  ARG_UNUSED);
  26. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->RXB0CTRL)),  RXB1CTRL_INIT_VAL,  ARG_UNUSED);
  27. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->RXB1CTRL)),  RXB1CTRL_INIT_VAL,  ARG_UNUSED);
  28. // switch to normal mode or loopback mode ( for testing)
  29. //CAN_SPI_CMD( SPI_CMD_BITMOD, TOLONG(&(MCP2510_MAP->CANCTRL)), 0xe0, 0x40 ); // LOOP BACK MODE
  30. CAN_SPI_CMD( SPI_CMD_BITMOD, TOLONG(&(MCP2510_MAP->CANCTRL)), 0xe0, 0x00 ); // NORMAL OPERATION MODE
  31. // Flush the MX1 SPI receive buffer
  32. CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->CANSTAT)), ARG_UNUSED, ARG_UNUSED );
  33. } /* Transmit data */
  34. /*
  35.  TxBuf : select the transmit buffer( 0=buffer0 or 1=buffer1 2=buffer2 )
  36.  IdType : 0=standard id or 1=extended id
  37.  id : frame identifier
  38.  DataLen : the number of byte
  39.  data : the pointer to data byte
  40. */ void MCP2510_TX( int TxBuf, int IdType, unsigned int id, int DataLen, char * data )
  41. {
  42. int i, offset;
  43. switch( TxBuf ){
  44. case TXBUF0:
  45. offset = 0;
  46. break;
  47. case TXBUF1:
  48. offset = 0x10;
  49. break;
  50. case TXBUF2:
  51. offset = 0x20;
  52. break;
  53. }
  54. // Set the frame identifier
  55. if( IdType==STANDID ){
  56. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->TXB0SIDL))+offset, (id&0x7)<<5, ARG_UNUSED );
  57. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->TXB0SIDH))+offset, (id>>3)&0xff, ARG_UNUSED );
  58. }else if( IdType==EXTID ){
  59. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->TXB0EID0))+offset, id&0xff, ARG_UNUSED );
  60. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->TXB0EID8))+offset, (id>>8)&0xff, ARG_UNUSED );
  61. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->TXB0SIDL)), ((id>>16)&0x3)|0x08, ARG_UNUSED );
  62. }
  63. // Set the data length
  64. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->TXB0DLC))+offset, DataLen, ARG_UNUSED );
  65. // fill the data
  66. if( DataLen>8 )
  67. DataLen = 8;
  68. for( i=0; i<DataLen; i++ ){
  69. CAN_SPI_CMD( SPI_CMD_WRITE, TOLONG(&(MCP2510_MAP->TXB0D0))+offset+i, data[i], ARG_UNUSED );
  70. } // initiate transmit i = 0; while( CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->TXB0CTRL))+offset, ARG_UNUSED, ARG_UNUSED )&0x08 ){ i++; if(i == 1000)printk("Please connect the CAN PORT with wire."); } CAN_SPI_CMD( SPI_CMD_BITMOD, TOLONG(&(MCP2510_MAP->TXB0CTRL))+offset, 0x08, 0x08 ); }
  71. /* Receive data */
  72. /*
  73.  * RxBuf : The receive buffer from which the data is get
  74.  * IdType : Identifier type of the data frame ( STANDID, EXTID )
  75.  * id : The identifier of the received frame
  76.  * DataLen : The number of bytes received
  77.  * data : The received data
  78.  */
  79. void MCP2510_RX( int RxBuf, int *IdType, unsigned int *id, int *DataLen, char *data )
  80. {
  81. unsigned int flag;
  82. int offset, i;
  83. switch( RxBuf ){
  84. case RXBUF0:
  85. flag = 0x1;
  86. offset = 0x00;
  87. break;
  88. case RXBUF1:
  89. flag = 0x2;
  90. offset = 0x10;
  91. break;
  92. }
  93. // wait for a frame to com
  94. while( !(CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->CANINTF)), ARG_UNUSED, ARG_UNUSED )&flag) ); // Get the identifier
  95. if( CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->RXB0SIDL))+offset, ARG_UNUSED, ARG_UNUSED )&0x08 ){
  96. // Extended identifier
  97. if( IdType )
  98. *IdType = EXTID;
  99. if( id ){
  100. *id = (CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->RXB0SIDL))+offset, ARG_UNUSED, ARG_UNUSED )&0x3)<<16;
  101. *id |= (CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->RXB0EID8))+offset, ARG_UNUSED, ARG_UNUSED ))<<8;
  102. *id |= (CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->RXB0EID0))+offset, ARG_UNUSED, ARG_UNUSED ));
  103. }
  104. }else{
  105. // Standard identifier
  106. if( IdType )
  107. *IdType = STANDID;
  108. if( id ){
  109. *id = (CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->RXB0SIDH))+offset, ARG_UNUSED, ARG_UNUSED ))<<3;
  110. *id |= (CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->RXB0SIDL))+offset, ARG_UNUSED, ARG_UNUSED ))>>5;
  111. }
  112. }
  113. // Get the data frame lenth
  114. if( DataLen )
  115. *DataLen = (CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->RXB0DLC))+offset, ARG_UNUSED, ARG_UNUSED )&0xf);
  116. // Get the data
  117. for( i=0; DataLen&&(i<*DataLen)&&data; i++ ){
  118. data[i] = CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->RXB0D0))+offset+i, ARG_UNUSED, ARG_UNUSED );
  119. }
  120. // clear the receive int flag
  121. CAN_SPI_CMD( SPI_CMD_BITMOD, TOLONG(&(MCP2510_MAP->CANINTF)), flag, 0x00 );
  122. }
  123. /*  * Atomicly increment an index into shortp_in_buffer  */ static inline void incr_buffer_pointer(volatile unsigned long*index, int delta, int i) { unsigned long newvalue = *index + delta; barrier ();  /* Don't optimize these two together */ *index = (newvalue >= (Device[i]->RecvBuf + BUF_SIZE)) ? Device[i]->RecvBuf : newvalue; } static void mcpcan0_handler(int irq, void *dev_id, struct pt_regs *regs) { struct MCP_device *dev = dev_id; struct mcpcan_data datagram; int written; char regvalue, regvalue2; //struct mcpcan_data *datagramptr; printk("rextern irq 5 handledn"); regvalue = CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->CANINTE)), ARG_UNUSED, ARG_UNUSED ); printk("CANINTE = 0x%02xn",regvalue); regvalue = CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->CANINTF)), ARG_UNUSED, ARG_UNUSED ); printk("CANINTF = 0x%02xn",regvalue); if(regvalue & 0x01){ datagram.BufNo = RXBUF0; MCP2510_RX(datagram.BufNo, &(datagram.IdType), &(datagram.id), &(datagram.DataLen), datagram.data ); printk("RXBUF0n");         //printk("datagram.BufNo = %dn",datagram.BufNo);         //printk("datagram.IdType = %dn",datagram.IdType);         //printk("datagram.id = %dn",datagram.id);         //printk("datagram.DataLen = %dn",datagram.DataLen);         //printk("datagram.data = %sn",datagram.data);     /* Write a 16 byte record. Assume BUF_SIZE is a multiple of 16 */ memcpy((void *)dev->RecvHead, &datagram, sizeof(struct mcpcan_data)); //datagramptr = (struct mcpcan_data *)dev->RecvHead; //printk("RecvHead->BufNo = %dn",datagramptr->BufNo); //printk("RecvHead->IdType = %dn",datagramptr->IdType); //printk("RecvHead->id = %dn",datagramptr->id); //printk("RecvHead->DataLen = %dn",datagramptr->DataLen); //printk("RecvHead->data = %sn",datagramptr->data); incr_buffer_pointer(&(dev->RecvHead), sizeof(struct mcpcan_data), dev->MinorNum); wake_up_interruptible(&(dev->inq)); /* awake any reading process */ CAN_SPI_CMD( SPI_CMD_BITMOD, TOLONG(&(MCP2510_MAP->CANINTF)), 0x01, 0x00 );     //in fact already clear interrupt bit in RX  } if(regvalue & 0x02){ datagram.BufNo = RXBUF1; //this function should be rewritten to receive datagram from RXB1?????????????????? MCP2510_RX(datagram.BufNo, &(datagram.IdType), &(datagram.id), &(datagram.DataLen), datagram.data ); printk("RXBUF1n");     /* Write a 16 byte record. Assume BUF_SIZE is a multiple of 16 */ memcpy((void *)dev->RecvHead, &datagram, sizeof(struct mcpcan_data)); incr_buffer_pointer(&(dev->RecvHead), sizeof(struct mcpcan_data), dev->MinorNum); wake_up_interruptible(&dev->inq); /* awake any reading process */ CAN_SPI_CMD( SPI_CMD_BITMOD, TOLONG(&(MCP2510_MAP->CANINTF)), 0x02, 0x00 );     //in fact already clear interrupt bit in RX } if(regvalue & 0xe0){ printk("CAN error with CANINTF = 0x%02x.n", regvalue); if(regvalue & 0x80)printk("MERRF:message error.n"); if(regvalue & 0x40)printk("WAKIF:wake up interrupt.n"); if(regvalue & 0x20)printk("ERRIF:CAN bus error interrupt.n"); regvalue2 = CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->EFLG)), ARG_UNUSED, ARG_UNUSED ); printk("EFLG = 0x%02x.n", regvalue2);                 regvalue2 = CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->TEC)), ARG_UNUSED, ARG_UNUSED );                 printk("TEC = 0x%02x.n", regvalue2);                 regvalue2 = CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->REC)), ARG_UNUSED, ARG_UNUSED );                 printk("REC = 0x%02x.n", regvalue2); CAN_SPI_CMD( SPI_CMD_BITMOD, TOLONG(&(MCP2510_MAP->CANINTF)), 0xe0, 0x00 ); } if(CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->CANINTF)), ARG_UNUSED, ARG_UNUSED ) & 0x1c){  //didn't open the 3 send interrupts CAN_SPI_CMD( SPI_CMD_BITMOD, TOLONG(&(MCP2510_MAP->CANINTF)), 0x1c, 0x00 ); //clear TX bits directly(it will set 1,but no int) }         regvalue = CAN_SPI_CMD( SPI_CMD_READ, TOLONG(&(MCP2510_MAP->CANINTF)), ARG_UNUSED, ARG_UNUSED ); printk("after interrupt processing, CANINTF = 0x%02xn",regvalue); } static void mcpcan1_handler(int irq, void *dev_id, struct pt_regs *regs) { //printk("rextern irq ? handled"); } /*****************************MCPCAN open**********************************/ static int device_open(struct inode *inode,struct file *filp) {     int MinorNum; int eint_irq; int ret_val;     struct MCP_device *dev; static int irqcount0=0, irqcount1=0;     MinorNum = MINOR(inode->i_rdev);     if(MinorNum >= MCP_NUM) return(-ENODEV);              dev = Device[MinorNum];     filp->private_data = dev;        //save to private_data request_region(0x59000000,0x38,"MCPCAN");    //SPI interface request_region(0x56000000,0x94,"MCPCAN");  //I/O port request_region(0x4c00000c,4,"MCPCAN");  //clock   /* register the eint5 irq */ if(irqcount0 == 0){ eint_irq = IRQ_EINT5; set_external_irq(eint_irq, EXT_FALLING_EDGE, GPIO_PULLUP_DIS); ret_val = request_irq(eint_irq, mcpcan0_handler, SA_INTERRUPT | SA_SHIRQ, MCP_name[dev->MinorNum], dev); if (ret_val < 0) { return ret_val; } irqcount0++; } MCP2510_Init(MinorNum); MOD_INC_USE_COUNT;     return 0; } /*****************************Functin MCP_release**************************/ static int device_release(struct inode *inode,struct file *filp) { struct MCP_device *dev = filp->private_data; release_region(0x59000000,0x38); release_region(0x56000000,0x94); release_region(0x4c00000c,4); free_irq(dev->IrqNum,dev);    //shared irq MOD_DEC_USE_COUNT; return 0; } ssize_t device_read (struct file *filp, char *buf, size_t count, loff_t *f_pos) { int count0; struct MCP_device *dev = filp->private_data; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; while (dev->RecvHead == dev->RecvTail) { /* nothing to read */ up(&dev->sem); /* release the lock */ if (filp->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(dev->inq, (dev->RecvHead != dev->RecvTail))) return -ERESTARTSYS; /* signal: tell the fs layer to handle it */ /* otherwise loop, but first reacquire the lock */ if (down_interruptible(&dev->sem)) return -ERESTARTSYS; } /* ok, data is there, return something */ /* count0 is the number of readable data bytes */ count0 = dev->RecvHead - dev->RecvTail; if (count0 < 0) /* wrapped */ count0 = dev->RecvBuf + BUF_SIZE - dev->RecvTail; if (count0 < count) count = count0; if (copy_to_user(buf, (char *)dev->RecvTail, count)){ up (&dev->sem); return -EFAULT; } incr_buffer_pointer(&(dev->RecvTail), count, dev->MinorNum); up (&dev->sem); return count; } ssize_t device_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos) { mcpcan_data     temp; struct MCP_device *dev = filp->private_data; if(down_interruptible(&dev->sem)) return -ERESTARTSYS; copy_from_user(&temp, buf, sizeof(struct mcpcan_data)); MCP2510_TX(temp.BufNo,temp.IdType,temp.id,temp.DataLen,temp.data ); up(&dev->sem); return count; } /* This function is called whenever a process tries to   * do an ioctl on our device file. We get two extra   * parameters (additional to the inode and file   * structures, which all device functions get): the number  * of the ioctl called and the parameter given to the   * ioctl function.  *  * If the ioctl is write or read/write (meaning output   * is returned to the calling process), the ioctl call   * returns the output of this function.  */ /* int device_ioctl(     struct inode *inode,     struct file *file,     unsigned int ioctl_num,// The number of the ioctl      unsigned long ioctl_param) // The parameter to it  { } */ /*****************************File operations define************************/ struct file_operations Fops = {     .open = device_open,    //open     .release = device_release,    //release .read = device_read, .write = device_write };
  124. /****************************Module initialize******************************/ static int __init mcpcan_init_module(void) {         int res;         //EXPORT_NO_SYMBOLS;         /* Register the character device (atleast try) */         res = register_chrdev(MCP_major,"MCPCAN",&Fops);         if(res < 0) {                printk("device register failed with %d.n",res);                return res;         }         if(MCP_major == 0) MCP_major = res;     MCP_device_init(); printk ("%s The major device number is %d.n", "Registeration is a success",  MCP_major);            printk ("If you want to talk to the device driver,n"); printk ("you'll have to create a device file. n"); printk ("We suggest you use:n"); printk ("mknod /dev/%s c %d 0n", "can",MCP_major); printk ("The device file name is important, becausen"); printk ("the ioctl program assumes that's then"); printk ("file you'll use.n");         return 0; } /****************************Module release********************************/ static void __exit mcpcan_cleanup_module(void) { int i;     unregister_chrdev(MCP_major,"MCPCAN"); for(i=0; i<2; i++){ kfree((void *)Device[i]->RecvBuf); kfree(Device[i]->SendBuf); kfree(Device[i]); }     printk("MCPCAN release success.n"); } module_init(mcpcan_init_module); module_exit(mcpcan_cleanup_module);