pax-pwn/utils/linux/Driver/ttyPos.c

1327 lines
29 KiB
C
Raw Permalink Normal View History

#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);