/* * cpia_usb CPiA USB driver * * Supports CPiA based parallel port Video Camera's. * * Copyright (C) 1999 Jochen Scharrlach <Jochen.Scharrlach@schwaben.de> * Copyright (C) 1999, 2000 Johannes Erdfelt <johannes@erdfelt.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ /* #define _CPIA_DEBUG_ 1 */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/wait.h> #include <linux/list.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/usb.h> #include "cpia.h" #define USB_REQ_CPIA_GRAB_FRAME 0xC1 #define USB_REQ_CPIA_UPLOAD_FRAME 0xC2 #define WAIT_FOR_NEXT_FRAME 0 #define FORCE_FRAME_UPLOAD 1 #define FRAMES_PER_DESC 10 #define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */ #define CPIA_NUMSBUF 2 #define STREAM_BUF_SIZE (PAGE_SIZE * 4) #define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) struct cpia_sbuf { char *data; struct urb *urb; }; #define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100) enum framebuf_status { FRAME_EMPTY, FRAME_READING, FRAME_READY, FRAME_ERROR, }; struct framebuf { int length; enum framebuf_status status; u8 data[FRAMEBUF_LEN]; struct framebuf *next; }; struct usb_cpia { /* Device structure */ struct usb_device *dev; unsigned char iface; wait_queue_head_t wq_stream; int cursbuf; /* Current receiving sbuf */ struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */ int streaming; int open; int present; struct framebuf *buffers[3]; struct framebuf *curbuff, *workbuff; }; static int cpia_usb_open(void *privdata); static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), void *cbdata); static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data); static int cpia_usb_streamStart(void *privdata); static int cpia_usb_streamStop(void *privdata); static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock); static int cpia_usb_close(void *privdata); #define ABOUT "USB driver for Vision CPiA based cameras" static struct cpia_camera_ops cpia_usb_ops = { cpia_usb_open, cpia_usb_registerCallback, cpia_usb_transferCmd, cpia_usb_streamStart, cpia_usb_streamStop, cpia_usb_streamRead, cpia_usb_close, 0, THIS_MODULE }; static LIST_HEAD(cam_list); static spinlock_t cam_list_lock_usb; static void cpia_usb_complete(struct urb *urb) { int i; char *cdata; struct usb_cpia *ucpia; if (!urb || !urb->context) return; ucpia = (struct usb_cpia *) urb->context; if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open) return; if (ucpia->workbuff->status == FRAME_EMPTY) { ucpia->workbuff->status = FRAME_READING; ucpia->workbuff->length = 0; } for (i = 0; i < urb->number_of_packets; i++) { int n = urb->iso_frame_desc[i].actual_length; int st = urb->iso_frame_desc[i].status; cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; if (st) printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st); if (FRAMEBUF_LEN < ucpia->workbuff->length + n) { printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n", ucpia->workbuff->length, n); return; } if (n) { if ((ucpia->workbuff->length > 0) || (0x19 == cdata[0] && 0x68 == cdata[1])) { memcpy(ucpia->workbuff->data + ucpia->workbuff->length, cdata, n); ucpia->workbuff->length += n; } else DBG("Ignoring packet!\n"); } else { if (ucpia->workbuff->length > 4 && 0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] && 0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] && 0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] && 0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) { ucpia->workbuff->status = FRAME_READY; ucpia->curbuff = ucpia->workbuff; ucpia->workbuff = ucpia->workbuff->next; ucpia->workbuff->status = FRAME_EMPTY; ucpia->workbuff->length = 0; if (waitqueue_active(&ucpia->wq_stream)) wake_up_interruptible(&ucpia->wq_stream); } } } /* resubmit */ urb->dev = ucpia->dev; if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) printk(KERN_ERR "%s: usb_submit_urb ret %d\n", __func__, i); } static int cpia_usb_open(void *privdata) { struct usb_cpia *ucpia = (struct usb_cpia *) privdata; struct urb *urb; int ret, retval = 0, fx, err; if (!ucpia) return -EINVAL; ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); if (!ucpia->sbuf[0].data) return -EINVAL; ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); if (!ucpia->sbuf[1].data) { retval = -EINVAL; goto error_0; } ret = usb_set_interface(ucpia->dev, ucpia->iface, 3); if (ret < 0) { printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", ret); retval = -EBUSY; goto error_1; } ucpia->buffers[0]->status = FRAME_EMPTY; ucpia->buffers[0]->length = 0; ucpia->buffers[1]->status = FRAME_EMPTY; ucpia->buffers[1]->length = 0; ucpia->buffers[2]->status = FRAME_EMPTY; ucpia->buffers[2]->length = 0; ucpia->curbuff = ucpia->buffers[0]; ucpia->workbuff = ucpia->buffers[1]; /* We double buffer the Iso lists, and also know the polling * interval is every frame (1 == (1 << (bInterval -1))). */ urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); if (!urb) { printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); retval = -ENOMEM; goto error_1; } ucpia->sbuf[0].urb = urb; urb->dev = ucpia->dev; urb->context = ucpia; urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); urb->transfer_flags = URB_ISO_ASAP; urb->transfer_buffer = ucpia->sbuf[0].data; urb->complete = cpia_usb_complete; urb->number_of_packets = FRAMES_PER_DESC; urb->interval = 1; urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; for (fx = 0; fx < FRAMES_PER_DESC; fx++) { urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; } urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); if (!urb) { printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 1\n"); retval = -ENOMEM; goto error_urb0; } ucpia->sbuf[1].urb = urb; urb->dev = ucpia->dev; urb->context = ucpia; urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); urb->transfer_flags = URB_ISO_ASAP; urb->transfer_buffer = ucpia->sbuf[1].data; urb->complete = cpia_usb_complete; urb->number_of_packets = FRAMES_PER_DESC; urb->interval = 1; urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; for (fx = 0; fx < FRAMES_PER_DESC; fx++) { urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; } /* queue the ISO urbs, and resubmit in the completion handler */ err = usb_submit_urb(ucpia->sbuf[0].urb, GFP_KERNEL); if (err) { printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n", err); goto error_urb1; } err = usb_submit_urb(ucpia->sbuf[1].urb, GFP_KERNEL); if (err) { printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n", err); goto error_urb1; } ucpia->streaming = 1; ucpia->open = 1; return 0; error_urb1: /* free urb 1 */ usb_free_urb(ucpia->sbuf[1].urb); ucpia->sbuf[1].urb = NULL; error_urb0: /* free urb 0 */ usb_free_urb(ucpia->sbuf[0].urb); ucpia->sbuf[0].urb = NULL; error_1: kfree (ucpia->sbuf[1].data); ucpia->sbuf[1].data = NULL; error_0: kfree (ucpia->sbuf[0].data); ucpia->sbuf[0].data = NULL; return retval; } // // convenience functions // /**************************************************************************** * * WritePacket * ***************************************************************************/ static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t size) { if (!packet) return -EINVAL; return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), packet[1] + (packet[0] << 8), USB_TYPE_VENDOR | USB_RECIP_DEVICE, packet[2] + (packet[3] << 8), packet[4] + (packet[5] << 8), buf, size, 1000); } /**************************************************************************** * * ReadPacket * ***************************************************************************/ static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size) { if (!packet || size <= 0) return -EINVAL; return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), packet[1] + (packet[0] << 8), USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, packet[2] + (packet[3] << 8), packet[4] + (packet[5] << 8), buf, size, 1000); } static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data) { int err = 0; int databytes; struct usb_cpia *ucpia = (struct usb_cpia *)privdata; struct usb_device *udev = ucpia->dev; if (!udev) { DBG("Internal driver error: udev is NULL\n"); return -EINVAL; } if (!command) { DBG("Internal driver error: command is NULL\n"); return -EINVAL; } databytes = (((int)command[7])<<8) | command[6]; if (command[0] == DATA_IN) { u8 buffer[8]; if (!data) { DBG("Internal driver error: data is NULL\n"); return -EINVAL; } err = ReadPacket(udev, command, buffer, 8); if (err < 0) return err; memcpy(data, buffer, databytes); } else if(command[0] == DATA_OUT) WritePacket(udev, command, data, databytes); else { DBG("Unexpected first byte of command: %x\n", command[0]); err = -EINVAL; } return 0; } static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), void *cbdata) { return -ENODEV; } static int cpia_usb_streamStart(void *privdata) { return -ENODEV; } static int cpia_usb_streamStop(void *privdata) { return -ENODEV; } static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock) { struct usb_cpia *ucpia = (struct usb_cpia *) privdata; struct framebuf *mybuff; if (!ucpia || !ucpia->present) return -1; if (ucpia->curbuff->status != FRAME_READY) interruptible_sleep_on(&ucpia->wq_stream); else DBG("Frame already waiting!\n"); mybuff = ucpia->curbuff; if (!mybuff) return -1; if (mybuff->status != FRAME_READY || mybuff->length < 4) { DBG("Something went wrong!\n"); return -1; } memcpy(frame, mybuff->data, mybuff->length); mybuff->status = FRAME_EMPTY; /* DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n", */ /* mybuff->length, frame[0], frame[1], */ /* frame[mybuff->length-4], frame[mybuff->length-3], */ /* frame[mybuff->length-2], frame[mybuff->length-1]); */ return mybuff->length; } static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try) { if (!ucpia->streaming) return; ucpia->streaming = 0; /* Set packet size to 0 */ if (try) { int ret; ret = usb_set_interface(ucpia->dev, ucpia->iface, 0); if (ret < 0) { printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret); return; } } /* Unschedule all of the iso td's */ if (ucpia->sbuf[1].urb) { usb_kill_urb(ucpia->sbuf[1].urb); usb_free_urb(ucpia->sbuf[1].urb); ucpia->sbuf[1].urb = NULL; } kfree(ucpia->sbuf[1].data); ucpia->sbuf[1].data = NULL; if (ucpia->sbuf[0].urb) { usb_kill_urb(ucpia->sbuf[0].urb); usb_free_urb(ucpia->sbuf[0].urb); ucpia->sbuf[0].urb = NULL; } kfree(ucpia->sbuf[0].data); ucpia->sbuf[0].data = NULL; } static int cpia_usb_close(void *privdata) { struct usb_cpia *ucpia = (struct usb_cpia *) privdata; if(!ucpia) return -ENODEV; ucpia->open = 0; /* ucpia->present = 0 protects against trying to reset the * alt setting if camera is physically disconnected while open */ cpia_usb_free_resources(ucpia, ucpia->present); return 0; } /* Probing and initializing */ static int cpia_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); struct usb_host_interface *interface; struct usb_cpia *ucpia; struct cam_data *cam; int ret; /* A multi-config CPiA camera? */ if (udev->descriptor.bNumConfigurations != 1) return -ENODEV; interface = intf->cur_altsetting; printk(KERN_INFO "USB CPiA camera found\n"); ucpia = kzalloc(sizeof(*ucpia), GFP_KERNEL); if (!ucpia) { printk(KERN_ERR "couldn't kmalloc cpia struct\n"); return -ENOMEM; } ucpia->dev = udev; ucpia->iface = interface->desc.bInterfaceNumber; init_waitqueue_head(&ucpia->wq_stream); ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0])); if (!ucpia->buffers[0]) { printk(KERN_ERR "couldn't vmalloc frame buffer 0\n"); goto fail_alloc_0; } ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1])); if (!ucpia->buffers[1]) { printk(KERN_ERR "couldn't vmalloc frame buffer 1\n"); goto fail_alloc_1; } ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2])); if (!ucpia->buffers[2]) { printk(KERN_ERR "couldn't vmalloc frame buffer 2\n"); goto fail_alloc_2; } ucpia->buffers[0]->next = ucpia->buffers[1]; ucpia->buffers[1]->next = ucpia->buffers[2]; ucpia->buffers[2]->next = ucpia->buffers[0]; ret = usb_set_interface(udev, ucpia->iface, 0); if (ret < 0) { printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", ret); /* goto fail_all; */ } /* Before register_camera, important */ ucpia->present = 1; cam = cpia_register_camera(&cpia_usb_ops, ucpia); if (!cam) { LOG("failed to cpia_register_camera\n"); goto fail_all; } spin_lock( &cam_list_lock_usb ); list_add( &cam->cam_data_list, &cam_list ); spin_unlock( &cam_list_lock_usb ); usb_set_intfdata(intf, cam); return 0; fail_all: vfree(ucpia->buffers[2]); ucpia->buffers[2] = NULL; fail_alloc_2: vfree(ucpia->buffers[1]); ucpia->buffers[1] = NULL; fail_alloc_1: vfree(ucpia->buffers[0]); ucpia->buffers[0] = NULL; fail_alloc_0: kfree(ucpia); return -EIO; } static void cpia_disconnect(struct usb_interface *intf); static struct usb_device_id cpia_id_table [] = { { USB_DEVICE(0x0553, 0x0002) }, { USB_DEVICE(0x0813, 0x0001) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, cpia_id_table); MODULE_LICENSE("GPL"); static struct usb_driver cpia_driver = { .name = "cpia", .probe = cpia_probe, .disconnect = cpia_disconnect, .id_table = cpia_id_table, }; static void cpia_disconnect(struct usb_interface *intf) { struct cam_data *cam = usb_get_intfdata(intf); struct usb_cpia *ucpia; struct usb_device *udev; usb_set_intfdata(intf, NULL); if (!cam) return; ucpia = (struct usb_cpia *) cam->lowlevel_data; spin_lock( &cam_list_lock_usb ); list_del(&cam->cam_data_list); spin_unlock( &cam_list_lock_usb ); ucpia->present = 0; cpia_unregister_camera(cam); if(ucpia->open) cpia_usb_close(cam->lowlevel_data); ucpia->curbuff->status = FRAME_ERROR; if (waitqueue_active(&ucpia->wq_stream)) wake_up_interruptible(&ucpia->wq_stream); udev = interface_to_usbdev(intf); ucpia->curbuff = ucpia->workbuff = NULL; vfree(ucpia->buffers[2]); ucpia->buffers[2] = NULL; vfree(ucpia->buffers[1]); ucpia->buffers[1] = NULL; vfree(ucpia->buffers[0]); ucpia->buffers[0] = NULL; cam->lowlevel_data = NULL; kfree(ucpia); } static int __init usb_cpia_init(void) { printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, CPIA_USB_MAJ_VER,CPIA_USB_MIN_VER,CPIA_USB_PATCH_VER); spin_lock_init(&cam_list_lock_usb); return usb_register(&cpia_driver); } static void __exit usb_cpia_cleanup(void) { usb_deregister(&cpia_driver); } module_init (usb_cpia_init); module_exit (usb_cpia_cleanup);