/[rdesktop]/sourceforge.net/rdesktop/trunk/rdpsnd.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /sourceforge.net/rdesktop/trunk/rdpsnd.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1298 by ossman_, Thu Oct 19 11:27:40 2006 UTC revision 1353 by stargo, Sun Dec 24 15:22:15 2006 UTC
# Line 1  Line 1 
1  /* -*- c-basic-offset: 8 -*-  /* -*- c-basic-offset: 8 -*-
2     rdesktop: A Remote Desktop Protocol client.     rdesktop: A Remote Desktop Protocol client.
3     Sound Channel Process Functions     Sound Channel Process Functions
4       Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
5     Copyright (C) Matthew Chapman 2003     Copyright (C) Matthew Chapman 2003
6     Copyright (C) GuoJunBo guojunbo@ict.ac.cn 2003     Copyright (C) GuoJunBo guojunbo@ict.ac.cn 2003
7    
# Line 30  Line 31 
31  #define RDPSND_SET_VOLUME       3  #define RDPSND_SET_VOLUME       3
32  #define RDPSND_UNKNOWN4         4  #define RDPSND_UNKNOWN4         4
33  #define RDPSND_COMPLETION       5  #define RDPSND_COMPLETION       5
34  #define RDPSND_SERVERTICK       6  #define RDPSND_PING                     6
35  #define RDPSND_NEGOTIATE        7  #define RDPSND_NEGOTIATE        7
36    
37  #define MAX_FORMATS             10  #define MAX_FORMATS             10
38  #define MAX_QUEUE               10  #define MAX_QUEUE               10
39    
 BOOL g_dsp_busy = False;  
 int g_dsp_fd;  
   
40  static VCHANNEL *rdpsnd_channel;  static VCHANNEL *rdpsnd_channel;
41  static struct audio_driver *drivers = NULL;  static struct audio_driver *drivers = NULL;
42  struct audio_driver *current_driver = NULL;  struct audio_driver *current_driver = NULL;
# Line 47  static BOOL device_open; Line 45  static BOOL device_open;
45  static WAVEFORMATEX formats[MAX_FORMATS];  static WAVEFORMATEX formats[MAX_FORMATS];
46  static unsigned int format_count;  static unsigned int format_count;
47  static unsigned int current_format;  static unsigned int current_format;
48  unsigned int queue_hi, queue_lo;  unsigned int queue_hi, queue_lo, queue_pending;
49  struct audio_packet packet_queue[MAX_QUEUE];  struct audio_packet packet_queue[MAX_QUEUE];
50    
51    static uint8 packet_opcode;
52    static struct stream packet;
53    
54  void (*wave_out_play) (void);  void (*wave_out_play) (void);
55    
56    static void rdpsnd_queue_write(STREAM s, uint16 tick, uint8 index);
57    static void rdpsnd_queue_init(void);
58    static void rdpsnd_queue_complete_pending(void);
59    static long rdpsnd_queue_next_completion(void);
60    
61  static STREAM  static STREAM
62  rdpsnd_init_packet(uint16 type, uint16 size)  rdpsnd_init_packet(uint16 type, uint16 size)
63  {  {
# Line 66  rdpsnd_init_packet(uint16 type, uint16 s Line 72  rdpsnd_init_packet(uint16 type, uint16 s
72  static void  static void
73  rdpsnd_send(STREAM s)  rdpsnd_send(STREAM s)
74  {  {
 #ifdef RDPSND_DEBUG  
         printf("RDPSND send:\n");  
         hexdump(s->channel_hdr + 8, s->end - s->channel_hdr - 8);  
 #endif  
   
75          channel_send(s, rdpsnd_channel);          channel_send(s, rdpsnd_channel);
76  }  }
77    
78  void  static void
79  rdpsnd_send_completion(uint16 tick, uint8 packet_index)  rdpsnd_send_completion(uint16 tick, uint8 packet_index)
80  {  {
81          STREAM s;          STREAM s;
# Line 85  rdpsnd_send_completion(uint16 tick, uint Line 86  rdpsnd_send_completion(uint16 tick, uint
86          out_uint8(s, 0);          out_uint8(s, 0);
87          s_mark_end(s);          s_mark_end(s);
88          rdpsnd_send(s);          rdpsnd_send(s);
89    
90            DEBUG_SOUND(("RDPSND: -> RDPSND_COMPLETION(tick: %u, index: %u)\n",
91                         (unsigned) tick, (unsigned) packet_index));
92    }
93    
94    
95    static BOOL
96    rdpsnd_auto_select(void)
97    {
98            static BOOL failed = False;
99    
100            if (!failed)
101            {
102                    current_driver = drivers;
103                    while (current_driver != NULL)
104                    {
105                            DEBUG(("trying %s...\n", current_driver->name));
106                            if (current_driver->wave_out_open())
107                            {
108                                    DEBUG(("selected %s\n", current_driver->name));
109                                    current_driver->wave_out_close();
110                                    return True;
111                            }
112                            current_driver = current_driver->next;
113                    }
114    
115                    warning("no working audio-driver found\n");
116                    failed = True;
117                    current_driver = NULL;
118            }
119    
120            return False;
121  }  }
122    
123  static void  static void
124  rdpsnd_process_negotiate(STREAM in)  rdpsnd_process_negotiate(STREAM in)
125  {  {
126          unsigned int in_format_count, i;          uint16 in_format_count, i;
127            uint8 pad;
128            uint16 version;
129          WAVEFORMATEX *format;          WAVEFORMATEX *format;
130          STREAM out;          STREAM out;
131          BOOL device_available = False;          BOOL device_available = False;
132          int readcnt;          int readcnt;
133          int discardcnt;          int discardcnt;
134    
135          in_uint8s(in, 14);      /* flags, volume, pitch, UDP port */          in_uint8s(in, 14);      /* initial bytes not valid from server */
136          in_uint16_le(in, in_format_count);          in_uint16_le(in, in_format_count);
137          in_uint8s(in, 4);       /* pad, status, pad */          in_uint8(in, pad);
138            in_uint16_le(in, version);
139            in_uint8s(in, 1);       /* padding */
140    
141            DEBUG_SOUND(("RDPSND: RDPSND_NEGOTIATE(formats: %d, pad: 0x%02x, version: %x)\n",
142                         (int) in_format_count, (unsigned) pad, (unsigned) version));
143    
144          if (current_driver->wave_out_open())          if (!current_driver)
145                    device_available = rdpsnd_auto_select();
146    
147            if (current_driver && !device_available && current_driver->wave_out_open())
148          {          {
149                  current_driver->wave_out_close();                  current_driver->wave_out_close();
150                  device_available = True;                  device_available = True;
# Line 150  rdpsnd_process_negotiate(STREAM in) Line 193  rdpsnd_process_negotiate(STREAM in)
193          out_uint16(out, 0);     /* UDP port */          out_uint16(out, 0);     /* UDP port */
194    
195          out_uint16_le(out, format_count);          out_uint16_le(out, format_count);
196          out_uint8(out, 0x95);   /* pad? */          out_uint8(out, 0);      /* padding */
197          out_uint16_le(out, 2);  /* status */          out_uint16_le(out, 2);  /* version */
198          out_uint8(out, 0x77);   /* pad? */          out_uint8(out, 0);      /* padding */
199    
200          for (i = 0; i < format_count; i++)          for (i = 0; i < format_count; i++)
201          {          {
# Line 167  rdpsnd_process_negotiate(STREAM in) Line 210  rdpsnd_process_negotiate(STREAM in)
210          }          }
211    
212          s_mark_end(out);          s_mark_end(out);
213    
214            DEBUG_SOUND(("RDPSND: -> RDPSND_NEGOTIATE(formats: %d)\n", (int) format_count));
215    
216          rdpsnd_send(out);          rdpsnd_send(out);
217  }  }
218    
219  static void  static void
220  rdpsnd_process_servertick(STREAM in)  rdpsnd_process_ping(STREAM in)
221  {  {
222          uint16 tick1, tick2;          uint16 tick;
223          STREAM out;          STREAM out;
224    
225          /* in_uint8s(in, 4); unknown */          in_uint16_le(in, tick);
226          in_uint16_le(in, tick1);  
227          in_uint16_le(in, tick2);          DEBUG_SOUND(("RDPSND: RDPSND_PING(tick: 0x%04x)\n", (unsigned) tick));
228    
229          out = rdpsnd_init_packet(RDPSND_SERVERTICK | 0x2300, 4);          out = rdpsnd_init_packet(RDPSND_PING | 0x2300, 4);
230          out_uint16_le(out, tick1);          out_uint16_le(out, tick);
231          out_uint16_le(out, tick2);          out_uint16_le(out, 0);
232          s_mark_end(out);          s_mark_end(out);
233          rdpsnd_send(out);          rdpsnd_send(out);
234    
235            DEBUG_SOUND(("RDPSND: -> (tick: 0x%04x)\n", (unsigned) tick));
236  }  }
237    
238  static void  static void
239  rdpsnd_process(STREAM s)  rdpsnd_process_packet(uint8 opcode, STREAM s)
240  {  {
241          uint8 type;          uint16 vol_left, vol_right;
         uint16 datalen;  
         uint32 volume;  
242          static uint16 tick, format;          static uint16 tick, format;
243          static uint8 packet_index;          static uint8 packet_index;
         static BOOL awaiting_data_packet;  
         static unsigned char missing_bytes[4] = { 0, 0, 0, 0 };  
244    
245  #ifdef RDPSND_DEBUG          switch (opcode)
         printf("RDPSND recv:\n");  
         hexdump(s->p, s->end - s->p);  
 #endif  
   
         if (awaiting_data_packet)  
246          {          {
247                  if (format >= MAX_FORMATS)                  case RDPSND_WRITE:
248                  {                          in_uint16_le(s, tick);
249                          error("RDPSND: Invalid format index\n");                          in_uint16_le(s, format);
250                          return;                          in_uint8(s, packet_index);
251                  }                          in_uint8s(s, 3);
252                            DEBUG_SOUND(("RDPSND: RDPSND_WRITE(tick: %u, format: %u, index: %u, data: %u bytes)\n", (unsigned) tick, (unsigned) format, (unsigned) packet_index, (unsigned) s->size - 8));
253    
254                  if (!device_open || (format != current_format))                          if (format >= MAX_FORMATS)
                 {  
                         if (!device_open && !current_driver->wave_out_open())  
255                          {                          {
256                                  rdpsnd_send_completion(tick, packet_index);                                  error("RDPSND: Invalid format index\n");
257                                  return;                                  break;
258                          }                          }
259                          if (!current_driver->wave_out_set_format(&formats[format]))  
260                            if (!device_open || (format != current_format))
261                          {                          {
262                                  rdpsnd_send_completion(tick, packet_index);                                  /*
263                                  current_driver->wave_out_close();                                   * If we haven't selected a device by now, then either
264                                  device_open = False;                                   * we've failed to find a working device, or the server
265                                  return;                                   * is sending bogus RDPSND_WRITE.
266                                     */
267                                    if (!current_driver)
268                                    {
269                                            rdpsnd_send_completion(tick, packet_index);
270                                            break;
271                                    }
272                                    if (!device_open && !current_driver->wave_out_open())
273                                    {
274                                            rdpsnd_send_completion(tick, packet_index);
275                                            break;
276                                    }
277                                    if (!current_driver->wave_out_set_format(&formats[format]))
278                                    {
279                                            rdpsnd_send_completion(tick, packet_index);
280                                            current_driver->wave_out_close();
281                                            device_open = False;
282                                            break;
283                                    }
284                                    device_open = True;
285                                    current_format = format;
286                          }                          }
                         device_open = True;  
                         current_format = format;  
                 }  
   
                 /* Insert the 4 missing bytes retrieved from last RDPSND_WRITE */  
                 memcpy(s->data, missing_bytes, 4);  
287    
288                  current_driver->                          rdpsnd_queue_write(rdpsnd_dsp_process
289                          wave_out_write(rdpsnd_dsp_process                                             (s->p, s->end - s->p, current_driver,
290                                         (s, current_driver, &formats[current_format]), tick,                                              &formats[current_format]), tick, packet_index);
291                                         packet_index);                          return;
                 awaiting_data_packet = False;  
                 return;  
         }  
   
         in_uint8(s, type);  
         in_uint8s(s, 1);        /* unknown? */  
         in_uint16_le(s, datalen);  
   
         switch (type)  
         {  
                 case RDPSND_WRITE:  
                         in_uint16_le(s, tick);  
                         in_uint16_le(s, format);  
                         in_uint8(s, packet_index);  
                         /* Here are our lost bytes, but why? */  
                         memcpy(missing_bytes, s->end - 4, 4);  
                         awaiting_data_packet = True;  
292                          break;                          break;
293                  case RDPSND_CLOSE:                  case RDPSND_CLOSE:
294                          current_driver->wave_out_close();                          DEBUG_SOUND(("RDPSND: RDPSND_CLOSE()\n"));
295                            if (device_open)
296                                    current_driver->wave_out_close();
297                          device_open = False;                          device_open = False;
298                          break;                          break;
299                  case RDPSND_NEGOTIATE:                  case RDPSND_NEGOTIATE:
300                          rdpsnd_process_negotiate(s);                          rdpsnd_process_negotiate(s);
301                          break;                          break;
302                  case RDPSND_SERVERTICK:                  case RDPSND_PING:
303                          rdpsnd_process_servertick(s);                          rdpsnd_process_ping(s);
304                          break;                          break;
305                  case RDPSND_SET_VOLUME:                  case RDPSND_SET_VOLUME:
306                          in_uint32(s, volume);                          in_uint16_le(s, vol_left);
307                            in_uint16_le(s, vol_right);
308                            DEBUG_SOUND(("RDPSND: RDPSND_VOLUME(left: 0x%04x (%u %%), right: 0x%04x (%u %%))\n", (unsigned) vol_left, (unsigned) vol_left / 655, (unsigned) vol_right, (unsigned) vol_right / 655));
309                          if (device_open)                          if (device_open)
310                          {                                  current_driver->wave_out_volume(vol_left, vol_right);
                                 current_driver->wave_out_volume((volume & 0xffff),  
                                                                 (volume & 0xffff0000) >> 16);  
                         }  
311                          break;                          break;
312                  default:                  default:
313                          unimpl("RDPSND packet type %d\n", type);                          unimpl("RDPSND packet type %x\n", opcode);
314                          break;                          break;
315          }          }
316  }  }
317    
318  static BOOL  static void
319  rdpsnd_auto_open(void)  rdpsnd_process(STREAM s)
320  {  {
321          static BOOL failed = False;          uint16 len;
322    
323          if (!failed)          while (!s_check_end(s))
324          {          {
325                  struct audio_driver *auto_driver = current_driver;                  /* New packet */
326                    if (packet.size == 0)
                 current_driver = drivers;  
                 while (current_driver != NULL)  
327                  {                  {
328                          DEBUG(("trying %s...\n", current_driver->name));                          if ((s->end - s->p) < 4)
                         if (current_driver->wave_out_open())  
329                          {                          {
330                                  DEBUG(("selected %s\n", current_driver->name));                                  error("RDPSND: Split at packet header. Things will go south from here...\n");
331                                  return True;                                  return;
332                          }                          }
333                          g_dsp_fd = 0;                          in_uint8(s, packet_opcode);
334                          current_driver = current_driver->next;                          in_uint8s(s, 1);        /* Padding */
335                            in_uint16_le(s, len);
336    
337                            DEBUG_SOUND(("RDPSND: == Opcode %x Length: %d ==\n",
338                                         (int) packet_opcode, (int) len));
339    
340                            packet.p = packet.data;
341                            packet.end = packet.data + len;
342                            packet.size = len;
343                  }                  }
344                    else
345                    {
346                            len = MIN(s->end - s->p, packet.end - packet.p);
347    
348                  warning("no working audio-driver found\n");                          /* Microsoft's server is so broken it's not even funny... */
349                  failed = True;                          if (packet_opcode == RDPSND_WRITE)
350                  current_driver = auto_driver;                          {
351          }                                  if ((packet.p - packet.data) < 12)
352                                            len = MIN(len, 12 - (packet.p - packet.data));
353                                    else if ((packet.p - packet.data) == 12)
354                                    {
355                                            DEBUG_SOUND(("RDPSND: Eating 4 bytes of %d bytes...\n",
356                                                         len));
357                                            in_uint8s(s, 4);
358                                            len -= 4;
359                                    }
360                            }
361    
362          return False;                          in_uint8a(s, packet.p, len);
363                            packet.p += len;
364                    }
365    
366                    /* Packet fully assembled */
367                    if (packet.p == packet.end)
368                    {
369                            packet.p = packet.data;
370                            rdpsnd_process_packet(packet_opcode, &packet);
371                            packet.size = 0;
372                    }
373            }
374  }  }
375    
376  static void  static void
# Line 338  rdpsnd_register_drivers(char *options) Line 403  rdpsnd_register_drivers(char *options)
403  #endif  #endif
404  #if defined(RDPSND_LIBAO)  #if defined(RDPSND_LIBAO)
405          *reg = libao_register(options);          *reg = libao_register(options);
406      assert(*reg);          assert(*reg);
407          reg = &((*reg)->next);          reg = &((*reg)->next);
408  #endif  #endif
409            *reg = NULL;
410  }  }
411    
412  BOOL  BOOL
413  rdpsnd_init(char *optarg)  rdpsnd_init(char *optarg)
414  {  {
         static struct audio_driver auto_driver;  
415          struct audio_driver *pos;          struct audio_driver *pos;
416          char *driver = NULL, *options = NULL;          char *driver = NULL, *options = NULL;
417    
418          drivers = NULL;          drivers = NULL;
419    
420            packet.data = xmalloc(65536);
421            packet.p = packet.end = packet.data;
422            packet.size = 0;
423    
424          rdpsnd_channel =          rdpsnd_channel =
425                  channel_register("rdpsnd", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP,                  channel_register("rdpsnd", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP,
426                                   rdpsnd_process);                                   rdpsnd_process);
# Line 362  rdpsnd_init(char *optarg) Line 431  rdpsnd_init(char *optarg)
431                  return False;                  return False;
432          }          }
433    
434            rdpsnd_queue_init();
435    
436          if (optarg != NULL && strlen(optarg) > 0)          if (optarg != NULL && strlen(optarg) > 0)
437          {          {
438                  driver = options = optarg;                  driver = options = optarg;
# Line 382  rdpsnd_init(char *optarg) Line 453  rdpsnd_init(char *optarg)
453          rdpsnd_register_drivers(options);          rdpsnd_register_drivers(options);
454    
455          if (!driver)          if (!driver)
         {  
                 auto_driver.wave_out_open = &rdpsnd_auto_open;  
                 current_driver = &auto_driver;  
456                  return True;                  return True;
         }  
457    
458          pos = drivers;          pos = drivers;
459          while (pos != NULL)          while (pos != NULL)
# Line 418  rdpsnd_show_help(void) Line 485  rdpsnd_show_help(void)
485  }  }
486    
487  void  void
488  rdpsnd_play(void)  rdpsnd_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv)
489    {
490            long next_pending;
491    
492            if (device_open)
493                    current_driver->add_fds(n, rfds, wfds, tv);
494    
495            next_pending = rdpsnd_queue_next_completion();
496            if (next_pending >= 0)
497            {
498                    long cur_timeout;
499    
500                    cur_timeout = tv->tv_sec * 1000000 + tv->tv_usec;
501                    if (cur_timeout > next_pending)
502                    {
503                            tv->tv_sec = next_pending / 1000000;
504                            tv->tv_usec = next_pending % 1000000;
505                    }
506            }
507    }
508    
509    void
510    rdpsnd_check_fds(fd_set * rfds, fd_set * wfds)
511  {  {
512          current_driver->wave_out_play();          rdpsnd_queue_complete_pending();
513    
514            if (device_open)
515                    current_driver->check_fds(rfds, wfds);
516  }  }
517    
518  inline void  static void
519  rdpsnd_queue_write(STREAM s, uint16 tick, uint8 index)  rdpsnd_queue_write(STREAM s, uint16 tick, uint8 index)
520  {  {
521          struct audio_packet *packet = &packet_queue[queue_hi];          struct audio_packet *packet = &packet_queue[queue_hi];
522          unsigned int next_hi = (queue_hi + 1) % MAX_QUEUE;          unsigned int next_hi = (queue_hi + 1) % MAX_QUEUE;
523    
524          if (next_hi == queue_lo)          if (next_hi == queue_pending)
525          {          {
526                  error("No space to queue audio packet\n");                  error("No space to queue audio packet\n");
527                  return;                  return;
# Line 441  rdpsnd_queue_write(STREAM s, uint16 tick Line 533  rdpsnd_queue_write(STREAM s, uint16 tick
533          packet->tick = tick;          packet->tick = tick;
534          packet->index = index;          packet->index = index;
535    
536          if (!g_dsp_busy)          gettimeofday(&packet->arrive_tv, NULL);
                 current_driver->wave_out_play();  
537  }  }
538    
539  inline struct audio_packet *  struct audio_packet *
540  rdpsnd_queue_current_packet(void)  rdpsnd_queue_current_packet(void)
541  {  {
542          return &packet_queue[queue_lo];          return &packet_queue[queue_lo];
543  }  }
544    
545  inline BOOL  BOOL
546  rdpsnd_queue_empty(void)  rdpsnd_queue_empty(void)
547  {  {
548          return (queue_lo == queue_hi);          return (queue_lo == queue_hi);
549  }  }
550    
551  inline void  static void
552  rdpsnd_queue_init(void)  rdpsnd_queue_init(void)
553  {  {
554          queue_lo = queue_hi = 0;          queue_pending = queue_lo = queue_hi = 0;
555  }  }
556    
557  inline void  void
558  rdpsnd_queue_next(void)  rdpsnd_queue_next(unsigned long completed_in_us)
559  {  {
560          xfree(packet_queue[queue_lo].s.data);          struct audio_packet *packet;
561    
562            assert(!rdpsnd_queue_empty());
563    
564            packet = &packet_queue[queue_lo];
565    
566            gettimeofday(&packet->completion_tv, NULL);
567    
568            packet->completion_tv.tv_usec += completed_in_us;
569            packet->completion_tv.tv_sec += packet->completion_tv.tv_usec / 1000000;
570            packet->completion_tv.tv_usec %= 1000000;
571    
572          queue_lo = (queue_lo + 1) % MAX_QUEUE;          queue_lo = (queue_lo + 1) % MAX_QUEUE;
573    
574            rdpsnd_queue_complete_pending();
575  }  }
576    
577  inline int  int
578  rdpsnd_queue_next_tick(void)  rdpsnd_queue_next_tick(void)
579  {  {
580          if (((queue_lo + 1) % MAX_QUEUE) != queue_hi)          if (((queue_lo + 1) % MAX_QUEUE) != queue_hi)
# Line 482  rdpsnd_queue_next_tick(void) Line 586  rdpsnd_queue_next_tick(void)
586                  return (packet_queue[queue_lo].tick + 65535) % 65536;                  return (packet_queue[queue_lo].tick + 65535) % 65536;
587          }          }
588  }  }
589    
590    static void
591    rdpsnd_queue_complete_pending(void)
592    {
593            struct timeval now;
594            long elapsed;
595            struct audio_packet *packet;
596    
597            gettimeofday(&now, NULL);
598    
599            while (queue_pending != queue_lo)
600            {
601                    packet = &packet_queue[queue_pending];
602    
603                    if (now.tv_sec < packet->completion_tv.tv_sec)
604                            break;
605    
606                    if ((now.tv_sec == packet->completion_tv.tv_sec) &&
607                        (now.tv_usec < packet->completion_tv.tv_usec))
608                            break;
609    
610                    elapsed = (packet->completion_tv.tv_sec - packet->arrive_tv.tv_sec) * 1000000 +
611                            (packet->completion_tv.tv_usec - packet->arrive_tv.tv_usec);
612                    elapsed /= 1000;
613    
614                    xfree(packet->s.data);
615                    rdpsnd_send_completion((packet->tick + elapsed) % 65536, packet->index);
616                    queue_pending = (queue_pending + 1) % MAX_QUEUE;
617            }
618    }
619    
620    static long
621    rdpsnd_queue_next_completion(void)
622    {
623            struct audio_packet *packet;
624            long remaining;
625            struct timeval now;
626    
627            if (queue_pending == queue_lo)
628                    return -1;
629    
630            gettimeofday(&now, NULL);
631    
632            packet = &packet_queue[queue_pending];
633    
634            remaining = (packet->completion_tv.tv_sec - now.tv_sec) * 1000000 +
635                    (packet->completion_tv.tv_usec - now.tv_usec);
636    
637            if (remaining < 0)
638                    return 0;
639    
640            return remaining;
641    }

Legend:
Removed from v.1298  
changed lines
  Added in v.1353

  ViewVC Help
Powered by ViewVC 1.1.26