#define SCALE_4CIFS 1/* 640x480(NTSC) or 704x576(PAL) */ #define SCALE_2CIFS 2/* 640x240(NTSC) or 704x288(PAL) */ #define SCALE_1CIFS 3/* 320x240(NTSC) or 352x288(PAL) */ /* SCALE_4CIFSI is the 2 fields interpolated into one */ #define SCALE_4CIFSI 4/* 640x480(NTSC) or 704x576(PAL) high quality */
/* buffer for one video frame */ struct s2255_buffer { /* common v4l buffer stuff -- must be first */ struct vb2_v4l2_buffer vb; struct list_head list;
};
/* current cypress EEPROM firmware version */ #define S2255_CUR_USB_FWVER ((3 << 8) | 12) /* current DSP FW version */ #define S2255_CUR_DSP_FWVER 10104 /* Need DSP version 5+ for video status feature */ #define S2255_MIN_DSP_STATUS 5 #define S2255_MIN_DSP_COLORFILTER 8 #define S2255_NORMS (V4L2_STD_ALL)
/* USB device table */ #define USB_SENSORAY_VID 0x1943 staticconststruct usb_device_id s2255_table[] = {
{USB_DEVICE(USB_SENSORAY_VID, 0x2255)},
{USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, s2255_table);
#define BUFFER_TIMEOUT msecs_to_jiffies(400)
/* image formats. */ /* JPEG formats must be defined last to support jpeg_enable parameter */ staticconststruct s2255_fmt formats[] = {
{
.fourcc = V4L2_PIX_FMT_YUYV,
.depth = 16
/* kickstarts the firmware loading. from probe
*/ staticvoid s2255_timer(struct timer_list *t)
{ struct s2255_dev *dev = timer_container_of(dev, t, timer); struct s2255_fw *data = dev->fw_data; if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
pr_err("s2255: can't submit urb\n");
atomic_set(&data->fw_state, S2255_FW_FAILED); /* wake up anything waiting for the firmware */
wake_up(&data->wait_fw); return;
}
}
/* this loads the firmware asynchronously. Originallythiswasdonesynchronouslyinprobe. Butitisbettertoloaditasynchronouslyherethanblock insidetheprobefunction.Blockinginsideprobeaffectsboottime. FWloadingistriggeredbythetimerintheprobefunction
*/ staticvoid s2255_fwchunk_complete(struct urb *urb)
{ struct s2255_fw *data = urb->context; struct usb_device *udev = urb->dev; int len; if (urb->status) {
dev_err(&udev->dev, "URB failed with status %d\n", urb->status);
atomic_set(&data->fw_state, S2255_FW_FAILED); /* wake up anything waiting for the firmware */
wake_up(&data->wait_fw); return;
} if (data->fw_urb == NULL) {
s2255_dev_err(&udev->dev, "disconnected\n");
atomic_set(&data->fw_state, S2255_FW_FAILED); /* wake up anything waiting for the firmware */
wake_up(&data->wait_fw); return;
} #define CHUNK_SIZE 512 /* all USB transfers must be done with continuous kernel memory. can'tallocatemorethan128kincurrentlinuxkernel,so uploadthefirmwareinchunks
*/ if (data->fw_loaded < data->fw_size) {
len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ?
data->fw_size % CHUNK_SIZE : CHUNK_SIZE;
if (len < CHUNK_SIZE)
memset(data->pfw_data, 0, CHUNK_SIZE);
/* write to the configuration pipe, synchronously */ staticint s2255_write_config(struct usb_device *udev, unsignedchar *pbuf, int size)
{ int pipe; int done; long retval = -1; if (udev) {
pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP);
retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500);
} return retval;
}
static u32 get_transfer_size(struct s2255_mode *mode)
{ int linesPerFrame = LINE_SZ_DEF; int pixelsPerLine = NUM_LINES_DEF;
u32 outImageSize;
u32 usbInSize; unsignedint mask_mult;
if (mode == NULL) return0;
if (mode->format == FORMAT_NTSC) { switch (mode->scale) { case SCALE_4CIFS: case SCALE_4CIFSI:
linesPerFrame = NUM_LINES_4CIFS_NTSC * 2;
pixelsPerLine = LINE_SZ_4CIFS_NTSC; break; case SCALE_2CIFS:
linesPerFrame = NUM_LINES_2CIFS_NTSC;
pixelsPerLine = LINE_SZ_2CIFS_NTSC; break; case SCALE_1CIFS:
linesPerFrame = NUM_LINES_1CIFS_NTSC;
pixelsPerLine = LINE_SZ_1CIFS_NTSC; break; default: break;
}
} elseif (mode->format == FORMAT_PAL) { switch (mode->scale) { case SCALE_4CIFS: case SCALE_4CIFSI:
linesPerFrame = NUM_LINES_4CIFS_PAL * 2;
pixelsPerLine = LINE_SZ_4CIFS_PAL; break; case SCALE_2CIFS:
linesPerFrame = NUM_LINES_2CIFS_PAL;
pixelsPerLine = LINE_SZ_2CIFS_PAL; break; case SCALE_1CIFS:
linesPerFrame = NUM_LINES_1CIFS_PAL;
pixelsPerLine = LINE_SZ_1CIFS_PAL; break; default: break;
}
}
outImageSize = linesPerFrame * pixelsPerLine; if ((mode->color & MASK_COLOR) != COLOR_Y8) { /* 2 bytes/pixel if not monochrome */
outImageSize *= 2;
}
/* total bytes to send including prefix and 4K padding;
must be a multiple of USB_READ_SIZE */
usbInSize = outImageSize + PREFIX_SIZE; /* always send prefix */
mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1; /* if size not a multiple of USB_READ_SIZE */ if (usbInSize & ~mask_mult)
usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK); return usbInSize;
}
dprintk(dev, 1, "s2255: %s\n", __func__);
state = atomic_read(&dev->fw_data->fw_state); switch (state) { case S2255_FW_DISCONNECTING: return -ENODEV; case S2255_FW_FAILED:
s2255_dev_err(&dev->udev->dev, "firmware load failed. retrying.\n");
s2255_fwload_start(dev);
wait_event_timeout(dev->fw_data->wait_fw,
((atomic_read(&dev->fw_data->fw_state)
== S2255_FW_SUCCESS) ||
(atomic_read(&dev->fw_data->fw_state)
== S2255_FW_DISCONNECTING)),
msecs_to_jiffies(S2255_LOAD_TIMEOUT)); /* state may have changed, re-read */
state = atomic_read(&dev->fw_data->fw_state); break; case S2255_FW_NOTLOADED: case S2255_FW_LOADED_DSPWAIT: /* give S2255_LOAD_TIMEOUT time for firmware to load in case
driver loaded and then device immediately opened */
pr_info("%s waiting for firmware load\n", __func__);
wait_event_timeout(dev->fw_data->wait_fw,
((atomic_read(&dev->fw_data->fw_state)
== S2255_FW_SUCCESS) ||
(atomic_read(&dev->fw_data->fw_state)
== S2255_FW_DISCONNECTING)),
msecs_to_jiffies(S2255_LOAD_TIMEOUT)); /* state may have changed, re-read */
state = atomic_read(&dev->fw_data->fw_state); break; case S2255_FW_SUCCESS: default: break;
} /* state may have changed in above switch statement */ switch (state) { case S2255_FW_SUCCESS: break; case S2255_FW_FAILED:
pr_info("2255 firmware load failed.\n"); return -ENODEV; case S2255_FW_DISCONNECTING:
pr_info("%s: disconnecting\n", __func__); return -ENODEV; case S2255_FW_LOADED_DSPWAIT: case S2255_FW_NOTLOADED:
pr_info("%s: firmware not loaded, please retry\n",
__func__); /* *Timeoutonfirmwareloadmeansdeviceunusable. *Setfirmwarefailurestate. *Onnexts2255_openthefirmwarewillbereloaded.
*/
atomic_set(&dev->fw_data->fw_state,
S2255_FW_FAILED); return -EAGAIN; default:
pr_info("%s: unknown state\n", __func__); return -EFAULT;
} if (!vc->configured) { /* configure channel to default state */
vc->fmt = &formats[0];
s2255_set_mode(vc, &vc->mode);
vc->configured = 1;
} return0;
}
staticvoid s2255_destroy(struct s2255_dev *dev)
{
dprintk(dev, 1, "%s", __func__); /* board shutdown stops the read pipe if it is running */
s2255_board_shutdown(dev); /* make sure firmware still not trying to load */
timer_shutdown_sync(&dev->timer); /* only started in .probe and .open */ if (dev->fw_data->fw_urb) {
usb_kill_urb(dev->fw_data->fw_urb);
usb_free_urb(dev->fw_data->fw_urb);
dev->fw_data->fw_urb = NULL;
}
release_firmware(dev->fw_data->fw);
kfree(dev->fw_data->pfw_data);
kfree(dev->fw_data); /* reset the DSP so firmware can be reloaded next time */
s2255_reset_dsppower(dev);
mutex_destroy(&dev->lock);
usb_put_dev(dev->udev);
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev->cmdbuf);
kfree(dev);
}
staticint s2255_probe_v4l(struct s2255_dev *dev)
{ int ret; int i; int cur_nr = video_nr; struct s2255_vc *vc; struct vb2_queue *q;
ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev); if (ret) return ret; /* initialize all video 4 linux */ /* register 4 video devices */ for (i = 0; i < MAX_CHANNELS; i++) {
vc = &dev->vc[i];
INIT_LIST_HEAD(&vc->buf_list);
if (ret) {
dev_err(&dev->udev->dev, "failed to register video device!\n"); break;
}
refcount_inc(&dev->num_channels);
v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
video_device_node_name(&vc->vdev));
}
pr_info("Sensoray 2255 V4L driver Revision: %s\n",
S2255_VERSION); /* if no channels registered, return error and probe will fail*/ if (refcount_read(&dev->num_channels) == 0) {
v4l2_device_unregister(&dev->v4l2_dev); return ret;
} if (refcount_read(&dev->num_channels) != MAX_CHANNELS)
pr_warn("s2255: Not all channels available.\n"); return0;
}
/* this function moves the usb stream read pipe data *intothesystembuffers. *returns0onsuccess,EAGAINifmoredatatoprocess(callthis *functionagain). * *Receivedframestructure: *bytes0-3:marker:0x2255DA4AL(S2255_MARKER_FRAME) *bytes4-7:channel:0-3 *bytes8-11:payloadsize:sizeoftheframe *bytes12-payloadsize+12:framedata
*/ staticint save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
{ char *pdest;
u32 offset = 0; int bframe = 0; char *psrc; unsignedlong copy_size; unsignedlong size;
s32 idx = -1; struct s2255_framei *frm; unsignedchar *pdata; struct s2255_vc *vc;
dprintk(dev, 100, "buffer to user\n");
vc = &dev->vc[dev->cc];
idx = vc->cur_frame;
frm = &vc->buffer.frame[idx]; if (frm->ulState == S2255_READ_IDLE) { int jj; unsignedint cc;
__le32 *pdword; /*data from dsp is little endian */ int payload; /* search for marker codes */
pdata = (unsignedchar *)pipe_info->transfer_buffer;
pdword = (__le32 *)pdata; for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) { switch (*pdword) { case S2255_MARKER_FRAME:
dprintk(dev, 4, "marker @ offset: %d [%x %x]\n",
jj, pdata[0], pdata[1]);
offset = jj + PREFIX_SIZE;
bframe = 1;
cc = le32_to_cpu(pdword[1]); if (cc >= MAX_CHANNELS) {
dprintk(dev, 0, "bad channel\n"); return -EINVAL;
} /* reverse it */
dev->cc = G_chnmap[cc];
vc = &dev->vc[dev->cc];
payload = le32_to_cpu(pdword[3]); if (payload > vc->req_image_size) {
vc->bad_payload++; /* discard the bad frame */ return -EINVAL;
}
vc->pkt_size = payload;
vc->jpg_size = le32_to_cpu(pdword[4]); break; case S2255_MARKER_RESPONSE:
pdata += DEF_USB_BLOCK;
jj += DEF_USB_BLOCK; if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS) break;
cc = G_chnmap[le32_to_cpu(pdword[1])]; if (cc >= MAX_CHANNELS) break;
vc = &dev->vc[cc]; switch (pdword[2]) { case S2255_RESPONSE_SETMODE: /* check if channel valid */ /* set mode ready */
vc->setmode_ready = 1;
wake_up(&vc->wait_setmode);
dprintk(dev, 5, "setmode rdy %d\n", cc); break; case S2255_RESPONSE_FW:
dev->chn_ready |= (1 << cc); if ((dev->chn_ready & 0x0f) != 0x0f) break; /* all channels ready */
pr_info("s2255: fw loaded\n");
atomic_set(&dev->fw_data->fw_state,
S2255_FW_SUCCESS);
wake_up(&dev->fw_data->wait_fw); break; case S2255_RESPONSE_STATUS:
vc->vidstatus = le32_to_cpu(pdword[3]);
vc->vidstatus_ready = 1;
wake_up(&vc->wait_vidstatus);
dprintk(dev, 5, "vstat %x chan %d\n",
le32_to_cpu(pdword[3]), cc); break; default:
pr_info("s2255 unknown resp\n");
}
pdata++; break; default:
pdata++; break;
} if (bframe) break;
} /* for */ if (!bframe) return -EINVAL;
}
vc = &dev->vc[dev->cc];
idx = vc->cur_frame;
frm = &vc->buffer.frame[idx]; /* search done. now find out if should be acquiring on this channel */ if (!vb2_is_streaming(&vc->vb_vidq)) { /* we found a frame, but this channel is turned off */
frm->ulState = S2255_READ_IDLE; return -EINVAL;
}
/* *Createthesystemringbuffertocopyframesintofromthe *usbreadpipe.
*/ staticint s2255_create_sys_buffers(struct s2255_vc *vc)
{ unsignedlong i; unsignedlong reqsize;
vc->buffer.dwFrames = SYS_FRAMES; /* always allocate maximum size(PAL) for system buffers */
reqsize = SYS_FRAMES_MAXSIZE;
if (reqsize > SYS_FRAMES_MAXSIZE)
reqsize = SYS_FRAMES_MAXSIZE;
for (i = 0; i < SYS_FRAMES; i++) { /* allocate the frames */
vc->buffer.frame[i].lpvbits = vmalloc(reqsize);
vc->buffer.frame[i].size = reqsize; if (vc->buffer.frame[i].lpvbits == NULL) {
pr_info("out of memory. using less frames\n");
vc->buffer.dwFrames = i; break;
}
}
/* make sure internal states are set */ for (i = 0; i < SYS_FRAMES; i++) {
vc->buffer.frame[i].ulState = 0;
vc->buffer.frame[i].cur_size = 0;
}
for (i = 0; i < MAX_CHANNELS; i++) { if (vb2_is_streaming(&dev->vc[i].vb_vidq))
s2255_stop_acquire(&dev->vc[i]);
}
s2255_stop_readpipe(dev); for (i = 0; i < MAX_CHANNELS; i++)
s2255_release_sys_buffers(&dev->vc[i]); /* release transfer buffer */
kfree(dev->pipe.transfer_buffer); return0;
}
staticvoid read_pipe_completion(struct urb *purb)
{ struct s2255_pipeinfo *pipe_info; struct s2255_dev *dev; int status; int pipe;
pipe_info = purb->context; if (pipe_info == NULL) {
dev_err(&purb->dev->dev, "no context!\n"); return;
}
dev = pipe_info->dev; if (dev == NULL) {
dev_err(&purb->dev->dev, "no context!\n"); return;
}
status = purb->status; /* if shutting down, do not resubmit, exit immediately */ if (status == -ESHUTDOWN) {
dprintk(dev, 2, "%s: err shutdown\n", __func__);
pipe_info->err_count++; return;
}
if (pipe_info->state == 0) {
dprintk(dev, 2, "%s: exiting USB pipe", __func__); return;
}
/* standard usb probe function */ staticint s2255_probe(struct usb_interface *interface, conststruct usb_device_id *id)
{ struct s2255_dev *dev = NULL; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; int i; int retval = -ENOMEM;
__le32 *pdata; int fw_size;
/* allocate memory for our device state and initialize it to zero */
dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL); if (dev == NULL) {
s2255_dev_err(&interface->dev, "out of memory\n"); return -ENOMEM;
}
dev->cmdbuf = kzalloc(S2255_CMDBUF_SIZE, GFP_KERNEL); if (dev->cmdbuf == NULL) {
s2255_dev_err(&interface->dev, "out of memory\n"); goto errorFWDATA1;
}
refcount_set(&dev->num_channels, 0);
dev->pid = id->idProduct;
dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL); if (!dev->fw_data) goto errorFWDATA1;
mutex_init(&dev->lock);
mutex_init(&dev->cmdlock); /* grab usb_device and save it */
dev->udev = usb_get_dev(interface_to_usbdev(interface)); if (dev->udev == NULL) {
dev_err(&interface->dev, "null usb device\n");
retval = -ENODEV; goto errorUDEV;
}
dev_dbg(&interface->dev, "dev: %p, udev %p interface %p\n",
dev, dev->udev, interface);
dev->interface = interface; /* set up the endpoint information */
iface_desc = interface->cur_altsetting;
dev_dbg(&interface->dev, "num EP: %d\n",
iface_desc->desc.bNumEndpoints); for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc; if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { /* we found the bulk in endpoint */
dev->read_endpoint = endpoint->bEndpointAddress;
}
}
if (!dev->read_endpoint) {
dev_err(&interface->dev, "Could not find bulk-in endpoint\n"); goto errorEP;
}
timer_setup(&dev->timer, s2255_timer, 0);
init_waitqueue_head(&dev->fw_data->wait_fw); for (i = 0; i < MAX_CHANNELS; i++) { struct s2255_vc *vc = &dev->vc[i];
vc->idx = i;
vc->dev = dev;
init_waitqueue_head(&vc->wait_setmode);
init_waitqueue_head(&vc->wait_vidstatus);
spin_lock_init(&vc->qlock);
mutex_init(&vc->vb_lock);
}
dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->fw_data->fw_urb) goto errorFWURB;
dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL); if (!dev->fw_data->pfw_data) {
dev_err(&interface->dev, "out of memory!\n"); goto errorFWDATA2;
} /* load the first chunk */ if (request_firmware(&dev->fw_data->fw,
FIRMWARE_FILE_NAME, &dev->udev->dev)) {
dev_err(&interface->dev, "sensoray 2255 failed to get firmware\n"); goto errorREQFW;
} /* check the firmware is valid */
fw_size = dev->fw_data->fw->size;
pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8];
if (*pdata != S2255_FW_MARKER) {
dev_err(&interface->dev, "Firmware invalid.\n");
retval = -ENODEV; goto errorFWMARKER;
} else { /* make sure firmware is the latest */
__le32 *pRel;
pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4];
pr_info("s2255 dsp fw version %x\n", le32_to_cpu(*pRel));
dev->dsp_fw_ver = le32_to_cpu(*pRel); if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER)
pr_info("s2255: f2255usb.bin out of date.\n"); if (dev->pid == 0x2257 &&
dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
pr_warn("2257 needs firmware %d or above.\n",
S2255_MIN_DSP_COLORFILTER);
}
usb_reset_device(dev->udev); /* load 2255 board specific */
retval = s2255_board_init(dev); if (retval) goto errorBOARDINIT;
s2255_fwload_start(dev); /* loads v4l specific */
retval = s2255_probe_v4l(dev); if (retval) goto errorBOARDINIT;
dev_info(&interface->dev, "Sensoray 2255 detected\n"); return0;
errorBOARDINIT:
s2255_board_shutdown(dev);
errorFWMARKER:
release_firmware(dev->fw_data->fw);
errorREQFW:
kfree(dev->fw_data->pfw_data);
errorFWDATA2:
usb_free_urb(dev->fw_data->fw_urb);
errorFWURB:
timer_shutdown_sync(&dev->timer);
errorEP:
usb_put_dev(dev->udev);
errorUDEV:
kfree(dev->fw_data);
mutex_destroy(&dev->lock);
errorFWDATA1:
kfree(dev->cmdbuf);
kfree(dev);
pr_warn("Sensoray 2255 driver load failed: 0x%x\n", retval); return retval;
}
/* disconnect routine. when board is removed physically or with rmmod */ staticvoid s2255_disconnect(struct usb_interface *interface)
{ struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface)); int i; int channels = refcount_read(&dev->num_channels);
mutex_lock(&dev->lock);
v4l2_device_disconnect(&dev->v4l2_dev);
mutex_unlock(&dev->lock); /*see comments in the uvc_driver.c usb disconnect function */
refcount_inc(&dev->num_channels); /* unregister each video device. */ for (i = 0; i < channels; i++)
video_unregister_device(&dev->vc[i].vdev); /* wake up any of our timers */
atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING);
wake_up(&dev->fw_data->wait_fw); for (i = 0; i < MAX_CHANNELS; i++) {
dev->vc[i].setmode_ready = 1;
wake_up(&dev->vc[i].wait_setmode);
dev->vc[i].vidstatus_ready = 1;
wake_up(&dev->vc[i].wait_vidstatus);
} if (refcount_dec_and_test(&dev->num_channels))
s2255_destroy(dev);
dev_info(&interface->dev, "%s\n", __func__);
}
MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)");
MODULE_LICENSE("GPL");
MODULE_VERSION(S2255_VERSION);
MODULE_FIRMWARE(FIRMWARE_FILE_NAME);
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.31 Sekunden
(vorverarbeitet am 2026-10-02)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.