Hello,
I am trying to setup a socket endpoint for network I/O (UDP) into my driver and I am having some trouble. I am seemingly able to create and bind a UDP socket using sock_create_kern without any failure. The problem comes when I try to send data over the datagram it always fails with EINVAL. Is kernel_sendmsg a valid routine for sending data over unconnected datagrams? Should I be using kernel_sendpage? Am I initializing the datagram correctly? Is there any working example of how to use UDB sockets at the kernel level? I have attached the driver source if you wish to see the initialization and write routines. Thank you for your time. Thanks, Dave Cogley Software Engineer Ultra Stereo Labs, Inc. (805) 549-0161 mailto:[EMAIL PROTECTED]
/* * banjodecoder.c * USL, Inc. * * This file contains the driver for the Banjo image and audio decoders. * * Revision 0.1 07/18/2007 dcogley * - Initial Creation * */ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/proc_fs.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/ctype.h> #include <linux/net.h> #include <linux/socket.h> #include <linux/in.h> #include <linux/udp.h> #include <asm/io.h> #include <asm/uaccess.h> #include "banjodecoder.h" #define DRIVER_VERSION "0.1" #define DRIVER_NAME "banjodecoder" MODULE_DESCRIPTION("USL, Inc. Image Decoder driver v" DRIVER_VERSION); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Dave Cogley"); int banjo_major = BANJO_MAJOR; module_param(banjo_major, int, 0); struct banjo_dev* banjo_device = 0; void __iomem* banjo_regmap = 0; void __iomem* gpio1_or = 0; // the data port socket descriptor static struct udp_sock* sockData = 0; struct sockaddr_in sinDataAddr = { 0 }; // the control port socket descriptor static struct udp_sock* sockControl = 0; struct sockaddr_in sinControlAddr = { 0 }; // the transmit buffer volatile static char sockBuf[MAX_TX_PACKET + PKTALIGN]; volatile static char* sockTxBuf; extern int banjosock_init(const char* serverip); extern void banjosock_cleanup(void); extern int banjosock_decueFeature(void); extern int banjosock_cueFeature(const char* feature); extern int banjosock_requestFrame(int frame); /* Macros for accessing the indirect EBC registers */ void mtebc(u32 reg, u32 data) { mtdcr(ebccfga, reg); mtdcr(ebccfgd, data); } u32 mfebc(u32 reg) { mtdcr(ebccfga, reg); return mfdcr(ebccfgd); } void enable_per_ready(int enable) { u32 pb1ap; // set peripheral 1 access permissions to disable "peripheral ready" pb1ap = mfebc(PB1AP); if (enable) pb1ap |= PB1AP_ENABLE; else pb1ap &= ~PB1AP_ENABLE; mtebc(PB1AP, pb1ap); } static void setup_dma_buffer(void) { // setup count and control register mtdcr(DMA2P40_CTC0, DMA_CONTROLTC0); // 64b source address mtdcr(DMA2P40_SAH0, 0x00000000); // must be zero mtdcr(DMA2P40_SAL0, DMABUFFERBASE); // destination address is fixed to the banjo decoder media address // the destination address really does not matter to the FPGA but // because of address alignment problems we want to start at the // base address mtdcr(DMA2P40_DAH0, 0x00000001); // destination is addressable on the EBC mtdcr(DMA2P40_DAL0, BANJO_DECODER_BASE); } irqreturn_t dma_status_int(int irq, void *dev_id, struct pt_regs *regs) { u32 status; // get and clear the DMA status status = mfdcr(DMA2P40_SR); mtdcr(DMA2P40_SR, status); // status is TC expire, EOT request (never happens) or error occured if (status & DMA0_TC_REACHED || status & DMA0_EOT_REQ || status & DMA0_ERROR) { if (status & DMA0_ERROR) { // frame xfer asseterted a DMA error banjo_device->frames_dma_error++; } else { // frame successfully transfered banjo_device->frames_sent++; } // drive GPIO48 (EOTn) high to indicate end of transfer writel(EOTN_DRIVE_HIGH, gpio1_or); // release the DMA buffer banjo_device->dma.flags = 0; } return IRQ_HANDLED; } irqreturn_t frame_req_int(int irq, void *dev_id, struct pt_regs *regs) { u32 control; // increment frames requested banjo_device->frames_request++; // is buffer available? if (!(banjo_device->dma.flags & BANJO_DMA_FLAG_DMAREADY)) { banjo_device->frames_not_ready++; return IRQ_HANDLED; // frame request made and buffer was not ready! } setup_dma_buffer(); // drive GPIO48 (EOTn) low writel(EOTN_DRIVE_LOW, gpio1_or); // enable the DMA channel // the DMA channel is now armed and will begin the transfer immediatly // to the configured DMA addresses control = mfdcr(DMA2P40_CR0); control |= DMA_ENABLE_CHANNEL; mtdcr(DMA2P40_CR0, control); return IRQ_HANDLED; } int banjo_open(struct inode* inode, struct file* filp) { return 0; } int banjo_release(struct inode* inode, struct file* filp) { return 0; } int banjo_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg) { int ret = 0; u32 status; u32 frame; switch (cmd) { // reset the banjo decoder case BANJO_IOCDECODERRESET: writel(0xffffffff, banjo_regmap + BANJO_RESET_ADDR); writel(0x00000000, banjo_regmap + BANJO_RESET_ADDR); mdelay(BANJO_RESET_DELAY); break; case BANJO_IOCDECODERSTATUS: // peripheral ready must be enabled while reading resigeter map enable_per_ready(1); status = readl(banjo_regmap + BANJO_STATUS_ADDR); ret = __put_user(status, (long __user*)arg); enable_per_ready(0); break; case BANJO_IOCENABLESTREAM: // enable the frame request interrupts writel(0xffffffff, banjo_regmap + BANJO_STATUS_ADDR); break; case BANJO_IOCCONNECTSERVER: // initialize network layer // banjosock_init("128.0.0.1\0"); banjosock_init("192.168.1.49\0"); break; case BANJO_IOCDISCONNECTSERVER: banjosock_cleanup(); break; case BANJO_IOCCUEFEATURE: // decue current feature banjosock_cueFeature("urn:uuid:a36c264b-2926-fc46-8e10-93e24a75d1c0"); break; case BANJO_IOCDECUEFEATURE: // decue current feature banjosock_decueFeature(); break; case BANJO_IOCREQUESTFRAME: // get the frame we want to request __get_user(frame, (long __user*)arg); // make a frame request and pass the current DMA frame buffer banjosock_requestFrame(frame); banjo_device->dma.flags |= BANJO_DMA_FLAG_DMAREADY; break; } return ret; } static inline int do_sendto(struct socket* sock, struct kvec* vec, int count, int len, unsigned flags, struct sockaddr_in* sin) { struct msghdr msg; msg.msg_name = sin; msg.msg_namelen = sizeof (struct sockaddr_in); msg.msg_flags = flags; return kernel_sendmsg(sock, &msg, vec, count, len); } static int _sendto(struct socket* sock, const void *buf, int len, struct sockaddr_in* sin) { struct kvec vec; vec.iov_base = (void*)buf; vec.iov_len = len; return do_sendto(sock, &vec, 1, len, 0, sin); } static int _recv(struct socket *sock, void* buf, int size, unsigned flags) { struct msghdr msg = { NULL, }; struct kvec iov = { buf, size }; return kernel_recvmsg(sock, &msg, &iov, 1, size, flags); } int banjosock_inetaddr(const char* cp, u32* dst) { int value; int digit; int i; char temp; char bytes[4] = { 0 }; char *end = bytes; static const int addr_class_max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff }; temp = *cp; while (1) { if (!isdigit(temp)) return 0; value = 0; digit = 0; for (;;) { if (isascii(temp) && isdigit(temp)) { value = (value * 10) + temp - '0'; temp = *++cp; digit = 1; } else break; } if (temp == '.') { if ((end > bytes + 2) || (value > 255)) return 0; *end++ = value; temp = *++cp; } else break; } if (value > addr_class_max[end - bytes]) return 0; *((u32*)dst) = *((u32*)bytes) | htonl(value); return 1; /* success */ } // initialize our socket i/o int banjosock_init(const char* serverip) { // setup data and control addresses sinDataAddr.sin_family = AF_INET; sinDataAddr.sin_port = htons(DATAPORT); banjosock_inetaddr(serverip, &sinDataAddr.sin_addr.s_addr); sinControlAddr.sin_family = AF_INET; sinControlAddr.sin_port = htons(CONTROLPORT); banjosock_inetaddr(serverip, &sinControlAddr.sin_addr.s_addr); // allocate a data stream descriptor if (sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sockData) < 0 || !sockData || kernel_connect(sockData, sinDataAddr, sizeof (sinDataAddr)) < 0) { printk(KERN_ERR "banjosock_init: problem creating / binding DATA socket\n"); return -1; } // allocate a control port descriptor if (sock_create_kern(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &sockControl) < 0 || !sockControl || kernel_connect(sockControl, sinControlAddr, sizeof (sinControlAddr)) < 0) { printk(KERN_ERR "banjosock_init: problem creating / binding CONTROL socket\n"); return -1; } // align the tx buffer in memory sockTxBuf = &sockBuf[0] + (PKTALIGN - 1); sockTxBuf -= (u32)sockTxBuf % PKTALIGN; return 0; } void banjosock_cleanup(void) { if (sockData) sock_release(sockData); if (sockControl) sock_release(sockControl); } static int banjosock_sendmesg(u32 type, u32 size, u32 param, u32 reserved) { // append a header and send it to the server struct Header* h = (struct Header*)&sockTxBuf; h->type = htonl(type); h->size = htonl(size); h->parameter = htonl(param); h->reserved = htonl(reserved); return _sendto(sockData, (void*)sockTxBuf, size + sizeof (struct Header), (struct sockaddr_in*)&sinDataAddr); } static int banjosock_sendwindow(u32 window) { // send the next window request to the server via the control port u32* l = (u32*)&sockTxBuf; *l = htonl(window); return _sendto(sockData, (void*)sockTxBuf, sizeof (u32), (struct sockaddr_in*)&sinControlAddr); } int banjosock_handledata(void) { int ret = 0; u32 recv, total; char hdrbuf[sizeof (struct Header)]; struct Header* h = (struct Header*)&hdrbuf; // get the header coming down the line ret = _recv(sockData, hdrbuf, sizeof (struct Header), 0); if (ret < 0) { printk(KERN_ERR "banjosock_handledata: failed to receive data from socket (%d)\n", ret); return -1; } // convert to host h->type = htonl(h->type); h->size = htonl(h->size); h->parameter = htonl(h->parameter); h->reserved = htonl(h->reserved); switch (h->type) { case ACK: break; case NAK: break; case EOT: break; case VIDEO_FRAME: // get the data still remaining in queue recv = 0; total = h->size; while (recv < total) { ret = _recv(sockData, (void*)(banjo_device->dma.buf + recv), total - recv, 0); recv += ret; // request more data at window intervals if (recv < total && (recv % WINDOWSIZE) == 0) { ret = banjosock_sendwindow(WINDOWSIZE); if (ret < 0) { printk(KERN_ERR "banjosock_handledata: failed to send window msg (%d)\n", ret); return -1; } } } break; } return ret; } int banjosock_decueFeature(void) { int ret; // de-cue the current feature ret = banjosock_sendmesg(DECUE_FEATURE, 0, 0, 0); if (ret < 0) { printk(KERN_ERR "banjosock_decueFeature: failed to send de-cue msg (%d)\n", ret); return -1; } return banjosock_handledata(); } int banjosock_cueFeature(const char* feature) { int ret; // assemble the tx packet char* pkt = (char*)sockTxBuf; pkt += sizeof (struct Header); strcpy(pkt, feature); // cue the requested feature ret = banjosock_sendmesg(CUE_FEATURE, strlen(feature), 0, 0); if (ret < 0) { printk(KERN_ERR "banjosock_cueFeature: failed to send cue feature msg (%d)\n", ret); return -1; } return banjosock_handledata(); } int banjosock_requestFrame(int frame) { int ret; // cue the requested feature ret = banjosock_sendmesg(VIDEO_FRAME_REQUEST, 0, frame, WINDOWSIZE); if (ret < 0) { printk(KERN_ERR "banjosock_requestFrame: failed to send request frame msg (%d)\n", ret); return -1; } return banjosock_handledata(); } int banjo_dmastatus_proc(char* buf, char** start, off_t offset, int count, int* eof, void* data) { int len = 0; len += sprintf(buf + len, "\nBanjo Decoder - DMA"); len += sprintf(buf + len, "\n\tDCRN_DMASR\t: %08X", mfdcr(DMA2P40_SR)); len += sprintf(buf + len, "\n\trequests\t: %d", banjo_device->frames_request); len += sprintf(buf + len, "\n\tframes sent\t: %d", banjo_device->frames_sent); len += sprintf(buf + len, "\n\tnot ready\t: %d", banjo_device->frames_not_ready); len += sprintf(buf + len, "\n\tdma error\t: %d", banjo_device->frames_dma_error); len += sprintf(buf + len, "\n\n"); *eof = 1; return len; } struct file_operations banjo_fops = { .owner = THIS_MODULE, .llseek = 0, .read = 0, .write = 0, .ioctl = banjo_ioctl, .open = banjo_open, .release = banjo_release, }; static void banjo_setup_cdev(struct banjo_dev* dev, int index) { int devno = MKDEV(banjo_major, index); cdev_init(&dev->cdev, &banjo_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &banjo_fops; cdev_add(&dev->cdev, devno, 1); } static void __init dma_init(void) { // setup the channel 0 control register mtdcr(DMA2P40_CR0, DMA_CONFIG0); // remap DMA buffer banjo_device->dma.buf = ioremap(DMABUFFERBASE, DMABUFFERSIZE); banjo_device->dma.flags = 0; enable_per_ready(0); } static void __init init_interrupts(void) { // map interrupt handler for DMA status request_irq(DMA_STATUS_IRQ, dma_status_int, SA_INTERRUPT, DRIVER_NAME, dma_status_int); // map interrupt handler for frame ready interrupt IRQ7 request_irq(FRAME_READY_IRQ, frame_req_int, SA_INTERRUPT, DRIVER_NAME, frame_req_int); } static int __init banjo_init(void) { int result; dev_t dev = MKDEV(banjo_major, 0); printk(KERN_INFO "USL, Inc. Image Decoder driver v" DRIVER_VERSION "\n"); /* * Register your major, and accept a dynamic number. */ if (banjo_major) result = register_chrdev_region(dev, 0, DRIVER_NAME); else { result = alloc_chrdev_region(&dev, 0, 0, DRIVER_NAME); banjo_major = MAJOR(dev); } if (result < 0) return result; // remap the FPGA registers banjo_regmap = ioremap(BANJO_DECODER_BASE, BANJO_REGISTERMAP_SIZE); // remap GPIO1 port address gpio1_or = ioremap(GPIO1_OR, sizeof (long)); // allocate all device structures banjo_device = kmalloc(sizeof (struct banjo_dev), GFP_KERNEL); memset(banjo_device, 0, sizeof (struct banjo_dev)); // register character devices with the kernel banjo_setup_cdev(banjo_device, 0); // create /proc fs entries create_proc_read_entry("banjodma", 0, NULL, banjo_dmastatus_proc, NULL); // initialize DMA dma_init(); // initialize interrupts init_interrupts(); return 0; } static void __exit banjo_exit(void) { free_irq(FRAME_READY_IRQ, NULL); free_irq(DMA_STATUS_IRQ, NULL); banjosock_cleanup(); cdev_del(&banjo_device->cdev); kfree(banjo_device); remove_proc_entry("banjodma", NULL); iounmap(gpio1_or); iounmap(banjo_regmap); iounmap(banjo_device->dma.buf); unregister_chrdev_region(MKDEV(banjo_major, 0), 1); } module_init(banjo_init); module_exit(banjo_exit);
_______________________________________________ Linuxppc-embedded mailing list Linuxppc-embedded@ozlabs.org https://ozlabs.org/mailman/listinfo/linuxppc-embedded