lkml.org 
[lkml]   [2003]   [Oct]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Subject[PATCH 4/14] video capture updates for saa7146 core
    Date
    From
    - [DVB] some Kconfig simplifications
    - [DVB] FIELD_ALTERNATE capture was broken, add a "wait for vbi" command before actually waiting for the field change
    - [DVB] improvements regarding streaming capture to gfx card memory.
    - [DVB] captured frames could only be page aligned. fixed.
    - [DVB] fix pgtable_build_single, it should work for all kinds of buffers now (system memory (kernel/user) and gfx-memory)
    - [DVB] Fix bytesperline-calculation for V4L2_FIELD_ALTERNATE vs. V4L2_FIELD_INTERLACED capture
    diff -uNrwB --new-file xx-linux-2.6.0-test5/drivers/media/common/Kconfig linux-2.6.0-test5/drivers/media/common/Kconfig
    --- xx-linux-2.6.0-test5/drivers/media/common/Kconfig 2003-09-10 11:28:54.000000000 +0200
    +++ linux-2.6.0-test5/drivers/media/common/Kconfig 2003-07-29 12:01:11.000000000 +0200
    @@ -1,11 +1,8 @@
    config VIDEO_SAA7146
    - tristate
    - default y if DVB_AV7110=y || DVB_BUDGET=y || DVB_BUDGET_AV=y || VIDEO_MXB=y || VIDEO_DPC=y || VIDEO_HEXIUM_ORION=y || VIDEO_HEXIUM_GEMINI=y
    - default m if DVB_AV7110=m || DVB_BUDGET=m || DVB_BUDGET_AV=m || VIDEO_MXB=m || VIDEO_DPC=m || VIDEO_HEXIUM_ORION=m || VIDEO_HEXIUM_GEMINI=m
    + def_tristate DVB_AV7110 || DVB_BUDGET || DVB_BUDGET_AV || \
    + VIDEO_MXB || VIDEO_DPC || VIDEO_HEXIUM_ORION || \
    + VIDEO_HEXIUM_GEMINI
    depends on VIDEO_DEV && PCI && I2C
    -
    config VIDEO_VIDEOBUF
    - tristate
    - default y if VIDEO_SAA7134=y || VIDEO_BT848=y || VIDEO_SAA7146=y
    - default m if VIDEO_SAA7134=m || VIDEO_BT848=m || VIDEO_SAA7146=m
    + def_tristate VIDEO_SAA7134 || VIDEO_BT848 || VIDEO_SAA7146
    depends on VIDEO_DEV
    diff -uNrwB --new-file xx-linux-2.6.0-test5/drivers/media/common/saa7146_core.c linux-2.6.0-test5/drivers/media/common/saa7146_core.c
    --- xx-linux-2.6.0-test5/drivers/media/common/saa7146_core.c 2003-09-10 11:28:41.000000000 +0200
    +++ linux-2.6.0-test5/drivers/media/common/saa7146_core.c 2003-08-21 15:13:41.000000000 +0200
    @@ -139,34 +139,42 @@
    return 0;
    }

    -void saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, struct scatterlist *list, int length )
    +void saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt,
    + struct scatterlist *list, int sglen )
    {
    u32 *ptr, fill;
    + int nr_pages = 0;
    int i,p;

    -//fm DEB_EE(("pci:%p, pt:%p, sl:%p, len:%d\n",pci,pt,list,length));
    + BUG_ON( 0 == sglen);

    /* if we have a user buffer, the first page may not be
    aligned to a page boundary. */
    pt->offset = list->offset;

    ptr = pt->cpu;
    - for (i = 0; i < length; i++, list++) {
    + for (i = 0; i < sglen; i++, list++) {
    +/*
    + printk("i:%d, adr:0x%08x, len:%d, offset:%d\n", i,sg_dma_address(list), sg_dma_len(list), list->offset);
    +*/
    for (p = 0; p * 4096 < list->length; p++, ptr++) {
    - *ptr = sg_dma_address(list) - list->offset;
    + *ptr = sg_dma_address(list) + p * 4096;
    + nr_pages++;
    }
    }


    /* safety; fill the page table up with the last valid page */
    fill = *(ptr-1);
    - for(;i<1024;i++) {
    + for(i=nr_pages;i<1024;i++) {
    *ptr++ = fill;
    }
    +
    /*
    ptr = pt->cpu;
    - for(j=0;j<60;j++) {
    - printk("ptr1 %d: 0x%08x\n",j,ptr[j]);
    + printk("offset: %d\n",pt->offset);
    + for(i=0;i<5;i++) {
    + printk("ptr1 %d: 0x%08x\n",i,ptr[i]);
    }
    */
    }
    diff -uNrwB --new-file xx-linux-2.6.0-test5/drivers/media/common/saa7146_fops.c linux-2.6.0-test5/drivers/media/common/saa7146_fops.c
    --- xx-linux-2.6.0-test5/drivers/media/common/saa7146_fops.c 2003-09-10 11:29:20.000000000 +0200
    +++ linux-2.6.0-test5/drivers/media/common/saa7146_fops.c 2003-07-31 14:25:06.000000000 +0200
    @@ -304,6 +304,7 @@
    return videobuf_poll_stream(file, &fh->vbi_q, wait);
    q = &fh->vbi_q;
    } else {
    + DEB_D(("using video queue.\n"));
    q = &fh->video_q;
    }

    @@ -311,14 +312,17 @@
    buf = list_entry(q->stream.next, struct videobuf_buffer, stream);

    if (!buf) {
    + DEB_D(("buf == NULL!\n"));
    return POLLERR;
    }

    poll_wait(file, &buf->done, wait);
    if (buf->state == STATE_DONE || buf->state == STATE_ERROR) {
    + DEB_D(("poll succeeded!\n"));
    return POLLIN|POLLRDNORM;
    }

    + DEB_D(("nothing to poll for, buf->state:%d\n",buf->state));
    return 0;
    }

    diff -uNrwB --new-file xx-linux-2.6.0-test5/drivers/media/common/saa7146_hlp.c linux-2.6.0-test5/drivers/media/common/saa7146_hlp.c
    --- xx-linux-2.6.0-test5/drivers/media/common/saa7146_hlp.c 2003-09-10 11:28:41.000000000 +0200
    +++ linux-2.6.0-test5/drivers/media/common/saa7146_hlp.c 2003-08-11 13:13:10.000000000 +0200
    @@ -742,6 +742,7 @@

    int width = buf->fmt->width;
    int height = buf->fmt->height;
    + int bytesperline = buf->fmt->bytesperline;
    enum v4l2_field field = buf->fmt->field;

    int depth = sfmt->depth;
    @@ -749,7 +750,11 @@
    DEB_CAP(("[size=%dx%d,fields=%s]\n",
    width,height,v4l2_field_names[field]));

    + if( bytesperline != 0) {
    + vdma1.pitch = bytesperline*2;
    + } else {
    vdma1.pitch = (width*depth*2)/8;
    + }
    vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels);
    vdma1.base_page = buf->pt[0].dma | ME1;

    @@ -799,6 +804,8 @@
    vdma2->pitch = width;
    vdma3->pitch = width;

    + /* fixme: look at bytesperline! */
    +
    if( 0 != vv->vflip ) {
    vdma2->prot_addr = buf->pt[1].offset;
    vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[1].offset;
    @@ -871,6 +878,8 @@
    DEB_CAP(("[size=%dx%d,fields=%s]\n",
    width,height,v4l2_field_names[field]));

    + /* fixme: look at bytesperline! */
    +
    /* fixme: what happens for user space buffers here?. The offsets are
    most likely wrong, this version here only works for page-aligned
    buffers, modifications to the pagetable-functions are necessary...*/
    @@ -997,8 +1006,10 @@
    WRITE_RPS0(CMD_PAUSE | o_wait);
    WRITE_RPS0(CMD_PAUSE | e_wait);
    } else if ( vv->last_field == V4L2_FIELD_TOP ) {
    + WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09));
    WRITE_RPS0(CMD_PAUSE | o_wait);
    } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
    + WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09));
    WRITE_RPS0(CMD_PAUSE | e_wait);
    }

    @@ -1033,16 +1044,6 @@

    DEB_CAP(("buf:%p, next:%p\n",buf,next));

    -/*
    - printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
    - printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
    - printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
    - printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
    - printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
    - printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
    - printk("vdma%d => vptr : 0x%08x\n", 1,saa7146_read(dev,PCI_VDP1));
    -*/
    -
    vdma1_prot_addr = saa7146_read(dev, PROT_ADDR1);
    if( 0 == vdma1_prot_addr ) {
    /* clear out beginning of streaming bit (rps register 0)*/
    @@ -1069,6 +1070,16 @@
    program_capture_engine(dev,0);
    }

    +/*
    + printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
    + printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
    + printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
    + printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
    + printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
    + printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
    + printk("vdma%d => vptr : 0x%08x\n", 1,saa7146_read(dev,PCI_VDP1));
    +*/
    +
    /* write the address of the rps-program */
    saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);

    diff -uNrwB --new-file xx-linux-2.6.0-test5/drivers/media/common/saa7146_video.c linux-2.6.0-test5/drivers/media/common/saa7146_video.c
    --- xx-linux-2.6.0-test5/drivers/media/common/saa7146_video.c 2003-09-10 11:29:20.000000000 +0200
    +++ linux-2.6.0-test5/drivers/media/common/saa7146_video.c 2003-08-26 22:30:46.000000000 +0200
    @@ -169,6 +169,7 @@
    struct saa7146_format *fmt;
    enum v4l2_field field;
    int maxw, maxh;
    + int calc_bpl;

    DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh));

    @@ -211,8 +212,18 @@
    f->fmt.pix.width = maxw;
    if (f->fmt.pix.height > maxh)
    f->fmt.pix.height = maxh;
    - f->fmt.pix.sizeimage =
    - (f->fmt.pix.width * f->fmt.pix.height * fmt->depth)/8;
    +
    + calc_bpl = (f->fmt.pix.width * fmt->depth)/8;
    +
    + if (f->fmt.pix.bytesperline < calc_bpl)
    + f->fmt.pix.bytesperline = calc_bpl;
    +
    + if (f->fmt.pix.bytesperline > (2*PAGE_SIZE * fmt->depth)/8) /* arbitrary constraint */
    + f->fmt.pix.bytesperline = calc_bpl;
    +
    + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
    + DEB_D(("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",f->fmt.pix.width,f->fmt.pix.height,f->fmt.pix.bytesperline,f->fmt.pix.sizeimage));
    +
    return 0;
    }
    case V4L2_BUF_TYPE_VIDEO_OVERLAY:
    @@ -359,41 +370,41 @@

    static struct v4l2_queryctrl controls[] = {
    {
    - .id = V4L2_CID_BRIGHTNESS,
    - .name = "Brightness",
    - .minimum = 0,
    - .maximum = 255,
    - .step = 1,
    - .default_value = 128,
    - .type = V4L2_CTRL_TYPE_INTEGER,
    + id: V4L2_CID_BRIGHTNESS,
    + name: "Brightness",
    + minimum: 0,
    + maximum: 255,
    + step: 1,
    + default_value: 128,
    + type: V4L2_CTRL_TYPE_INTEGER,
    },{
    - .id = V4L2_CID_CONTRAST,
    - .name = "Contrast",
    - .minimum = 0,
    - .maximum = 127,
    - .step = 1,
    - .default_value = 64,
    - .type = V4L2_CTRL_TYPE_INTEGER,
    + id: V4L2_CID_CONTRAST,
    + name: "Contrast",
    + minimum: 0,
    + maximum: 127,
    + step: 1,
    + default_value: 64,
    + type: V4L2_CTRL_TYPE_INTEGER,
    },{
    - .id = V4L2_CID_SATURATION,
    - .name = "Saturation",
    - .minimum = 0,
    - .maximum = 127,
    - .step = 1,
    - .default_value = 64,
    - .type = V4L2_CTRL_TYPE_INTEGER,
    + id: V4L2_CID_SATURATION,
    + name: "Saturation",
    + minimum: 0,
    + maximum: 127,
    + step: 1,
    + default_value: 64,
    + type: V4L2_CTRL_TYPE_INTEGER,
    },{
    - .id = V4L2_CID_VFLIP,
    - .name = "Vertical flip",
    - .minimum = 0,
    - .maximum = 1,
    - .type = V4L2_CTRL_TYPE_BOOLEAN,
    + id: V4L2_CID_VFLIP,
    + name: "Vertical flip",
    + minimum: 0,
    + maximum: 1,
    + type: V4L2_CTRL_TYPE_BOOLEAN,
    },{
    - .id = V4L2_CID_HFLIP,
    - .name = "Horizontal flip",
    - .minimum = 0,
    - .maximum = 1,
    - .type = V4L2_CTRL_TYPE_BOOLEAN,
    + id: V4L2_CID_HFLIP,
    + name: "Horizontal flip",
    + minimum: 0,
    + maximum: 1,
    + type: V4L2_CTRL_TYPE_BOOLEAN,
    },
    };
    static int NUM_CONTROLS = sizeof(controls)/sizeof(struct v4l2_queryctrl);
    @@ -549,7 +560,7 @@
    int length = buf->vb.dma.sglen;
    struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);

    - DEB_EE(("dev:%p, buf:%p\n",dev,buf));
    + DEB_EE(("dev:%p, buf:%p, sg_len:%d\n",dev,buf,length));

    if( 0 != IS_PLANAR(sfmt->trans)) {
    struct saa7146_pgtable *pt1 = &buf->pt[0];
    @@ -729,6 +740,81 @@
    return 0;
    }

    +/* capturing to framebuffer */
    +
    +int overlay_reqbufs(struct saa7146_dev *dev, struct v4l2_requestbuffers *req)
    +{
    +/* struct saa7146_fh *fh = file->private_data;
    +
    + if (req->count > VIDEO_MAX_FRAME)
    + req->count = VIDEO_MAX_FRAME;
    +
    + *size = fh->video_fmt.sizeimage;
    +
    +*/
    + return 0;
    +}
    +int overlay_querybuf(struct saa7146_dev *dev, struct v4l2_buffer *buf)
    +{
    + return 0;
    +}
    +int overlay_qbuf(struct saa7146_dev *dev, struct v4l2_buffer *b)
    +{
    +/* if (b->index < 0 || b->index >= VIDEO_MAX_FRAME) {
    + DEB_D(("index %d out of bounds.\n",b->index));
    + goto -EINVAL;
    + }
    +
    + buf = q->bufs[b->index];
    + if (NULL == buf) {
    + printk("videobuf_qbuf: NULL == buf\n");
    + goto done;
    + }
    + if (0 == buf->baddr) {
    + printk("videobuf_qbuf: 0 == buf->baddr\n");
    + goto done;
    + }
    + if (buf->state == STATE_QUEUED ||
    + buf->state == STATE_ACTIVE) {
    + printk("videobuf_qbuf: already queued or activated.\n");
    + goto done;
    + }
    +
    + field = videobuf_next_field(q);
    + retval = q->ops->buf_prepare(file,buf,field);
    + if (0 != retval) {
    + printk("videobuf_qbuf: buf_prepare() failed.\n");
    + goto done;
    + }
    +
    + list_add_tail(&buf->stream,&q->stream);
    + if (q->streaming) {
    + spin_lock_irqsave(q->irqlock,flags);
    + q->ops->buf_queue(file,buf);
    + spin_unlock_irqrestore(q->irqlock,flags);
    + }
    + retval = 0;
    +
    + done:
    + up(&q->lock);
    + return retval;
    +*/
    + return 0;
    +}
    +int overlay_dqbuf(struct saa7146_dev *dev, struct v4l2_buffer *buf)
    +{
    + return 0;
    +}
    +int overlay_streamon(struct saa7146_dev *dev)
    +{
    + return 0;
    +}
    +int overlay_streamoff(struct saa7146_dev *dev)
    +{
    + return 0;
    +}
    +
    +
    /*
    * This function is _not_ called directly, but from
    * video_generic_ioctl (and maybe others). userspace
    @@ -794,7 +880,7 @@

    strcpy(cap->driver, "saa7146 v4l2");
    strlcpy(cap->card, dev->ext->name, sizeof(cap->card));
    - sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
    + sprintf(cap->bus_info,"PCI:%s",dev->pci->slot_name);
    cap->version = SAA7146_VERSION_CODE;
    cap->capabilities =
    V4L2_CAP_VIDEO_CAPTURE |
    @@ -884,8 +970,11 @@

    ctrl = ctrl_by_id(c->id);
    if( NULL == ctrl ) {
    + return -EINVAL;
    +/*
    c->flags = V4L2_CTRL_FLAG_DISABLED;
    return 0;
    +*/
    }

    DEB_EE(("VIDIOC_QUERYCTRL: id:%d\n",c->id));
    @@ -1037,44 +1126,71 @@
    return err;
    }
    case VIDIOC_REQBUFS: {
    - DEB_D(("VIDIOC_REQBUFS \n"));
    - return videobuf_reqbufs(file,q,arg);
    + struct v4l2_requestbuffers *req = arg;
    + DEB_D(("VIDIOC_REQBUFS, type:%d\n",req->type));
    +/*
    + if( req->type == V4L2_BUF_TYPE_VIDEO_OVERLAY ) {
    + return overlay_reqbufs(dev,req);
    + }
    +*/
    + return videobuf_reqbufs(file,q,req);
    }
    case VIDIOC_QUERYBUF: {
    - DEB_D(("VIDIOC_QUERYBUF \n"));
    - return videobuf_querybuf(q,arg);
    + struct v4l2_buffer *buf = arg;
    + DEB_D(("VIDIOC_QUERYBUF, type:%d, offset:%d\n",buf->type,buf->m.offset));
    +/* if( buf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY ) {
    + return overlay_querybuf(dev,buf);
    + }
    + */ return videobuf_querybuf(q,buf);
    }
    case VIDIOC_QBUF: {
    - struct v4l2_buffer *b = arg;
    + struct v4l2_buffer *buf = arg;
    int ret = 0;
    - ret = videobuf_qbuf(file,q,b);
    - DEB_D(("VIDIOC_QBUF: ret:%d, index:%d\n",ret,b->index));
    +/* if( buf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY ) {
    + return overlay_qbuf(dev,buf);
    + }
    + */ ret = videobuf_qbuf(file,q,buf);
    + DEB_D(("VIDIOC_QBUF: ret:%d, index:%d\n",ret,buf->index));
    return ret;
    }
    case VIDIOC_DQBUF: {
    - struct v4l2_buffer *b = arg;
    + struct v4l2_buffer *buf = arg;
    int ret = 0;
    - ret = videobuf_dqbuf(file,q,b);
    - DEB_D(("VIDIOC_DQBUF: ret:%d, index:%d\n",ret,b->index));
    +/* if( buf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY ) {
    + return overlay_dqbuf(dev,buf);
    + }
    + */ ret = videobuf_dqbuf(file,q,buf);
    + DEB_D(("VIDIOC_DQBUF: ret:%d, index:%d\n",ret,buf->index));
    return ret;
    }
    case VIDIOC_STREAMON: {
    - DEB_D(("VIDIOC_STREAMON \n"));
    + int *type = arg;
    + DEB_D(("VIDIOC_STREAMON, type:%d\n",*type));
    +
    if( 0 != ops->capture_begin ) {
    if( 0 != (err = ops->capture_begin(fh))) {
    return err;
    }
    }
    +/* if( *type == V4L2_BUF_TYPE_VIDEO_OVERLAY ) {
    + err = overlay_streamon(dev);
    + } else { */
    err = videobuf_streamon(file,q);
    +/* } */
    return err;
    }
    case VIDIOC_STREAMOFF: {
    - DEB_D(("VIDIOC_STREAMOFF \n"));
    + int *type = arg;
    +
    + DEB_D(("VIDIOC_STREAMOFF, type:%d\n",*type));
    if( 0 != ops->capture_end ) {
    ops->capture_end(fh);
    }
    - err = videobuf_streamoff(file,q);
    - return 0;
    +/* if( *type == V4L2_BUF_TYPE_VIDEO_OVERLAY ) {
    + return overlay_streamoff(dev);
    + }
    + */ err = videobuf_streamoff(file,q);
    + return err;
    }
    case VIDIOCGMBUF:
    {
    @@ -1134,6 +1250,8 @@
    struct saa7146_buf *buf = (struct saa7146_buf *)vb;
    int size,err = 0;

    + DEB_CAP(("vbuf:%p\n",vb));
    +
    /* sanity checks */
    if (fh->video_fmt.width < 64 ||
    fh->video_fmt.height < 64 ||
    @@ -1152,6 +1270,7 @@
    DEB_CAP(("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n",
    fh->video_fmt.width,fh->video_fmt.height,size,v4l2_field_names[fh->video_fmt.field]));
    if (buf->vb.width != fh->video_fmt.width ||
    + buf->vb.bytesperline != fh->video_fmt.bytesperline ||
    buf->vb.height != fh->video_fmt.height ||
    buf->vb.size != size ||
    buf->vb.field != field ||
    @@ -1163,6 +1282,7 @@
    if (STATE_NEEDS_INIT == buf->vb.state) {
    struct saa7146_format *sfmt;

    + buf->vb.bytesperline = fh->video_fmt.bytesperline;
    buf->vb.width = fh->video_fmt.width;
    buf->vb.height = fh->video_fmt.height;
    buf->vb.size = size;
    @@ -1280,6 +1400,7 @@
    fh->video_fmt.width = 384;
    fh->video_fmt.height = 288;
    fh->video_fmt.pixelformat = V4L2_PIX_FMT_BGR24;
    + fh->video_fmt.bytesperline = 0;
    fh->video_fmt.field = V4L2_FIELD_ANY;
    sfmt = format_by_fourcc(dev,fh->video_fmt.pixelformat);
    fh->video_fmt.sizeimage = (fh->video_fmt.width * fh->video_fmt.height * sfmt->depth)/8;
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2005-03-22 13:49    [W:0.055 / U:31.292 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site