ant-wireless 0.1.0.pre.20210617213631

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,233 @@
1
+ /*
2
+ * callbacks.c - ANT callback handling
3
+ * $Id$
4
+ *
5
+ * This contains code adapted from Kim Burge Strand's Library-of-Massive-Fun-And-Overjoy
6
+ * project, namely the code that calls back into Ruby from ANT callbacks. Used under
7
+ * the conditions of the WTFPL. For more info on how it works, see the associated article:
8
+ *
9
+ * https://www.burgestrand.se//articles/asynchronous-callbacks-in-ruby-c-extensions/
10
+ *
11
+ * Authors:
12
+ * * Michael Granger <ged@FaerieMUD.org>
13
+ *
14
+ */
15
+
16
+ #include "ant_ext.h"
17
+
18
+
19
+ /*
20
+ * Three globals to allow for Ruby/C-thread communication:
21
+ * - mutex & condition to synchronize access to callback_queue
22
+ * - callback_queue to store actual callback data in
23
+ * Be careful with the functions that manipulate the callback
24
+ * queue; they must do so in the protection of a mutex.
25
+ */
26
+ pthread_mutex_t rant_callback_mutex = PTHREAD_MUTEX_INITIALIZER;
27
+ pthread_cond_t rant_callback_cond = PTHREAD_COND_INITIALIZER;
28
+ rant_callback_t *rant_callback_queue = NULL;
29
+
30
+ typedef struct callback_waiting_t callback_waiting_t;
31
+ struct callback_waiting_t {
32
+ rant_callback_t *callback;
33
+ bool abort;
34
+ };
35
+
36
+
37
+ /*
38
+ * Use this function to add a callback node onto the global
39
+ * callback queue.
40
+ * Do note that we are adding items to the front of the linked
41
+ * list, and as such events will always be handled by most recent
42
+ * first. To remedy this, add to the end of the queue instead.
43
+ */
44
+ static void
45
+ callback_queue_push( rant_callback_t *callback )
46
+ {
47
+ callback->next = rant_callback_queue;
48
+ rant_callback_queue = callback;
49
+ }
50
+
51
+
52
+ /*
53
+ * Use this function to pop off a callback node from the
54
+ * global callback queue. Returns NULL if queue is empty.
55
+ */
56
+ static rant_callback_t *
57
+ callback_queue_pop(void)
58
+ {
59
+ rant_callback_t *callback = rant_callback_queue;
60
+
61
+ if ( callback )
62
+ {
63
+ rant_callback_queue = callback->next;
64
+ }
65
+ return callback;
66
+ }
67
+
68
+
69
+ /*
70
+ * Queue a +callback+ for handling by Ruby. Blocks until it's handled.
71
+ */
72
+ bool
73
+ rant_callback( rant_callback_t *callback )
74
+ {
75
+ pthread_mutex_init( &callback->mutex, NULL );
76
+ pthread_cond_init( &callback->cond, NULL );
77
+
78
+ callback->handled = false;
79
+
80
+ // Put callback data in global callback queue
81
+ pthread_mutex_lock( &rant_callback_mutex );
82
+ callback_queue_push( callback );
83
+ pthread_mutex_unlock( &rant_callback_mutex );
84
+
85
+ // Notify waiting Ruby thread that we have callback data
86
+ pthread_cond_signal( &rant_callback_cond );
87
+
88
+ // Wait for callback to be handled
89
+ pthread_mutex_lock( &callback->mutex );
90
+ while ( callback->handled == false )
91
+ {
92
+ pthread_cond_wait( &callback->cond, &callback->mutex );
93
+ }
94
+ pthread_mutex_unlock( &callback->mutex );
95
+
96
+ // Clean up
97
+ pthread_mutex_destroy( &callback->mutex );
98
+ pthread_cond_destroy( &callback->cond );
99
+
100
+ return callback->rval;
101
+ }
102
+
103
+
104
+ /*
105
+ * Executed for each callback notification; what we receive
106
+ * are the callback parameters. The job of this method is to:
107
+ * 1. Convert callback parameters into Ruby values
108
+ * 2. Call the appropriate callback with said parameters
109
+ * 3. Convert the Ruby return value into a C value
110
+ * 4. Hand over the C value to the C callback
111
+ */
112
+ static VALUE
113
+ handle_callback( void *cb )
114
+ {
115
+ rant_callback_t *callback = (rant_callback_t *)cb;
116
+ int state = 0;
117
+ VALUE rval;
118
+
119
+ // callback->fn( callback->data );
120
+ rval = rb_protect( callback->fn, (VALUE)callback->data, &state );
121
+
122
+ // tell the callback that it has been handled, we are done
123
+ pthread_mutex_lock( &callback->mutex );
124
+
125
+ callback->handled = true;
126
+ callback->rval = RTEST( rval ) ? true : false;
127
+
128
+ pthread_cond_signal( &callback->cond );
129
+ pthread_mutex_unlock( &callback->mutex );
130
+
131
+ if ( state ) {
132
+ rb_jump_tag( state );
133
+ }
134
+
135
+ return rval;
136
+ }
137
+
138
+
139
+ /*
140
+ * Wait for the next callback in the global callback queue.
141
+ */
142
+ static void *
143
+ wait_for_callback_signal( void *w_ptr )
144
+ {
145
+ callback_waiting_t *waiting = (callback_waiting_t*) w_ptr;
146
+
147
+ pthread_mutex_lock( &rant_callback_mutex );
148
+
149
+ // abort signal is used when ruby wants us to stop waiting
150
+ while ( waiting->abort == false && (waiting->callback = callback_queue_pop()) == NULL )
151
+ {
152
+ pthread_cond_wait( &rant_callback_cond, &rant_callback_mutex );
153
+ }
154
+
155
+ pthread_mutex_unlock( &rant_callback_mutex );
156
+
157
+ return NULL;
158
+ }
159
+
160
+
161
+ /*
162
+ * Unblocking function: tell the callback thread to abort if Ruby says it's
163
+ * shutdown time.
164
+ */
165
+ static void
166
+ stop_waiting_for_callback_signal( void *w_ptr )
167
+ {
168
+ callback_waiting_t *waiting = (callback_waiting_t*) w_ptr;
169
+
170
+ pthread_mutex_lock( &rant_callback_mutex );
171
+
172
+ waiting->abort = true;
173
+
174
+ pthread_cond_signal( &rant_callback_cond );
175
+ pthread_mutex_unlock( &rant_callback_mutex );
176
+ }
177
+
178
+
179
+ /*
180
+ * Callback handler thread routine; loops until told to abort. Each loop:
181
+ *
182
+ * - Release the GVL
183
+ * - Wait on a signal on the global condition variable with an unblock function
184
+ * that tells it to abort.
185
+ * - If there's a callback, create a child thread to run it.
186
+ *
187
+ */
188
+ static VALUE
189
+ callback_thread( void *unused )
190
+ {
191
+ callback_waiting_t waiting = {
192
+ .callback = NULL,
193
+ .abort = false
194
+ };
195
+
196
+ while ( waiting.abort == false )
197
+ {
198
+ // release the GIL while waiting for a callback notification
199
+ rb_thread_call_without_gvl( wait_for_callback_signal, &waiting,
200
+ stop_waiting_for_callback_signal, &waiting );
201
+
202
+ // if ruby wants us to abort, this will be NULL
203
+ if ( waiting.callback )
204
+ {
205
+ // rant_log( "debug", "Starting a callback thread." );
206
+ rb_thread_create( handle_callback, (void *)waiting.callback );
207
+ }
208
+ }
209
+
210
+ return Qnil;
211
+ }
212
+
213
+
214
+
215
+ /*
216
+ * Start a Thread which will wait for ANT callbacks and dispatch them when they arrive.
217
+ */
218
+ void
219
+ rant_start_callback_thread()
220
+ {
221
+ // ThreadGroup isn't a public symbol, so have to look it up
222
+ VALUE cThGroup = rb_define_class( "ThreadGroup", rb_cObject );
223
+ VALUE thread_group = rb_class_new_instance( 0, NULL, cThGroup );
224
+ VALUE callback_dispatcher = rb_thread_create( callback_thread, (void *)NULL );
225
+
226
+ rb_funcallv( thread_group, rb_intern("add"), 1, &callback_dispatcher );
227
+ rb_ivar_set( rant_mAnt, rb_intern("@callback_threads"), thread_group );
228
+ rb_ivar_set( rant_mAnt, rb_intern("@callback_dispatcher"), callback_dispatcher );
229
+ rb_attr( rb_singleton_class(rant_mAnt), rb_intern("callback_threads"), 1, 0, 0 );
230
+ rb_attr( rb_singleton_class(rant_mAnt), rb_intern("callback_dispatcher"), 1, 0, 0 );
231
+ }
232
+
233
+
@@ -0,0 +1,524 @@
1
+ /*
2
+ * channel.c - Ant::Channel class
3
+ * $Id$
4
+ *
5
+ * Authors:
6
+ * * Michael Granger <ged@FaerieMUD.org>
7
+ *
8
+ */
9
+
10
+ #include "ant_ext.h"
11
+
12
+ VALUE rant_cAntChannel;
13
+
14
+ VALUE rant_mAntDataUtilities;
15
+
16
+ static void rant_channel_free( void * );
17
+ static void rant_channel_mark( void * );
18
+
19
+
20
+ static const rb_data_type_t rant_channel_datatype_t = {
21
+ .wrap_struct_name = "Ant::Channel",
22
+ .function = {
23
+ .dmark = rant_channel_mark,
24
+ .dfree = rant_channel_free,
25
+ },
26
+ .data = NULL,
27
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
28
+ };
29
+
30
+
31
+
32
+ /*
33
+ * Free function
34
+ */
35
+ static void
36
+ rant_channel_free( void *ptr )
37
+ {
38
+ if ( ptr ) {
39
+ rant_channel_t *channel = (rant_channel_t *)ptr;
40
+ ANT_AssignChannelEventFunction( channel->channel_num, NULL, NULL );
41
+ ANT_UnAssignChannel( channel->channel_num );
42
+
43
+ channel->callback = Qnil;
44
+
45
+ xfree( ptr );
46
+ ptr = NULL;
47
+ }
48
+ }
49
+
50
+
51
+ /*
52
+ * Mark function
53
+ */
54
+ static void
55
+ rant_channel_mark( void *ptr )
56
+ {
57
+ rant_channel_t *channel = (rant_channel_t *)ptr;
58
+ rb_gc_mark( channel->callback );
59
+ }
60
+
61
+
62
+ /*
63
+ * Alloc function
64
+ */
65
+ static VALUE
66
+ rant_channel_alloc( VALUE klass )
67
+ {
68
+ rant_channel_t *ptr;
69
+
70
+ VALUE rval = TypedData_Make_Struct( klass, rant_channel_t, &rant_channel_datatype_t, ptr );
71
+ ptr->callback = Qnil;
72
+
73
+ return rval;
74
+ }
75
+
76
+
77
+
78
+ /*
79
+ * Fetch the data pointer and check it for sanity.
80
+ */
81
+ rant_channel_t *
82
+ rant_get_channel( VALUE self )
83
+ {
84
+ rant_channel_t *ptr;
85
+
86
+ if ( !IsChannel(self) ) {
87
+ rb_raise( rb_eTypeError, "wrong argument type %s (expected Ant::Channel)",
88
+ rb_class2name(CLASS_OF( self )) );
89
+ }
90
+
91
+ ptr = DATA_PTR( self );
92
+ assert( ptr );
93
+
94
+ return ptr;
95
+ }
96
+
97
+
98
+ /*
99
+ * Clear the registry after channel have been reset.
100
+ */
101
+ void
102
+ rant_channel_clear_registry()
103
+ {
104
+ VALUE registry = rb_iv_get( rant_cAntChannel, "@registry" );
105
+ rb_hash_clear( registry );
106
+ }
107
+
108
+
109
+
110
+ /*
111
+ * call-seq:
112
+ * channel.initialize
113
+ *
114
+ * Set up the channel.
115
+ *
116
+ */
117
+ static VALUE
118
+ rant_channel_init( VALUE self, VALUE channel_number, VALUE channel_type, VALUE network_number,
119
+ VALUE extended_options )
120
+ {
121
+ rant_channel_t *ptr = rant_get_channel( self );
122
+ VALUE registry = rb_iv_get( rant_cAntChannel, "@registry" );
123
+
124
+ ptr->channel_num = NUM2USHORT( channel_number );
125
+ MEMZERO( ptr->buffer, unsigned char, MESG_MAX_SIZE );
126
+
127
+ rb_iv_set( self, "@channel_type", channel_type );
128
+ rb_iv_set( self, "@network_number", network_number );
129
+ rb_iv_set( self, "@extended_options", extended_options );
130
+
131
+ rb_hash_aset( registry, channel_number, self );
132
+
133
+ return self;
134
+ }
135
+
136
+
137
+ /*
138
+ * call-seq:
139
+ * channel.channel_number -> integer
140
+ *
141
+ * Return the channel number assigned to the Channel.
142
+ *
143
+ */
144
+ static VALUE
145
+ rant_channel_channel_number( VALUE self )
146
+ {
147
+ rant_channel_t *ptr = rant_get_channel( self );
148
+
149
+ return INT2FIX( ptr->channel_num );
150
+ }
151
+
152
+
153
+ /*
154
+ * call-seq:
155
+ * channel.set_channel_id( device_number, device_type, transmission_type, timeout=0 )
156
+ *
157
+ * Set the channel ID using the combination of the +device_number+,
158
+ * +device_type+, and +transmission_type+. If the assignment hasn't been set in
159
+ * +timeout+ seconds, aborts and returns +nil+.
160
+ *
161
+ */
162
+ static VALUE
163
+ rant_channel_set_channel_id( int argc, VALUE *argv, VALUE self )
164
+ {
165
+ rant_channel_t *ptr = rant_get_channel( self );
166
+ VALUE device_number, device_type, transmission_type, timeout;
167
+ unsigned short usDeviceNumber;
168
+ unsigned char ucDeviceType,
169
+ ucTransmissionType;
170
+ unsigned int ulResponseTime = 0;
171
+ bool result;
172
+
173
+ rb_scan_args( argc, argv, "31", &device_number, &device_type, &transmission_type, &timeout );
174
+
175
+ usDeviceNumber = NUM2USHORT( device_number );
176
+ ucDeviceType = NUM2USHORT( device_type );
177
+ ucTransmissionType = NUM2USHORT( transmission_type );
178
+
179
+ if ( RTEST(timeout) )
180
+ ulResponseTime = NUM2UINT( timeout );
181
+
182
+ result = ANT_SetChannelId_RTO( ptr->channel_num, usDeviceNumber, ucDeviceType,
183
+ ucTransmissionType, ulResponseTime );
184
+
185
+ if ( !result ) {
186
+ rb_raise( rb_eRuntimeError, "Failed to set the channel id." );
187
+ }
188
+
189
+ return Qtrue;
190
+ }
191
+
192
+
193
+ // ANT_SetChannelPeriod_RTO(UCHAR ucANTChannel_, USHORT usMesgPeriod_, ULONG ulResponseTime_);
194
+ // ANT_SetChannelSearchTimeout_RTO(UCHAR ucANTChannel_, UCHAR ucSearchTimeout_, ULONG ulResponseTime_);
195
+ // ANT_SetChannelRFFreq_RTO(UCHAR ucANTChannel_, UCHAR ucRFFreq_, ULONG ulResponseTime_);
196
+
197
+
198
+ /*
199
+ * call-seq:
200
+ * channel.open( tineout=0 )
201
+ *
202
+ * Open the channel. If it hasn't completed within +timeout+ seconds, raises a RuntimeError.
203
+ */
204
+ static VALUE
205
+ rant_channel_open( int argc, VALUE *argv, VALUE self )
206
+ {
207
+ rant_channel_t *ptr = rant_get_channel( self );
208
+ VALUE timeout;
209
+ unsigned int ulResponseTime = 0;
210
+
211
+ rb_scan_args( argc, argv, "01", &timeout );
212
+
213
+ if ( RTEST(timeout) )
214
+ ulResponseTime = NUM2UINT( timeout );
215
+
216
+ if ( !ANT_OpenChannel_RTO( ptr->channel_num, ulResponseTime ) ) {
217
+ rb_raise( rb_eRuntimeError, "Failed to open the channel." );
218
+ }
219
+
220
+ return Qtrue;
221
+ }
222
+
223
+
224
+ /*
225
+ * call-seq:
226
+ * channel.close
227
+ *
228
+ * Close the channel and remove it from the registry.
229
+ *
230
+ */
231
+ static VALUE
232
+ rant_channel_close( int argc, VALUE *argv, VALUE self )
233
+ {
234
+ rant_channel_t *ptr = rant_get_channel( self );
235
+ VALUE timeout;
236
+ VALUE registry = rb_iv_get( rant_cAntChannel, "@registry" );
237
+ unsigned int ulResponseTime = 0;
238
+
239
+ rb_scan_args( argc, argv, "01", &timeout );
240
+
241
+ if ( RTEST(timeout) )
242
+ ulResponseTime = NUM2UINT( timeout );
243
+
244
+ rant_log_obj( self, "info", "Closing channel %d (with timeout %d).", ptr->channel_num, ulResponseTime );
245
+ if ( !ANT_CloseChannel_RTO( ptr->channel_num, ulResponseTime ) ) {
246
+ rb_raise( rb_eRuntimeError, "Failed to close the channel." );
247
+ }
248
+ rant_log_obj( self, "info", "Channel %d closed.", ptr->channel_num );
249
+
250
+ rb_hash_delete( registry, INT2FIX( ptr->channel_num ) );
251
+
252
+ return Qtrue;
253
+ }
254
+
255
+
256
+ /*
257
+ * call-seq:
258
+ * channel.closed? -> true or false
259
+ *
260
+ * Returns +true+ if the channel has been closed; i.e., if it's not longer the
261
+ * registered channel for its channel number.
262
+ *
263
+ */
264
+ static VALUE
265
+ rant_channel_closed_p( VALUE self )
266
+ {
267
+ rant_channel_t *ptr = rant_get_channel( self );
268
+ VALUE registry = rb_iv_get( rant_cAntChannel, "@registry" );
269
+ VALUE channel = rb_hash_lookup( registry, INT2FIX( ptr->channel_num ) );
270
+
271
+ return self == channel ? Qfalse : Qtrue;
272
+ }
273
+
274
+
275
+ /*
276
+ * Event callback functions
277
+ */
278
+
279
+ struct on_event_call {
280
+ unsigned char ucANTChannel;
281
+ unsigned char ucEvent;
282
+ };
283
+
284
+
285
+ /*
286
+ * Handle the event callback -- Ruby side.
287
+ */
288
+ static VALUE
289
+ rant_channel_call_event_callback( VALUE callPtr )
290
+ {
291
+ struct on_event_call *call = (struct on_event_call *)callPtr;
292
+ VALUE registry = rb_iv_get( rant_cAntChannel, "@registry" );
293
+ VALUE channel = rb_hash_fetch( registry, INT2FIX(call->ucANTChannel) );
294
+ rant_channel_t *ptr = rant_get_channel( channel );
295
+ VALUE rb_callback = ptr->callback;
296
+ VALUE rval = Qnil;
297
+
298
+ if ( RTEST(rb_callback) ) {
299
+ VALUE args[3];
300
+
301
+ args[0] = INT2FIX( call->ucANTChannel );
302
+ args[1] = INT2FIX( call->ucEvent );
303
+ args[2] = rb_enc_str_new( (char *)ptr->buffer, MESG_MAX_SIZE, rb_ascii8bit_encoding() );
304
+
305
+ rval = rb_funcallv_public( rb_callback, rb_intern("call"), 3, args );
306
+ }
307
+
308
+ MEMZERO( ptr->buffer, unsigned char, MESG_MAX_SIZE );
309
+
310
+ return rval;
311
+ }
312
+
313
+
314
+ /*
315
+ * Handle the event callback -- C side.
316
+ */
317
+ static BOOL
318
+ rant_channel_on_event_callback( unsigned char ucANTChannel, unsigned char ucEvent )
319
+ {
320
+ rant_callback_t callback;
321
+ struct on_event_call call;
322
+
323
+ call.ucANTChannel = ucANTChannel;
324
+ call.ucEvent = ucEvent;
325
+
326
+ callback.data = &call;
327
+ callback.fn = rant_channel_call_event_callback;
328
+
329
+ return rant_callback( &callback );
330
+ }
331
+
332
+
333
+ /*
334
+ * call-seq:
335
+ * channel.on_event {|channel_num, event_id, data| ... }
336
+ *
337
+ * Set up a callback for events on the receiving channel.
338
+ *
339
+ */
340
+ static VALUE
341
+ rant_channel_on_event( int argc, VALUE *argv, VALUE self )
342
+ {
343
+ rant_channel_t *ptr = rant_get_channel( self );
344
+ VALUE callback = Qnil;
345
+
346
+ rb_scan_args( argc, argv, "0&", &callback );
347
+
348
+ if ( !RTEST(callback) ) {
349
+ rb_raise( rb_eLocalJumpError, "block required, but not given" );
350
+ }
351
+
352
+ rant_log_obj( self, "debug", "Channel event callback is: %s", RSTRING_PTR(rb_inspect(callback)) );
353
+ ptr->callback = callback;
354
+
355
+ ANT_AssignChannelEventFunction( ptr->channel_num, rant_channel_on_event_callback, ptr->buffer );
356
+
357
+ return Qtrue;
358
+ }
359
+
360
+
361
+ /*
362
+ * call-seq:
363
+ * channel.send_burst_transfer( data )
364
+ *
365
+ * Send the given +data+ as one or more burst packets.
366
+ *
367
+ */
368
+ static VALUE
369
+ rant_channel_send_burst_transfer( VALUE self, VALUE data )
370
+ {
371
+ rant_channel_t *ptr = rant_get_channel( self );
372
+ unsigned char *data_s;
373
+ long data_len = RSTRING_LEN( data );
374
+ unsigned short usNumDataPackets = data_len / 8,
375
+ remainingBytes = data_len % 8;
376
+
377
+ data_s = ALLOC_N( unsigned char, data_len );
378
+ strncpy( (char *)data_s, StringValuePtr(data), data_len );
379
+
380
+ // Pad it to 8-byte alignment
381
+ if ( remainingBytes ) {
382
+ int pad_bytes = (8 - remainingBytes);
383
+ REALLOC_N( data_s, unsigned char, data_len + pad_bytes );
384
+ memset( data_s + data_len, 0, pad_bytes );
385
+
386
+ usNumDataPackets += 1;
387
+ }
388
+
389
+ VALUE data_string = rb_enc_str_new( (char *)data_s, usNumDataPackets * 8, rb_ascii8bit_encoding() );
390
+ VALUE hexdump = rb_funcall( rant_mAntDataUtilities, rb_intern("hexdump"), 1, data_string );
391
+
392
+ rant_log_obj( self, "debug", "Sending burst packets:\n%s", RSTRING_PTR(hexdump) );
393
+ if ( !ANT_SendBurstTransfer(ptr->channel_num, data_s, usNumDataPackets) ) {
394
+ rb_raise( rb_eRuntimeError, "failed to send burst transfer." );
395
+ }
396
+
397
+ return Qtrue;
398
+ }
399
+
400
+
401
+ /*
402
+ * call-seq:
403
+ * channel.send_acknowledged_data( data )
404
+ *
405
+ * Send the given +data+ as an acknowledged transmission. The +data+ cannot be longer
406
+ * than 8 bytes in length.
407
+ *
408
+ */
409
+ static VALUE
410
+ rant_channel_send_acknowledged_data( VALUE self, VALUE data )
411
+ {
412
+ rant_channel_t *ptr = rant_get_channel( self );
413
+ UCHAR aucTempBuffer[] = {0, 0, 0, 0, 0, 0, 0, 0};
414
+
415
+ StringValue( data );
416
+ if ( RSTRING_LEN(data) > 8 ) {
417
+ rb_raise( rb_eArgError, "Data can't be longer than 8 bytes." );
418
+ }
419
+ strncpy( (char *)aucTempBuffer, StringValuePtr(data), RSTRING_LEN(data) );
420
+
421
+ ANT_SendAcknowledgedData( ptr->channel_num, aucTempBuffer );
422
+
423
+ return Qtrue;
424
+ }
425
+
426
+
427
+ /*
428
+ * call-seq:
429
+ * channel.send_broadcast_data( data )
430
+ *
431
+ * Send the given +data+ as a broadcast transmission. The +data+ cannot be longer
432
+ * than 8 bytes in length.
433
+ *
434
+ */
435
+ static VALUE
436
+ rant_channel_send_broadcast_data( VALUE self, VALUE data )
437
+ {
438
+ rant_channel_t *ptr = rant_get_channel( self );
439
+ UCHAR aucTempBuffer[8] = {0, 0, 0, 0, 0, 0, 0, 0,};
440
+
441
+ StringValue( data );
442
+ if ( RSTRING_LEN(data) > 8 ) {
443
+ rb_raise( rb_eArgError, "Data can't be longer than 8 bytes." );
444
+ }
445
+ strncpy( (char *)aucTempBuffer, StringValuePtr(data), RSTRING_LEN(data) );
446
+
447
+ ANT_SendBroadcastData( ptr->channel_num, aucTempBuffer );
448
+
449
+ return Qtrue;
450
+ }
451
+
452
+
453
+ /*
454
+ * call-seq:
455
+ * channel.set_channel_rf_freq( frequency )
456
+ *
457
+ * Set the ANT RF +frequency+.
458
+ *
459
+ */
460
+ static VALUE
461
+ rant_channel_set_channel_rf_freq( VALUE self, VALUE frequency )
462
+ {
463
+ rant_channel_t *ptr = rant_get_channel( self );
464
+ unsigned short ucRFFreq = NUM2USHORT( frequency );
465
+
466
+ if ( ucRFFreq > 124 ) {
467
+ rb_raise( rb_eArgError, "frequency must be between 0 and 124." );
468
+ }
469
+
470
+ ANT_SetChannelRFFreq( ptr->channel_num, ucRFFreq );
471
+
472
+ return Qtrue;
473
+ }
474
+
475
+
476
+ void
477
+ init_ant_channel()
478
+ {
479
+ #ifdef FOR_RDOC
480
+ rb_cData = rb_define_class( "Data" );
481
+ rant_mAnt = rb_define_module( "Ant" );
482
+ #endif
483
+
484
+ /*
485
+ * Document-module: Ant::Channel
486
+ *
487
+ * An assigned ANT channel object.
488
+ *
489
+ */
490
+ rant_cAntChannel = rb_define_class_under( rant_mAnt, "Channel", rb_cObject );
491
+ rb_iv_set( rant_cAntChannel, "@registry", rb_hash_new() );
492
+
493
+ rant_mAntDataUtilities = rb_define_module_under( rant_mAnt, "DataUtilities" );
494
+
495
+ rb_define_alloc_func( rant_cAntChannel, rant_channel_alloc );
496
+ rb_define_protected_method( rant_cAntChannel, "initialize", rant_channel_init, 4 );
497
+
498
+ rb_define_method( rant_cAntChannel, "channel_number", rant_channel_channel_number, 0 );
499
+
500
+ rb_attr( rant_cAntChannel, rb_intern("channel_type"), 1, 0, 0 );
501
+ rb_attr( rant_cAntChannel, rb_intern("network_number"), 1, 0, 0 );
502
+ rb_attr( rant_cAntChannel, rb_intern("extended_options"), 1, 0, 0 );
503
+
504
+ rb_define_method( rant_cAntChannel, "set_channel_id", rant_channel_set_channel_id, -1 );
505
+ // rb_define_method( rant_cAntChannel, "set_channel_period",
506
+ // rant_channel_set_channel_period, -1 );
507
+ // rb_define_method( rant_cAntChannel, "set_channel_search_timeout",
508
+ // rant_channel_set_channel_search_timeout, -1 );
509
+ rb_define_method( rant_cAntChannel, "set_channel_rf_freq", rant_channel_set_channel_rf_freq, 1 );
510
+
511
+ rb_define_method( rant_cAntChannel, "open", rant_channel_open, -1 );
512
+ rb_define_method( rant_cAntChannel, "close", rant_channel_close, -1 );
513
+ rb_define_method( rant_cAntChannel, "closed?", rant_channel_closed_p, 0 );
514
+
515
+ rb_define_method( rant_cAntChannel, "send_burst_transfer", rant_channel_send_burst_transfer, 1 );
516
+ rb_define_method( rant_cAntChannel, "send_acknowledged_data", rant_channel_send_acknowledged_data, 1 );
517
+ rb_define_method( rant_cAntChannel, "send_broadcast_data", rant_channel_send_broadcast_data, 1 );
518
+
519
+ rb_define_method( rant_cAntChannel, "on_event", rant_channel_on_event, -1 );
520
+
521
+ rb_require( "ant/channel" );
522
+ }
523
+
524
+