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