Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: rawprint lwip
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Кот-баюн
Имеется следующая железка: интерфейсная плата принтера на Cortex-M3 + lwip.
Поверх lwip реализован простой raw-print сервер, который слушает порт 9100 и отсылает принятые
данные в принтер. Все работает хорошо до тех пор, пока в одно и то же время не поступают две и
более задач с планировщиков печати (спулеров) разных хостов. Суть вопроса: необходимо средствами
lwip разграничить доступ к принтеру, то есть обеспечить режим "занят" в момент обработки текущей задачи.

На данный момент есть эксперементальный код, который, собственно практически не работает...
Идея в том чтобы запомнить ip для текущей задачи (rawprint_remote_ip) и пока соединение не будет закрыто отправлять
все новые задачи через tcp_poll в ожидание.
Инициализация:
Код
void
rawprintd_init(void)
{
  struct tcp_pcb *pcb;

  LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprintd_init\n"));

  pcb = tcp_new();
  tcp_bind(pcb, IP_ADDR_ANY, 9100);
  pcb = tcp_listen(pcb);
  tcp_accept(pcb, rawprint_accept);

}

Подтвержение:
CODE
static err_t
rawprint_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{
struct rawprint_state *rs;

LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);

LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_accept 0x%08x\n", pcb));

/* Allocate memory for the structure that holds the state of the
connection. */
rs = (struct rawprint_state *)mem_malloc(sizeof(struct rawprint_state));

if (rs == NULL) {
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_accept: Out of memory\n"));
return ERR_MEM;
}

/* Initialize the structure. */
rs->retries = 0;

/* Tell TCP that this is the structure we wish to be passed for our
callbacks. */
tcp_arg(pcb, rs);

/* Tell TCP that we wish to be informed of incoming data by a call
to the http_recv() function. */
if (pcb->local_port == 9100)
{
if (rawprint_remote_ip == 0)
{
tcp_recv(pcb, rawprint_recv);

tcp_err(pcb, conn_err);

rawprint_remote_ip = pcb->remote_ip.addr;

rawprint_send_status(pcb, false);

LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_remote_ip %08x\n", rawprint_remote_ip));
}
else
{
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("go poll\n"));
tcp_poll(pcb, rawprint_poll, 1);
}
}

return ERR_OK;
}

Прием данных:
CODE
static err_t
rawprint_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
tBoolean bSendStatus = false;
struct rawprint_state *rs;

LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_recv 0x%08x port %d\n", pcb, (unsigned int)pcb->local_port));

rs = arg;

if ((err == ERR_OK) && (p != NULL) && rs)
{

/* Inform TCP that we have taken the data. */
tcp_recved(pcb, p->tot_len);

LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_recv length %d bytes\n", p->tot_len));

if (p->len)
{
const unsigned char *pucData = p->payload;
switch(pcb->local_port)
{
case 9100:
if (!((pucData[0] == 0x00) && (p->len == 1)))
rawprint_printer_write(p);
if ((memcmp(g_pucReadPort, pucData, sizeof(g_pucReadPort)) == 0) && (p->len == sizeof(g_pucReadPort)))
bSendStatus = true;
break;
}
}
pbuf_free(p);
}

if (((err == ERR_OK) && (p == NULL)) || (bSendStatus))
{
rawprint_send_status(pcb, true);
}

return ERR_OK;
}

Опрос:
CODE
static err_t
rawprint_poll(void *arg, struct tcp_pcb *pcb)
{
struct rawprint_state *rs;

rs = arg;

LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_poll 0x%08x port %d\n", pcb, pcb->local_port));

/* printf("Polll\n");*/
if ((rs == NULL) && (pcb->state == ESTABLISHED)) {
/* printf("Null, close\n");*/
tcp_abort(pcb);
return ERR_ABRT;
} else {
if (rawprint_remote_ip == 0)
{
tcp_recv(pcb, rawprint_recv);

tcp_err(pcb, conn_err);

rawprint_remote_ip = pcb->remote_ip.addr;

rawprint_send_status(pcb, false);

LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_remote_ip %08x\n", rawprint_remote_ip));
}

}

return ERR_OK;
}

Закрытие соединения:
CODE
/*-----------------------------------------------------------------------------------*/
static void
close_conn(struct tcp_pcb *pcb, struct rawprint_state *rs)
{
err_t err;
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("Closing connection 0x%08x\n", pcb));

tcp_arg(pcb, NULL);
tcp_sent(pcb, NULL);
tcp_recv(pcb, NULL);
if(rs) {
mem_free(rs);
}

if (pcb->remote_ip.addr == rawprint_remote_ip){
rawprint_remote_ip = 0;
}

err = tcp_close(pcb);
if(err != ERR_OK)
{
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("Error %d closing 0x%08x\n", err, pcb));
}
}


Я не совсем уверен в правильности вызова функции tcp_poll без tcp_recv в случае, когда задача уже запущена (rawprint_remote_ip != 0).
Есть у кого-нибудь идеи как реализовать правильно без буферизации, если это, конечно возможно?
Golikov A.
if (((err == ERR_OK) && (p == NULL)) || (bSendStatus))
{
rawprint_send_status(pcb, true);
}

вот это несколько странно.

Если со стороны компьютера закрыть коннект, условие выполниться, нулевой буфер данных придет. А вы на это попробуете послать обратно статус, а его уже могут и не ждать...

В калбеках функции есть аргументы которые можно задать при установке соединения, я бы в эти аргументы ставил бы порядковый номер задачи-конекта. Чтобы каждый коннект при вызове ресивед имел свой номер. И еще глобально бы хранил очередь номеров, когда начинается печать, запоминается номер который начал печать, его приемы обрабатываете и пихаете в принтер, все остальные пропускаете. Не подтверждайте им что вы приняли от них данные. Как то так...

Кот-баюн
Цитата(Golikov A. @ May 14 2013, 12:17) *
if (((err == ERR_OK) && (p == NULL)) || (bSendStatus))
{
rawprint_send_status(pcb, true);
}

вот это несколько странно.


Если со стороны компьютера закрыть коннект, условие выполниться, нулевой буфер данных придет. А вы на это попробуете послать обратно статус, а его уже могут и не ждать...


С этим как раз все ок. Спулер желает знать статус принтера до выполнения задачи (при коннекте) и после ее завершения.

Цитата(Golikov A. @ May 14 2013, 12:17) *
В калбеках функции есть аргументы которые можно задать при установке соединения, я бы в эти аргументы ставил бы порядковый номер задачи-конекта. Чтобы каждый коннект при вызове ресивед имел свой номер. И еще глобально бы хранил очередь номеров, когда начинается печать, запоминается номер который начал печать, его приемы обрабатываете и пихаете в принтер, все остальные пропускаете. Не подтверждайте им что вы приняли от них данные. Как то так...


Изначально я пытался сделать так, но без очереди и уникального номера задачи, так как с этим нормально справляется ip, так как он и является уникальным для любой входящей задачи. Например вот так:
Подтверждение.
CODE
static err_t
rawprint_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{
struct rawprint_state *rs;

LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);

LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_accept 0x%08x\n", pcb));

/* Allocate memory for the structure that holds the state of the
connection. */
rs = (struct rawprint_state *)mem_malloc(sizeof(struct rawprint_state));

if (rs == NULL) {
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_accept: Out of memory\n"));
return ERR_MEM;
}

/* Initialize the structure. */
rs->retries = 0;

/* Tell TCP that this is the structure we wish to be passed for our
callbacks. */
tcp_arg(pcb, rs);


tcp_recv(pcb, rawprint_recv);

tcp_err(pcb, conn_err);

/* Tell TCP that we wish to be informed of incoming data by a call
to the http_recv() function. */
if (pcb->local_port == 9100)
{
if (rawprint_remote_ip == 0)
{

rawprint_remote_ip = pcb->remote_ip.addr;

rawprint_send_status(pcb, false);

LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_remote_ip %08x\n", rawprint_remote_ip));
}
}

return ERR_OK;
}


То есть мы принимаем все соединения и только одно получает право на выполнение. Соблюдение FIFO нас не очень интересует.

Тогда
Прием данных:
CODE
static err_t
rawprint_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
tBoolean bSendStatus = false;
struct rawprint_state *rs;

LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_recv 0x%08x port %d\n", pcb, (unsigned int)pcb->local_port));

rs = arg;

if ((err == ERR_OK) && (p != NULL) && rs)
{

if (pcb->local_port == 9100)
{
if (pcb->remote_ip.addr == rawprint_remote_ip)
{
/* Inform TCP that we have taken the data. */
tcp_recved(pcb, p->tot_len);
}
else if (rawprint_remote_ip == 0)
{
/* Inform TCP that we have taken the data. */
tcp_recved(pcb, p->tot_len);

rawprint_remote_ip = pcb->remote_ip.addr;

LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_remote_ip 0x%08x\n", rawprint_remote_ip));
}
else
{
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("discard***\n", rawprint_remote_ip));
return ERR_OK;
}
}
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_recv length %d bytes\n", p->tot_len));

if (p->len)
{
const unsigned char *pucData = p->payload;
switch(pcb->local_port)
{
case 9100:
if (!((pucData[0] == 0x00) && (p->len == 1)))
rawprint_printer_write(p);
if ((memcmp(g_pucReadPort, pucData, sizeof(g_pucReadPort)) == 0) && (p->len == sizeof(g_pucReadPort)))
bSendStatus = true;
break;
}
}
pbuf_free(p);
}

if (((err == ERR_OK) && (p == NULL)) || (bSendStatus))
{
rawprint_send_status(pcb, true);
}

return ERR_OK;
}


То есть мы подтверждаем прием данных только тогда, когда задача является активной (pcb->remote_ip.addr == rawprint_remote_ip) либо ни одна задача таковой не является
(rawprint_remote_ip == 0). Не работает. (((((
Golikov A.
почему не работает?
а где рав принт ремоте сбрасывается в 0?
по идее по дисконекту это должно было бы произойти.

а вы учитываете что p - имеет еще поле тотал лен и ссылку на следущий p

вызывая функцию ресивед вы говорите сколько данных из всего окна вы обработали, но те окна что вы пропустили не обработав, могут получить и 2 пакет данных и третий, и перейдя к ним надо бы все данные обработать...
Кот-баюн
Цитата(Golikov A. @ May 15 2013, 11:27) *
почему не работает?
а где рав принт ремоте сбрасывается в 0?
по идее по дисконекту это должно было бы произойти.

Так и есть.. см в первом посте:

Закрытие соединения:
CODE
/*-----------------------------------------------------------------------------------*/
static void
close_conn(struct tcp_pcb *pcb, struct rawprint_state *rs)
{
err_t err;
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("Closing connection 0x%08x\n", pcb));

tcp_arg(pcb, NULL);
tcp_sent(pcb, NULL);
tcp_recv(pcb, NULL);
if(rs) {
mem_free(rs);
}

if (pcb->remote_ip.addr == rawprint_remote_ip){
rawprint_remote_ip = 0;
}

err = tcp_close(pcb);
if(err != ERR_OK)
{
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("Error %d closing 0x%08x\n", err, pcb));
}
}


Если ip закрываемого соединения ip текущей задачи (pcb->remote_ip.addr == rawprint_remote_ip).

Цитата(Golikov A. @ May 15 2013, 11:27) *
а вы учитываете что p - имеет еще поле тотал лен и ссылку на следущий p

Да. Учитываю. Вот код функции записи в принтер.

Код
static void
rawprint_printer_write(struct pbuf *psBuf)
{
    while(psBuf != NULL)
    {
        int iCount = 0;
        const unsigned char *pucData = psBuf->payload;
        while (iCount < psBuf->len)
        {
            iCount += PrinterWriteBuffer(&pucData[iCount], psBuf->len - iCount);
        }
        psBuf = psBuf->next;
    }
}


Цитата(Golikov A. @ May 15 2013, 11:27) *
вызывая функцию ресивед вы говорите сколько данных из всего окна вы обработали, но те окна что вы пропустили не обработав, могут получить и 2 пакет данных и третий, и перейдя к ним надо бы все данные обработать...


Вот это место не очень понял.
Golikov A.
что происходит с данными тех соединений что не подключены к печати.

То что у вас обрабатывается вы для него ресивед вызываете, тем самым передающей стороне подтверждаете что данные обработаны и приняты.

А для тех что нет? вы ресивед не вызываете, буфер забивается, передатчик накидав все окно данных перестает передавать. И вот тут вопрос, вызовется ли теперь хоть когда то функция ресив, если передатчик уже наелся кексов и не передает?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.