diff options
Diffstat (limited to 'drivers/media/video/em28xx')
-rw-r--r-- | drivers/media/video/em28xx/Kconfig | 5 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-cards.c | 103 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-dvb.c | 16 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-input.c | 80 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-vbi.c | 1 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-video.c | 30 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx.h | 4 |
7 files changed, 155 insertions, 84 deletions
diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index 66aefd6eef5..985100ea17a 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -1,9 +1,9 @@ config VIDEO_EM28XX tristate "Empia EM28xx USB video capture support" - depends on VIDEO_DEV && I2C && INPUT + depends on VIDEO_DEV && I2C select VIDEO_TUNER select VIDEO_TVEEPROM - depends on VIDEO_IR + depends on RC_CORE select VIDEOBUF_VMALLOC select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO @@ -37,6 +37,7 @@ config VIDEO_EM28XX_DVB select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE select DVB_TDA10023 if !DVB_FE_CUSTOMISE + select DVB_S921 if !DVB_FE_CUSTOMISE select VIDEOBUF_DVB ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index f7e9168157a..87f77a34eea 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -33,6 +33,7 @@ #include <media/saa7115.h> #include <media/tvp5150.h> #include <media/tvaudio.h> +#include <media/mt9v011.h> #include <media/i2c-addr.h> #include <media/tveeprom.h> #include <media/v4l2-common.h> @@ -268,6 +269,20 @@ static struct em28xx_reg_seq dikom_dk300_digital[] = { }; +/* Reset for the most [digital] boards */ +static struct em28xx_reg_seq leadership_digital[] = { + {EM2874_R80_GPIO, 0x70, 0xff, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq leadership_reset[] = { + {EM2874_R80_GPIO, 0xf0, 0xff, 10}, + {EM2874_R80_GPIO, 0xb0, 0xff, 10}, + {EM2874_R80_GPIO, 0xf0, 0xff, 10}, + { -1, -1, -1, -1}, +}; + + /* * Board definitions */ @@ -1224,6 +1239,19 @@ struct em28xx_board em28xx_boards[] = { .vmux = SAA7115_COMPOSITE0, } }, }, + + [EM2874_LEADERSHIP_ISDBT] = { + .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ, + .xclk = EM28XX_XCLK_FREQUENCY_10MHZ, + .name = "EM2874 Leadership ISDBT", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = leadership_reset, + .dvb_gpio = leadership_digital, + .has_dvb = 1, + }, + [EM2880_BOARD_MSI_DIGIVOX_AD] = { .name = "MSI DigiVox A/D", .valid = EM28XX_BOARD_NOT_VALIDATED, @@ -1469,7 +1497,7 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2882_BOARD_TERRATEC_HYBRID_XS] = { - .name = "Terratec Hybrid XS (em2882)", + .name = "Terratec Cinnergy Hybrid T USB XS (em2882)", .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, @@ -1633,11 +1661,11 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_VIDEO2, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_VIDEO2, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2860_BOARD_TERRATEC_AV350] = { @@ -1754,6 +1782,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2868), .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2875), + .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0xe300), .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U }, { USB_DEVICE(0xeb1a, 0xe303), @@ -1791,7 +1821,7 @@ struct usb_device_id em28xx_id_table[] = { { USB_DEVICE(0x0ccd, 0x005e), .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS }, { USB_DEVICE(0x0ccd, 0x0042), - .driver_info = EM2880_BOARD_TERRATEC_HYBRID_XS }, + .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS }, { USB_DEVICE(0x0ccd, 0x0043), .driver_info = EM2870_BOARD_TERRATEC_XS }, { USB_DEVICE(0x0ccd, 0x0047), @@ -1873,6 +1903,7 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = { {0x77800080, EM2860_BOARD_TVP5150_REFERENCE_DESIGN, TUNER_ABSENT}, {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC}, {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF}, + {0x6b800080, EM2874_LEADERSHIP_ISDBT, TUNER_ABSENT}, }; /* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */ @@ -1887,11 +1918,6 @@ static unsigned short tvp5150_addrs[] = { I2C_CLIENT_END }; -static unsigned short mt9v011_addrs[] = { - 0xba >> 1, - I2C_CLIENT_END -}; - static unsigned short msp3400_addrs[] = { 0x80 >> 1, 0x88 >> 1, @@ -2407,8 +2433,9 @@ void em28xx_register_i2c_ir(struct em28xx *dev) dev->init_data.ir_codes = RC_MAP_RC5_HAUPPAUGE_NEW; dev->init_data.get_key = em28xx_get_key_em_haup; dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; + break; case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: - dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE;; + dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; break; @@ -2430,8 +2457,36 @@ void em28xx_card_setup(struct em28xx *dev) dev->board.is_webcam = 0; else dev->progressive = 1; - } else - em28xx_set_model(dev); + } + + if (!dev->board.is_webcam) { + switch (dev->model) { + case EM2820_BOARD_UNKNOWN: + case EM2800_BOARD_UNKNOWN: + /* + * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD. + * + * This occurs because they share identical USB vendor and + * product IDs. + * + * What we do here is look up the EEPROM hash of the K-WORLD + * and if it is found then we decide that we do not have + * a DIGIVOX and reset the device to the K-WORLD instead. + * + * This solution is only valid if they do not share eeprom + * hash identities which has not been determined as yet. + */ + if (em28xx_hint_board(dev) < 0) + em28xx_errdev("Board not discovered\n"); + else { + em28xx_set_model(dev); + em28xx_pre_card_setup(dev); + } + break; + default: + em28xx_set_model(dev); + } + } em28xx_info("Identified as %s (card=%d)\n", dev->board.name, dev->model); @@ -2565,11 +2620,17 @@ void em28xx_card_setup(struct em28xx *dev) "tvp5150", 0, tvp5150_addrs); if (dev->em28xx_sensor == EM28XX_MT9V011) { + struct mt9v011_platform_data pdata; + struct i2c_board_info mt9v011_info = { + .type = "mt9v011", + .addr = 0xba >> 1, + .platform_data = &pdata, + }; struct v4l2_subdev *sd; - sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, - &dev->i2c_adap, "mt9v011", 0, mt9v011_addrs); - v4l2_subdev_call(sd, core, s_config, 0, &dev->sensor_xtal); + pdata.xtal = dev->sensor_xtal; + sd = v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap, + &mt9v011_info, NULL); } @@ -2632,8 +2693,14 @@ static void request_modules(struct em28xx *dev) INIT_WORK(&dev->request_module_wk, request_module_async); schedule_work(&dev->request_module_wk); } + +static void flush_request_modules(struct em28xx *dev) +{ + flush_work_sync(&dev->request_module_wk); +} #else #define request_modules(dev) +#define flush_request_modules(dev) #endif /* CONFIG_MODULES */ /* @@ -2749,8 +2816,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, em28xx_pre_card_setup(dev); if (!dev->board.is_em2800) { - /* Sets I2C speed to 100 KHz */ - retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); + /* Resets I2C speed */ + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed); if (retval < 0) { em28xx_errdev("%s: em28xx_write_regs_req failed!" " retval [%d]\n", @@ -3060,6 +3127,8 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) em28xx_info("disconnecting %s\n", dev->vdev->name); + flush_request_modules(dev); + /* wait until all current v4l2 io is finished then deallocate resources */ mutex_lock(&dev->lock); diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 3ac8d3025fe..c7c04bf712a 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -37,6 +37,7 @@ #include "mt352_priv.h" /* FIXME */ #include "tda1002x.h" #include "tda18271.h" +#include "s921.h" MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); @@ -245,6 +246,10 @@ static struct lgdt3305_config em2870_lgdt3304_dev = { .qam_if_khz = 4000, }; +static struct s921_config sharp_isdbt = { + .demod_address = 0x30 >> 1 +}; + static struct zl10353_config em28xx_zl10353_with_xc3028 = { .demod_address = (0x1e >> 1), .no_tuner = 1, @@ -481,6 +486,7 @@ static int dvb_init(struct em28xx *dev) if (!dev->board.has_dvb) { /* This device does not support the extension */ + printk(KERN_INFO "em28xx_dvb: This device does not support the extension\n"); return 0; } @@ -496,6 +502,16 @@ static int dvb_init(struct em28xx *dev) em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); /* init frontend */ switch (dev->model) { + case EM2874_LEADERSHIP_ISDBT: + dvb->frontend = dvb_attach(s921_attach, + &sharp_isdbt, &dev->i2c_adap); + + if (!dvb->frontend) { + result = -EINVAL; + goto out_free; + } + + break; case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 6759cd5570d..ba1ba8648c8 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -25,7 +25,6 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/interrupt.h> -#include <linux/input.h> #include <linux/usb.h> #include <linux/slab.h> @@ -64,7 +63,7 @@ struct em28xx_ir_poll_result { struct em28xx_IR { struct em28xx *dev; - struct input_dev *input; + struct rc_dev *rc; char name[32]; char phys[32]; @@ -75,10 +74,6 @@ struct em28xx_IR { unsigned int last_readcount; int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); - - /* IR device properties */ - - struct ir_dev_props props; }; /********************************************************** @@ -302,12 +297,12 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) poll_result.toggle_bit, poll_result.read_count, poll_result.rc_address, poll_result.rc_data[0]); if (ir->full_code) - ir_keydown(ir->input, + rc_keydown(ir->rc, poll_result.rc_address << 8 | poll_result.rc_data[0], poll_result.toggle_bit); else - ir_keydown(ir->input, + rc_keydown(ir->rc, poll_result.rc_data[0], poll_result.toggle_bit); @@ -331,9 +326,9 @@ static void em28xx_ir_work(struct work_struct *work) schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); } -static int em28xx_ir_start(void *priv) +static int em28xx_ir_start(struct rc_dev *rc) { - struct em28xx_IR *ir = priv; + struct em28xx_IR *ir = rc->priv; INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); schedule_delayed_work(&ir->work, 0); @@ -341,30 +336,30 @@ static int em28xx_ir_start(void *priv) return 0; } -static void em28xx_ir_stop(void *priv) +static void em28xx_ir_stop(struct rc_dev *rc) { - struct em28xx_IR *ir = priv; + struct em28xx_IR *ir = rc->priv; cancel_delayed_work_sync(&ir->work); } -int em28xx_ir_change_protocol(void *priv, u64 ir_type) +int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type) { int rc = 0; - struct em28xx_IR *ir = priv; + struct em28xx_IR *ir = rc_dev->priv; struct em28xx *dev = ir->dev; u8 ir_config = EM2874_IR_RC5; /* Adjust xclk based o IR table for RC5/NEC tables */ - if (ir_type == IR_TYPE_RC5) { + if (rc_type == RC_TYPE_RC5) { dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; ir->full_code = 1; - } else if (ir_type == IR_TYPE_NEC) { + } else if (rc_type == RC_TYPE_NEC) { dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE; ir_config = EM2874_IR_NEC; ir->full_code = 1; - } else if (ir_type != IR_TYPE_UNKNOWN) + } else if (rc_type != RC_TYPE_UNKNOWN) rc = -EINVAL; em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, @@ -391,7 +386,7 @@ int em28xx_ir_change_protocol(void *priv, u64 ir_type) int em28xx_ir_init(struct em28xx *dev) { struct em28xx_IR *ir; - struct input_dev *input_dev; + struct rc_dev *rc; int err = -ENOMEM; if (dev->board.ir_codes == NULL) { @@ -400,28 +395,27 @@ int em28xx_ir_init(struct em28xx *dev) } ir = kzalloc(sizeof(*ir), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ir || !input_dev) + rc = rc_allocate_device(); + if (!ir || !rc) goto err_out_free; /* record handles to ourself */ ir->dev = dev; dev->ir = ir; - - ir->input = input_dev; + ir->rc = rc; /* * em2874 supports more protocols. For now, let's just announce * the two protocols that were already tested */ - ir->props.allowed_protos = IR_TYPE_RC5 | IR_TYPE_NEC; - ir->props.priv = ir; - ir->props.change_protocol = em28xx_ir_change_protocol; - ir->props.open = em28xx_ir_start; - ir->props.close = em28xx_ir_stop; + rc->allowed_protos = RC_TYPE_RC5 | RC_TYPE_NEC; + rc->priv = ir; + rc->change_protocol = em28xx_ir_change_protocol; + rc->open = em28xx_ir_start; + rc->close = em28xx_ir_stop; /* By default, keep protocol field untouched */ - err = em28xx_ir_change_protocol(ir, IR_TYPE_UNKNOWN); + err = em28xx_ir_change_protocol(rc, RC_TYPE_UNKNOWN); if (err) goto err_out_free; @@ -435,27 +429,27 @@ int em28xx_ir_init(struct em28xx *dev) usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); - input_dev->name = ir->name; - input_dev->phys = ir->phys; - input_dev->id.bustype = BUS_USB; - input_dev->id.version = 1; - input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); - input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); - - input_dev->dev.parent = &dev->udev->dev; - - + rc->input_name = ir->name; + rc->input_phys = ir->phys; + rc->input_id.bustype = BUS_USB; + rc->input_id.version = 1; + rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + rc->dev.parent = &dev->udev->dev; + rc->map_name = dev->board.ir_codes; + rc->driver_name = MODULE_NAME; /* all done */ - err = ir_input_register(ir->input, dev->board.ir_codes, - &ir->props, MODULE_NAME); + err = rc_register_device(rc); if (err) goto err_out_stop; return 0; + err_out_stop: dev->ir = NULL; err_out_free: + rc_free_device(rc); kfree(ir); return err; } @@ -468,8 +462,8 @@ int em28xx_ir_fini(struct em28xx *dev) if (!ir) return 0; - em28xx_ir_stop(ir); - ir_input_unregister(ir->input); + em28xx_ir_stop(ir->rc); + rc_unregister_device(ir->rc); kfree(ir); /* done */ @@ -557,7 +551,7 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev) { if (dev->sbutton_input_dev != NULL) { em28xx_info("Deregistering snapshot button\n"); - cancel_rearming_delayed_work(&dev->sbutton_query_work); + cancel_delayed_work_sync(&dev->sbutton_query_work); input_unregister_device(dev->sbutton_input_dev); dev->sbutton_input_dev = NULL; } diff --git a/drivers/media/video/em28xx/em28xx-vbi.c b/drivers/media/video/em28xx/em28xx-vbi.c index 7f1c4a2173b..2b4c9cba2d6 100644 --- a/drivers/media/video/em28xx/em28xx-vbi.c +++ b/drivers/media/video/em28xx/em28xx-vbi.c @@ -23,6 +23,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/hardirq.h> #include <linux/init.h> #include "em28xx.h" diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 2c300728003..f34d524ccb0 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1434,7 +1434,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, /* It isn't an AC97 control. Sends it to the v4l2 dev interface */ if (rc == 1) { - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); + rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); /* * In the case of non-AC97 volume controls, we still need @@ -1708,11 +1708,15 @@ static int vidioc_streamoff(struct file *file, void *priv, fh, type, fh->resources, dev->resources); if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - videobuf_streamoff(&fh->vb_vidq); - res_free(fh, EM28XX_RESOURCE_VIDEO); + if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { + videobuf_streamoff(&fh->vb_vidq); + res_free(fh, EM28XX_RESOURCE_VIDEO); + } } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - videobuf_streamoff(&fh->vb_vbiq); - res_free(fh, EM28XX_RESOURCE_VBI); + if (res_check(fh, EM28XX_RESOURCE_VBI)) { + videobuf_streamoff(&fh->vb_vbiq); + res_free(fh, EM28XX_RESOURCE_VBI); + } } return 0; @@ -1934,19 +1938,6 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) O_NONBLOCK); } -#ifdef CONFIG_VIDEO_V4L1_COMPAT -static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) -{ - struct em28xx_fh *fh = priv; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); - else - return videobuf_cgmbuf(&fh->vb_vbiq, mbuf, 8); -} -#endif - - /* ----------------------------------------------------------- */ /* RADIO ESPECIFIC IOCTLS */ /* ----------------------------------------------------------- */ @@ -2359,9 +2350,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_register = vidioc_s_register, .vidioc_g_chip_ident = vidioc_g_chip_ident, #endif -#ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = vidiocgmbuf, -#endif }; static const struct video_device em28xx_video_template = { diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 6a75e6a4fc2..6f2795a3d4b 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -33,7 +33,7 @@ #include <media/videobuf-vmalloc.h> #include <media/v4l2-device.h> #include <media/ir-kbd-i2c.h> -#include <media/ir-core.h> +#include <media/rc-core.h> #if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) #include <media/videobuf-dvb.h> #endif @@ -117,6 +117,8 @@ #define EM2800_BOARD_VC211A 74 #define EM2882_BOARD_DIKOM_DK300 75 #define EM2870_BOARD_KWORLD_A340 76 +#define EM2874_LEADERSHIP_ISDBT 77 + /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 |