|
@@ -0,0 +1,1326 @@
|
|
|
+#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;i<POS_TTY_MINORS;i++)
|
|
|
+ {
|
|
|
+ if (pdx_table[i] == NULL)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(i==POS_TTY_MINORS)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ pdx = kzalloc(sizeof(*pdx), /* GFP_KERNEL */ GFP_ATOMIC);
|
|
|
+ if (!pdx) {
|
|
|
+ ERR("OUT OF MEMORY pdx\n");
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ pdx->BioPack = 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;i<POS_TTY_MINORS;i++)
|
|
|
+ pdx_table[i] = NULL;
|
|
|
+
|
|
|
+ pos_tty_driver = alloc_tty_driver(POS_TTY_MINORS);
|
|
|
+ if (!pos_tty_driver)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ pos_tty_driver->owner = 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);
|