linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
From: "Dave Cogley" <dcogley@uslinc.com>
To: <linuxppc-embedded@ozlabs.org>
Subject: Socket I/O in Kernel
Date: Tue, 30 Oct 2007 10:46:49 -0700	[thread overview]
Message-ID: <000f01c81b1c$dc2f49e0$2001a8c0@DCOGLEYNEW> (raw)


[-- Attachment #1.1: Type: text/plain, Size: 774 bytes --]

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:dcogley@uslinc.com

 


[-- Attachment #1.2: Type: text/html, Size: 3297 bytes --]

[-- Attachment #2: banjodecoder.c --]
[-- Type: text/plain, Size: 14931 bytes --]

/*
 * 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);

                 reply	other threads:[~2007-10-30 17:47 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='000f01c81b1c$dc2f49e0$2001a8c0@DCOGLEYNEW' \
    --to=dcogley@uslinc.com \
    --cc=linuxppc-embedded@ozlabs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).