#include "ttyPos.h" #define DRV_VERSION "303" #define VERSION_DATE "2014.10.13_01" #define MAX_RETRY_S 5 #define DRV_NAME "ttyPos" static struct tty_pos *pdx_table[POS_TTY_MINORS]; #ifdef LINUX_VERSION_CODE #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)) static struct tty_port pos_port[POS_TTY_MINORS]; #endif #endif static unsigned char ResetPipePort(struct tty_pos *dev) { struct tty_pos *pdx = dev; int retval; retval = usb_clear_halt(pdx->udev, usb_sndbulkpipe(pdx->udev, pdx->bulk_out_epAddr)); if (retval) { ERR("%s - ERROR CLEAR %X HALT = %d", __func__, pdx->bulk_out_epAddr, retval); goto reset_port; } retval = usb_clear_halt(pdx->udev, usb_rcvbulkpipe(pdx->udev, pdx->bulk_in_epAddr)); if (retval) { ERR("%s - ERROR CLEAR %X HALT = %d", __func__, pdx->bulk_in_epAddr, retval); goto reset_port; } return 0; reset_port: retval = usb_reset_device(pdx->udev); if (retval) { ERR("%s - ERROR RESETTING DEVICE: %d", __func__, retval); } return retval; } static int VerifyChecksum(ST_BULK_IO *p_bio) { unsigned char a, b; int i, dn; dn = p_bio->Len + 4; a = 0; for (i = 2; i < dn; i++) { a ^= ((unsigned char *)p_bio)[i]; } a ^= p_bio->SeqNo & 0x0f; a ^= p_bio->ReqType & 0x0f; b = (p_bio->SeqNo & 0xf0) + (p_bio->ReqType >> 4); if (a != b) return 1; /* clear checksum field */ p_bio->SeqNo &= 0x0f; p_bio->ReqType &= 0x0f; return 0; } static void SetChecksum(ST_BULK_IO *p_bio) { unsigned char a; int i, dn; dn = p_bio->Len + 4; a = 0; for (i = 2; i < dn; i++) { a ^= ((unsigned char *)p_bio)[i]; } a ^= p_bio->SeqNo & 0x0f; a ^= p_bio->ReqType & 0x0f; /* fill high 4 bits of checksum into high 4 bits of ID field */ p_bio->SeqNo = (p_bio->SeqNo & 0x0f) | (a & 0xf0); /* fill low 4 bits of checksum into high 4 bits of REQ_TYPE field */ p_bio->ReqType |= (a << 4); } static void UrbCallBack(struct urb *urb) { struct tty_pos *pdx; pdx = (struct tty_pos *)urb->context; atomic_set(&pdx->urb_done, 1); wake_up(&pdx->urb_wait); } static int SendAndWaitUrb(struct tty_pos *dev) { struct tty_pos *pdx = dev; int retval; atomic_set(&pdx->urb_done, 0); /* send the data out the bulk port */ retval = usb_submit_urb(pdx->urb, /* GFP_KERNEL */ GFP_ATOMIC); if (retval) { ERR("%s - FAILED SUBMITTING WRITE URB: %d", __func__, retval); retval = 1; goto exit; } retval = wait_event_timeout(pdx->urb_wait, (atomic_read(&pdx->urb_done) == 1), pdx->timeout_jiffies); if (retval == 0) { /* INFO("URB TIMEOUT\n"); */ if (atomic_read(&pdx->urb_done) == 0) { /* urb is not done */ #if 0 usb_unlink_urb(pdx->urb); printk(KERN_ALERT "usb_done: %d; %d\n", (atomic_read(&pdx->urb_done) == 1), /* pdx->urb_done, */ usb_unlink_urb(pdx->urb)); #endif usb_kill_urb(pdx->urb); #if 0 wait_event(pdx->urb_wait, (atomic_read(&pdx->urb_done) == 1)); #endif } #if 0 INFO("urb->status: %d, %d\n", pdx->urb->status, pdx->urb->actual_length); #endif retval = 2; goto exit; } else if (retval < 0) { ERR("%s - WAIT FAILED: %d", __func__, retval); retval = 3; goto exit; } else { retval = 0; #if 0 printk(KERN_INFO "Use jiffies: %d", pdx->timeout_jiffies - retval); #endif } if (pdx->urb->status) { /* if (pdx->urb->status != -EREMOTEIO) */ { ERR("%s - nonzero status received: %d", __func__, pdx->urb->status); ERR("urb status:%d\n",pdx->urb->status); retval = 4; goto exit; } } exit: return retval; } /* error codes: 11~29 */ static int ProcessCommand(struct tty_pos *dev) { struct tty_pos *pdx = dev; int retval; /* stage 1: send command pack */ SetChecksum((ST_BULK_IO *)pdx->BioPack); if (pdx->urb == NULL) return 18; if (pdx->urb->status == -EINPROGRESS) { #if 0 ERR("URB IN PROGRESS\n"); #endif return 19; } usb_fill_bulk_urb(pdx->urb, pdx->udev, usb_sndbulkpipe(pdx->udev, pdx->bulk_out_epAddr), pdx->BioPack, pdx->BioPack->Len + 4, UrbCallBack, pdx); retval = SendAndWaitUrb((struct tty_pos *)pdx); if (retval != 0) return retval + 10; /* stage 2: receive answer pack */ /* clear pack flags */ pdx->BioPack->SeqNo = 0; pdx->BioPack->ReqType = 0; pdx->BioPack->Len = 0; if (pdx->urb == NULL) return 28; usb_fill_bulk_urb(pdx->urb, pdx->udev, usb_rcvbulkpipe(pdx->udev, pdx->bulk_in_epAddr), pdx->BioPack, sizeof(*pdx->BioPack), UrbCallBack, pdx); retval = SendAndWaitUrb((struct tty_pos *)pdx); if (retval != 0) return retval + 20; if (VerifyChecksum((ST_BULK_IO *)pdx->BioPack)) { unsigned int i; /* unsigned char x; */ ERR("VERIFY CHECKSUM FAILED: %d\n", retval); ERR("%X; %X; %X\n", pdx->BioPack->SeqNo, pdx->BioPack->ReqType, pdx->BioPack->Len); for (i = 0; i < 508; i++) { INFO("%X\n", pdx->BioPack->Data[i]); } #if 0 for (i = 0, x = pdx->BioPack.Data[0]; i < pdx->BioPack.Len; i++, x++) { if (pdx->BioPack.Data[i] != x) { printk(KERN_ALERT "%d: %X; %X\n", i - 1, pdx->BioPack.Data[i - 1], x - 1); printk(KERN_ALERT "%d: %X; %X\n", i, pdx->BioPack.Data[i], x); printk(KERN_ALERT "%d: %X; %X\n", i + 1, pdx->BioPack.Data[i + 1], x + 1); printk(KERN_ALERT "%d: %X; %X\n", i + 2, pdx->BioPack.Data[i + 2], x + 2); printk(KERN_ALERT "%d: %X; %X\n", i + 3, pdx->BioPack.Data[i + 3], x + 3); break; } } #endif return 29; } return 0; } static void SleepMs(unsigned int nMs, struct tty_pos *dev) { #if 0 set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(nMs * HZ / 1000); #endif struct tty_pos *pdx = dev; unsigned int timeout = (nMs * HZ / 1000); if (timeout < 1) { timeout = 1; } wait_event_timeout(pdx->write_wait, (atomic_read(&pdx->write_flag) == 1), timeout); } static int ThreadCallBack(void *data) { struct tty_pos *pdx = data; unsigned char loops; int retval; unsigned int i, rlen, wlen; struct tty_struct *tty; retval = ResetPipePort(pdx); if (retval != 0) { retval = 1; goto exit; } tty = pdx->tty; while (pdx->ThreadState == THREAD_CREATED) { /* get device buffer status */ for (loops = 0; (loops < MAX_RETRY_S) && (pdx->ThreadState == THREAD_CREATED); loops++) { /* building command pack */ pdx->SeqCount = (pdx->SeqCount + 1) & 0x0f; pdx->BioPack->SeqNo = pdx->SeqCount; pdx->BioPack->ReqType = STATUS_COMMAND; pdx->BioPack->Len = 0; retval = ProcessCommand((struct tty_pos *)pdx); if (retval != 0) { retval += 100; goto loop_s_tail; } if (pdx->urb->actual_length != 20) { retval = 130; goto loop_s_tail; } if (pdx->BioPack->SeqNo != pdx->SeqCount) { retval = 131; goto loop_s_tail; } if (pdx->BioPack->ReqType != STATUS_COMMAND) { retval = 132; goto loop_s_tail; } memcpy(&pdx->BioDevState, pdx->BioPack->Data, sizeof(pdx->BioDevState)); if ((!pdx->BioDevState.TxLeft) && IS_POOL_EMPTY(pdx->TxPool)) { SleepMs(10, (struct tty_pos *)pdx); } loop_s_tail: if (retval == 0) break; ERR("STATUS RETRY, loop: %d, err: %d, seq: %02X\n", loops, retval, pdx->SeqCount); ResetPipePort(pdx); } if (retval != 0) goto exit; r_process: /* read from usb device */ for (loops = 0; (loops < MAX_RETRY_S) && (pdx->ThreadState == THREAD_CREATED); loops++) { if (!pdx->BioDevState.TxLeft) goto w_process; rlen = pdx->BioDevState.TxLeft; if (rlen > sizeof(pdx->BioPack->Data)) { rlen = sizeof(pdx->BioPack->Data); } #ifdef LINUX_VERSION_CODE #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)) rlen = tty_buffer_request_room(&pos_port[pdx->devIndex],rlen); #else rlen = tty_buffer_request_room(tty, rlen); #endif #endif if (!loops) { pdx->SeqCount = (pdx->SeqCount + 1) & 0x0f; } pdx->BioPack->SeqNo = pdx->SeqCount; pdx->BioPack->ReqType = READ_COMMAND; pdx->BioPack->Len = 2; /* in dlen required */ pdx->BioPack->Data[0] = (unsigned short)rlen & 0xff; pdx->BioPack->Data[1] = (unsigned short)rlen >> 8; retval = ProcessCommand((struct tty_pos *)pdx); if (retval != 0) { retval += 200; goto loop_r_tail; } if (pdx->BioPack->SeqNo != pdx->SeqCount) { retval = 231; goto loop_r_tail; } if (pdx->BioPack->ReqType != READ_COMMAND) { if ((pdx->BioPack->ReqType == STATUS_COMMAND) && (pdx->BioPack->Len >= sizeof(pdx->BioDevState))) { memcpy(&pdx->BioDevState, pdx->BioPack->Data, sizeof(pdx->BioDevState)); goto w_process; /* no data to fetch */ } retval = 232; ERR(" %02X, ERROR req_type: %02X.\n", pdx->SeqCount, pdx->BioPack->ReqType); goto loop_r_tail; } if (pdx->urb->actual_length < (int)pdx->BioPack->Len + 4) { retval = 233; goto loop_r_tail; } if (pdx->BioPack->Len > rlen) { ERR("MORE DATA FETCHED THAN DECLARED, NEED: " "%d, RN: %d\n", rlen, pdx->BioPack->Len); retval = 234; goto exit; } rlen = pdx->BioPack->Len; #if 0 for (j = 0; j < rlen - 1; j++) { if ((pdx->BioPack.Data[j] + 1 != pdx->BioPack.Data[j + 1]) && (pdx->BioPack.Data[j + 1] != 0)) { inerr = 235; goto exit; } } #endif #ifdef LINUX_VERSION_CODE #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)) tty_insert_flip_string(&pos_port[pdx->devIndex], pdx->BioPack->Data, rlen); tty_flip_buffer_push(&pos_port[pdx->devIndex]); #else tty_insert_flip_string(tty, pdx->BioPack->Data, rlen); tty_flip_buffer_push(tty); #endif #endif pdx->BioDevState.TxLeft -= rlen; #if 0 printk(KERN_ALERT "%02X, RN: %d\n", pdx->SeqCount, rlen); #endif if (pdx->BioDevState.TxLeft) goto r_process; loop_r_tail: if (retval == 0) break; ERR("RX RETRY, loop: %d, err: %d, SEQ: %02X\n", loops, retval, pdx->SeqCount); ResetPipePort(pdx); } if (retval) goto exit; w_process: /* write to usb device */ wlen = GET_USING_POOL(pdx->TxPool); if (wlen > sizeof(pdx->BioPack->Data)) { wlen = sizeof(pdx->BioPack->Data); } if (wlen > pdx->BioDevState.RxLeft) { wlen = pdx->BioDevState.RxLeft; } for (loops = 0; (loops < MAX_RETRY_S) && (pdx->ThreadState == THREAD_CREATED); loops++) { if (wlen == 0) break; if (!loops) { pdx->SeqCount = (pdx->SeqCount + 1) & 0x0f; } pdx->BioPack->SeqNo = pdx->SeqCount; pdx->BioPack->ReqType = WRITE_COMMAND; pdx->BioPack->Len = (unsigned short)wlen; for (i = 0; i < wlen; i++) { pdx->BioPack->Data[i] = pdx->TxPool.Buffer[(pdx->TxPool.ReadPos + i) % POOL_SIZE]; } retval = ProcessCommand((struct tty_pos *)pdx); if (retval != 0) { retval += 300; goto loop_w_tail; } if (pdx->urb->actual_length != 20) { retval = 330; goto loop_w_tail; } if (pdx->BioPack->SeqNo != pdx->SeqCount) { retval = 331; ERR("***** Mismatched with SEQ: %02X, " "wlen: %d, loop: %d\n", pdx->SeqCount, wlen, loops); ERR(" SEQ: %02X, type: %02X, dn: %d\n", pdx->BioPack->SeqNo, pdx->BioPack->ReqType, pdx->BioPack->Len); for (i = 0; i < pdx->BioPack->Len; i++) { INFO("%02X ", pdx->BioPack->Data[i]); } if (((pdx->BioPack->SeqNo + 1) % 16) == pdx->SeqCount) goto loop_w_tail; goto exit; } /* mismatched seq_no */ if (pdx->BioPack->ReqType != STATUS_COMMAND) { retval = 332; ERR(" SEQ: %02X, type: %02X, dn: %d\n", pdx->BioPack->SeqNo, pdx->BioPack->ReqType, pdx->BioPack->Len); for (i = 0; i < sizeof(pdx->BioPack->Data); i++) { INFO("%02X ", pdx->BioPack->Data[i]); } INFO("\n"); goto exit; } /* mismatched req_type */ #if 0 printk(KERN_ALERT "%02X, WN: %d\n", pdx->SeqCount, wlen); #endif pdx->TxPool.ReadPos = (pdx->TxPool.ReadPos + wlen) % POOL_SIZE; memcpy(&pdx->BioDevState, pdx->BioPack->Data, sizeof(pdx->BioDevState)); if (!IS_POOL_EMPTY(pdx->TxPool)) goto w_process; atomic_set(&pdx->write_flag, 0); loop_w_tail: if (retval == 0) break; ERR("TX RETRY, loop: %d, err: %d, SEQ: %02X\n", loops, retval, pdx->SeqCount); ResetPipePort(pdx); } if (retval) goto exit; } exit: #if 0 INFO("ThreadCallBack Exit\n"); #endif if (retval != 0) { ERR("%02X, ERR: %d\n", pdx->SeqCount, retval); ResetPipePort(pdx); } pdx->ThreadState = THREAD_STOPPED; complete(&pdx->ThreadExit_completion); do_exit(0); } static void pos_delete(struct kref *kref) { struct tty_pos *pdx = to_pos_dev(kref); if (pdx == NULL) return; if(pdx->devIndex >= POS_TTY_MINORS) return; if (pdx->tty) { pdx->tty->driver_data = NULL; } pdx_table[pdx->devIndex] = NULL; usb_free_urb(pdx->urb); usb_put_dev(pdx->udev); kfree(pdx->BioPack); kfree(pdx); } static int pos_open(struct tty_struct *tty, struct file *filp) { struct tty_pos *pdx; if(tty==NULL)return USB_ERR_MEM_SYSTEM; if(tty->index >= POS_TTY_MINORS || tty->index<0)return USB_ERR_MEM_SYSTEM; // INFO("%s index:%d\n",__func__,tty->index); pdx = pdx_table[tty->index]; if (pdx == NULL) return USB_ERR_DEV_ABSENT; if (THREAD_IS_RUNNING(pdx->ThreadState)) { return USB_ERR_BUSY; } tty->driver_data = pdx; pdx->tty = tty; #ifdef LINUX_VERSION_CODE #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)) tty_port_tty_set(&pos_port[pdx->devIndex], tty); #endif #endif kref_get(&pdx->kref); #ifdef LINUX_VERSION_CODE #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)) reinit_completion(&pdx->ThreadExit_completion); #else INIT_COMPLETION(pdx->ThreadExit_completion); #endif #endif pdx->ThreadState = THREAD_CREATED; #ifdef LINUX_VERSION_CODE #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)) { struct task_struct *thread; thread = kthread_run(ThreadCallBack,(struct tty_pos *)pdx,"ThreadCallBack"); if (IS_ERR(thread)) { ERR("FAILED TO CREATE KERNEL THREAD!\n"); pdx->ThreadState = THREAD_INIT; kref_put(&pdx->kref, pos_delete); return USB_ERR_RC_SYSTEM; } } #else { pid_t pid; pid = kernel_thread(ThreadCallBack, (struct tty_pos *)pdx, CLONE_FS | CLONE_FILES); if (pid < 0) { ERR("FAILED TO CREATE KERNEL THREAD!\n"); pdx->ThreadState = THREAD_INIT; kref_put(&pdx->kref, pos_delete); return USB_ERR_RC_SYSTEM; } } #endif #endif pdx->filp = filp; return 0; } static void pos_close(struct tty_struct *tty, struct file *filp) { struct tty_pos *pdx = tty->driver_data; if (pdx == NULL) return; if(pdx_table[pdx->devIndex]==NULL) return; if (pdx->ThreadState == THREAD_CREATED) { pdx->ThreadState = THREAD_CLOSE; wait_for_completion(&pdx->ThreadExit_completion); } pdx->ThreadState = THREAD_INIT; kref_put(&pdx->kref, pos_delete); //tty_ldisc_flush(tty); } static int pos_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct tty_pos *pdx = tty->driver_data; unsigned int wn, i; int retval; if (!pdx) return USB_ERR_NOT_OPEN; if (!THREAD_IS_RUNNING(pdx->ThreadState)) { retval = USB_ERR_INVALID; goto exit; } if (count == 0) { retval = 0; goto exit; } wn = GET_SPACE_POOL(pdx->TxPool); if (wn >= count) { wn = count; } else return USB_ERR_BUF; for (i = 0; i < wn; i++) { pdx->TxPool.Buffer[(pdx->TxPool.WritePos + i) % POOL_SIZE] = buf[i]; } pdx->TxPool.WritePos = (pdx->TxPool.WritePos + wn) % POOL_SIZE; retval = wn; atomic_set(&pdx->write_flag, 1); wake_up(&pdx->write_wait); exit: return retval; } static int pos_write_room(struct tty_struct *tty) { struct tty_pos *pdx = tty->driver_data; int room = -EINVAL; if (!pdx) return -ENODEV; room = GET_SPACE_POOL(pdx->TxPool); return room; } #if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) static int pos_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg) #else static int pos_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) #endif { struct tty_pos *pdx = tty->driver_data; #if 0 INFO("pos_ioctl\n"); #endif if (!pdx) return -ENODEV; switch (cmd) { case TIOCGSERIAL: case TIOCMIWAIT: case TIOCGICOUNT: default: break; } #if 0 INFO("%s-cmd: 0x%X\n", __func__, cmd); #endif return -ENOIOCTLCMD; } #define RELEVANT_IFLAG(iflag) \ ((iflag) & (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK)) static void pos_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { unsigned int cflag; #ifdef LINUX_VERSION_CODE #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)) cflag = tty->termios.c_cflag; #else cflag = tty->termios->c_cflag; #endif #endif /* check that they really want us to change something */ if (old_termios) { if ((cflag == old_termios->c_cflag) && #ifdef LINUX_VERSION_CODE #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)) (RELEVANT_IFLAG(tty->termios.c_iflag) == #else (RELEVANT_IFLAG(tty->termios->c_iflag) == #endif #endif RELEVANT_IFLAG(old_termios->c_iflag))) { #if 0 INFO(" - nothing to change...\n"); #endif return; } } #if 0 /* get the byte size */ switch (cflag & CSIZE) { case CS5: INFO(" - data bits = 5\n"); break; case CS6: INFO(" - data bits = 6\n"); break; case CS7: INFO(" - data bits = 7\n"); break; default: case CS8: INFO(" - data bits = 8\n"); break; } /* determine the parity */ if (cflag & PARENB) { if (cflag & PARODD) { INFO(" - parity = odd\n"); } else { INFO(" - parity = even\n"); } } else { INFO(" - parity = none\n"); } /* figure out the stop bits requested */ if (cflag & CSTOPB) { INFO(" - stop bits = 2\n"); } else { INFO(" - stop bits = 1\n"); } /* figure out the hardware flow control settings */ if (cflag & CRTSCTS) { INFO(" - RTS/CTS is enabled\n"); } else { INFO(" - RTS/CTS is disabled\n"); } /* determine software flow control. * if we are implementing XON/XOFF, set the start and * stop character in the device */ if (I_IXOFF(tty) || I_IXON(tty)) { unsigned char stop_char = STOP_CHAR(tty); unsigned char start_char = START_CHAR(tty); /* if we are implementing INBOUND XON/XOFF */ if (I_IXOFF(tty)) { INFO(" - INBOUND XON/XOFF is enabled, " "XON = %2x, XOFF = %2x", start_char, stop_char); } else { INFO(" - INBOUND XON/XOFF is disabled"); } /* if we are implementing OUTBOUND XON/XOFF */ if (I_IXON(tty)) { INFO(" - OUTBOUND XON/XOFF is enabled, " "XON = %2x, XOFF = %2x", start_char, stop_char); } else { INFO(" - OUTBOUND XON/XOFF is disabled"); } } /* get the baud rate wanted */ INFO(" - baud rate = %d\n", tty_get_baud_rate(tty)); #endif } static void pos_throttle(struct tty_struct *tty) { /* INFO("pos_throttle\n"); */ } static void pos_unthrottle(struct tty_struct *tty) { /* INFO("pos_unthrottle\n"); */ } static void pos_flush_buffer(struct tty_struct *tty) { struct tty_pos *pdx = tty->driver_data; #if 0 INFO("pos_flush_buffer\n"); #endif if (!pdx) return; INIT_POOL_BUFFER(pdx->TxPool); } static int pos_chars_in_buffer(struct tty_struct *tty) { int in_buf_len; struct tty_pos *pdx = tty->driver_data; if (!pdx) return -ENODEV; in_buf_len = GET_USING_POOL(pdx->TxPool); #if 0 printk(KERN_ALERT "pos_chars_in_buffer: %d\n", in_buf_len); #endif return in_buf_len; } /* Our fake UART values */ #define MCR_DTR 0x01 #define MCR_RTS 0x02 #define MCR_LOOP 0x04 #define MSR_CTS 0x08 #define MSR_CD 0x10 #define MSR_RI 0x20 #define MSR_DSR 0x40 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) static int pos_tiocmget(struct tty_struct *tty, struct file *filp) #else static int pos_tiocmget(struct tty_struct *tty) #endif { struct tty_pos *pdx = tty->driver_data; unsigned int msr, mcr, result; #if 0 INFO("pos_tiocmget\n"); #endif if (!pdx) return -ENODEV; msr = pdx->msr; mcr = pdx->mcr; result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) | /* DTR is set */ ((mcr & MCR_RTS) ? TIOCM_RTS : 0) | /* RTS is set */ ((mcr & MCR_LOOP) ? TIOCM_LOOP : 0) | /* LOOP is set */ ((msr & MSR_CTS) ? TIOCM_CTS : 0) | /* CTS is set */ ((msr & MSR_CD) ? TIOCM_CAR : 0) | /* Carrier detect is set */ ((msr & MSR_RI) ? TIOCM_RI : 0) | /* Ring Indicator is set */ ((msr & MSR_DSR) ? TIOCM_DSR : 0); /* DSR is set */ return result; } #if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) static int pos_tiocmset(struct tty_struct *tty, struct file *filp, unsigned int set, unsigned int clear) #else static int pos_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) #endif { struct tty_pos *pdx = tty->driver_data; unsigned int mcr; if (!pdx) return -ENODEV; mcr = pdx->mcr; if (set & TIOCM_RTS) { mcr |= MCR_RTS; } if (set & TIOCM_DTR) { mcr |= MCR_DTR; /* mcr |= MCR_RTS; */ } if (clear & TIOCM_RTS) { mcr &= ~MCR_RTS; } if (clear & TIOCM_DTR) { mcr &= ~MCR_DTR; /* mcr &= ~MCR_RTS; */ } /* set the new MCR value in the device */ pdx->mcr = mcr; return 0; } static const struct tty_operations pos_ops = { .open = pos_open, .close = pos_close, .write = pos_write, .write_room = pos_write_room, .ioctl = pos_ioctl, .set_termios = pos_set_termios, .throttle = pos_throttle, .unthrottle = pos_unthrottle, .flush_buffer = pos_flush_buffer, .chars_in_buffer = pos_chars_in_buffer, .tiocmget = pos_tiocmget, .tiocmset = pos_tiocmset, }; struct tty_driver *pos_tty_driver; static int pos_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct tty_pos *pdx; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; int i, retval = -ENOMEM; for(i=0;iBioPack = kzalloc(sizeof(*pdx->BioPack), GFP_ATOMIC); if (!pdx) { ERR("OUT OF MEMORY BioPack\n"); return -ENOMEM; } printk(KERN_ALERT "ttyPos probe:%s %s,index:%d\n",DRV_VERSION,VERSION_DATE,i); pdx->devIndex = i; pdx_table[pdx->devIndex] = pdx; INIT_POOL_BUFFER(pdx->TxPool); pdx->timeout_jiffies = 400 * HZ / 1000; /* 400ms */ kref_init(&pdx->kref); init_waitqueue_head(&pdx->urb_wait); atomic_set(&pdx->urb_done, 0); init_waitqueue_head(&pdx->write_wait); atomic_set(&pdx->write_flag, 0); init_completion(&pdx->ThreadExit_completion); pdx->udev = usb_get_dev(interface_to_usbdev(interface)); pdx->interface = interface; pdx->urb = usb_alloc_urb(0, /* GFP_KERNEL */ GFP_ATOMIC); if (!pdx->urb) { retval = -ENOMEM; ERR("FAILED ALLOC URB!\n"); goto error; } iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; if (endpoint->bEndpointAddress & 0x80) { pdx->bulk_in_epAddr = endpoint->bEndpointAddress; } else { pdx->bulk_out_epAddr = endpoint->bEndpointAddress; } } if (!(pdx->bulk_in_epAddr && pdx->bulk_out_epAddr)) { ERR("COULD NOT FIND BOTH BULK-IN AND BULK-OUT ENDPOINT\n"); } usb_set_intfdata(interface, pdx); #ifdef LINUX_VERSION_CODE #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)) tty_port_register_device(&pos_port[pdx->devIndex], pos_tty_driver, pdx->devIndex, NULL); #else tty_register_device(pos_tty_driver, pdx->devIndex, NULL); #endif #endif dev_info(&interface->dev, "USB POS device now attached to PosUsb-%d", interface->minor); return 0; error: if (pdx) { kref_put(&pdx->kref, pos_delete); } ERR("--pos_probe error\n"); return retval; } static void pos_usb_disconnect(struct usb_interface *interface) { struct tty_pos *pdx; #if 0 INFO("++pos_disconnect\n"); #endif pdx = usb_get_intfdata(interface); if (pdx == NULL) return; tty_unregister_device(pos_tty_driver, pdx->devIndex); if (pdx->ThreadState == THREAD_CREATED) { pdx->ThreadState = THREAD_CLOSE; #if 0 INFO("Wait thread exit\n"); #endif wait_for_completion(&pdx->ThreadExit_completion); #if 0 INFO("Wait thread exit success!\n"); #endif } pdx->ThreadState = THREAD_INIT; usb_set_intfdata(interface, NULL); #if 0 usb_deregister_dev(interface, &pos_class); #endif pdx->interface = NULL; kref_put(&pdx->kref, pos_delete); #if 0 INFO("--pos_disconnect\n"); #endif } #if 1 static int pos_usb_suspend(struct usb_interface *intf, pm_message_t message) { // printk(KERN_ALERT "pos_suspend\n"); return 0; } static int pos_usb_resume(struct usb_interface *intf) { // printk(KERN_ALERT "pos_resume\n"); return 0; } #endif #ifdef OLD_USB_DRIVER static void pos_usb_pre_reset(struct usb_interface *intf) { /* struct tty_pos *pdx = usb_get_intfdata(intf); */ #if 0 INFO("pos_pre_reset\n"); #endif } #else static int pos_usb_pre_reset(struct usb_interface *intf) { /* struct tty_pos *pdx = usb_get_intfdata(intf); */ #if 0 INFO("pos_pre_reset\n"); #endif return 0; } #endif #ifdef OLD_USB_DRIVER static void pos_usb_post_reset(struct usb_interface *intf) { #if 0 INFO("pos_post_reset\n"); #endif } #else static int pos_usb_post_reset(struct usb_interface *intf) { #if 0 INFO("pos_post_reset\n"); #endif return 0; } #endif static struct usb_driver pos_usb_driver = { .name = "PosUsb", .probe = pos_usb_probe, .disconnect = pos_usb_disconnect, #if 1 .suspend = pos_usb_suspend, .resume = pos_usb_resume, .supports_autosuspend = 1, #endif .pre_reset = pos_usb_pre_reset, .post_reset = pos_usb_post_reset, .id_table = pos_usb_table, }; /* Compatible with TTY_DRIVER_DYNAMIC_DEV and TTY_DRIVER_NO_DEVFS */ #define TTY_USB_DEV 0x0008 #ifdef LINUX_VERSION_CODE #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)) static int pos_port_activate(struct tty_port *port, struct tty_struct *tty) { return 0; } static void pos_port_shutdown(struct tty_port *port) { } static int pos_carrier_raised(struct tty_port *port) { return 0; } static void pos_dtr_rts(struct tty_port *port, int onoff) { } static const struct tty_port_operations pos_port_ops = { .activate = pos_port_activate, .shutdown = pos_port_shutdown, .carrier_raised = pos_carrier_raised, .dtr_rts = pos_dtr_rts, }; #endif #endif static int __init pos_tty_init(void) { int result,i; printk(KERN_ALERT "ttyPos:%s %s\n",DRV_VERSION,VERSION_DATE); for(i=0;iowner = THIS_MODULE; pos_tty_driver->driver_name = "usbpos"; pos_tty_driver->name = "ttyPos"; pos_tty_driver->major = 0;//POS_TTY_MAJOR;//The major number will be chosen dynamically pos_tty_driver->minor_start = 0; pos_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; pos_tty_driver->subtype = SERIAL_TYPE_NORMAL; pos_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_USB_DEV; pos_tty_driver->init_termios = tty_std_termios; pos_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; tty_set_operations(pos_tty_driver, &pos_ops); #ifdef LINUX_VERSION_CODE #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)) for (i = 0; i < POS_TTY_MINORS; i++) { tty_port_init(&pos_port[i]); pos_port[i].ops = &pos_port_ops; pos_port[i].close_delay = HZ / 2; /* .5 seconds */ pos_port[i].closing_wait = 30 * HZ;/* 30 seconds */ } #endif #endif result = tty_register_driver(pos_tty_driver); if (result) { ERR("%s - tty_register_driver failed\n", __func__); goto byebye1; } #ifdef LINUX_VERSION_CODE #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)) for (i = 0; i < POS_TTY_MINORS; i++) tty_port_destroy(&pos_port[i]); #endif #endif result = usb_register(&pos_usb_driver); if (result) { ERR("%s - usb_register failed; err: %d\n",__func__, result); goto byebye2; } return 0; byebye2: tty_unregister_driver(pos_tty_driver); byebye1: put_tty_driver(pos_tty_driver); return result; } static void __exit pos_tty_exit(void) { unsigned int i; usb_deregister(&pos_usb_driver); tty_unregister_driver(pos_tty_driver); put_tty_driver(pos_tty_driver); #ifdef LINUX_VERSION_CODE #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)) for (i = 0; i < POS_TTY_MINORS; i++) tty_port_destroy(&pos_port[i]); #endif #endif INFO("pos_tty_exit\n"); } module_init(pos_tty_init); module_exit(pos_tty_exit); MODULE_LICENSE("GPL"); MODULE_ALIAS_LDISC(N_SLIP);