📄 lucas_com.c
字号:
/****//**pre-processing directives for version control*/#define DEBUG#define PRE_2_6/**pre-processing directives for functionality*/#define easy_printk(x) printk(KERN_INFO x)#define easy_printk2(x,y) printk(KERN_INFO x,y)#define easy_printk3(x,y,z) printk(KERN_INFO x,y,z)#define myprintk(x) easy_printk2("lucas%05d-->\t",se_mypr_num++); easy_printk(x)#define myprintk2(x,y) easy_printk2("lucas%05d-->\t",se_mypr_num++); easy_printk2(x,y)#define myprintk3(x,y,z) easy_printk2("lucas%05d-->\t",se_mypr_num++); easy_printk3(x,y,z)#ifdef DEBUG#define dbgprintk(x) easy_printk2("lucas debug%05d-->\t",se_dbgpr_num++); easy_printk(x)#define dbgprintk2(x,y) easy_printk2("lucas debug%05d-->\t",se_dbgpr_num++); easy_printk2(x,y)#define dbgprintk3(x,y,z) easy_printk2("lucas debug%05d-->\t",se_dbgpr_num++); easy_printk3(x,y,z)#else #define dbgprintk(x)#define dbgprintk2(x,y)#define dbgprintk3(x,y,z)#endif/**file inclusion*/#include<linux/init.h>#include<linux/module.h>#include<linux/cdev.h>#include<linux/types.h>#include<linux/fs.h>#include<asm/io.h>#include<linux/interrupt.h>#include<asm/uaccess.h>MODULE_LICENSE("Dual BSD/GPL");/**function declaration*/void wait(long _micro_sec);int com_open(struct inode* _pinode, struct file* _pfile);int com_release(struct inode* _pinode, struct file* _pfile);ssize_t com_lq_read(struct file* _pfile, char __user* _puser, size_t _size, loff_t* _poff);ssize_t com_irq_read(struct file* _pfile, char __user* _puser, size_t _size, loff_t* _poff);ssize_t com_lq_write(struct file* _pfile, const char __user* _puser, size_t _size, loff_t* _poff);ssize_t com_irq_write(struct file* _pfile, const char __user* _puser, size_t _size, loff_t* _poff);int com_ioctl(struct inode* _pinode, struct file* _pfile, unsigned int _arg1, unsigned long _arg2); static irqreturn_t com_handler(int _irq, void* _dev_id, struct pt_regs* _regs);/**user-defined types*///The se_fops must be an external valuestruct file_operations se_fops={ .owner=THIS_MODULE, .open=com_open, .release=com_release, .ioctl=com_ioctl, .read=com_irq_read, .write=com_lq_write, };struct dev_id{ int maj_no; int min_no; //char* dev_name;};/**extern variables*/static int se_mypr_num=1;static int se_dbgpr_num=1;static int se_open_times=0;struct dev_id se_com_dev_id;static int se_rd_bytes=0;static char se_rd_buf[3];static int se_rd_index=0;static char se_wr_buf[3];static int se_wr_index=0;/**const variables*/static const int MAJ_NUM=100;static const char *NAME="lucas_com";static const int PORT_DATA1=0x3f8;static const int PORT_DATA2=0x3f9;static const int PORT_LINE_CTL=0x3fb;static const int PORT_LINE_STS=0x3fd;static const int PORT_IRQ_CTL=0x3f9;static const int PORT_MODEM_CTL=0x3fc;static const int BAUD_RATE=9600;static const int FREQ=1843200/16;static const int IRQ_COM=4;static const int ENABLED_IRQ=0x01; //enabled IRQ bit static const long QUERY_TIME_OUT=10^6; //time out for query/**function definition*/static int com_init(void){ #ifdef PRE_2_6 int reg_rslt; int low_byte,upp_byte; myprintk2("Driver %s initializing\n",NAME); reg_rslt=register_chrdev(MAJ_NUM,NAME,&se_fops); //manipulate the I/O ports low_byte=(FREQ/BAUD_RATE)&0xff; upp_byte=((FREQ/BAUD_RATE)>>8)&0xff; //set the baud rate outb_p(0x80,PORT_LINE_CTL); outb_p(low_byte,PORT_DATA1); outb_p(upp_byte,PORT_DATA2); //set the data format outb_p(0x03,PORT_LINE_CTL); return 0; #else #endif }static void com_exit(void){ #ifdef PRE_2_6 int ureg_rslt; myprintk2("%s is removed\n",NAME); ureg_rslt=unregister_chrdev(MAJ_NUM,NAME); #else #endif}int com_open(struct inode* _pinode, struct file* _pfile){ int result; int one_byte; se_open_times++; dbgprintk2("opening driver %s\n",NAME); dbgprintk2("Device is openned %d times\n",se_open_times); //firstly check device-specific errors //request IRQ for the first open if(1==se_open_times) { result=request_irq(IRQ_COM,com_handler,0,NAME,&se_com_dev_id); if(0==result) { myprintk3("Success requesting IRQ %d for driver %s\n",IRQ_COM,NAME); //if the IRQ is allocated successfully, then enable the //hardware IRQ, note the order here does matter. //firstly disable the modem IRQ to refresh /** one_byte=inb_p(PORT_MODEM_CTL); outb_p(one_byte&(~0x0b),PORT_MODEM_CTL);//may have bug here dbgprintk2("MODEM_CTL is 0x%x after disabled\n",inb_p(PORT_MODEM_CTL)); **/ //then enable the modem IRQ one_byte=inb_p(PORT_MODEM_CTL); outb_p(one_byte|0x0b,PORT_MODEM_CTL);//may have bug here dbgprintk2("MODEM_CTL is 0x%x after enabled\n",inb_p(PORT_MODEM_CTL)); //firstly disable according bits in the IRQ register to refresh /** one_byte=inb_p(PORT_IRQ_CTL); outb_p(one_byte&(~ENABLED_IRQ),PORT_IRQ_CTL); dbgprintk2("IRQ_CTL is 0x%x after disabled\n",inb_p(PORT_IRQ_CTL)); **/ //then enable according bits in the IRQ register outb_p(one_byte|ENABLED_IRQ,PORT_IRQ_CTL); dbgprintk2("IRQ_CTL is 0x%x after enabled\n",inb_p(PORT_IRQ_CTL)); } else { printk(KERN_ALERT "Error requesting IRQ for driver %s\n",NAME); } } return 0;}int com_release(struct inode* _pinode, struct file* _pfile){ int one_byte; se_open_times--; myprintk2("releasing driver %s\n",NAME); dbgprintk2("Device is openned %d times\n",se_open_times); if(0==se_open_times) { //releasing the requested IRQ as well free_irq(IRQ_COM,&se_com_dev_id); //disable the hardware as well one_byte=inb_p(PORT_MODEM_CTL); //firstly disable modem IRQ outb_p(one_byte&(~0x0b),PORT_MODEM_CTL);//may have bug here dbgprintk2("MODEM_CTL is 0x%x after disabled\n",inb_p(PORT_MODEM_CTL)); //then disable according bits in the IRQ register one_byte=inb_p(PORT_IRQ_CTL); outb_p(one_byte&(~ENABLED_IRQ),PORT_IRQ_CTL); dbgprintk2("IRQ_CTL is 0x%x after disabled\n",inb_p(PORT_IRQ_CTL)); myprintk3("driver %s freed the IRQ%d\n",NAME,IRQ_COM); } return 0;}/**reading from the COM using the loop-query mode**/ssize_t com_lq_read(struct file* _pfile, char __user* _puser, size_t _size, loff_t* _poff){ int one_bit=0,one_byte=0; int read_bytes=0; char buf[_size]; int i=0; dbgprintk2("user buffer address:%llu\n",_puser); dbgprintk2("reading from driver %s\n",NAME); //to do copy_to_user(_puser,se_rd_buf,sizeof(se_rd_buf)); return 0; //query for the status of line register //and check bit D0 for whether the data is ready //for receiving one_byte=inb_p(PORT_LINE_STS); dbgprintk2("The line status in hex:0x%x\n",one_byte); dbgprintk2("The line status after previous query:0x%x\n",inb_p(PORT_LINE_STS)); one_bit=one_byte&0x01; while(0) { //firstly wait for some time until time is out wait(QUERY_TIME_OUT); if(1==one_bit) { one_byte=inb_p(PORT_DATA1); dbgprintk2("One char %c is received\n",one_byte); if(i<_size) { buf[i]=one_byte; i++; read_bytes++; } else //specified size is read { copy_to_user(_puser,buf,_size); return read_bytes; } //The commented code is not needed as the hardware will do the job automatically //one_byte=one_byte&0xfe; //outb_p(one_byte,PORT_LINE_STS); } else //time is out { } }} /**reading from the COM using the interrupt mode**/ssize_t com_irq_read(struct file* _pfile, char __user* _puser, size_t _size, loff_t* _poff){ unsigned long copied_bytes=0; dbgprintk2("reading from %s using IRQ\n",NAME); dbgprintk2("user buffer address:%ull\n",_puser); copied_bytes=sizeof(se_rd_buf)-copy_to_user(_puser,se_rd_buf,sizeof(se_rd_buf)); dbgprintk2("copied_bytes by com_irq_read %u\n",copied_bytes); return copied_bytes; }/**writing to the COM using loop-query mode**/ssize_t com_lq_write(struct file* _pfile, const char __user* _puser, size_t _size, loff_t* _poff){ int one_bit=0,one_byte=0; unsigned long written_bytes=0; dbgprintk2("writing to driver %s\n",NAME); copy_from_user(se_wr_buf,_puser,sizeof(se_wr_buf)); for(se_wr_index=0;se_wr_index<sizeof(se_wr_buf);se_wr_index++) { //check bit D5 for whether //the send register is empty one_bit=0; while(0==one_bit) { one_byte=inb_p(PORT_LINE_STS); dbgprintk2("The line status in hex:0x%x\n",one_byte); dbgprintk2("The line status after previous query:0x%x\n",inb_p(PORT_LINE_STS)); one_bit=(one_byte>>5)&0x01; } //to be modified if(1==one_bit) { outb_p(se_wr_buf[se_wr_index],PORT_DATA1); written_bytes++; /**The commented code is not needed as the hardware will do the job automatically one_byte=one_byte|0x60; outb_p(one_byte,PORT_LINE_STS); */ } } return written_bytes;}int com_ioctl(struct inode* _pinode, struct file* _pfile, unsigned int _arg1, unsigned long _arg2){ dbgprintk2("Controlling %s\n",NAME); return 0;} static irqreturn_t com_handler(int _irq, void* _dev_id, struct pt_regs* _regs){ int copied_bytes=0; int one_byte; //firstly CLI of interface card if needed if(1) //if(MAJ_NUM==((struct dev_id *)_dev_id)->maj_no) { dbgprintk("Entering com_handler\n"); one_byte=inb_p(PORT_DATA1); dbgprintk2("A char %c was read\n",one_byte); //dbgprintk3("A char '\%c\'=0x%x was read\n",one_byte,one_byte); se_rd_buf[se_rd_index]=one_byte; se_rd_index=(se_rd_index+1)%sizeof(se_rd_buf); } else { return IRQ_NONE; } return IRQ_HANDLED; }//waiting for specified secondsvoid wait(long _micro_sec){ long long loop=(long long)3*10^9*_micro_sec/10^6; while(loop--) { //do nothing; }}module_init(com_init);module_exit(com_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -