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

Reply via email to