diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-06-08 18:10:32 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-06-08 18:10:32 +0200 |
commit | 9470195a9cd13e6d90221b8b5d897e9232da8d28 (patch) | |
tree | 12f57a49fe94310396f7108c267560c74189c0e0 /sound | |
parent | d362af62ed98f58c64a2b3dd58c79d25ad181b0b (diff) |
ALSA: ctxfi - Clean up probe routines
Clean up probe routines and model detection routines so that the driver
won't call and check the PCI subsystem id at each time.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/ctxfi/ctatc.c | 132 | ||||
-rw-r--r-- | sound/pci/ctxfi/ctatc.h | 17 | ||||
-rw-r--r-- | sound/pci/ctxfi/ctdaio.c | 6 | ||||
-rw-r--r-- | sound/pci/ctxfi/cthardware.c | 31 | ||||
-rw-r--r-- | sound/pci/ctxfi/cthardware.h | 20 | ||||
-rw-r--r-- | sound/pci/ctxfi/cthw20k1.c | 88 | ||||
-rw-r--r-- | sound/pci/ctxfi/xfi.c | 15 |
7 files changed, 135 insertions, 174 deletions
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 7898a375df0..002a70e0b13 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -39,29 +39,40 @@ | (0x10 << 16) \ | ((IEC958_AES3_CON_FS_48000) << 24)) -static const struct ct_atc_chip_sub_details atc_sub_details[NUM_CTCARDS] = { - [CTSB0760] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB0760, - .nm_model = "SB076x"}, - [CTHENDRIX] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, - .nm_model = "Hendrix"}, - [CTSB08801] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB08801, - .nm_model = "SB0880"}, - [CTSB08802] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB08802, - .nm_model = "SB0880"}, - [CTSB08803] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB08803, - .nm_model = "SB0880"} +static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = { + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0022, "SB055x", CTSB055X), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x002f, "SB055x", CTSB055X), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0029, "SB073x", CTSB073X), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0031, "SB073x", CTSB073X), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0x6000, + PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "UAA", CTUAA), + SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_CREATIVE, + "Unknown", CT20K1_UNKNOWN), + { } /* terminator */ }; -static struct ct_atc_chip_details atc_chip_details[] = { - {.vendor = PCI_VENDOR_ID_CREATIVE, - .device = PCI_DEVICE_ID_CREATIVE_20K1, - .sub_details = NULL, - .nm_card = "X-Fi 20k1"}, - {.vendor = PCI_VENDOR_ID_CREATIVE, - .device = PCI_DEVICE_ID_CREATIVE_20K2, - .sub_details = atc_sub_details, - .nm_card = "X-Fi 20k2"}, - {} /* terminator */ +static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = { + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760, + "SB0760", CTSB0760), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08801, + "SB0880", CTSB0880), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802, + "SB0880", CTSB0880), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08803, + "SB0880", CTSB0880), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0x6000, + PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "UAA", CTHENDRIX), + { } /* terminator */ +}; + +static const char *ct_subsys_name[NUM_CTCARDS] = { + [CTSB055X] = "SB055x", + [CTSB073X] = "SB073x", + [CTSB0760] = "SB076x", + [CTUAA] = "UAA", + [CT20K1_UNKNOWN] = "Unknown", + [CTHENDRIX] = "Hendrix", + [CTSB0880] = "SB0880", }; static struct { @@ -1208,62 +1219,39 @@ static int atc_dev_free(struct snd_device *dev) static int __devinit atc_identify_card(struct ct_atc *atc) { - u16 subsys; - u8 revision; - struct pci_dev *pci = atc->pci; - const struct ct_atc_chip_details *d; - enum CTCARDS i; - - subsys = pci->subsystem_device; - revision = pci->revision; - atc->chip_details = NULL; - atc->model = NUM_CTCARDS; - for (d = atc_chip_details; d->vendor; d++) { - if (d->vendor != pci->vendor || d->device != pci->device) - continue; + const struct snd_pci_quirk *p; + const struct snd_pci_quirk *list; - if (NULL == d->sub_details) { - atc->chip_details = d; - break; - } - for (i = 0; i < NUM_CTCARDS; i++) { - if ((d->sub_details[i].subsys == subsys) || - (((subsys & 0x6000) == 0x6000) && - ((d->sub_details[i].subsys & 0x6000) == 0x6000))) { - atc->model = i; - break; - } - } - if (i >= NUM_CTCARDS) - continue; - - atc->chip_details = d; + switch (atc->chip_type) { + case ATC20K1: + atc->chip_name = "20K1"; + list = subsys_20k1_list; + break; + case ATC20K2: + atc->chip_name = "20K2"; + list = subsys_20k2_list; break; - /* not take revision into consideration now */ + default: + return -ENOENT; } - if (!d->vendor) + p = snd_pci_quirk_lookup(atc->pci, list); + if (!p) return -ENOENT; - + atc->model = p->value; + atc->model_name = ct_subsys_name[atc->model]; + snd_printd("ctxfi: chip %s model %s (%04x:%04x) is found\n", + atc->chip_name, atc->model_name, + atc->pci->subsystem_vendor, + atc->pci->subsystem_device); return 0; } int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc) { enum CTALSADEVS i; - struct hw *hw = atc->hw; int err; - switch (hw->get_chip_type(hw)) { - case ATC20K1: - alsa_dev_funcs[MIXER].public_name = "20K1"; - break; - case ATC20K2: - alsa_dev_funcs[MIXER].public_name = "20K2"; - break; - default: - alsa_dev_funcs[MIXER].public_name = "Unknown"; - break; - } + alsa_dev_funcs[MIXER].public_name = atc->chip_name; for (i = 0; i < NUM_CTALSADEVS; i++) { if (NULL == alsa_dev_funcs[i].create) @@ -1287,7 +1275,7 @@ static int __devinit atc_create_hw_devs(struct ct_atc *atc) struct card_conf info = {0}; int i, err; - err = create_hw_obj(atc->pci, &hw); + err = create_hw_obj(atc->pci, atc->chip_type, atc->model, &hw); if (err) { printk(KERN_ERR "Failed to create hw obj!!!\n"); return err; @@ -1328,7 +1316,6 @@ static int __devinit atc_get_resources(struct ct_atc *atc) struct sum_desc sum_dsc = {0}; struct sum_mgr *sum_mgr; int err, i; - unsigned short subsys_id; atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL); if (NULL == atc->daios) @@ -1359,13 +1346,10 @@ static int __devinit atc_get_resources(struct ct_atc *atc) } atc->n_daio++; } - subsys_id = atc->pci->subsystem_device; - if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { - /* SB073x cards */ + if (atc->model == CTSB073X) da_desc.type = SPDIFI1; - } else { + else da_desc.type = SPDIFIO; - } err = daio_mgr->get_daio(daio_mgr, &da_desc, (struct daio **)&atc->daios[i]); if (err) { @@ -1555,7 +1539,8 @@ static struct ct_atc atc_preset __devinitdata = { */ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, - unsigned int rsr, unsigned int msr, struct ct_atc **ratc) + unsigned int rsr, unsigned int msr, + int chip_type, struct ct_atc **ratc) { struct ct_atc *atc; static struct snd_device_ops ops = { @@ -1576,6 +1561,7 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, atc->pci = pci; atc->rsr = rsr; atc->msr = msr; + atc->chip_type = chip_type; spin_lock_init(&atc->atc_lock); diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index 04459aa0d4d..a03347232e8 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -37,15 +37,6 @@ enum CTALSADEVS { /* Types of alsa devices */ NUM_CTALSADEVS /* This should always be the last */ }; -enum CTCARDS { - CTSB0760, - CTHENDRIX, - CTSB08801, - CTSB08802, - CTSB08803, - NUM_CTCARDS /* This should always be the last */ -}; - struct ct_atc_chip_sub_details { u16 subsys; const char *nm_model; @@ -89,8 +80,10 @@ struct ct_atc { unsigned int msr; /* master sample rate in rsr */ unsigned int pll_rate; /* current rate of Phase Lock Loop */ - const struct ct_atc_chip_details *chip_details; - enum CTCARDS model; + int chip_type; + int model; + const char *chip_name; + const char *model_name; struct ct_vm *vm; /* device virtual memory manager for this card */ int (*map_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm); @@ -147,7 +140,7 @@ struct ct_atc { int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, - unsigned int rsr, unsigned int msr, + unsigned int rsr, unsigned int msr, int chip_type, struct ct_atc **ratc); int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc); diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index befead4eeaa..082e35c08c0 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -116,7 +116,7 @@ static struct rsc_ops daio_in_rsc_ops_20k2 = { static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw) { - switch (hw->get_chip_type(hw)) { + switch (hw->chip_type) { case ATC20K1: switch (type) { case SPDIFOO: return 0; @@ -343,7 +343,7 @@ static int daio_rsc_init(struct daio *daio, int err; unsigned int idx_l, idx_r; - switch (((struct hw *)hw)->get_chip_type(hw)) { + switch (((struct hw *)hw)->chip_type) { case ATC20K1: idx_l = idx_20k1[desc->type].left; idx_r = idx_20k1[desc->type].right; @@ -367,7 +367,7 @@ static int daio_rsc_init(struct daio *daio, if (desc->type <= DAIO_OUT_MAX) { daio->rscl.ops = daio->rscr.ops = &daio_out_rsc_ops; } else { - switch (((struct hw *)hw)->get_chip_type(hw)) { + switch (((struct hw *)hw)->chip_type) { case ATC20K1: daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k1; break; diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c index 5ec6813d391..8e64f4862e8 100644 --- a/sound/pci/ctxfi/cthardware.c +++ b/sound/pci/ctxfi/cthardware.c @@ -20,34 +20,16 @@ #include "cthw20k2.h" #include <linux/bug.h> -static enum CHIPTYP __devinitdata get_chip_type(struct hw *hw) -{ - enum CHIPTYP type; - - switch (hw->pci->device) { - case 0x0005: /* 20k1 device */ - type = ATC20K1; - break; - case 0x000B: /* 20k2 device */ - type = ATC20K2; - break; - default: - type = ATCNONE; - break; - } - - return type; -} - -int __devinit create_hw_obj(struct pci_dev *pci, struct hw **rhw) +int __devinit create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type, + enum CTCARDS model, struct hw **rhw) { int err; - switch (pci->device) { - case 0x0005: /* 20k1 device */ + switch (chip_type) { + case ATC20K1: err = create_20k1_hw_obj(rhw); break; - case 0x000B: /* 20k2 device */ + case ATC20K2: err = create_20k2_hw_obj(rhw); break; default: @@ -58,7 +40,8 @@ int __devinit create_hw_obj(struct pci_dev *pci, struct hw **rhw) return err; (*rhw)->pci = pci; - (*rhw)->get_chip_type = get_chip_type; + (*rhw)->chip_type = chip_type; + (*rhw)->model = model; return 0; } diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h index 8f11644ddc9..4a8e04f090a 100644 --- a/sound/pci/ctxfi/cthardware.h +++ b/sound/pci/ctxfi/cthardware.h @@ -27,6 +27,19 @@ enum CHIPTYP { ATCNONE }; +enum CTCARDS { + /* 20k1 models */ + CTSB055X, + CTSB073X, + CTUAA, + CT20K1_UNKNOWN, + /* 20k2 models */ + CTSB0760, + CTHENDRIX, + CTSB0880, + NUM_CTCARDS /* This should always be the last */ +}; + /* Type of input source for ADC */ enum ADCSRC{ ADC_MICIN, @@ -48,7 +61,6 @@ struct hw { int (*card_init)(struct hw *hw, struct card_conf *info); int (*card_stop)(struct hw *hw); int (*pll_init)(struct hw *hw, unsigned int rsr); - enum CHIPTYP (*get_chip_type)(struct hw *hw); int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source); int (*select_adc_source)(struct hw *hw, enum ADCSRC source); int (*have_digit_io_switch)(struct hw *hw); @@ -156,9 +168,13 @@ struct hw { int irq; unsigned long io_base; unsigned long mem_base; + + enum CHIPTYP chip_type; + enum CTCARDS model; }; -int create_hw_obj(struct pci_dev *pci, struct hw **rhw); +int create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type, + enum CTCARDS model, struct hw **rhw); int destroy_hw_obj(struct hw *hw); unsigned int get_field(unsigned int data, unsigned int field); diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 38b87b6ee6d..5d58650beb7 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1432,11 +1432,9 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) { u32 data; u16 gpioorg; - u16 subsys_id; unsigned int ret; - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); - if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { + if (hw->model == CTSB055X) { /* SB055x, unmute outputs */ gpioorg = (u16)hw_read_20kx(hw, GPIO); gpioorg &= 0xffbf; /* set GPIO6 to low */ @@ -1538,19 +1536,14 @@ static int is_adc_input_selected_hendrix(struct hw *hw, enum ADCSRC type) static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) { - u16 subsys_id; - - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); - if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { - /* SB055x cards */ + switch (hw->model) { + case CTSB055X: return is_adc_input_selected_SB055x(hw, type); - } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { - /* SB073x cards */ + case CTSB073X: return is_adc_input_selected_hendrix(hw, type); - } else if ((subsys_id & 0xf000) == 0x6000) { - /* Vista compatible cards */ + case CTHENDRIX: return is_adc_input_selected_hendrix(hw, type); - } else { + default: return is_adc_input_selected_SBx(hw, type); } } @@ -1692,20 +1685,17 @@ adc_input_select_hendrix(struct hw *hw, enum ADCSRC type, unsigned char boost) static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) { - u16 subsys_id; - - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); - if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { - /* SB055x cards */ - return adc_input_select_SB055x(hw, type, (ADC_MICIN == type)); - } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { - /* SB073x cards */ - return adc_input_select_hendrix(hw, type, (ADC_MICIN == type)); - } else if ((subsys_id & 0xf000) == 0x6000) { - /* Vista compatible cards */ - return adc_input_select_hendrix(hw, type, (ADC_MICIN == type)); - } else { - return adc_input_select_SBx(hw, type, (ADC_MICIN == type)); + int state = type == ADC_MICIN; + + switch (hw->model) { + case CTSB055X: + return adc_input_select_SB055x(hw, type, state); + case CTSB073X: + return adc_input_select_hendrix(hw, type, state); + case CTHENDRIX: + return adc_input_select_hendrix(hw, type, state); + default: + return adc_input_select_SBx(hw, type, state); } } @@ -1781,28 +1771,16 @@ static int adc_init_SBx(struct hw *hw, int input, int mic20db) static int hw_adc_init(struct hw *hw, const struct adc_conf *info) { - int err; - u16 subsys_id; - - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); - if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { - /* Sb055x card */ - err = adc_init_SB055x(hw, info->input, info->mic20db); - } else { - err = adc_init_SBx(hw, info->input, info->mic20db); - } - - return err; + if (hw->model == CTSB055X) + return adc_init_SB055x(hw, info->input, info->mic20db); + else + return adc_init_SBx(hw, info->input, info->mic20db); } static int hw_have_digit_io_switch(struct hw *hw) { - u16 subsys_id; - - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); /* SB073x and Vista compatible cards have no digit IO switch */ - return !((subsys_id == 0x0029) || (subsys_id == 0x0031) - || ((subsys_id & 0xf000) == 0x6000)); + return !(hw->model == CTSB073X || hw->model == CTHENDRIX); } #define CTLBITS(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) @@ -1918,7 +1896,6 @@ static int hw_card_start(struct hw *hw) { int err; struct pci_dev *pci = hw->pci; - u16 subsys_id; err = pci_enable_device(pci); if (err < 0) @@ -1939,8 +1916,7 @@ static int hw_card_start(struct hw *hw) goto error1; /* Switch to X-Fi mode from UAA mode if neeeded */ - pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsys_id); - if ((0x5 == pci->device) && (0x6000 == (subsys_id & 0x6000))) { + if (hw->model == CTHENDRIX) { err = uaa_to_xfi(pci); if (err) goto error2; @@ -2004,7 +1980,6 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) { int err; unsigned int gctl; - u16 subsys_id; u32 data; struct dac_conf dac_info = {0}; struct adc_conf adc_info = {0}; @@ -2044,19 +2019,20 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) hw_write_20kx(hw, SRCIP, 0); mdelay(30); - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); /* Detect the card ID and configure GPIO accordingly. */ - if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { - /* SB055x cards */ + switch (hw->model) { + case CTSB055X: hw_write_20kx(hw, GPIOCTL, 0x13fe); - } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { - /* SB073x cards */ + break; + case CTSB073X: hw_write_20kx(hw, GPIOCTL, 0x00e6); - } else if ((subsys_id & 0xf000) == 0x6000) { - /* Vista compatible cards */ + break; + case CTHENDRIX: /* Vista compatible cards */ hw_write_20kx(hw, GPIOCTL, 0x00c2); - } else { + break; + default: hw_write_20kx(hw, GPIOCTL, 0x01e6); + break; } trn_info.vm_pgt_phys = info->vm_pgt_phys; diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index 279dac6c34d..2d3dd89af15 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -15,6 +15,7 @@ #include <sound/core.h> #include <sound/initval.h> #include "ctatc.h" +#include "cthardware.h" MODULE_AUTHOR("Creative Technology Ltd"); MODULE_DESCRIPTION("X-Fi driver version 1.03"); @@ -41,8 +42,12 @@ MODULE_PARM_DESC(enable, "Enable Creative X-Fi driver"); static struct pci_device_id ct_pci_dev_ids[] = { /* only X-Fi is supported, so... */ - { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1) }, - { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K2) }, + { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1), + .driver_data = ATC20K1, + }, + { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K2), + .driver_data = ATC20K2, + }, { 0, } }; MODULE_DEVICE_TABLE(pci, ct_pci_dev_ids); @@ -79,7 +84,8 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) "1 and 2, Value 2 is assumed.\n"); multiple = 2; } - err = ct_atc_create(card, pci, reference_rate, multiple, &atc); + err = ct_atc_create(card, pci, reference_rate, multiple, + pci_id->driver_data, &atc); if (err < 0) goto error; @@ -92,7 +98,8 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) strcpy(card->driver, "SB-XFi"); strcpy(card->shortname, "Creative X-Fi"); - strcpy(card->longname, "Creative ALSA Driver X-Fi"); + snprintf(card->longname, sizeof(card->longname), "%s %s %s", + card->shortname, atc->chip_name, atc->model_name); err = snd_card_register(card); if (err < 0) |