Related
Code image
Hello DPDK Users,
I was trying to generate the incremental data value of UDP packets via DPDK.
But when I am transmitting it, I verified the same frames in Wireshark.
The data part of packets is changing illogically.
No sequence can be seen in data.
It is optimized the data value which I was wrote. The last value i tried to write was there in all the bytes of payload, not as expected in incremental order.
Can anyone help me, why is this occurring and what could be the easiest way to generate incremental DPDK packets?
The Code is given in the image file above.
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include<assert.h>
uint64_t temp1;
uint64_t temp2;
int temp3;
int test_type;
double sec;
time_t start_t,end_t;
double diff_t;
#include<time.h>
#include<signal.h>
#include<linux/kernel.h>
#include <rte_memory.h>
#include <rte_launch.h>
#include <rte_eal.h>
#include <rte_per_lcore.h>
#include <rte_lcore.h>
#include <rte_ethdev.h>
#include <rte_udp.h>
#include <rte_tcp.h>
#include <rte_ip.h>
#include <rte_arp.h>
#include <rte_icmp.h>
#include <rte_cycles.h>
#include <rte_lcore.h>
#include <rte_mbuf.h>
#define RX_RING_SIZE 2028
#define TX_RING_SIZE 2048
#define NUM_MBUFS 8191
#define MBUF_CACHE_SIZE 250
#define BURST_SIZE 32
#define UDP_SRC_PORT 6666
#define UDP_DST_PORT 6666
#define TCP_SRC_PORT 6666
#define TCP_DST_PORT 6666
#define IP_DEFTTL 64
#define IP_VERSION 0x40
#define IP_HDRLEN 0x05
#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN)
#define TX_PACKET_LENGTH 64
#define ETHER_MAX_LEN 1518
#if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
#define RTE_BE_TO_CPU_16(be_16_v) (be_16_v)
#define RTE_CPU_TO_BE_16(cpu_16_v) (cpu_16_v)
#else
#define RTE_BE_TO_CPU_16(be_16_v) \
(uint16_t) ((((be_16_v) & 0xFF) << 8) | ((be_16_v) >> 8))
#define RTE_CPU_TO_BE_16(cpu_16_v) \
(uint16_t) ((((cpu_16_v) & 0xFF) << 8) | ((cpu_16_v) >> 8))
#endif
#define rte_ctrlmbuf_data(m) ((char *)((m)->buf_addr)+(m)->data_off)
// convert a quad-dot IP string to uint32_t IP address
uint32_t string_to_ip(char *s) {
unsigned char a[4];
int rc = sscanf(s, "%d.%d.%d.%d",a+0,a+1,a+2,a+3);
if(rc != 4){
fprintf(stderr, "bad source IP address format. Use like: -s 198.19.111.179\n");
exit(1);
}
return
(uint32_t)(a[0]) << 24 |
(uint32_t)(a[1]) << 16 |
(uint32_t)(a[2]) << 8 |
(uint32_t)(a[3]);
}
// convert six colon separated hex bytes string to uint64_t Ethernet MAC address
uint64_t string_to_mac(char *s) {
unsigned char a[6];
int rc = sscanf(s, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
a + 0, a + 1, a + 2, a + 3, a + 4, a + 5);
if(rc !=6 ){
fprintf(stderr, "bad MAC address format. Use like: -m 0a:38:ca:f6:f3:20\n");
exit(1);
}
return
(uint64_t)(a[0]) << 40 |
(uint64_t)(a[1]) << 32 |
(uint64_t)(a[2]) << 24 |
(uint64_t)(a[3]) << 16 |
(uint64_t)(a[4]) << 8 |
(uint64_t)(a[5]);
}
uint64_t DST_MAC;
uint32_t IP_SRC_ADDR,IP_DST_ADDR;
static const struct rte_eth_conf port_conf_default = {
.rxmode = { .max_rx_pkt_len = RTE_ETHER_MAX_LEN }
};
static struct rte_ipv4_hdr pkt_ip_hdr;
static struct rte_udp_hdr pkt_udp_hdr;
struct rte_ether_addr my_addr;
struct rte_mempool *mbuf_pool;
struct rte_data_hdr pkt_data_hdr;
static void setup_pkt_udp_ip_headers(struct rte_ipv4_hdr *ip_hdr,
struct rte_udp_hdr *pudp_hdr,
uint16_t pkt_data_len)
{
uint16_t *ptr16;
uint32_t ip_cksum;
uint16_t pkt_len;
int sizeof_=sizeof(struct rte_udp_hdr);
int sizeof__=sizeof(struct rte_ipv4_hdr);
//initialize udp headers
pkt_len = (uint16_t) (pkt_data_len + sizeof_);
pudp_hdr->src_port = rte_cpu_to_be_16(UDP_SRC_PORT);
pudp_hdr->dst_port = rte_cpu_to_be_16(UDP_DST_PORT);
pudp_hdr->dgram_len = RTE_CPU_TO_BE_16(pkt_len);
pudp_hdr->dgram_cksum = 32; // No udp checksum.
//Initialize IP header.
pkt_len = (uint16_t) (pkt_len + sizeof__);
ip_hdr->version_ihl = IP_VHL_DEF;
ip_hdr->type_of_service = 0;
ip_hdr->fragment_offset = 0;
ip_hdr->time_to_live = IP_DEFTTL;
ip_hdr->next_proto_id = IPPROTO_UDP;
ip_hdr->packet_id = 0;
ip_hdr->total_length = RTE_CPU_TO_BE_16(pkt_len);
ip_hdr->src_addr = rte_cpu_to_be_32(IP_SRC_ADDR);
ip_hdr->dst_addr = rte_cpu_to_be_32(IP_DST_ADDR);
//Compute IP header checksum.
ptr16 = (unaligned_uint16_t*) ip_hdr;
ip_cksum = 0;
ip_cksum += ptr16[0]; ip_cksum += ptr16[1];
ip_cksum += ptr16[2]; ip_cksum += ptr16[3];
ip_cksum += ptr16[4];
ip_cksum += ptr16[6]; ip_cksum += ptr16[7];
ip_cksum += ptr16[8]; ip_cksum += ptr16[9];
//Reduce 32 bit checksum to 16 bits and complement it.
ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) +
(ip_cksum & 0x0000FFFF);
if (ip_cksum > 65535)
ip_cksum -= 65535;
ip_cksum = (~ip_cksum) & 0x0000FFFF;
if (ip_cksum == 0)
ip_cksum = 0xFFFF;
ip_hdr->hdr_checksum = (uint16_t) ip_cksum;
}
union {
uint64_t as_int;
struct rte_ether_addr as_addr;
} dst_eth_addr;
static void packetsend_withSequence()
{
int counter=1;
int pkt_drop_counter=0;
struct rte_ether_hdr eth_hdr;
struct rte_mbuf *pkt=NULL;
struct rte_mbuf *pkts_burst[1];
struct rte_mbuf *m;
char *data;
char temppacketdata[100];
while(1){
pkt = rte_pktmbuf_alloc(mbuf_pool);
if(pkt == NULL) {printf("trouble at rte_mbuf_raw_alloc\n");}
data = rte_pktmbuf_append(pkt, TX_PACKET_LENGTH);
if(data == NULL ) {printf("trouble at data alloc\n");}
// set up addresses
printf("\n rte_pktmbuf_pkt_len is %d", rte_pktmbuf_pkt_len(pkt));
dst_eth_addr.as_int=rte_cpu_to_be_64(DST_MAC);
rte_ether_addr_copy(&dst_eth_addr,ð_hdr.d_addr);
rte_ether_addr_copy(&my_addr, ð_hdr.s_addr);
eth_hdr.ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
memset(temppacketdata,0x0,sizeof(temppacketdata));
memcpy(temppacketdata, ð_hdr,(size_t)sizeof(eth_hdr)); //copy eth header
memcpy(temppacketdata+(size_t)sizeof(eth_hdr), &pkt_ip_hdr,(size_t)sizeof(pkt_ip_hdr)); //copy IP header
memcpy(temppacketdata+(size_t)sizeof(eth_hdr)+(size_t)sizeof(pkt_ip_hdr), &pkt_udp_hdr,(size_t)sizeof(pkt_udp_hdr)); //copy UDP header
memcpy(temppacketdata+(size_t)sizeof(eth_hdr)+(size_t)sizeof(pkt_ip_hdr)+(size_t)sizeof(pkt_udp_hdr), &counter,(size_t)sizeof(counter)); //copy int data
memcpy(data,temppacketdata,rte_pktmbuf_pkt_len(pkt));
pkts_burst[0] = pkt;
const uint16_t nb_tx = rte_eth_tx_burst(0, 0, pkts_burst,1);
if (unlikely(nb_tx < 1))
{
pkt_drop_counter++;
rte_pktmbuf_free(pkt); // This frees chained segs
}
else{
counter++;
printf("%d",nb_tx);
}
if(counter > 1000)
break;
}
printf(" pkt_drop_counter = %d counter =%d",pkt_drop_counter,counter);
}
// Initialize Port
static inline int
port_init(uint16_t port, struct rte_mempool *mbuf_pool)
{
struct rte_eth_conf port_conf = port_conf_default;
const uint16_t rx_rings = 1, tx_rings = 1;
uint16_t nb_rxd = RX_RING_SIZE;
uint16_t nb_txd = TX_RING_SIZE;
int retval;
uint16_t q;
struct rte_eth_dev_info dev_info;
struct rte_eth_txconf txconf;
if (!rte_eth_dev_is_valid_port(port))
return -1;
rte_eth_dev_info_get(port, &dev_info);
if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
port_conf.txmode.offloads |=
DEV_TX_OFFLOAD_MBUF_FAST_FREE;
/* Configure the Ethernet device. */
retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
if (retval != 0)
return retval;
retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &nb_rxd, &nb_txd);
if (retval != 0)
return retval;
txconf = dev_info.default_txconf;
txconf.offloads = port_conf.txmode.offloads;
//Allocate and set up 1 TX queue
for (q = 0; q < tx_rings; q++) {
retval = rte_eth_tx_queue_setup(port, q, nb_txd,
rte_eth_dev_socket_id(port), &txconf);
if (retval < 0)
return retval;
}
/* Allocate and set up 1 RX queue per Ethernet port. */
for (q = 0; q < rx_rings; q++) {
retval = rte_eth_rx_queue_setup(port, q, nb_rxd,
rte_eth_dev_socket_id(port), NULL, mbuf_pool);
if (retval < 0)
return retval;
}
/* Start the Ethernet port. */
retval = rte_eth_dev_start(port);
if (retval < 0)
return retval;
/* get the port MAC address. */
rte_eth_macaddr_get(port, &my_addr);
printf("Port %u MAC: %02" PRIx8 " %02" PRIx8 " %02" PRIx8
" %02" PRIx8 " %02" PRIx8 " %02" PRIx8 "\n",
port,
my_addr.addr_bytes[0], my_addr.addr_bytes[1],
my_addr.addr_bytes[2], my_addr.addr_bytes[3],
my_addr.addr_bytes[4], my_addr.addr_bytes[5]);
return 0;
}
int main(int argc, char **argv)
{
int ret,c;
uint16_t pkt_data_len;
int mac_flag=0,ip_src_flag=0,ip_dst_flag=0;
ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_panic("Cannot init EAL\n");
argc -= ret;
argv += ret;
while ((c = getopt(argc, argv, "m:s:d:h")) != -1)
switch(c) {
case 'm':
// note, not quite sure why last two bytes are zero, but that is how DPDK likes it
DST_MAC=0ULL;
DST_MAC=string_to_mac(optarg)<<16;
mac_flag=1;
break;
case 's':
IP_SRC_ADDR=string_to_ip(optarg);
ip_src_flag=1;
break;
case 'd':
IP_DST_ADDR=string_to_ip(optarg);
ip_dst_flag=1;
break;
case 'h':
printf("usage -- -m [dst MAC] -s [src IP] -d [dst IP]\n");
exit(0);
break;
}
if(mac_flag==0) {
fprintf(stderr, "missing -m for destination MAC adress\n");
exit(1);
}
if(ip_src_flag==0) {
fprintf(stderr, "missing -s for IP source adress\n");
exit(1);
}
if(ip_dst_flag==0) {
fprintf(stderr, "missing -d for IP destination adress\n");
exit(1);
}
/* Creates a new mempool in memory to hold the mbufs. */
mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS,
MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
if (mbuf_pool == NULL)
rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
// initialize port 0
if (port_init(0,mbuf_pool) != 0)
rte_exit(EXIT_FAILURE, "Cannot init port 0\n");
/* Initialize all ports. */
if (port_init(1, mbuf_pool) != 0)
rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu16 "\n",
1);
pkt_data_len = (uint16_t) (TX_PACKET_LENGTH - (sizeof(struct rte_ether_hdr) +
sizeof(struct rte_ipv4_hdr) +
sizeof(struct rte_udp_hdr)));
setup_pkt_udp_ip_headers(&pkt_ip_hdr, &pkt_udp_hdr, pkt_data_len);
packetsend_withSequence();
// send_packet();
return(0);
}
As you known, it is possible catch any signal but kill and stop/count with an handler.
There’s three kind of invalid address access :
The attempt to execute/jump at an invalid address.
The attempt to read at an invalid address.
The attempt to write at an invalid address.
I’m only interested in rejecting invalid read accesses. So the idea is to catch all segmention faults and abort() if it’s not an invalid read access.
So far, I only know how to use SEGV_MAPERR and SEGV_ACCERR with sigaction which is irrelevant of course.
It turns out that in Linux on x86-64 (aka AMD64) architecture, this is in fact quite feasible.
Here is an example program, crasher.c:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <ucontext.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#if !defined(__linux__) || !defined(__x86_64__)
#error This example only works in Linux on x86-64.
#endif
#define ALTSTACK_SIZE 262144
static const char hex_digit[16] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
static inline const char *signal_name(const int signum)
{
switch (signum) {
case SIGSEGV: return "SIGSEGV";
case SIGBUS: return "SIGBUS";
case SIGILL: return "SIGILL";
case SIGFPE: return "SIGFPE";
case SIGTRAP: return "SIGTRAP";
default: return "(unknown)";
}
}
static inline ssize_t internal_write(int fd, const void *buf, size_t len)
{
ssize_t retval;
asm volatile ( "syscall\n\t"
: "=a" (retval)
: "a" (1), "D" (fd), "S" (buf), "d" (len)
: "rcx", "r11" );
return retval;
}
static inline int wrerr(const char *p, const char *q)
{
while (p < q) {
ssize_t n = internal_write(STDERR_FILENO, p, (size_t)(q - p));
if (n > 0)
p += n;
else
if (n == 0)
return EIO;
else
return -n;
}
return 0;
}
static inline int wrs(const char *p)
{
if (p) {
const char *q = p;
while (*q)
q++;
return wrerr(p, q);
}
return 0;
}
static inline int wrh(unsigned long h)
{
static char buffer[4 + 2 * sizeof h];
char *p = buffer + sizeof buffer;
do {
*(--p) = hex_digit[h & 15];
h /= 16UL;
} while (h);
*(--p) = 'x';
*(--p) = '0';
return wrerr(p, buffer + sizeof buffer);
}
static void crash_handler(int signum, siginfo_t *info, void *contextptr)
{
if (info) {
ucontext_t *const ctx = (ucontext_t *const)contextptr;
wrs(signal_name(signum));
if (ctx->uc_mcontext.gregs[REG_ERR] & 16) {
const unsigned long sp = ctx->uc_mcontext.gregs[REG_RSP];
/* Instruction fetch */
wrs(": Bad jump to ");
wrh((unsigned long)(info->si_addr));
if (sp && !(sp & 7)) {
wrs(" probably by the instruction just before ");
wrh(*(unsigned long *)sp);
}
wrs(".\n");
} else
if (ctx->uc_mcontext.gregs[REG_ERR] & 2) {
/* Write access */
wrs(": Invalid write attempt to ");
wrh((unsigned long)(info->si_addr));
wrs(" by instruction at ");
wrh(ctx->uc_mcontext.gregs[REG_RIP]);
wrs(".\n");
} else {
/* Read access */
wrs(": Invalid read attempt from ");
wrh((unsigned long)(info->si_addr));
wrs(" by instruction at ");
wrh(ctx->uc_mcontext.gregs[REG_RIP]);
wrs(".\n");
}
}
raise(SIGKILL);
}
static int install_crash_handler(void)
{
stack_t altstack;
struct sigaction act;
altstack.ss_size = ALTSTACK_SIZE;
altstack.ss_flags = 0;
altstack.ss_sp = mmap(NULL, altstack.ss_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
if (altstack.ss_sp == MAP_FAILED) {
const int retval = errno;
fprintf(stderr, "Cannot map memory for alternate stack: %s.\n", strerror(retval));
return retval;
}
if (sigaltstack(&altstack, NULL)) {
const int retval = errno;
fprintf(stderr, "Cannot use alternate signal stack: %s.\n", strerror(retval));
return retval;
}
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
act.sa_sigaction = crash_handler;
if (sigaction(SIGSEGV, &act, NULL) == -1 ||
sigaction(SIGBUS, &act, NULL) == -1 ||
sigaction(SIGILL, &act, NULL) == -1 ||
sigaction(SIGFPE, &act, NULL) == -1) {
const int retval = errno;
fprintf(stderr, "Cannot install crash signal handlers: %s.\n", strerror(retval));
return retval;
}
return 0;
}
int main(int argc, char *argv[])
{
void (*jump)(void) = 0;
unsigned char *addr = (unsigned char *)0;
if (argc < 2 || argc > 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s call [ address ]\n", argv[0]);
fprintf(stderr, " %s read [ address ]\n", argv[0]);
fprintf(stderr, " %s write [ address ]\n", argv[0]);
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
if (argc > 2 && argv[2][0] != '\0') {
char *end = NULL;
unsigned long val;
errno = 0;
val = strtoul(argv[2], &end, 0);
if (errno) {
fprintf(stderr, "%s: %s.\n", argv[2], strerror(errno));
return EXIT_FAILURE;
}
if (end)
while (*end == '\t' || *end == '\n' || *end == '\v' ||
*end == '\f' || *end == '\r' || *end == ' ')
end++;
if (!end || end <= argv[2] || *end) {
fprintf(stderr, "%s: Not a valid address.\n", argv[2]);
return EXIT_FAILURE;
}
jump = (void *)val;
addr = (void *)val;
}
if (install_crash_handler())
return EXIT_FAILURE;
if (argv[1][0] == 'c' || argv[1][0] == 'C') {
printf("Calling address %p: ", (void *)jump);
fflush(stdout);
jump();
printf("Done.\n");
} else
if (argv[1][0] == 'r' || argv[1][0] == 'R') {
unsigned char val;
printf("Reading from address %p: ", (void *)addr);
fflush(stdout);
val = *addr;
printf("0x%02x, done.\n", val);
} else
if (argv[1][0] == 'w' || argv[1][1] == 'W') {
printf("Writing 0xC4 to address %p: ", (void *)addr);
fflush(stdout);
*addr = 0xC4;
printf("Done.\n");
}
printf("No crash.\n");
return EXIT_SUCCESS;
}
Compile it using e.g.
gcc -Wall -O2 crasher.c -o crasher
You can test a call, a read, or a write to an arbitrary address by specifying the operation and optionally the address on the command line. Run without parameters to see the usage.
Some example runs on my machine:
./crasher call 0x100
Calling address 0x100: SIGSEGV: Bad jump to 0x100 probably by the instruction just before 0x400c4e.
Killed
./crasher write 0x24
Writing 0xC4 to address 0x24: SIGSEGV: Invalid write attempt to 0x24 by instruction at 0x400bad.
Killed
./crasher read 0x16
Reading from address 0x16: SIGSEGV: Invalid read attempt from 0x16 by instruction at 0x400ca3.
Killed
./crasher write 0x400ca3
Writing 0xC4 to address 0x400ca3: SIGSEGV: Invalid write attempt to 0x400ca3 by instruction at 0x400bad.
Killed
./crasher read 0x400ca3
Reading from address 0x400ca3: 0x41, done.
No crash.
Note that the type of the access is obtained from the ((ucontext_t *)contextptr)->uc_mcontext.gregs[REG_ERR] register (from the signal handler context); it matches the x86_pf_error_code enums as defined in arch/x86/mm/fault.c in the Linux kernel sources.
The crash handler itself is quite straightforward, only needing to exmine the aforementioned "register" to obtain the information the OP seeks.
For outputting the crash report, I open-coded the write() syscall. (For some reason, the small buffer needed by the wrh() function cannot be on the stack, so I just made it static instead.)
I did not bother to implement the mincore() syscall to verify for example the stack address (sp in the crash_handler() function); it might be necessary to avoid double faults (SIGSEGV occurring in the crash_handler() itself).
Similarly, I didn't bother to open-code the raise() at the end of crash_handler(), because nowadays on x86-64 it is implemented in the C library using the tgkill(pid, tid, signum) syscall, which means I'd also had to open-code the getpid() and gettid() syscalls. I was just lazy.
Finally, the above code is written quite carelessly, as I myself only found this after exchanging comments with the OP, user2284570, and just wanted to throw something together to see if this approach actually works reliably. (It seems it does, but I've only tested this lightly and only on one machine.) So, if you notice any bugs, typos, thinkos, or other things to fix in the code, please let me know in a comment, so I can fix it.
I am new to serail programing in Linux (Fedora 12) using C Language. I have a simple device, it is supposed to get 3 bytes command in hex (wirte) like {0x02,0x03,0x0D} and return 4 bytes response.
First I wrote a simple java program on windows, and I get the correct response.. as and when,I switch to linux, I can't read from serial com port neither using java nor C language.
I tried using libraries like rs232 .. but still the problem remains.
I can open "/dev/ttyS0", and write on it .. (none of them returns any Error), but read is not possible ..
If I use canonical mode, the program blocks on reading until i kill the program.. If use non-canonical mode, with VMIN=0 and VTIME=5, read function returns whith -107725432 bytes for example ..
(I have tried reading and writing byte by byte or all at the same time .. no difference ..)
rs232.c
#include "rs232.h"
int Cport[6],
error;
struct termios new_port_settings,
old_port_settings[6];
char comports[6][16]={"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2","/dev/ttyS3","/dev/ttyS4","/dev/ttyS5"};
int RS232_OpenComport(int comport_number, int baudrate, const char *mode)
{
int baudr,
status;
if((comport_number>5)||(comport_number<0))
{
printf("illegal comport number\n");
return(1);
}
switch(baudrate)
{
case 2400 : baudr = B2400;
break;
case 4800 : baudr = B4800;
break;
case 9600 : baudr = B9600;
break;
case 19200 : baudr = B19200;
break;
default : printf("invalid baudrate\n");
return(1);
break;
}
int cbits=CS8,
cpar=0,
ipar=IGNPAR,
bstop=0;
if(strlen(mode) != 3)
{
printf("invalid mode \"%s\"\n", mode);
return(1);
}
switch(mode[0])
{
case '8': cbits = CS8;
break;
case '7': cbits = CS7;
break;
case '6': cbits = CS6;
break;
case '5': cbits = CS5;
break;
default : printf("invalid number of data-bits '%c'\n", mode[0]);
return(1);
break;
}
switch(mode[1])
{
case 'N':
case 'n': cpar = 0;
ipar = IGNPAR;
break;
case 'E':
case 'e': cpar = PARENB;
ipar = INPCK;
break;
case 'O':
case 'o': cpar = (PARENB | PARODD);
ipar = INPCK;
break;
default : printf("invalid parity '%c'\n", mode[1]);
return(1);
break;
}
switch(mode[2])
{
case '1': bstop = 0;
break;
case '2': bstop = CSTOPB;
break;
default : printf("invalid number of stop bits '%c'\n", mode[2]);
return(1);
break;
}
Cport[comport_number] = open(comports[comport_number], O_RDWR | O_NOCTTY);
if(Cport[comport_number]==-1)
{
perror("unable to open comport ");
return(1);
}
/* lock access so that another process can't also use the port */
if(flock(Cport[comport_number], LOCK_EX | LOCK_NB) != 0)
{
close(Cport[comport_number]);
perror("Another process has locked the comport.");
return(1);
}
error = tcgetattr(Cport[comport_number], old_port_settings + comport_number);
if(error==-1)
{
close(Cport[comport_number]);
perror("unable to read portsettings ");
return(1);
}
memset(&new_port_settings, 0, sizeof(new_port_settings)); /* clear the new struct */
new_port_settings.c_cflag = cbits | cpar | bstop | CLOCAL | CREAD;
new_port_settings.c_iflag = ipar;
new_port_settings.c_oflag = 0;
new_port_settings.c_lflag = 0;
new_port_settings.c_cc[VMIN] = 0; /* block untill n bytes are received */
new_port_settings.c_cc[VTIME] = 5; /* block untill a timer expires (n * 100 mSec.) */
cfsetispeed(&new_port_settings, baudr);
cfsetospeed(&new_port_settings, baudr);
error = tcsetattr(Cport[comport_number], TCSANOW, &new_port_settings);
if(error==-1)
{
close(Cport[comport_number]);
perror("unable to adjust portsettings ");
return(1);
}
if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1)
{
perror("unable to get portstatus");
return(1);
}
status |= TIOCM_DTR; /* turn on DTR */
status |= TIOCM_RTS; /* turn on RTS */
if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1)
{
perror("unable to set portstatus");
return(1);
}
return(0);
}
int RS232_PollComport(int comport_number, unsigned char *buf, int size)
{
int n;
n = read(Cport[comport_number], buf, size);
return(n);
}
int RS232_SendBuf(int comport_number, unsigned char *buf, int size)
{
return(write(Cport[comport_number], buf, size));
}
void RS232_CloseComport(int comport_number)
{
int status;
if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1)
{
perror("unable to get portstatus");
}
status &= ~TIOCM_DTR; /* turn off DTR */
status &= ~TIOCM_RTS; /* turn off RTS */
if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1)
{
perror("unable to set portstatus");
}
tcsetattr(Cport[comport_number], TCSANOW, old_port_settings + comport_number);
close(Cport[comport_number]);
flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */
}
void RS232_flushRXTX(int comport_number)
{
tcflush(Cport[comport_number], TCIOFLUSH);
}
and
main.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "rs232.h"
int main()
{
int cport_nr=0, bdrate=9600; /* 9600 baud */
char mode[]={'8','N','1',0},
str[512];
unsigned char buf[6000];
memset(buf, '\0' , 6000);
int buf_SIZE=sizeof(buf);
if(RS232_OpenComport(cport_nr, bdrate, mode))
{
printf("Can not open comport\n");
return(0);
}
unsigned char wr_buff[5];
memset(wr_buff, '\0', 5);
wr_buff[0] = 0x02;
wr_buff[1] = 0x01;
wr_buff[2] = 0x0D;
int cnt = RS232_SendBuf(cport_nr, wr_buff, 3);
printf("Number of bytes that has been written: %d\n",cnt);
if (cnt <= 0 )
return(-1);
cnt =0 ;
usleep(100000);
printf("Start Reading ... \n");
int i = 0;
do {
cnt = RS232_PollComport(cport_nr,(buf+i), 1);
i++;}
while(cnt>0);
printf ("%d bytes have been read\n");
RS232_CloseComport(cport_nr);
return (1);
}
I am really confused .. I tried almost every samples on the Internet .. An
y Idea Please?!
I have traced the program, using strace ..
...
write(3,"\2\3\r",3) = 3
fstat64(1 , {st_mode=S_IFREG| 0755, st_size =0,..}) = 0
mmap2(NULL,4096,PROT_READ|PROT_WRITE, MAP_PRIVATE| MAP_ANONYMOUS,-1,0) = 0Xb78b4000
nanosleep({0, 100000000}, NULL) = 0
read(3,"",1) = 0
.....
Can the problem be related to fedora12?
P.S. : if I haven't got response in windows, I was sure that its a problem with the device.
Solved ...
it was a mistake of me ...
In fact, every thing was ok,
the problem was in the way I print number of bytes have been read.. I had forgotten to put "cnt" in respect of %d ...
printf ("%d bytes have been read\n"); --> printf ("%d bytes have been read\n",cnt);
also i should have read only 4 bytes based on the protocol of communication with my device ..
I have a small program that read barcodes from /dev/input/event4.
This is the code:
#include <sys/file.h>
#include <stdio.h>
#include <string.h>
#include <linux/input.h>
int main (int argc, char *argv[])
{
struct input_event ev;
int fd, rd;
//Open Device
if ((fd = open ("/dev/input/event4", O_RDONLY|O_NONBLOCK)) == -1){
printf ("not a vaild device.\n");
return -1;
}
while (1){
memset((void*)&ev, 0, sizeof(ev));
rd = read (fd, (void*)&ev, sizeof(ev));
if (rd <= 0){
printf ("rd: %d\n", rd);
sleep(1);
}
if(rd>0 && ev.value==0 && ev.type==1){
printf("type: %d, code: %d, value: %d, rd: %d\n", ev.type, ev.code, ev.value, rd);
}
}
return 0;
}
I have now created some barcodes with an online-generator (http://www.barcode-generator.de/V2/de/index.jsp). The barcodes are:
123456789 and 1234567890
The output of my programm when scanning the barcodes is:
type: 1, code: 2, value: 0, rd: 16
type: 1, code: 3, value: 0, rd: 16
type: 1, code: 4, value: 0, rd: 16
type: 1, code: 5, value: 0, rd: 16
type: 1, code: 6, value: 0, rd: 16
type: 1, code: 7, value: 0, rd: 16
type: 1, code: 8, value: 0, rd: 16
type: 1, code: 9, value: 0, rd: 16
type: 1, code: 10, value: 0, rd: 16
type: 1, code: 28, value: 0, rd: 16
for the 123456789
and
type: 1, code: 28, value: 0, rd: 16
for the 1234567890
So, the 10-digit-barcodes is not recognised correctly.
The code: 28 means this is an RETURN/ENTER, this is the internal terminator for a barcode, so this comes directly from the scanner.
Does anyone can tell my why ? Maybe there is something wrong with the code ?
Goodbye, Andre
You should only consider the event when the read() returns == sizeof ev, because we're reading from an input device here. If it returns zero, it means no more events are forthcoming (maybe device detached?). If it returns -1, check errno. If read() returns any other value, the kernel driver has gone bonkers and you could consider it a fatal error.
errno == EINTR is normal (occurs when a signal is delivered), it is not an error per se. It shouldn't happen here, but ignoring it (treating it as just a hiccup, not an error) is quite safe.
errno == EAGAIN occurs when you used O_NONBLOCK in the open() flags, and there is no new event available yet.
There is absolutely no reason to use O_NONBLOCK here. All it does is causes your code to waste CPU cycles, returning tens of thousands of times per second from the read() call just to return -1 with errno == EAGAIN. Just drop it, so that the read() will simply wait until a new event arrives, and returns it.
See my answer to the input_event structure description question.
In summary, for ev_type == 1 == EV_KEY:
ev_value == 0: key released (key up)
ev_value == 1: key pressed (key down)
ev_value == 2: autorepeat (key automatically repeated)
ev_code == 1 == KEY_ESC
ev_code == 2 == KEY_1
ev_code == 3 == KEY_2
ev_code == 10 == KEY_9
ev_code == 11 == KEY_0
ev_code == 28 == KEY_ENTER
The keypresses the device provided are actually 1 2 3 4 5 6 7 8 9 Enter.
(Note that you only showed the key release events; you should actually see two, one with ev_value == 1, followed by one with ev_value == 0, for each ev_code.)
The one Chinese one I've tried was very nice, although dirt cheap. It had a manual with a few barcodes, including some to switch between barcode formats (and number of digits). I vaguely remember using two barcodes to switch to another mode, and to use the minimum volume for the beeps. It seemed to retain the settings even after being detached.
Here is an example of what kind of implementation I'd use to read barcodes. I'd obviously split the barcode reading part to a separate file.
The below code is dedicated to public domain (licensed under CC0), so feel free to use it in any way you wish. There is no guarantees of any kind, so don't blame me for any breakage. (Any bug fixes are welcome; if reported, I will check and include in the below code. I recommend adding a comment below; I do read all comments to my answers every couple of days or so.)
The header file barcode.h:
#ifndef BARCODE_H
#define BARCODE_H
#include <stdlib.h>
#include <signal.h>
/* This flags turns nonzero if any signal
* installed with install_done is caught.
*/
extern volatile sig_atomic_t done;
/* Install signals that set 'done'.
*/
int install_done(const int signum);
/* Barcode device description.
* Do not meddle with the internals;
* this is here only to allow you
* to allocate one statically.
*/
typedef struct {
int fd;
volatile int timeout;
timer_t timer;
} barcode_dev;
/* Close a barcode device.
* Returns 0 if success, nonzero errno error code otherwise.
*/
int barcode_close(barcode_dev *const dev);
/* Open a barcode device.
* Returns 0 if success, nonzero errno error code otherwise.
*/
int barcode_open(barcode_dev *const dev, const char *const device_path);
/* Read a barcode, but do not spend more than maximum_ms.
* Returns the length of the barcode read.
* (although at most length-1 characters are saved at the buffer,
* the total length of the barcode is returned.)
* errno is always set; 0 if success, error code otherwise.
* If the reading timed out, errno will be set to ETIMEDOUT.
*/
size_t barcode_read(barcode_dev *const dev,
char *const buffer, const size_t length,
const unsigned long maximum_ms);
#endif /* BARCODE_H */
The implementation in the following barcode.c file currently accepts only digits (0 through 1), but it should be trivial to add any other necessary keys (like KEY_A through KEY_Z). The current one ignores shift, control, et cetera, as they are not provided by any scanners as far as I know. It uses SIGRTMAX-0 realtime signal and a custom timer per barcode device to read barcodes, so you'll need to link it against librt (-lrt):
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <linux/input.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* Link against the rt library; -lrt. */
#define UNUSED __attribute__((unused))
#define TIMEOUT_SIGNAL (SIGRTMAX-0)
/*
* done - flag used to exit program at SIGINT, SIGTERM etc.
*/
volatile sig_atomic_t done = 0;
static void handle_done(int signum UNUSED)
{
done = 1;
}
int install_done(const int signum)
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
/*
* Barcode input event device, and associated timeout timer.
*/
typedef struct {
int fd;
volatile int timeout;
timer_t timer;
} barcode_dev;
static void handle_timeout(int signum UNUSED, siginfo_t *info, void *context UNUSED)
{
if (info && info->si_code == SI_TIMER && info->si_value.sival_ptr)
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
__atomic_add_fetch((int *)info->si_value.sival_ptr, 1, __ATOMIC_SEQ_CST);
#else
__sync_add_and_fetch((int *)info->si_value.sival_ptr, 1);
#endif
}
static int install_timeouts(void)
{
static int installed = 0;
if (!installed) {
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_sigaction = handle_timeout;
act.sa_flags = SA_SIGINFO;
if (sigaction(TIMEOUT_SIGNAL, &act, NULL) == -1)
return errno;
installed = 1;
}
return 0;
}
int barcode_close(barcode_dev *const dev)
{
int retval = 0;
if (!dev)
return 0;
if (dev->fd != -1)
if (close(dev->fd) == -1)
retval = errno;
dev->fd = -1;
if (dev->timer)
if (timer_delete(dev->timer) == -1)
if (!retval)
retval = errno;
dev->timer = (timer_t)0;
/* Handle all pending TIMEOUT_SIGNALs */
while (1) {
struct timespec t;
siginfo_t info;
sigset_t s;
t.tv_sec = (time_t)0;
t.tv_nsec = 0L;
sigemptyset(&s);
if (sigtimedwait(&s, &info, &t) != TIMEOUT_SIGNAL)
break;
if (info.si_code != SI_TIMER || !info.si_value.sival_ptr)
continue;
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
__atomic_add_fetch((int *)info.si_value.sival_ptr, 1, __ATOMIC_SEQ_CST);
#else
__sync_add_and_fetch((int *)info.si_value.sival_ptr, 1);
#endif
}
return errno = retval;
}
int barcode_open(barcode_dev *const dev, const char *const device_path)
{
struct sigevent event;
int fd;
if (!dev)
return errno = EINVAL;
dev->fd = -1;
dev->timeout = -1;
dev->timer = (timer_t)0;
if (!device_path || !*device_path)
return errno = EINVAL;
if (install_timeouts())
return errno;
do {
fd = open(device_path, O_RDONLY | O_NOCTTY | O_CLOEXEC);
} while (fd == -1 && errno == EINTR);
if (fd == -1)
return errno;
errno = 0;
if (ioctl(fd, EVIOCGRAB, 1)) {
const int saved_errno = errno;
close(fd);
return errno = (saved_errno) ? errno : EACCES;
}
dev->fd = fd;
memset(&event, 0, sizeof event);
event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = TIMEOUT_SIGNAL;
event.sigev_value.sival_ptr = (void *)&(dev->timeout);
if (timer_create(CLOCK_REALTIME, &event, &dev->timer) == -1) {
const int saved_errno = errno;
close(fd);
return errno = (saved_errno) ? errno : EMFILE;
}
return errno = 0;
}
size_t barcode_read(barcode_dev *const dev,
char *const buffer, const size_t length,
const unsigned long maximum_ms)
{
struct itimerspec it;
size_t len = 0;
int status = ETIMEDOUT;
if (!dev || !buffer || length < 2 || maximum_ms < 1UL) {
errno = EINVAL;
return (size_t)0;
}
/* Initial timeout. */
it.it_value.tv_sec = maximum_ms / 1000UL;
it.it_value.tv_nsec = (maximum_ms % 1000UL) * 1000000L;
/* After elapsing, fire every 10 ms. */
it.it_interval.tv_sec = 0;
it.it_interval.tv_nsec = 10000000L;
if (timer_settime(dev->timer, 0, &it, NULL) == -1)
return (size_t)0;
/* Because of the repeated elapsing, it is safe to
* clear the timeout flag here. */
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 7)
__atomic_store_n((int *)&(dev->timeout), 0, __ATOMIC_SEQ_CST);
#else
__sync_fetch_and_and((int *)&(dev->timeout), 0);
#endif
while (!dev->timeout) {
struct input_event ev;
ssize_t n;
int digit;
n = read(dev->fd, &ev, sizeof ev);
if (n == (ssize_t)-1) {
if (errno == EINTR)
continue;
status = errno;
break;
} else
if (n == sizeof ev) {
/* We consider only key presses and autorepeats. */
if (ev.type != EV_KEY || (ev.value != 1 && ev.value != 2))
continue;
switch (ev.code) {
case KEY_0: digit = '0'; break;
case KEY_1: digit = '1'; break;
case KEY_2: digit = '2'; break;
case KEY_3: digit = '3'; break;
case KEY_4: digit = '4'; break;
case KEY_5: digit = '5'; break;
case KEY_6: digit = '6'; break;
case KEY_7: digit = '7'; break;
case KEY_8: digit = '8'; break;
case KEY_9: digit = '9'; break;
default: digit = '\0';
}
/* Non-digit key ends the code, except at beginning of code. */
if (digit == '\0') {
if (!len)
continue;
status = 0;
break;
}
if (len < length)
buffer[len] = digit;
len++;
continue;
} else
if (n == (ssize_t)0) {
status = ENOENT;
break;
} else {
status = EIO;
break;
}
}
/* Add terminator character to buffer. */
if (len + 1 < length)
buffer[len + 1] = '\0';
else
buffer[length - 1] = '\0';
/* Cancel timeout. */
it.it_value.tv_sec = 0;
it.it_value.tv_nsec = 0;
it.it_interval.tv_sec = 0;
it.it_interval.tv_nsec = 0L;
(void)timer_settime(dev->timer, 0, &it, NULL);
errno = status;
return len;
}
Here is an example program, example.c. You supply the input event device (I suggest using a symlink in /dev/input/by-id/ or /dev/input/by-path/ if your udev provides those, as event device indexes may not be stable across kernel versions and hardware boots), and the maximum duration you're willing to wait for/until next barcode.
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include "barcode.h"
#define BARCODE_MAXLEN 1023
int main(int argc, char *argv[])
{
barcode_dev dev;
unsigned long ms;
int status, exitcode;
if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s INPUT-EVENT-DEVICE IDLE-TIMEOUT\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "This program reads barcodes from INPUT-EVENT-DEVICE,\n");
fprintf(stderr, "waiting at most IDLE-TIMEOUT seconds for a new barcode.\n");
fprintf(stderr, "The INPUT-EVENT-DEVICE is grabbed, the digits do not appear as\n");
fprintf(stderr, "inputs in the machine.\n");
fprintf(stderr, "You can at any time end the program by sending it a\n");
fprintf(stderr, "SIGINT (Ctrl+C), SIGHUP, or SIGTERM signal.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
if (install_done(SIGINT) ||
install_done(SIGHUP) ||
install_done(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
{
double value, check;
char dummy;
if (sscanf(argv[2], " %lf %c", &value, &dummy) != 1 || value < 0.001) {
fprintf(stderr, "%s: Invalid idle timeout value (in seconds).\n", argv[2]);
return EXIT_FAILURE;
}
ms = (unsigned long)(value * 1000.0);
check = (double)ms / 1000.0;
if (value < check - 0.001 || value > check + 0.001 || ms < 1UL) {
fprintf(stderr, "%s: Idle timeout is too long.\n", argv[2]);
return EXIT_FAILURE;
}
}
if (barcode_open(&dev, argv[1])) {
fprintf(stderr, "%s: Cannot open barcode input event device: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
while (1) {
char code[BARCODE_MAXLEN + 1];
size_t len;
if (done) {
status = EINTR;
break;
}
len = barcode_read(&dev, code, sizeof code, ms);
if (errno) {
status = errno;
break;
}
if (len < (size_t)1) {
status = ETIMEDOUT;
break;
}
printf("%zu-digit barcode: %s\n", len, code);
fflush(stdout);
}
if (status == EINTR) {
fprintf(stderr, "Signaled to exit. Complying.\n");
fflush(stderr);
exitcode = EXIT_SUCCESS;
} else
if (status == ETIMEDOUT) {
fprintf(stderr, "Timed out, no more barcodes.\n");
fflush(stderr);
exitcode = EXIT_SUCCESS;
} else {
fprintf(stderr, "Error reading input event device %s: %s.\n", argv[1], strerror(status));
fflush(stderr);
exitcode = EXIT_FAILURE;
}
if (barcode_close(&dev)) {
fprintf(stderr, "Warning: Error closing input event device %s: %s.\n", argv[1], strerror(errno));
fflush(stderr);
exitcode = EXIT_FAILURE;
}
return exitcode;
}
As you can see, it just prints the barcodes to standard output (and any error messages and warnings to standard error). To compile it, I recommend using the following Makefile (indentation must be using Tab, not spaces):
CC := gcc
CFLAGS := -Wall -Wextra -O2
LDFLAGS := -lrt
.PHONY: all clean
all: clean example
clean:
rm -f example *.o
%.o: %.c
$(CC) $(CFLAGS) -c $^
example: example.o barcode.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o example
To compile, create the four files listed above, then run
make clean example
Running for example
./example /dev/input/event4 5.0
will read barcodes from /dev/input/event4, but will exit at Ctrl+C (INT signal), HUP signal, TERM signal, or if no barcode appears within 5 seconds.
Note that if only a partial barcode is read within that 5 seconds, we do get that partial part (and could just try and read the rest of it), but the above example program ignores the partial, and only shows the timeout.
Questions?
I'm trying to make a simple C program play an AIFF or WAV file. Based on what I see at http://www.xiph.org/ao/doc/, this should work, but instead it makes a buzzing sound no matter what file I feed it. What's wrong with this?
/* compile with "gcc -o playme playme.c -lao -ldl -lm" */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ao/ao.h>
#include <math.h>
ao_device *device;
ao_sample_format format;
int main(int argc, char *argv[])
{
int default_driver;
char *buffer;
unsigned long count;
FILE *fp;
if (argc != 2) {
printf("usage: %s <filename>\n", argv[0]);
exit(1);
}
ao_initialize();
default_driver = ao_default_driver_id();
memset(&format, 0, sizeof(format));
format.bits = 16;
format.channels = 2;
format.rate = 44100;
format.byte_format = AO_FMT_LITTLE;
device = ao_open_live(default_driver, &format, NULL /* no options */);
if (device == NULL) {
printf("Error opening sound device.\n");
exit(1);
}
fp = fopen(argv[1], "rb");
if (fp == NULL) {
printf("Cannot open %s.\n", argv[1]);
exit(2);
}
fseek(fp, 0, SEEK_END);
count = ftell(fp);
fseek(fp, 0, SEEK_SET);
fread(buffer, sizeof(char), count, fp);
ao_play(device, buffer, count);
ao_close(device);
ao_shutdown();
return 0;
}
A critical something I didn't realize is that libao does absolutely no decoding. It is therefore up to the programmer to extract the sample size, rate, channels, etc and feed those to libao before opening the audio device. libsndfile is available for doing this, but if you just want something quick and dirty, here's the code for playing an AIFF file:
/* compile with "gcc -o playaiff playaiff.c -lao -ldl -lm" */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ao/ao.h>
#include <math.h>
#define MAXCHAN 8
ao_device *device;
ao_sample_format format;
#define gshort( b) (((int)((b)[0]) << 8) + (int)((b)[1]))
#define glong( b) (((int)((b)[0]) << 24) + ((int)((b)[1]) << 16) +\
((int)((b)[2]) << 8) + (int)((b)[3]))
typedef struct {
short channels;
short samplesize;
int samplerate;
unsigned long samplecount;
int valid;
} aiffinfo;
aiffinfo getaiffinfo(FILE *);
static int IeeeExtendedToLong(unsigned char *);
int main(int argc, char *argv[])
{
int default_driver;
char *buffer;
aiffinfo info;
FILE *fp;
if (argc != 2) {
printf("usage: %s <filename>\n", argv[0]);
exit(1);
}
ao_initialize();
default_driver = ao_default_driver_id();
memset(&format, 0, sizeof(format));
fp = fopen(argv[1], "rb");
if (fp == NULL) {
printf("Cannot open %s.\n", argv[1]);
exit(2);
}
info = getaiffinfo(fp);
if (!info.valid) {
printf("Invalid AIFF file.\n");
exit(1);
}
format.bits = info.samplesize;
format.channels = info.channels;
format.rate = info.samplerate;
format.byte_format = AO_FMT_LITTLE;
device = ao_open_live(default_driver, &format, NULL /* no options */);
if (device == NULL) {
printf("Error opening sound device.\n");
exit(1);
}
buffer = malloc(sizeof(char) * info.samplecount);
fread(buffer, sizeof(char), info.samplecount, fp);
ao_play(device, buffer, info.samplecount);
ao_close(device);
ao_shutdown();
return 0;
}
aiffinfo getaiffinfo(FILE *fp)
{
int size;
int len;
int offset;
int blocksize;
int found = 0;
unsigned char chunk[18];
unsigned char fid[4];
aiffinfo info;
info.samplesize = 0;
info.valid = 0;
if (fread(chunk, 1, 4, fp) < 4) return info;
if (memcmp(chunk,"FORM",4)) return info;
if (fread(chunk, 1, 4, fp) < 4) return info;
size = glong(chunk);
if (size & 1) size++;
if (size < 20) return info;
if (fread(chunk, 1, 4, fp) < 4) return info;
if (memcmp(chunk, "AIFF", 4)) return info;
size -= 4;
while (size > 8) {
if (fread(fid, 1, 4, fp) < 4) return info; // chunck id
if (fread(chunk, 1, 4, fp) < 4) return info; // and len
size -= 8;
len = glong(chunk);
if (len < 0) return info;
if (len & 1) len++;
size -= len;
if (size < 0) return info;
if (memcmp(fid, "COMM", 4) == 0) {
if (len != 18) return info;
if (fread(chunk, 1, 18, fp) < 18) return info;
info.channels = gshort(chunk);
if (info.channels < 1) return info;
if (info.channels > MAXCHAN) return info;
info.samplecount = glong(chunk+2);
if (info.samplecount < 1) return info;
info.samplerate = IeeeExtendedToLong(chunk + 8);
if (info.samplerate <= 0) return info;
info.samplesize = gshort(chunk + 6);
if (info.samplesize < 1 || info.samplesize > 16) return info;
} else if (memcmp(fid,"SSND",4)==0){
if (!info.channels) return info;
if (fread(chunk, 1, 4, fp) < 4) return info;
offset = glong(chunk);
if (fread(chunk, 1, 4, fp) < 4) return info;
blocksize = glong(chunk);
if (blocksize) return info;
if (offset) fseek(fp, offset,SEEK_CUR);
found = 1;
break;
} else fseek (fp, len, SEEK_CUR);
}
if (!found) return info;
if (!info.channels) return info;
// printf("Looks good so far.\n");
info.valid = 1;
return info;
}
/****************************************************************
* Extended precision IEEE floating-point conversion routine.
****************************************************************/
#ifndef Uint32
#define Uint32 unsigned int
#endif
#ifndef HUGE_INT32
#define HUGE_INT32 0x7fffffff
#endif /* HUGE_VAL */
static int IeeeExtendedToLong( unsigned char *bytes)
{
int f = 0;
int expon;
Uint32 hiMant;
Uint32 loMant;
expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF);
hiMant = ((Uint32) (bytes[2] & 0xFF) << 24)
| ((Uint32) (bytes[3] & 0xFF) << 16)
| ((Uint32) (bytes[4] & 0xFF) << 8)
| ((Uint32) (bytes[5] & 0xFF));
loMant = ((Uint32) (bytes[6] & 0xFF) << 24)
| ((Uint32) (bytes[7] & 0xFF) << 16)
| ((Uint32) (bytes[8] & 0xFF) << 8)
| ((Uint32) (bytes[9] & 0xFF));
if (expon == 0 && hiMant == 0 && loMant == 0) f = 0;
else if (expon == 0x7FFF) f = HUGE_INT32;
else {
expon -= 16382;
expon = 32-expon;
if (expon < 0) f = HUGE_INT32;
else f = hiMant >> expon;
}
if (bytes[0] & 0x80)
return -f;
else
return f;
}