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).