static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg) { struct r3964_client_info *pClient; struct r3964_client_info **ppClient; struct r3964_message *pMsg; if ((arg & R3964_SIG_ALL) == 0) { /* Remove client from client list */ for (ppClient = &pInfo->firstClient; *ppClient; ppClient = &(*ppClient)->next) { pClient = *ppClient; if (pClient->pid == pid) { TRACE_PS("removing client %d from client list", pid_nr(pid)); *ppClient = pClient->next; while (pClient->msg_count) { pMsg = remove_msg(pInfo, pClient); if (pMsg) { kfree(pMsg); TRACE_M("enable_signals - msg " "kfree %p", pMsg); } } put_pid(pClient->pid); kfree(pClient); TRACE_M("enable_signals - kfree %p", pClient); return 0; } } return -EINVAL; } else { pClient = findClient(pInfo, pid); if (pClient) { /* update signal options */ pClient->sig_flags = arg; } else { /* add client to client list */ pClient = kmalloc(sizeof(struct r3964_client_info), GFP_KERNEL); TRACE_M("enable_signals - kmalloc %p", pClient); if (pClient == NULL) return -ENOMEM; TRACE_PS("add client %d to client list", pid_nr(pid)); spin_lock_init(&pClient->lock); pClient->sig_flags = arg; pClient->pid = get_pid(pid); pClient->next = pInfo->firstClient; pClient->first_msg = NULL; pClient->last_msg = NULL; pClient->next_block_to_read = NULL; pClient->msg_count = 0; pInfo->firstClient = pClient; } } return 0; }
static void trigger_transmit(struct r3964_info *pInfo) { unsigned long flags; save_flags(flags); cli(); if((pInfo->state == R3964_IDLE) && (pInfo->tx_first!=NULL)) { pInfo->state = R3964_TX_REQUEST; pInfo->count_down = R3964_TO_QVZ; pInfo->nRetry=0; pInfo->flags &= ~R3964_ERROR; restore_flags(flags); TRACE_PS("trigger_transmit - sent STX"); put_char(pInfo, STX); flush(pInfo); pInfo->bcc = 0; } else { restore_flags(flags); } }
static ssize_t r3964_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr) { struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data; struct r3964_client_info *pClient; struct r3964_message *pMsg; struct r3964_client_message theMsg; DECLARE_WAITQUEUE (wait, current); int pid = current->pid; int count; TRACE_L("read()"); pClient=findClient(pInfo, pid); if(pClient) { pMsg = remove_msg(pInfo, pClient); if(pMsg==NULL) { /* no messages available. */ if (file->f_flags & O_NONBLOCK) { return -EAGAIN; } /* block until there is a message: */ add_wait_queue(&pInfo->read_wait, &wait); repeat: current->state = TASK_INTERRUPTIBLE; pMsg = remove_msg(pInfo, pClient); if (!pMsg && !signal_pending(current)) { schedule(); goto repeat; } current->state = TASK_RUNNING; remove_wait_queue(&pInfo->read_wait, &wait); } /* If we still haven't got a message, we must have been signalled */ if (!pMsg) return -EINTR; /* deliver msg to client process: */ theMsg.msg_id = pMsg->msg_id; theMsg.arg = pMsg->arg; theMsg.error_code = pMsg->error_code; count = sizeof(struct r3964_client_message); kfree(pMsg); TRACE_M("r3964_read - msg kfree %x",(int)pMsg); if (copy_to_user(buf,&theMsg, count)) return -EFAULT; TRACE_PS("read - return %d", count); return count; } return -EPERM; }
static void trigger_transmit(struct r3964_info *pInfo) { unsigned long flags; spin_lock_irqsave(&pInfo->lock, flags); if((pInfo->state == R3964_IDLE) && (pInfo->tx_first!=NULL)) { pInfo->state = R3964_TX_REQUEST; pInfo->nRetry=0; pInfo->flags &= ~R3964_ERROR; mod_timer(&pInfo->tmr, jiffies + R3964_TO_QVZ); spin_unlock_irqrestore(&pInfo->lock, flags); TRACE_PS("trigger_transmit - sent STX"); put_char(pInfo, STX); flush(pInfo); pInfo->bcc = 0; } else { spin_unlock_irqrestore(&pInfo->lock, flags); } }
static ssize_t r3964_read(struct tty_struct *tty, struct file *file, unsigned char __user * buf, size_t nr) { struct r3964_info *pInfo = tty->disc_data; struct r3964_client_info *pClient; struct r3964_message *pMsg; struct r3964_client_message theMsg; int ret; TRACE_L("read()"); tty_lock(); pClient = findClient(pInfo, task_pid(current)); if (pClient) { pMsg = remove_msg(pInfo, pClient); if (pMsg == NULL) { /* no messages available. */ if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; goto unlock; } /* block until there is a message: */ wait_event_interruptible_tty(pInfo->read_wait, (pMsg = remove_msg(pInfo, pClient))); } /* If we still haven't got a message, we must have been signalled */ if (!pMsg) { ret = -EINTR; goto unlock; } /* deliver msg to client process: */ theMsg.msg_id = pMsg->msg_id; theMsg.arg = pMsg->arg; theMsg.error_code = pMsg->error_code; ret = sizeof(struct r3964_client_message); kfree(pMsg); TRACE_M("r3964_read - msg kfree %p", pMsg); if (copy_to_user(buf, &theMsg, ret)) { ret = -EFAULT; goto unlock; } TRACE_PS("read - return %d", ret); goto unlock; } ret = -EPERM; unlock: tty_unlock(); return ret; }
static void dump_block(const unsigned char *block, unsigned int length) { unsigned int i, j; char linebuf[16 * 3 + 1]; for (i = 0; i < length; i += 16) { for (j = 0; (j < 16) && (j + i < length); j++) { sprintf(linebuf + 3 * j, "%02x ", block[i + j]); } linebuf[3 * j] = '\0'; TRACE_PS("%s", linebuf); } }
static void remove_client_block(struct r3964_info *pInfo, struct r3964_client_info *pClient) { struct r3964_block_header *block; TRACE_PS("remove_client_block PID %d", pid_nr(pClient->pid)); block = pClient->next_block_to_read; if (block) { block->locks--; if (block->locks == 0) { remove_from_rx_queue(pInfo, block); } } pClient->next_block_to_read = NULL; }
static void transmit_block(struct r3964_info *pInfo) { struct tty_struct *tty = pInfo->tty; struct r3964_block_header *pBlock = pInfo->tx_first; int room=0; if((tty==NULL) || (pBlock==NULL)) { return; } if(tty->driver.write_room) room=tty->driver.write_room(tty); TRACE_PS("transmit_block %x, room %d, length %d", (int)pBlock, room, pBlock->length); while(pInfo->tx_position < pBlock->length) { if(room<2) break; if(pBlock->data[pInfo->tx_position]==DLE) { /* send additional DLE char: */ put_char(pInfo, DLE); } put_char(pInfo, pBlock->data[pInfo->tx_position++]); room--; } if((pInfo->tx_position == pBlock->length) && (room>=3)) { put_char(pInfo, DLE); put_char(pInfo, ETX); if(pInfo->flags & R3964_BCC) { put_char(pInfo, pInfo->bcc); } pInfo->state = R3964_WAIT_FOR_TX_ACK; pInfo->count_down = R3964_TO_QVZ; } flush(pInfo); }
static void transmit_block(struct r3964_info *pInfo) { struct tty_struct *tty = pInfo->tty; struct r3964_block_header *pBlock = pInfo->tx_first; int room = 0; if (tty == NULL || pBlock == NULL) { return; } room = tty_write_room(tty); TRACE_PS("transmit_block %p, room %d, length %d", pBlock, room, pBlock->length); while (pInfo->tx_position < pBlock->length) { if (room < 2) break; if (pBlock->data[pInfo->tx_position] == DLE) { put_char(pInfo, DLE); } put_char(pInfo, pBlock->data[pInfo->tx_position++]); room--; } if ((pInfo->tx_position == pBlock->length) && (room >= 3)) { put_char(pInfo, DLE); put_char(pInfo, ETX); if (pInfo->flags & R3964_BCC) { put_char(pInfo, pInfo->bcc); } pInfo->state = R3964_WAIT_FOR_TX_ACK; mod_timer(&pInfo->tmr, jiffies + R3964_TO_QVZ); } flush(pInfo); }
static void receive_char(struct r3964_info *pInfo, const unsigned char c) { switch (pInfo->state) { case R3964_TX_REQUEST: if (c == DLE) { TRACE_PS("TX_REQUEST - got DLE"); pInfo->state = R3964_TRANSMITTING; pInfo->tx_position = 0; transmit_block(pInfo); } else if (c == STX) { if (pInfo->nRetry == 0) { TRACE_PE("TX_REQUEST - init conflict"); if (pInfo->priority == R3964_SLAVE) { goto start_receiving; } } else { TRACE_PE("TX_REQUEST - secondary init " "conflict!? Switching to SLAVE mode " "for next rx."); goto start_receiving; } } else { TRACE_PE("TX_REQUEST - char != DLE: %x", c); retry_transmit(pInfo); } break; case R3964_TRANSMITTING: if (c == NAK) { TRACE_PE("TRANSMITTING - got NAK"); retry_transmit(pInfo); } else { TRACE_PE("TRANSMITTING - got invalid char"); pInfo->state = R3964_WAIT_ZVZ_BEFORE_TX_RETRY; mod_timer(&pInfo->tmr, jiffies + R3964_TO_ZVZ); } break; case R3964_WAIT_FOR_TX_ACK: if (c == DLE) { TRACE_PS("WAIT_FOR_TX_ACK - got DLE"); remove_from_tx_queue(pInfo, R3964_OK); pInfo->state = R3964_IDLE; trigger_transmit(pInfo); } else { retry_transmit(pInfo); } break; case R3964_WAIT_FOR_RX_REPEAT: /* FALLTROUGH */ case R3964_IDLE: if (c == STX) { /* Prevent rx_queue from overflow: */ if (pInfo->blocks_in_rx_queue >= R3964_MAX_BLOCKS_IN_RX_QUEUE) { TRACE_PE("IDLE - got STX but no space in " "rx_queue!"); pInfo->state = R3964_WAIT_FOR_RX_BUF; mod_timer(&pInfo->tmr, jiffies + R3964_TO_NO_BUF); break; } start_receiving: /* Ok, start receiving: */ TRACE_PS("IDLE - got STX"); pInfo->rx_position = 0; pInfo->last_rx = 0; pInfo->flags &= ~R3964_ERROR; pInfo->state = R3964_RECEIVING; mod_timer(&pInfo->tmr, jiffies + R3964_TO_ZVZ); pInfo->nRetry = 0; put_char(pInfo, DLE); flush(pInfo); pInfo->bcc = 0; } break; case R3964_RECEIVING: if (pInfo->rx_position < RX_BUF_SIZE) { pInfo->bcc ^= c; if (c == DLE) { if (pInfo->last_rx == DLE) { pInfo->last_rx = 0; goto char_to_buf; } pInfo->last_rx = DLE; break; } else if ((c == ETX) && (pInfo->last_rx == DLE)) { if (pInfo->flags & R3964_BCC) { pInfo->state = R3964_WAIT_FOR_BCC; mod_timer(&pInfo->tmr, jiffies + R3964_TO_ZVZ); } else { on_receive_block(pInfo); } } else { pInfo->last_rx = c; char_to_buf: pInfo->rx_buf[pInfo->rx_position++] = c; mod_timer(&pInfo->tmr, jiffies + R3964_TO_ZVZ); } } /* else: overflow-msg? BUF_SIZE>MTU; should not happen? */ break; case R3964_WAIT_FOR_BCC: pInfo->last_rx = c; on_receive_block(pInfo); break; } }
static void on_receive_block(struct r3964_info *pInfo) { unsigned int length; struct r3964_client_info *pClient; struct r3964_block_header *pBlock; length = pInfo->rx_position; /* compare byte checksum characters: */ if (pInfo->flags & R3964_BCC) { if (pInfo->bcc != pInfo->last_rx) { TRACE_PE("checksum error - got %x but expected %x", pInfo->last_rx, pInfo->bcc); pInfo->flags |= R3964_CHECKSUM; } } /* check for errors (parity, overrun,...): */ if (pInfo->flags & R3964_ERROR) { TRACE_PE("on_receive_block - transmission failed error %x", pInfo->flags & R3964_ERROR); put_char(pInfo, NAK); flush(pInfo); if (pInfo->nRetry < R3964_MAX_RETRIES) { pInfo->state = R3964_WAIT_FOR_RX_REPEAT; pInfo->nRetry++; mod_timer(&pInfo->tmr, jiffies + R3964_TO_RX_PANIC); } else { TRACE_PE("on_receive_block - failed after max retries"); pInfo->state = R3964_IDLE; } return; } /* received block; submit DLE: */ put_char(pInfo, DLE); flush(pInfo); del_timer_sync(&pInfo->tmr); TRACE_PS(" rx success: got %d chars", length); /* prepare struct r3964_block_header: */ pBlock = kmalloc(length + sizeof(struct r3964_block_header), GFP_KERNEL); TRACE_M("on_receive_block - kmalloc %p", pBlock); if (pBlock == NULL) return; pBlock->length = length; pBlock->data = ((unsigned char *)pBlock) + sizeof(struct r3964_block_header); pBlock->locks = 0; pBlock->next = NULL; pBlock->owner = NULL; memcpy(pBlock->data, pInfo->rx_buf, length); /* queue block into rx_queue: */ add_rx_queue(pInfo, pBlock); /* notify attached client processes: */ for (pClient = pInfo->firstClient; pClient; pClient = pClient->next) { if (pClient->sig_flags & R3964_SIG_DATA) { add_msg(pClient, R3964_MSG_DATA, length, R3964_OK, pBlock); } } wake_up_interruptible(&pInfo->read_wait); pInfo->state = R3964_IDLE; trigger_transmit(pInfo); }